Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Sanatan Dharma โ Sacred Texts & Divine Wisdom</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | |
| <link href="https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700;900&family=Cinzel:wght@400;600;700&family=Crimson+Pro:ital,wght@0,300;0,400;0,600;1,300;1,400&display=swap" rel="stylesheet" /> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" /> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" /> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> | |
| <style> | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| CSS VARIABLES & RESET | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| :root { | |
| --saffron: #FF6B00; | |
| --saffron-light: #FF9A3C; | |
| --saffron-glow: #FFB347; | |
| --gold: #D4A017; | |
| --gold-bright: #FFD700; | |
| --gold-pale: #F5E6A3; | |
| --crimson: #8B0000; | |
| --crimson-soft: #C0392B; | |
| --deep-maroon: #3D0000; | |
| --bg-dark: #0A0400; | |
| --bg-mid: #130800; | |
| --bg-panel: #1A0A00; | |
| --bg-card: #221005; | |
| --text-primary: #FFF5E0; | |
| --text-secondary:#D4B896; | |
| --text-muted: #8B6B4A; | |
| --border-gold: rgba(212,160,23,0.3); | |
| --border-saffron:rgba(255,107,0,0.4); | |
| --shadow-gold: 0 0 30px rgba(212,160,23,0.2); | |
| --shadow-saffron:0 0 40px rgba(255,107,0,0.3); | |
| --font-display: 'Cinzel Decorative', serif; | |
| --font-heading: 'Cinzel', serif; | |
| --font-body: 'Crimson Pro', serif; | |
| --transition: 0.35s cubic-bezier(0.4,0,0.2,1); | |
| } | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| html { scroll-behavior: smooth; } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PAGE LOADING OVERLAY | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #page-loader { | |
| position: fixed; | |
| inset: 0; | |
| z-index: 99999; | |
| background: radial-gradient(ellipse at center, #1A0A00 0%, #0A0400 70%); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 32px; | |
| transition: opacity 0.7s ease, visibility 0.7s ease; | |
| } | |
| #page-loader.hidden { | |
| opacity: 0; | |
| visibility: hidden; | |
| pointer-events: none; | |
| } | |
| .loader-om { | |
| font-size: 64px; | |
| animation: loaderOmPulse 1.6s ease-in-out infinite; | |
| filter: drop-shadow(0 0 24px #FF6B00); | |
| line-height: 1; | |
| } | |
| @keyframes loaderOmPulse { | |
| 0%,100% { transform: scale(1); filter: drop-shadow(0 0 16px #FF6B00); } | |
| 50% { transform: scale(1.15); filter: drop-shadow(0 0 36px #FFB347); } | |
| } | |
| .loader-title { | |
| font-family: 'Cinzel Decorative', serif; | |
| font-size: clamp(13px, 2.5vw, 18px); | |
| font-weight: 700; | |
| letter-spacing: 0.25em; | |
| color: #D4A017; | |
| text-transform: uppercase; | |
| text-align: center; | |
| padding: 0 20px; | |
| } | |
| .loader-spinner-ring { | |
| position: relative; | |
| width: 72px; | |
| height: 72px; | |
| } | |
| .loader-spinner-ring::before, | |
| .loader-spinner-ring::after { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| border-radius: 50%; | |
| border: 3px solid transparent; | |
| } | |
| .loader-spinner-ring::before { | |
| border-top-color: #FF6B00; | |
| border-right-color: #FF9A3C; | |
| animation: spinOuter 1s linear infinite; | |
| } | |
| .loader-spinner-ring::after { | |
| inset: 10px; | |
| border-top-color: #D4A017; | |
| border-left-color: #FFD700; | |
| animation: spinInner 0.75s linear infinite reverse; | |
| } | |
| @keyframes spinOuter { to { transform: rotate(360deg); } } | |
| @keyframes spinInner { to { transform: rotate(360deg); } } | |
| .loader-progress-wrap { | |
| width: min(340px, 80vw); | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| align-items: center; | |
| } | |
| .loader-progress-track { | |
| width: 100%; | |
| height: 5px; | |
| background: rgba(212,160,23,0.15); | |
| border-radius: 99px; | |
| overflow: hidden; | |
| border: 1px solid rgba(212,160,23,0.2); | |
| } | |
| .loader-progress-bar { | |
| height: 100%; | |
| width: 0%; | |
| background: linear-gradient(90deg, #FF6B00, #FFD700, #FF9A3C); | |
| border-radius: 99px; | |
| transition: width 0.3s ease; | |
| box-shadow: 0 0 10px rgba(255,107,0,0.6); | |
| } | |
| .loader-status { | |
| font-family: 'Cinzel', serif; | |
| font-size: 11px; | |
| letter-spacing: 0.18em; | |
| color: #8B6B4A; | |
| text-transform: uppercase; | |
| min-height: 16px; | |
| transition: color 0.3s ease; | |
| } | |
| .loader-dots::after { | |
| content: ''; | |
| animation: dotsCycle 1.2s steps(4, end) infinite; | |
| } | |
| @keyframes dotsCycle { | |
| 0% { content: ''; } | |
| 25% { content: '.'; } | |
| 50% { content: '..'; } | |
| 75% { content: '...'; } | |
| 100% { content: ''; } | |
| } | |
| body { | |
| background: var(--bg-dark); | |
| color: var(--text-primary); | |
| font-family: var(--font-body); | |
| font-size: 18px; | |
| line-height: 1.7; | |
| overflow-x: hidden; | |
| cursor: default; | |
| } | |
| /* Custom scrollbar */ | |
| ::-webkit-scrollbar { width: 6px; } | |
| ::-webkit-scrollbar-track { background: var(--bg-dark); } | |
| ::-webkit-scrollbar-thumb { background: var(--gold); border-radius: 3px; } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DAILY SHLOKA SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .shloka-outer { max-width: 860px; margin: 0 auto; } | |
| .shloka-card { | |
| background: linear-gradient(145deg, var(--bg-panel), var(--bg-card)); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 10px; | |
| padding: 44px 48px 36px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .shloka-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: 0; right: 0; height: 3px; | |
| background: linear-gradient(90deg, var(--saffron), var(--gold-bright), var(--saffron)); | |
| } | |
| .shloka-day-badge { | |
| display: inline-block; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 600; | |
| letter-spacing: 0.3em; | |
| text-transform: uppercase; | |
| color: var(--saffron-light); | |
| background: rgba(255,107,0,0.12); | |
| border: 1px solid rgba(255,107,0,0.3); | |
| border-radius: 20px; | |
| padding: 4px 14px; | |
| margin-bottom: 10px; | |
| } | |
| .shloka-source-tag { | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| color: var(--gold); | |
| margin-bottom: 22px; | |
| } | |
| .shloka-sanskrit { | |
| font-family: var(--font-display); | |
| font-size: clamp(17px, 2.5vw, 22px); | |
| color: var(--gold-pale); | |
| line-height: 1.9; | |
| margin-bottom: 14px; | |
| text-align: center; | |
| text-shadow: 0 0 20px rgba(212,160,23,0.25); | |
| } | |
| .shloka-transliteration { | |
| font-family: var(--font-body); | |
| font-size: 15px; | |
| font-style: italic; | |
| color: var(--text-secondary); | |
| text-align: center; | |
| margin-bottom: 18px; | |
| line-height: 1.7; | |
| } | |
| .shloka-translation { | |
| font-family: var(--font-body); | |
| font-size: 17px; | |
| color: var(--text-primary); | |
| text-align: center; | |
| line-height: 1.8; | |
| padding: 16px 0; | |
| border-top: 1px solid var(--border-gold); | |
| border-bottom: 1px solid var(--border-gold); | |
| margin-bottom: 28px; | |
| } | |
| .shloka-ai-panel { | |
| background: rgba(255,107,0,0.04); | |
| border: 1px solid rgba(255,107,0,0.2); | |
| border-radius: 7px; | |
| overflow: hidden; | |
| } | |
| .shloka-ai-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| padding: 12px 18px; | |
| background: rgba(255,107,0,0.08); | |
| border-bottom: 1px solid rgba(255,107,0,0.15); | |
| font-family: var(--font-heading); | |
| font-size: 12px; | |
| font-weight: 600; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--saffron-light); | |
| } | |
| .shloka-ai-badge { | |
| margin-left: auto; | |
| font-size: 9px; | |
| letter-spacing: 0.1em; | |
| background: rgba(255,107,0,0.2); | |
| border: 1px solid rgba(255,107,0,0.3); | |
| border-radius: 3px; | |
| padding: 2px 8px; | |
| color: var(--saffron); | |
| } | |
| .shloka-ai-body { padding: 18px 20px 0; min-height: 60px; } | |
| .shloka-ai-placeholder { color: var(--text-muted); font-style: italic; font-size: 15px; padding-bottom: 4px; } | |
| .shloka-ai-content { color: var(--text-primary); font-size: 16px; line-height: 1.8; padding-bottom: 4px; } | |
| .shloka-ai-content h4 { font-family: var(--font-heading); color: var(--gold); font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; margin: 14px 0 5px; } | |
| .shloka-ai-loading { | |
| display: flex; align-items: center; gap: 12px; | |
| color: var(--saffron-light); font-family: var(--font-heading); | |
| font-size: 12px; letter-spacing: 0.15em; text-transform: uppercase; | |
| padding-bottom: 4px; | |
| } | |
| .shloka-loader-ring { | |
| width: 20px; height: 20px; border-radius: 50%; | |
| border: 2px solid transparent; | |
| border-top-color: var(--saffron); | |
| border-right-color: var(--gold); | |
| animation: spinOuter 0.8s linear infinite; | |
| flex-shrink: 0; | |
| } | |
| .shloka-explain-btn { | |
| display: block; width: 100%; | |
| padding: 13px; | |
| background: linear-gradient(135deg, rgba(255,107,0,0.15), rgba(212,160,23,0.15)); | |
| border: none; border-top: 1px solid rgba(255,107,0,0.2); | |
| color: var(--saffron-light); | |
| font-family: var(--font-heading); font-size: 12px; | |
| font-weight: 700; letter-spacing: 0.25em; text-transform: uppercase; | |
| cursor: pointer; transition: all 0.3s; | |
| } | |
| .shloka-explain-btn:hover { background: linear-gradient(135deg, rgba(255,107,0,0.3), rgba(212,160,23,0.2)); color: var(--gold-bright); } | |
| .shloka-explain-btn:disabled { opacity: 0.5; cursor: not-allowed; } | |
| .shloka-nav { | |
| display: flex; align-items: center; justify-content: center; gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .shloka-nav-btn { | |
| width: 40px; height: 40px; border-radius: 50%; | |
| background: rgba(212,160,23,0.1); border: 1px solid var(--border-gold); | |
| color: var(--gold); font-size: 22px; cursor: pointer; | |
| transition: all 0.3s; display: flex; align-items: center; justify-content: center; | |
| } | |
| .shloka-nav-btn:hover { background: rgba(212,160,23,0.25); border-color: var(--gold); } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PILGRIMAGE MAP SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .map-outer { | |
| max-width: 1200px; margin: 0 auto; | |
| position: relative; | |
| background: linear-gradient(145deg, var(--bg-panel), var(--bg-card)); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 10px; | |
| overflow: hidden; | |
| padding: 20px; | |
| } | |
| .map-legend { | |
| display: flex; flex-wrap: wrap; gap: 14px 24px; | |
| margin-bottom: 16px; | |
| padding: 0 8px; | |
| } | |
| .map-legend-item { | |
| display: flex; align-items: center; gap: 8px; | |
| font-family: var(--font-heading); font-size: 11px; | |
| letter-spacing: 0.15em; text-transform: uppercase; | |
| color: var(--text-secondary); | |
| } | |
| .legend-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } | |
| #india-map-container { | |
| width: 100%; height: 580px; | |
| border-radius: 7px; | |
| overflow: hidden; | |
| border: 1px solid var(--border-gold); | |
| } | |
| #india-map-container .leaflet-container { background: #0A0400 ; } | |
| .site-info-panel { | |
| position: absolute; top: 70px; right: 30px; | |
| width: 280px; | |
| background: linear-gradient(145deg, var(--bg-panel), var(--bg-card)); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 8px; | |
| padding: 20px; | |
| z-index: 1000; | |
| opacity: 0; visibility: hidden; transform: translateX(20px); | |
| transition: all 0.3s ease; | |
| box-shadow: 0 8px 30px rgba(0,0,0,0.5); | |
| } | |
| .site-info-panel.open { opacity: 1; visibility: visible; transform: translateX(0); } | |
| .site-info-close { | |
| position: absolute; top: 10px; right: 10px; | |
| background: none; border: none; | |
| color: var(--text-muted); font-size: 13px; cursor: pointer; | |
| } | |
| .site-info-close:hover { color: var(--saffron); } | |
| .site-info-icon { font-size: 36px; margin-bottom: 8px; } | |
| .site-info-name { | |
| font-family: var(--font-display); font-size: 16px; color: var(--gold-bright); | |
| margin-bottom: 4px; line-height: 1.3; | |
| } | |
| .site-info-type { | |
| font-family: var(--font-heading); font-size: 10px; letter-spacing: 0.2em; | |
| text-transform: uppercase; color: var(--saffron); margin-bottom: 4px; | |
| } | |
| .site-info-state { | |
| font-family: var(--font-heading); font-size: 10px; letter-spacing: 0.15em; | |
| text-transform: uppercase; color: var(--text-muted); margin-bottom: 10px; | |
| } | |
| .site-info-desc { | |
| font-family: var(--font-body); font-size: 14px; color: var(--text-secondary); | |
| line-height: 1.6; | |
| border-top: 1px solid var(--border-gold); padding-top: 10px; margin: 0; | |
| } | |
| @media (max-width: 700px) { | |
| .site-info-panel { position: static; opacity: 1; visibility: visible; transform: none; width: 100%; margin-top: 12px; } | |
| .site-info-panel:not(.open) { display: none; } | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| HISTORY TIMELINE SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .timeline-scroll-hint { | |
| text-align: center; | |
| font-family: var(--font-heading); font-size: 10px; | |
| letter-spacing: 0.3em; text-transform: uppercase; | |
| color: var(--text-muted); margin-bottom: 20px; | |
| } | |
| .timeline-outer { | |
| overflow-x: auto; | |
| overflow-y: visible; | |
| padding: 40px 60px 20px; | |
| cursor: grab; | |
| -webkit-overflow-scrolling: touch; | |
| scrollbar-width: none; | |
| } | |
| .timeline-outer::-webkit-scrollbar { display: none; } | |
| .timeline-outer:active { cursor: grabbing; } | |
| .timeline-track { | |
| display: flex; | |
| align-items: center; | |
| position: relative; | |
| min-width: max-content; | |
| padding: 355px 0; | |
| } | |
| /* Central axis line */ | |
| .timeline-track::before { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; left: 0; right: 0; height: 2px; | |
| background: linear-gradient(90deg, transparent, var(--saffron) 5%, var(--gold) 50%, var(--saffron) 95%, transparent); | |
| transform: translateY(-50%); | |
| } | |
| .tl-item { | |
| position: relative; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| width: 180px; | |
| flex-shrink: 0; | |
| } | |
| .tl-item.above .tl-card { bottom: 100%; margin-bottom: 20px; top: auto; } | |
| .tl-item.below .tl-card { top: 100%; margin-top: 20px; bottom: auto; } | |
| .tl-dot { | |
| width: 14px; height: 14px; border-radius: 50%; | |
| border: 2px solid var(--gold); | |
| background: var(--bg-dark); | |
| box-shadow: 0 0 12px rgba(212,160,23,0.6); | |
| z-index: 2; flex-shrink: 0; | |
| transition: transform 0.3s, box-shadow 0.3s; | |
| } | |
| .tl-item:hover .tl-dot { | |
| transform: scale(1.6); | |
| box-shadow: 0 0 20px rgba(255,107,0,0.8); | |
| } | |
| .tl-connector { | |
| width: 2px; | |
| background: linear-gradient(180deg, var(--gold), transparent); | |
| } | |
| .tl-item.above .tl-connector { height: 40px; background: linear-gradient(0deg, var(--gold), transparent); } | |
| .tl-item.below .tl-connector { height: 40px; background: linear-gradient(180deg, var(--gold), transparent); } | |
| .tl-card { | |
| position: absolute; | |
| width: 160px; | |
| background: linear-gradient(145deg, var(--bg-panel), var(--bg-card)); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 6px; | |
| padding: 12px 14px; | |
| transition: all 0.3s; | |
| } | |
| .tl-item:hover .tl-card { | |
| border-color: var(--saffron); | |
| box-shadow: 0 4px 20px rgba(255,107,0,0.2); | |
| z-index: 5; | |
| } | |
| .tl-year { | |
| font-family: var(--font-heading); font-size: 11px; font-weight: 700; | |
| letter-spacing: 0.15em; color: var(--saffron-light); | |
| margin-bottom: 4px; | |
| } | |
| .tl-era { | |
| font-family: var(--font-heading); font-size: 9px; letter-spacing: 0.2em; | |
| text-transform: uppercase; color: var(--text-muted); margin-bottom: 5px; | |
| } | |
| .tl-title { | |
| font-family: var(--font-display); font-size: 12px; color: var(--gold-pale); | |
| line-height: 1.3; margin-bottom: 5px; | |
| } | |
| .tl-text { | |
| font-family: var(--font-body); font-size: 12px; color: var(--text-secondary); | |
| line-height: 1.5; | |
| } | |
| .tl-icon { font-size: 18px; margin-bottom: 4px; } | |
| .timeline-scroll-bar { | |
| max-width: 600px; margin: 10px auto 0; | |
| height: 3px; background: rgba(212,160,23,0.12); | |
| border-radius: 99px; position: relative; cursor: pointer; | |
| } | |
| .timeline-scroll-thumb { | |
| height: 100%; width: 30%; background: linear-gradient(90deg, var(--saffron), var(--gold)); | |
| border-radius: 99px; transition: width 0.1s; | |
| box-shadow: 0 0 8px rgba(255,107,0,0.5); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| CANVAS BACKGROUND | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #cosmos-canvas { | |
| position: fixed; | |
| top: 0; left: 0; | |
| width: 100%; height: 100%; | |
| pointer-events: none; | |
| z-index: 0; | |
| opacity: 0.6; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FLOATING PARTICLES | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .particle { | |
| position: fixed; | |
| pointer-events: none; | |
| z-index: 1; | |
| border-radius: 50%; | |
| animation: floatParticle linear infinite; | |
| } | |
| @keyframes floatParticle { | |
| 0% { transform: translateY(100vh) rotate(0deg); opacity: 0; } | |
| 10% { opacity: 1; } | |
| 90% { opacity: 0.6; } | |
| 100% { transform: translateY(-10vh) rotate(720deg); opacity: 0; } | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| NAVIGATION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| nav { | |
| position: fixed; | |
| top: 0; left: 0; right: 0; | |
| z-index: 1000; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 24px; | |
| height: 72px; | |
| background: linear-gradient(180deg, rgba(10,4,0,0.98) 0%, rgba(10,4,0,0.85) 100%); | |
| border-bottom: 1px solid var(--border-gold); | |
| backdrop-filter: blur(20px); | |
| } | |
| .nav-brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| text-decoration: none; | |
| } | |
| .nav-om { | |
| font-size: 28px; | |
| color: var(--saffron); | |
| animation: omPulse 3s ease-in-out infinite; | |
| display: inline-block; | |
| filter: drop-shadow(0 0 8px var(--saffron)); | |
| } | |
| @keyframes omPulse { | |
| 0%,100% { transform: scale(1); filter: drop-shadow(0 0 8px var(--saffron)); } | |
| 50% { transform: scale(1.1); filter: drop-shadow(0 0 16px var(--saffron-glow)); } | |
| } | |
| .nav-title { | |
| font-family: var(--font-heading); | |
| font-size: 16px; | |
| font-weight: 700; | |
| color: var(--gold-bright); | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| } | |
| .nav-links { | |
| display: flex; | |
| gap: 18px; | |
| list-style: none; | |
| } | |
| .nav-links a { | |
| font-family: var(--font-heading); | |
| font-size: 9.5px; | |
| font-weight: 600; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--text-secondary); | |
| text-decoration: none; | |
| transition: color var(--transition); | |
| position: relative; | |
| padding-bottom: 4px; | |
| white-space: nowrap; | |
| } | |
| .nav-links a::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: 0; left: 0; | |
| width: 0; height: 1px; | |
| background: var(--saffron); | |
| transition: width var(--transition); | |
| } | |
| .nav-links a:hover { color: var(--saffron-light); } | |
| .nav-links a:hover::after { width: 100%; } | |
| .nav-chat-btn { | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark) ; | |
| padding: 8px 20px ; | |
| border-radius: 4px; | |
| font-weight: 700 ; | |
| } | |
| .nav-chat-btn::after { display: none ; } | |
| .nav-chat-btn:hover { opacity: 0.9; color: var(--bg-dark) ; } | |
| /* Mobile nav toggle */ | |
| .nav-toggle { | |
| display: none; | |
| background: none; | |
| border: none; | |
| color: var(--gold); | |
| font-size: 22px; | |
| cursor: pointer; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| HERO SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #hero { | |
| position: relative; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| text-align: center; | |
| padding: 100px 20px 60px; | |
| z-index: 2; | |
| overflow: hidden; | |
| } | |
| /* Mandala background */ | |
| .mandala-bg { | |
| position: absolute; | |
| top: 50%; left: 50%; | |
| transform: translate(-50%, -50%); | |
| width: min(900px, 130vw); | |
| height: min(900px, 130vw); | |
| opacity: 0.06; | |
| animation: rotateMandala 120s linear infinite; | |
| pointer-events: none; | |
| } | |
| .mandala-bg-2 { | |
| width: min(600px, 90vw); | |
| height: min(600px, 90vw); | |
| opacity: 0.04; | |
| animation: rotateMandala 80s linear infinite reverse; | |
| } | |
| @keyframes rotateMandala { | |
| from { transform: translate(-50%, -50%) rotate(0deg); } | |
| to { transform: translate(-50%, -50%) rotate(360deg); } | |
| } | |
| @keyframes lotusBloom { | |
| 0%,100% { transform: scale(1) rotate(-3deg); filter: drop-shadow(0 0 20px var(--saffron)); } | |
| 50% { transform: scale(1.08) rotate(3deg); filter: drop-shadow(0 0 40px var(--saffron-glow)); } | |
| } | |
| .hero-om { | |
| font-family: var(--font-display); | |
| font-size: clamp(60px, 12vw, 120px); | |
| font-weight: 900; | |
| background: linear-gradient(135deg, var(--saffron) 0%, var(--gold-bright) 40%, var(--saffron-light) 70%, var(--gold) 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| line-height: 1.5; | |
| margin-bottom: 16px; | |
| animation: heroGlow 4s ease-in-out infinite; | |
| filter: drop-shadow(0 0 2px rgba(255,107,0,0.3)); | |
| } | |
| @keyframes heroGlow { | |
| 0%,100% { filter: drop-shadow(0 0 2px rgba(255,107,0,0.3)); } | |
| 50% { filter: drop-shadow(0 0 20px rgba(255,107,0,0.6)); } | |
| } | |
| .hero-title { | |
| font-family: var(--font-display); | |
| font-size: clamp(20px, 4vw, 42px); | |
| font-weight: 400; | |
| color: var(--gold-pale); | |
| letter-spacing: 0.12em; | |
| margin-bottom: 12px; | |
| text-shadow: 0 0 30px rgba(212,160,23,0.4); | |
| } | |
| .hero-subtitle { | |
| font-family: var(--font-heading); | |
| font-size: clamp(11px, 2vw, 14px); | |
| font-weight: 600; | |
| letter-spacing: 0.4em; | |
| text-transform: uppercase; | |
| color: var(--saffron-light); | |
| margin-bottom: 28px; | |
| } | |
| .hero-divider { | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| justify-content: center; | |
| margin-bottom: 28px; | |
| } | |
| .hero-divider-line { | |
| width: clamp(60px, 12vw, 140px); | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, var(--gold), transparent); | |
| } | |
| .hero-divider-sym { | |
| color: var(--saffron); | |
| font-size: 20px; | |
| } | |
| .hero-desc { | |
| max-width: 680px; | |
| font-size: clamp(16px, 2.5vw, 20px); | |
| color: var(--text-secondary); | |
| line-height: 1.8; | |
| margin-bottom: 48px; | |
| font-style: italic; | |
| } | |
| .hero-cta-group { | |
| display: flex; | |
| gap: 16px; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, var(--saffron) 0%, var(--gold) 100%); | |
| color: var(--bg-dark); | |
| padding: 14px 36px; | |
| border-radius: 3px; | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| font-weight: 700; | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| text-decoration: none; | |
| border: none; | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .btn-primary::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: -100%; | |
| width: 100%; height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); | |
| transition: left 0.5s; | |
| } | |
| .btn-primary:hover::before { left: 100%; } | |
| .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(255,107,0,0.5); } | |
| .btn-secondary { | |
| background: transparent; | |
| color: var(--gold-bright); | |
| padding: 13px 36px; | |
| border-radius: 3px; | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| font-weight: 600; | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| text-decoration: none; | |
| border: 1px solid var(--gold); | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| } | |
| .btn-secondary:hover { | |
| background: rgba(212,160,23,0.12); | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 24px rgba(212,160,23,0.2); | |
| } | |
| /* Scroll indicator */ | |
| .scroll-hint { | |
| position: absolute; | |
| bottom: 32px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 8px; | |
| color: var(--text-muted); | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| letter-spacing: 0.3em; | |
| text-transform: uppercase; | |
| animation: scrollBounce 2s ease-in-out infinite; | |
| } | |
| @keyframes scrollBounce { | |
| 0%,100% { transform: translateX(-50%) translateY(0); opacity: 0.5; } | |
| 50% { transform: translateX(-50%) translateY(8px); opacity: 1; } | |
| } | |
| .scroll-arrow { | |
| width: 20px; height: 20px; | |
| border-right: 1px solid var(--saffron); | |
| border-bottom: 1px solid var(--saffron); | |
| transform: rotate(45deg); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| SECTION COMMON | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| section { | |
| position: relative; | |
| z-index: 2; | |
| padding: 100px 40px; | |
| } | |
| .section-header { | |
| text-align: center; | |
| margin-bottom: 70px; | |
| } | |
| .section-label { | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| font-weight: 600; | |
| letter-spacing: 0.5em; | |
| text-transform: uppercase; | |
| color: var(--saffron); | |
| margin-bottom: 16px; | |
| display: block; | |
| } | |
| .section-title { | |
| font-family: var(--font-display); | |
| font-size: clamp(24px, 4vw, 42px); | |
| font-weight: 700; | |
| color: var(--gold-bright); | |
| margin-bottom: 16px; | |
| line-height: 1.2; | |
| } | |
| .section-underline { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 12px; | |
| margin-top: 20px; | |
| } | |
| .underline-line { | |
| width: 80px; height: 1px; | |
| background: linear-gradient(90deg, transparent, var(--saffron)); | |
| } | |
| .underline-line.right { background: linear-gradient(90deg, var(--saffron), transparent); } | |
| .underline-dot { | |
| width: 6px; height: 6px; | |
| background: var(--saffron); | |
| border-radius: 50%; | |
| box-shadow: 0 0 10px var(--saffron); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| STATS BAR | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #stats { | |
| padding: 0; | |
| background: linear-gradient(90deg, var(--bg-mid), rgba(255,107,0,0.06), var(--bg-mid)); | |
| border-top: 1px solid var(--border-gold); | |
| border-bottom: 1px solid var(--border-gold); | |
| } | |
| .stats-inner { | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| display: flex; | |
| justify-content: space-around; | |
| flex-wrap: wrap; | |
| padding: 36px 20px; | |
| gap: 24px; | |
| } | |
| .stat-item { | |
| text-align: center; | |
| flex: 1; | |
| min-width: 120px; | |
| } | |
| .stat-num { | |
| font-family: var(--font-display); | |
| font-size: clamp(26px, 4vw, 38px); | |
| font-weight: 900; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold-bright)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| display: block; | |
| line-height: 1; | |
| margin-bottom: 6px; | |
| } | |
| .stat-label { | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 600; | |
| letter-spacing: 0.3em; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| SACRED TEXTS โ PDF BOOKS SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #texts { | |
| background: radial-gradient(ellipse at 50% 0%, rgba(255,107,0,0.05) 0%, transparent 60%); | |
| } | |
| .books-grid { | |
| max-width: 1300px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); | |
| gap: 28px; | |
| } | |
| .book-card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 6px; | |
| overflow: hidden; | |
| transition: all var(--transition); | |
| position: relative; | |
| cursor: pointer; | |
| } | |
| .book-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: 0; right: 0; | |
| height: 3px; | |
| background: linear-gradient(90deg, var(--saffron), var(--gold-bright), var(--saffron)); | |
| opacity: 0; | |
| transition: opacity var(--transition); | |
| } | |
| .book-card:hover { transform: translateY(-6px); box-shadow: 0 20px 50px rgba(255,107,0,0.2), var(--shadow-gold); border-color: var(--saffron); } | |
| .book-card:hover::before { opacity: 1; } | |
| .book-cover { | |
| position: relative; | |
| height: 200px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| overflow: hidden; | |
| } | |
| .book-cover-art { | |
| position: absolute; | |
| inset: 0; | |
| opacity: 0.15; | |
| background-size: cover; | |
| background-position: center; | |
| filter: sepia(1) hue-rotate(15deg); | |
| transition: opacity var(--transition), transform var(--transition); | |
| } | |
| .book-card:hover .book-cover-art { opacity: 0.25; transform: scale(1.05); } | |
| .book-symbol { | |
| font-size: 52px; | |
| position: relative; | |
| z-index: 1; | |
| filter: drop-shadow(0 0 12px var(--saffron)); | |
| margin-bottom: 8px; | |
| } | |
| .book-category { | |
| font-family: var(--font-heading); | |
| font-size: 9px; | |
| font-weight: 700; | |
| letter-spacing: 0.4em; | |
| text-transform: uppercase; | |
| color: var(--saffron-light); | |
| position: relative; | |
| z-index: 1; | |
| background: rgba(10,4,0,0.6); | |
| padding: 3px 10px; | |
| border-radius: 2px; | |
| border: 1px solid rgba(255,107,0,0.3); | |
| } | |
| .book-info { | |
| padding: 22px 22px 18px; | |
| } | |
| .book-title { | |
| font-family: var(--font-heading); | |
| font-size: 16px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| margin-bottom: 8px; | |
| line-height: 1.3; | |
| } | |
| .book-desc { | |
| font-size: 13px; | |
| color: var(--text-muted); | |
| line-height: 1.6; | |
| margin-bottom: 16px; | |
| } | |
| .book-actions { | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .btn-read, .btn-download { | |
| flex: 1; | |
| padding: 9px 12px; | |
| border-radius: 3px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| border: none; | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 6px; | |
| text-decoration: none; | |
| } | |
| .btn-read { | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); | |
| } | |
| .btn-read:hover { opacity: 0.85; transform: translateY(-1px); } | |
| .btn-download { | |
| background: transparent; | |
| color: var(--gold); | |
| border: 1px solid var(--border-gold); | |
| } | |
| .btn-download:hover { background: rgba(212,160,23,0.1); border-color: var(--gold); } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PDF VIEWER MODAL | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #pdf-modal { | |
| display: none; | |
| position: fixed; | |
| inset: 0; | |
| z-index: 9000; | |
| background: rgba(5,2,0,0.98); | |
| backdrop-filter: blur(12px); | |
| flex-direction: column; | |
| animation: fadeIn 0.3s ease; | |
| height: 100vh; | |
| width: 100vw; | |
| } | |
| #pdf-modal.open { display: flex; } | |
| @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } | |
| .pdf-modal-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 16px 28px; | |
| border-bottom: 1px solid var(--border-gold); | |
| background: var(--bg-panel); | |
| flex-shrink: 0; | |
| } | |
| .pdf-modal-title { | |
| font-family: var(--font-heading); | |
| font-size: 15px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| letter-spacing: 0.1em; | |
| } | |
| .pdf-modal-controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| } | |
| .pdf-ctrl-btn { | |
| background: rgba(212,160,23,0.1); | |
| border: 1px solid var(--border-gold); | |
| color: var(--gold); | |
| width: 85px; height: 34px; | |
| border-radius: 3px; | |
| cursor: pointer; | |
| font-size: 15px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: all var(--transition); | |
| } | |
| .pdf-ctrl-btn:hover { background: rgba(212,160,23,0.2); } | |
| .pdf-page-info { | |
| font-family: var(--font-heading); | |
| font-size: 12px; | |
| color: var(--text-secondary); | |
| min-width: 80px; | |
| text-align: center; | |
| } | |
| .pdf-close-btn { | |
| background: rgba(139,0,0,0.3); | |
| border: 1px solid rgba(139,0,0,0.5); | |
| color: #ff6b6b; | |
| width: 36px; height: 36px; | |
| border-radius: 3px; | |
| cursor: pointer; | |
| font-size: 18px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: all var(--transition); | |
| flex-shrink: 0; | |
| } | |
| .pdf-close-btn:hover { background: rgba(139,0,0,0.6); } | |
| .pdf-viewer-body { | |
| flex: 1; | |
| overflow: hidden; | |
| display: flex; | |
| flex-direction: column; | |
| padding: 0; | |
| background: var(--bg-dark); | |
| min-height: 0; | |
| } | |
| #pdf-canvas-container { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| width: 100%; | |
| min-height: 0; | |
| overflow: hidden; | |
| } | |
| #pdf-iframe { | |
| width: 100%; | |
| flex: 1; | |
| min-height: 75vh; | |
| border: none; | |
| background: #fff; | |
| display: none; | |
| } | |
| #pdf-canvas { | |
| box-shadow: 0 0 60px rgba(0,0,0,0.8); | |
| max-width: 100%; | |
| height: auto; | |
| } | |
| .pdf-loading { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 16px; | |
| color: var(--text-secondary); | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| letter-spacing: 0.2em; | |
| padding: 60px; | |
| } | |
| .pdf-spinner { | |
| width: 48px; height: 48px; | |
| border: 2px solid rgba(212,160,23,0.2); | |
| border-top-color: var(--gold); | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DEITIES SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #deities { | |
| background: linear-gradient(180deg, transparent, rgba(139,0,0,0.04), transparent); | |
| } | |
| .deities-grid { | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); | |
| gap: 24px; | |
| } | |
| .deity-card { | |
| text-align: center; | |
| padding: 32px 16px 24px; | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 6px; | |
| transition: all var(--transition); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .deity-card::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: 0; left: 50%; | |
| transform: translateX(-50%); | |
| width: 0; height: 2px; | |
| background: linear-gradient(90deg, var(--saffron), var(--gold-bright)); | |
| transition: width var(--transition); | |
| } | |
| .deity-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-gold); } | |
| .deity-card:hover::after { width: 80%; } | |
| .deity-icon { | |
| font-size: 44px; | |
| display: block; | |
| margin-bottom: 12px; | |
| filter: drop-shadow(0 0 8px rgba(255,107,0,0.5)); | |
| animation: deityFloat 4s ease-in-out infinite; | |
| } | |
| .deity-card:nth-child(2) .deity-icon { animation-delay: -1s; } | |
| .deity-card:nth-child(3) .deity-icon { animation-delay: -2s; } | |
| .deity-card:nth-child(4) .deity-icon { animation-delay: -1.5s; } | |
| .deity-card:nth-child(5) .deity-icon { animation-delay: -0.5s; } | |
| .deity-card:nth-child(6) .deity-icon { animation-delay: -2.5s; } | |
| @keyframes deityFloat { | |
| 0%,100% { transform: translateY(0); } | |
| 50% { transform: translateY(-6px); } | |
| } | |
| .deity-name { | |
| font-family: var(--font-heading); | |
| font-size: 14px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| margin-bottom: 6px; | |
| letter-spacing: 0.05em; | |
| } | |
| .deity-epithet { | |
| font-size: 12px; | |
| color: var(--text-muted); | |
| font-style: italic; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| CHATBOT SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #chatbot-section { | |
| background: radial-gradient(ellipse at 50% 50%, rgba(212,160,23,0.04) 0%, transparent 70%); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| AI GURU CHATBOT โ FAB + FLOATING PANEL | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| /* FAB button */ | |
| #guru-fab { | |
| position: fixed; bottom: 100px; right: 40px; z-index: 600; | |
| width: 50px; height: 50px; border-radius: 50%; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| border: none; cursor: pointer; | |
| display: flex; align-items: center; justify-content: center; | |
| box-shadow: 0 4px 28px rgba(255,107,0,0.55); | |
| transition: transform .25s var(--transition), box-shadow .25s; | |
| animation: guruFabPop .6s 1.2s cubic-bezier(0.4,0,0.2,1) both; | |
| font-size: 26px; color: var(--bg-dark); | |
| } | |
| @keyframes guruFabPop { from { opacity:0; transform:scale(.6) } to { opacity:1; transform:scale(1) } } | |
| #guru-fab:hover { transform: scale(1.08); box-shadow: 0 8px 36px rgba(255,107,0,0.7); } | |
| #guru-fab .fab-open { display: block; } | |
| #guru-fab .fab-close { display: none; } | |
| #guru-fab.open .fab-open { display: none; } | |
| #guru-fab.open .fab-close { display: block; } | |
| .guru-fab-pulse { | |
| position: absolute; inset: -5px; border-radius: 50%; | |
| border: 2px solid rgba(255,107,0,0.45); | |
| animation: guruFabRipple 2.4s ease-in-out infinite; | |
| } | |
| @keyframes guruFabRipple { 0%,100%{transform:scale(1);opacity:.6} 50%{transform:scale(1.28);opacity:0} } | |
| .guru-fab-tooltip { | |
| position: absolute; right: 66px; bottom: 50%; | |
| transform: translateY(50%); | |
| background: var(--bg-panel); border: 1px solid var(--border-gold); | |
| color: var(--gold-pale); font-family: var(--font-heading); | |
| font-size: 11px; letter-spacing: .12em; text-transform: uppercase; | |
| padding: 6px 13px; border-radius: 5px; white-space: nowrap; | |
| opacity: 0; pointer-events: none; | |
| transition: opacity .2s; | |
| box-shadow: 0 4px 16px rgba(0,0,0,.5); | |
| } | |
| #guru-fab:hover .guru-fab-tooltip { opacity: 1; } | |
| /* Chat panel */ | |
| #guru-chat { | |
| position: fixed; bottom: 96px; right: 100px; z-index: 600; | |
| width: 450px; max-height: 450px; | |
| background: var(--bg-panel); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 18px; | |
| display: flex; flex-direction: column; | |
| overflow: hidden; | |
| box-shadow: 0 24px 72px rgba(0,0,0,.65), 0 0 0 1px rgba(255,107,0,0.08), var(--shadow-gold); | |
| transform: translateY(18px) scale(.97); opacity: 0; pointer-events: none; | |
| transition: transform .3s cubic-bezier(0.4,0,0.2,1), opacity .3s; | |
| } | |
| #guru-chat.open { transform: translateY(0) scale(1); opacity: 1; pointer-events: all; } | |
| /* Header */ | |
| .guru-header { | |
| padding: 15px 18px 13px; | |
| background: linear-gradient(135deg, rgba(255,107,0,0.14), rgba(212,160,23,0.07)); | |
| border-bottom: 1px solid var(--border-gold); | |
| display: flex; align-items: center; gap: 12px; flex-shrink: 0; | |
| position: relative; | |
| } | |
| .guru-avatar { | |
| width: 40px; height: 40px; border-radius: 50%; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| display: flex; align-items: center; justify-content: center; | |
| font-size: 20px; flex-shrink: 0; position: relative; | |
| box-shadow: 0 0 0 3px rgba(255,107,0,0.22); | |
| animation: guruAvatarGlow 3s ease-in-out infinite; | |
| } | |
| @keyframes guruAvatarGlow { | |
| 0%,100%{ box-shadow: 0 0 0 3px rgba(255,107,0,0.22), 0 0 12px rgba(255,107,0,0.3); } | |
| 50% { box-shadow: 0 0 0 3px rgba(255,107,0,0.4), 0 0 24px rgba(255,107,0,0.55); } | |
| } | |
| .guru-status-dot { | |
| position: absolute; bottom: 1px; right: 1px; | |
| width: 10px; height: 10px; border-radius: 50%; | |
| background: #4ade80; border: 2px solid var(--bg-panel); | |
| animation: guruStatusPulse 2s ease-in-out infinite; | |
| } | |
| @keyframes guruStatusPulse { 0%,100%{opacity:1} 50%{opacity:.5} } | |
| .guru-header-text { flex: 1; min-width: 0; } | |
| .guru-header-name { | |
| font-family: var(--font-heading); font-weight: 700; font-size: 14px; | |
| color: var(--gold-pale); line-height: 1.2; letter-spacing: .05em; | |
| } | |
| .guru-header-sub { | |
| font-size: 11px; color: var(--text-muted); | |
| white-space: nowrap; overflow: hidden; text-overflow: ellipsis; | |
| margin-top: 2px; | |
| } | |
| /* Model switcher badge + dropdown */ | |
| #guru-switcher-btn { | |
| background: none; border: none; cursor: pointer; padding: 4px 6px; | |
| border-radius: 6px; display: flex; align-items: center; gap: 4px; | |
| transition: background .2s; flex-shrink: 0; | |
| } | |
| #guru-switcher-btn:hover { background: rgba(255,255,255,0.07); } | |
| #guru-switcher-btn svg { width: 13px; height: 13px; fill: var(--text-muted); flex-shrink: 0; } | |
| .guru-model-badge { | |
| font-family: var(--font-heading); font-size: 10px; font-weight: 600; | |
| letter-spacing: .06em; text-transform: uppercase; | |
| background: rgba(255,158,0,0.14); border: 1px solid rgba(255,158,0,0.35); | |
| color: #fbbf24; border-radius: 4px; padding: 2px 8px; white-space: nowrap; | |
| } | |
| #guru-switcher-menu { | |
| position: absolute; top: 64px; right: 10px; z-index: 20; | |
| background: var(--bg-card); border: 1px solid var(--border-gold); | |
| border-radius: 12px; padding: 6px; min-width: 220px; | |
| box-shadow: 0 12px 40px rgba(0,0,0,.7); | |
| display: none; | |
| animation: guruMenuSlide .18s cubic-bezier(0.4,0,0.2,1) both; | |
| } | |
| @keyframes guruMenuSlide { from{opacity:0;transform:translateY(-6px)} to{opacity:1;transform:translateY(0)} } | |
| #guru-switcher-menu.open { display: block; } | |
| .guru-sw-title { | |
| font-family: var(--font-heading); font-size: 10px; font-weight: 700; | |
| letter-spacing: .1em; text-transform: uppercase; | |
| color: var(--text-muted); padding: 4px 10px 8px; | |
| border-bottom: 1px solid var(--border-gold); margin-bottom: 5px; | |
| } | |
| .guru-sw-item { | |
| display: flex; align-items: center; gap: 10px; | |
| padding: 9px 10px; border-radius: 8px; cursor: pointer; | |
| transition: background .15s; border: none; background: none; | |
| font-family: var(--font-body); font-size: 13px; color: var(--text-primary); | |
| width: 100%; text-align: left; | |
| } | |
| .guru-sw-item:hover { background: rgba(255,107,0,0.08); } | |
| .guru-sw-item.active { background: rgba(255,107,0,0.14); } | |
| .guru-sw-item.active .guru-sw-name { color: var(--saffron-light); } | |
| .guru-sw-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } | |
| .guru-sw-info { flex: 1; min-width: 0; } | |
| .guru-sw-name { font-weight: 600; font-size: 13px; line-height: 1.2; } | |
| .guru-sw-model { font-size: 11px; color: var(--text-muted); } | |
| .guru-sw-status { | |
| font-family: var(--font-heading); font-size: 10px; font-weight: 600; | |
| letter-spacing: .05em; padding: 2px 7px; border-radius: 4px; flex-shrink: 0; | |
| } | |
| .guru-sw-status.connected { background: rgba(74,222,128,0.12); color: #4ade80; border: 1px solid rgba(74,222,128,0.25); } | |
| .guru-sw-status.disconnected { background: rgba(255,255,255,0.05); color: var(--text-muted); border: 1px solid var(--border-gold); } | |
| .guru-sw-divider { height: 1px; background: var(--border-gold); margin: 5px 0; } | |
| .guru-sw-manage { | |
| width: 100%; padding: 8px 10px; border-radius: 7px; | |
| background: rgba(255,107,0,0.06); border: 1px solid var(--border-gold); | |
| color: var(--text-muted); font-size: 12px; font-family: var(--font-heading); | |
| letter-spacing: .08em; text-transform: uppercase; | |
| cursor: pointer; transition: all .2s; display: flex; align-items: center; gap: 7px; | |
| } | |
| .guru-sw-manage:hover { border-color: var(--saffron); color: var(--saffron-light); } | |
| /* Toggle row (web search + RAG) */ | |
| .guru-toggle-row { | |
| background: rgba(0,0,0,0.28); | |
| border-bottom: 1px solid var(--border-gold); | |
| padding: 9px 16px; | |
| display: flex; align-items: center; gap: 14px; | |
| flex-shrink: 0; flex-wrap: wrap; | |
| } | |
| .guru-toggle { | |
| display: flex; align-items: center; gap: 7px; | |
| cursor: pointer; user-select: none; | |
| } | |
| .gtoggle-cb { display: none; } | |
| .gtoggle-sw { | |
| width: 34px; height: 19px; | |
| background: rgba(255,255,255,0.06); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 10px; position: relative; | |
| transition: background .3s, border-color .3s; | |
| flex-shrink: 0; | |
| } | |
| .gtoggle-sw::after { | |
| content:''; position: absolute; | |
| top: 2px; left: 2px; width: 13px; height: 13px; | |
| background: var(--text-muted); border-radius: 50%; | |
| transition: all .3s; | |
| } | |
| .gtoggle-cb:checked + .gtoggle-sw { background: rgba(255,107,0,0.28); border-color: var(--saffron); } | |
| .gtoggle-cb:checked + .gtoggle-sw::after { left: calc(100% - 15px); background: var(--saffron); } | |
| .gtoggle-label { | |
| font-family: var(--font-heading); font-size: 10px; | |
| letter-spacing: .18em; text-transform: uppercase; | |
| color: var(--text-muted); white-space: nowrap; | |
| } | |
| /* RAG status bar */ | |
| #rag-status-bar { | |
| background: rgba(75,0,130,0.15); | |
| border-bottom: 1px solid rgba(75,0,130,0.4); | |
| padding: 7px 16px; | |
| display: none; align-items: center; gap: 10px; flex-wrap: wrap; | |
| font-family: var(--font-heading); font-size: 10px; | |
| letter-spacing: .15em; text-transform: uppercase; | |
| } | |
| #rag-status-bar.visible { display: flex; } | |
| .rag-badge { | |
| background: linear-gradient(135deg, #4B0082, var(--saffron)); | |
| color: var(--gold-pale); padding: 3px 10px; | |
| border-radius: 99px; font-size: 9px; letter-spacing: .2em; font-weight: 700; | |
| } | |
| .rag-text { color: #fff; flex: 1; } | |
| .rag-book-pills { display: flex; gap: 6px; flex-wrap: wrap; } | |
| .rag-pill { | |
| background: rgba(212,160,23,0.1); border: 1px solid var(--border-gold); | |
| color: var(--gold); padding: 2px 8px; border-radius: 99px; | |
| font-size: 9px; cursor: pointer; transition: all .2s; | |
| display: flex; align-items: center; gap: 4px; | |
| } | |
| .rag-pill:hover { background: rgba(212,160,23,0.2); } | |
| .rag-pill.active { background: rgba(255,107,0,0.2); border-color: var(--saffron); color: var(--saffron-light); } | |
| .rag-pill .pill-x { opacity: .5; font-size: 8px; } | |
| .rag-index-btn { | |
| background: transparent; border: 1px solid var(--border-saffron); | |
| color: var(--saffron-light); padding: 3px 12px; | |
| border-radius: 3px; font-family: var(--font-heading); | |
| font-size: 9px; letter-spacing: .15em; text-transform: uppercase; | |
| cursor: pointer; transition: all .2s; | |
| } | |
| .rag-index-btn:hover { background: rgba(255,107,0,0.1); } | |
| .rag-index-btn:disabled { opacity: .4; cursor: not-allowed; } | |
| #rag-progress { width:100%; height:3px; background:rgba(212,160,23,0.1); border-radius:99px; overflow:hidden; display:none; } | |
| #rag-progress.visible { display:block; } | |
| #rag-progress-bar { height:100%; width:0%; background:linear-gradient(90deg,#4B0082,var(--saffron),var(--gold)); border-radius:99px; transition:width .3s ease; } | |
| /* RAG context block */ | |
| .rag-context-block { | |
| background: rgba(75,0,130,0.1); border: 1px solid rgba(75,0,130,0.35); | |
| border-radius: 4px; padding: 8px 12px; margin-top: 8px; font-size: 13px; | |
| } | |
| .rag-context-title { | |
| font-family: var(--font-heading); font-size: 9px; letter-spacing: .25em; | |
| text-transform: uppercase; color: #9b59d4; margin-bottom: 5px; | |
| } | |
| .rag-chunk-item { | |
| color: var(--text-secondary); font-size: 12px; line-height: 1.5; | |
| border-left: 2px solid rgba(155,89,212,0.4); padding-left: 8px; | |
| margin-bottom: 5px; font-style: italic; | |
| } | |
| .rag-chunk-src { font-family: var(--font-heading); font-size: 9px; color: var(--text-muted); letter-spacing: .1em; margin-top: 2px; } | |
| /* Messages area */ | |
| .guru-msgs { | |
| flex: 1; overflow-y: auto; padding: 16px 14px; | |
| display: flex; flex-direction: column; gap: 12px; | |
| scrollbar-width: thin; scrollbar-color: var(--border-gold) transparent; | |
| } | |
| .guru-msgs::-webkit-scrollbar { width: 3px; } | |
| .guru-msgs::-webkit-scrollbar-thumb { background: var(--border-gold); border-radius: 2px; } | |
| /* Messages */ | |
| .msg { | |
| display: flex; gap: 9px; | |
| animation: msgSlide .28s cubic-bezier(0.4,0,0.2,1) both; | |
| max-width: 88%; | |
| } | |
| @keyframes msgSlide { from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:translateY(0)} } | |
| .msg.user { flex-direction: row-reverse; align-self: flex-end; } | |
| .msg-avatar { | |
| width: 34px; height: 34px; border-radius: 50%; | |
| display: flex; align-items: center; justify-content: center; | |
| font-size: 15px; flex-shrink: 0; | |
| } | |
| .msg.bot .msg-avatar { background: linear-gradient(135deg, var(--saffron), var(--gold)); } | |
| .msg.user .msg-avatar { background: rgba(212,160,23,0.12); border: 1px solid var(--border-gold); } | |
| .msg-body { display: flex; flex-direction: column; gap: 4px; } | |
| .msg.user .msg-body { align-items: flex-end; } | |
| .msg-bubble { | |
| padding: 10px 14px; border-radius: 14px; | |
| font-family: var(--font-body); font-size: 14px; line-height: 1.7; max-width: 100%; | |
| } | |
| .msg.bot .msg-bubble { | |
| background: rgba(255,107,0,0.06); border: 1px solid rgba(255,107,0,0.16); | |
| border-bottom-left-radius: 4px; border-left: 3px solid var(--saffron); | |
| color: var(--text-primary); | |
| } | |
| .msg.user .msg-bubble { | |
| background: linear-gradient(135deg, rgba(255,107,0,0.18), rgba(212,160,23,0.14)); | |
| border: 1px solid var(--border-gold); border-bottom-right-radius: 4px; | |
| color: var(--text-primary); | |
| } | |
| .msg-time { font-family: var(--font-heading); font-size: 10px; color: var(--text-muted); letter-spacing: .08em; } | |
| /* Sources */ | |
| .sources-block { | |
| margin-top: 8px; padding: 9px 12px; | |
| background: rgba(0,0,0,.3); border: 1px solid var(--border-gold); border-radius: 6px; font-size: 12px; | |
| } | |
| .sources-title { | |
| font-family: var(--font-heading); font-size: 9px; font-weight: 700; | |
| letter-spacing: .28em; text-transform: uppercase; | |
| color: var(--saffron); margin-bottom: 6px; display: flex; align-items: center; gap: 5px; | |
| } | |
| .source-link { | |
| display: flex; align-items: flex-start; gap: 5px; | |
| color: var(--text-secondary); text-decoration: none; | |
| margin-bottom: 4px; transition: color var(--transition); | |
| word-break: break-all; font-size: 11px; | |
| } | |
| .source-link:hover { color: var(--saffron-light); } | |
| .source-link::before { content:'โ'; color: var(--gold); flex-shrink: 0; } | |
| /* Typing dots */ | |
| .typing-indicator .msg-bubble { display:flex; gap:5px; align-items:center; padding:12px 18px; } | |
| .typing-dot { width:7px; height:7px; background:var(--saffron); border-radius:50%; animation:typingBounce 1.4s ease-in-out infinite; } | |
| .typing-dot:nth-child(2){animation-delay:.2s} .typing-dot:nth-child(3){animation-delay:.4s} | |
| @keyframes typingBounce { 0%,60%,100%{transform:translateY(0)} 30%{transform:translateY(-8px)} } | |
| /* Suggestion chips */ | |
| .guru-chips { | |
| padding: 0 14px 10px; display: flex; gap: 6px; flex-wrap: wrap; flex-shrink: 0; | |
| } | |
| .suggestion-chip { | |
| font-family: var(--font-heading); font-size: 11px; letter-spacing: .06em; | |
| padding: 5px 12px; border-radius: 100px; | |
| border: 1px solid rgba(255,107,0,0.3); background: rgba(255,107,0,0.07); | |
| color: var(--saffron-light); cursor: pointer; transition: all .18s; white-space: nowrap; | |
| } | |
| .suggestion-chip:hover { border-color: var(--saffron); background: rgba(255,107,0,0.18); color: var(--gold-bright); } | |
| /* Input row */ | |
| .guru-input-row { | |
| padding: 10px 12px 12px; border-top: 1px solid var(--border-gold); | |
| display: flex; gap: 8px; align-items: flex-end; flex-shrink: 0; | |
| background: rgba(0,0,0,0.22); | |
| } | |
| #chat-input { | |
| flex: 1; background: var(--bg-card); border: 1px solid var(--border-gold); | |
| border-radius: 10px; padding: 9px 13px; font-size: 14px; | |
| font-family: var(--font-body); color: var(--text-primary); outline: none; | |
| resize: none; max-height: 90px; line-height: 1.5; | |
| transition: border-color .2s; scrollbar-width: none; | |
| } | |
| #chat-input:focus { border-color: var(--saffron); } | |
| #chat-input::placeholder { color: var(--text-muted); } | |
| #send-btn { | |
| width: 38px; height: 38px; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| border: none; border-radius: 10px; cursor: pointer; | |
| display: flex; align-items: center; justify-content: center; | |
| flex-shrink: 0; transition: all .2s; color: var(--bg-dark); font-size: 17px; | |
| } | |
| #send-btn:hover { opacity: .88; transform: scale(1.07); } | |
| #send-btn:disabled { opacity: .4; cursor: not-allowed; transform: none; } | |
| .input-clear { | |
| display: none; position: absolute; /* kept for JS compat */ | |
| } | |
| .input-clear.visible { display: block; } | |
| .guru-footer-note { | |
| text-align: center; font-family: var(--font-heading); font-size: 10px; | |
| letter-spacing: .1em; color: var(--text-muted); padding: 0 14px 9px; flex-shrink: 0; | |
| } | |
| /* API Key Modal */ | |
| #guru-key-modal { | |
| position: fixed; inset: 0; background: rgba(0,0,0,.88); | |
| backdrop-filter: blur(16px); z-index: 700; | |
| display: flex; align-items: center; justify-content: center; padding: 20px; | |
| } | |
| .guru-key-box { | |
| background: var(--bg-panel); border: 1px solid var(--border-gold); | |
| border-radius: 18px; padding: 32px 28px; max-width: 420px; width: 100%; | |
| animation: msgSlide .3s cubic-bezier(0.4,0,0.2,1) both; position: relative; | |
| box-shadow: var(--shadow-gold); | |
| } | |
| .guru-key-box h3 { | |
| font-family: var(--font-display); font-size: 20px; color: var(--gold-pale); | |
| margin-bottom: 6px; letter-spacing: .05em; | |
| } | |
| .guru-key-sub { font-family: var(--font-body); font-size: 14px; color: var(--text-muted); margin-bottom: 22px; line-height: 1.65; } | |
| .guru-key-sub a { color: var(--saffron); text-decoration: none; } | |
| .guru-key-sub a:hover { text-decoration: underline; } | |
| .guru-modal-x { | |
| position: absolute; top: 14px; right: 14px; | |
| width: 30px; height: 30px; border-radius: 50%; | |
| background: var(--bg-card); border: 1px solid var(--border-gold); | |
| color: var(--text-muted); font-size: 14px; cursor: pointer; | |
| display: flex; align-items: center; justify-content: center; | |
| transition: all .2s; line-height: 1; | |
| } | |
| .guru-modal-x:hover { background: rgba(255,107,0,0.12); color: var(--saffron); border-color: var(--saffron); } | |
| .guru-saved-badge { | |
| font-family: var(--font-heading); font-size: 12px; font-weight: 600; color: #4ade80; | |
| background: rgba(74,222,128,0.1); border: 1px solid rgba(74,222,128,0.25); | |
| border-radius: 7px; padding: 7px 12px; margin-bottom: 12px; | |
| } | |
| #guru-key-input { | |
| width: 100%; background: var(--bg-card); border: 1px solid var(--border-gold); | |
| border-radius: 9px; padding: 11px 14px; font-size: 14px; | |
| font-family: var(--font-body); color: var(--text-primary); outline: none; | |
| margin-bottom: 14px; transition: border-color .2s; | |
| } | |
| #guru-key-input:focus { border-color: var(--saffron); } | |
| #guru-key-input::placeholder { color: var(--text-muted); } | |
| #guru-key-input.error { border-color: rgba(255,107,0,.8); } | |
| .guru-key-btns { display: flex; gap: 8px; } | |
| #guru-key-save { | |
| flex: 1; background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); border: none; border-radius: 8px; padding: 11px; | |
| font-family: var(--font-heading); font-size: 12px; font-weight: 700; | |
| letter-spacing: .1em; text-transform: uppercase; cursor: pointer; transition: all .2s; | |
| } | |
| #guru-key-save:hover { opacity: .88; } | |
| #guru-key-skip { | |
| background: transparent; color: var(--text-muted); border: 1px solid var(--border-gold); | |
| border-radius: 8px; padding: 11px 16px; font-family: var(--font-heading); | |
| font-size: 12px; letter-spacing: .1em; text-transform: uppercase; | |
| cursor: pointer; transition: all .2s; | |
| } | |
| #guru-key-skip:hover { border-color: var(--saffron); color: var(--saffron-light); } | |
| .guru-modal-note { font-family: var(--font-heading); font-size: 10px; letter-spacing: .1em; color: var(--text-muted); text-align: center; margin-top: 12px; line-height: 1.5; } | |
| .guru-error-bubble { background: rgba(255,107,0,0.09); border: 1px solid rgba(255,107,0,0.3); border-radius: 10px; padding: 9px 12px; font-size: 13px; color: var(--saffron-light); } | |
| @media (max-width: 480px) { | |
| #guru-chat { width: calc(100vw - 24px); right: 12px; bottom: 86px; } | |
| #guru-fab { right: 16px; bottom: 20px; } | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PHILOSOPHY SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #philosophy { | |
| background: linear-gradient(180deg, transparent, rgba(255,107,0,0.03), transparent); | |
| } | |
| .philosophy-grid { | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); | |
| gap: 28px; | |
| } | |
| .pillar-card { | |
| padding: 36px 28px; | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 6px; | |
| text-align: center; | |
| transition: all var(--transition); | |
| position: relative; | |
| } | |
| .pillar-card:hover { box-shadow: var(--shadow-saffron); transform: translateY(-4px); } | |
| .pillar-num { | |
| font-family: var(--font-display); | |
| font-size: 48px; | |
| font-weight: 900; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| line-height: 1; | |
| margin-bottom: 8px; | |
| opacity: 0.35; | |
| } | |
| .pillar-icon { font-size: 32px; margin-bottom: 12px; display: block; } | |
| .pillar-title { | |
| font-family: var(--font-heading); | |
| font-size: 17px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| margin-bottom: 12px; | |
| } | |
| .pillar-text { | |
| font-size: 14px; | |
| color: var(--text-muted); | |
| line-height: 1.8; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| QUOTE TICKER | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #quote-ticker { | |
| padding: 0; | |
| border-top: 1px solid var(--border-gold); | |
| border-bottom: 1px solid var(--border-gold); | |
| background: linear-gradient(90deg, var(--bg-mid), rgba(139,0,0,0.04), var(--bg-mid)); | |
| overflow: hidden; | |
| } | |
| .ticker-inner { | |
| display: flex; | |
| align-items: center; | |
| } | |
| .ticker-label { | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); | |
| padding: 16px 24px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.25em; | |
| text-transform: uppercase; | |
| white-space: nowrap; | |
| flex-shrink: 0; | |
| } | |
| .ticker-track { | |
| overflow: hidden; | |
| flex: 1; | |
| mask: linear-gradient(90deg, transparent, black 5%, black 95%, transparent); | |
| } | |
| .ticker-content { | |
| display: flex; | |
| gap: 0; | |
| animation: ticker 40s linear infinite; | |
| width: max-content; | |
| } | |
| @keyframes ticker { | |
| from { transform: translateX(0); } | |
| to { transform: translateX(-50%); } | |
| } | |
| .ticker-quote { | |
| padding: 16px 60px; | |
| font-size: 13px; | |
| color: var(--text-secondary); | |
| white-space: nowrap; | |
| font-style: italic; | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .ticker-sep { | |
| color: var(--saffron); | |
| font-size: 18px; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FOOTER | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| footer { | |
| position: relative; | |
| z-index: 2; | |
| background: var(--bg-mid); | |
| border-top: 1px solid var(--border-gold); | |
| padding: 60px 40px 30px; | |
| } | |
| .footer-inner { | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| } | |
| .footer-top { | |
| display: grid; | |
| grid-template-columns: 2fr repeat(3, 1fr); | |
| gap: 48px; | |
| margin-bottom: 48px; | |
| } | |
| .footer-brand .nav-om { font-size: 36px; display: block; margin-bottom: 10px; margin-left: 75px; } | |
| .footer-brand-name { | |
| font-family: var(--font-display); | |
| font-size: 18px; | |
| font-weight: 700; | |
| color: var(--gold-bright); | |
| margin-bottom: 12px; | |
| } | |
| .footer-brand-text { | |
| font-size: 13px; | |
| color: var(--text-muted); | |
| line-height: 1.8; | |
| max-width: 280px; | |
| } | |
| .footer-col-title { | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| font-weight: 700; | |
| letter-spacing: 0.3em; | |
| text-transform: uppercase; | |
| color: var(--saffron); | |
| margin-bottom: 20px; | |
| } | |
| .footer-links { list-style: none; display: flex; flex-direction: column; gap: 10px; } | |
| .footer-links a { | |
| font-size: 15px; | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| transition: color var(--transition); | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .footer-links a::before { content: '๐'; color: var(--gold); font-size: 15px; opacity: 0.5; } | |
| .footer-links a:hover { color: var(--saffron-light); } | |
| .footer-bottom { | |
| border-top: 1px solid var(--border-gold); | |
| padding-top: 24px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| flex-wrap: wrap; | |
| gap: 12px; | |
| } | |
| .footer-copy { | |
| font-size: 12px; | |
| color: var(--gold); | |
| font-family: var(--font-heading); | |
| letter-spacing: 0.1em; | |
| } | |
| .footer-mantra { | |
| font-size: 13px; | |
| color: var(--gold); | |
| font-family: var(--font-heading); | |
| letter-spacing: 0.15em; | |
| font-style: italic; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| RESPONSIVE | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| @media (max-width: 900px) { | |
| nav { padding: 0 20px; } | |
| .nav-links { display: none; } | |
| .nav-toggle { display: block; } | |
| .nav-links.open { | |
| display: flex; | |
| flex-direction: column; | |
| position: fixed; | |
| top: 72px; left: 0; right: 0; | |
| background: rgba(10,4,0,0.98); | |
| padding: 24px; | |
| border-bottom: 1px solid var(--border-gold); | |
| gap: 20px; | |
| z-index: 999; | |
| } | |
| section { padding: 70px 20px; } | |
| .footer-top { grid-template-columns: 1fr 1fr; } | |
| } | |
| @media (max-width: 600px) { | |
| .books-grid { grid-template-columns: 1fr; } | |
| .deities-grid { grid-template-columns: repeat(3, 1fr); } | |
| .footer-top { grid-template-columns: 1fr; gap: 32px; } | |
| .hero-cta-group { flex-direction: column; align-items: center; } | |
| .guru-chips { flex-wrap: nowrap; overflow-x: auto; } | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| MISC UTILITIES | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .text-center { text-align: center; } | |
| .mt-16 { margin-top: 16px; } | |
| .shree-symbol { | |
| display: inline-block; | |
| color: var(--saffron); | |
| font-size: 0.9em; | |
| margin: 0 4px; | |
| } | |
| /* Ripple on click */ | |
| .ripple { | |
| position: absolute; | |
| border-radius: 50%; | |
| background: rgba(255,107,0,0.3); | |
| animation: rippleOut 0.6s ease-out forwards; | |
| pointer-events: none; | |
| } | |
| @keyframes rippleOut { | |
| from { width: 0; height: 0; opacity: 1; transform: translate(-50%, -50%); } | |
| to { width: 120px; height: 120px; opacity: 0; transform: translate(-50%, -50%); } | |
| } | |
| /* Glow text utility */ | |
| .glow-gold { | |
| text-shadow: 0 0 20px rgba(212,160,23,0.5); | |
| } | |
| /* Section divider */ | |
| .section-sep { | |
| width: 100%; | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, var(--border-gold), transparent); | |
| margin: 0 auto; | |
| position: relative; | |
| z-index: 2; | |
| } | |
| /* Fade-in on scroll */ | |
| .reveal { | |
| opacity: 0; | |
| transform: translateY(24px); | |
| transition: opacity 0.7s ease, transform 0.7s ease; | |
| } | |
| .reveal.visible { | |
| opacity: 1; | |
| transform: none; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| SCROLL TO TOP BUTTON | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #scroll-top-btn { | |
| position: fixed; | |
| bottom: 35px; | |
| right: 40px; | |
| z-index: 9999; | |
| width: 50px; height: 50px; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); | |
| border: none; | |
| border-radius: 50%; | |
| font-size: 22px; | |
| font-weight: 700; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| box-shadow: 0 4px 20px rgba(255,107,0,0.5); | |
| opacity: 0; | |
| transform: translateY(20px) scale(0.85); | |
| transition: opacity 0.35s ease, transform 0.35s ease, box-shadow 0.35s ease; | |
| pointer-events: none; | |
| line-height: 1; | |
| } | |
| #scroll-top-btn.visible { | |
| opacity: 1; | |
| transform: translateY(0) scale(1); | |
| pointer-events: auto; | |
| } | |
| #scroll-top-btn:hover { | |
| box-shadow: 0 6px 30px rgba(255,107,0,0.75); | |
| transform: translateY(-3px) scale(1.05); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DEITY FAMILY TREE | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .family-tree-wrapper { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| .ft-legend { | |
| display: flex; | |
| gap: 24px; | |
| flex-wrap: wrap; | |
| padding: 16px 28px; | |
| border-bottom: 1px solid var(--border-gold); | |
| background: rgba(0,0,0,0.2); | |
| } | |
| .ft-legend-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 600; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| } | |
| .ft-leg-dot { | |
| width: 10px; height: 10px; | |
| border-radius: 50%; | |
| flex-shrink: 0; | |
| } | |
| .ft-download-btn { | |
| margin-left: auto; | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); | |
| border: none; | |
| border-radius: 4px; | |
| padding: 6px 16px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| flex-shrink: 0; | |
| } | |
| .ft-download-btn:hover { | |
| transform: translateY(-1px); | |
| box-shadow: 0 4px 20px rgba(255,107,0,0.4); | |
| opacity: 0.9; | |
| } | |
| .ft-download-btn i { font-size: 11px; } | |
| .ft-scroll-container { | |
| overflow-x: auto; | |
| overflow-y: auto; | |
| padding: 0; | |
| min-height: 560px; | |
| max-height: 780px; | |
| cursor: grab; | |
| } | |
| .ft-scroll-container:active { cursor: grabbing; } | |
| #family-tree-svg { | |
| width: 2200px; | |
| height: auto; | |
| min-width: 2200px; | |
| display: block; | |
| } | |
| /* Node styles applied via SVG/JS */ | |
| .ft-node-circle { | |
| cursor: pointer; | |
| transition: all 0.25s ease; | |
| } | |
| .ft-node-circle:hover { filter: brightness(1.3); } | |
| .ft-info-panel { | |
| position: absolute; | |
| top: 60px; right: 20px; | |
| width: 260px; | |
| background: var(--bg-panel); | |
| border: 1px solid var(--border-saffron); | |
| border-radius: 8px; | |
| padding: 24px 20px 20px; | |
| box-shadow: var(--shadow-saffron); | |
| display: none; | |
| z-index: 10; | |
| animation: fadeIn 0.25s ease; | |
| } | |
| .ft-info-panel.open { display: block; } | |
| .ft-info-close { | |
| position: absolute; | |
| top: 10px; right: 12px; | |
| color: var(--text-muted); | |
| cursor: pointer; | |
| font-size: 14px; | |
| transition: color var(--transition); | |
| } | |
| .ft-info-close:hover { color: var(--saffron); } | |
| .ft-info-icon { | |
| font-size: 40px; | |
| text-align: center; | |
| display: block; | |
| margin-bottom: 8px; | |
| filter: drop-shadow(0 0 8px var(--saffron)); | |
| } | |
| .ft-info-name { | |
| font-family: var(--font-heading); | |
| font-size: 17px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| text-align: center; | |
| margin-bottom: 3px; | |
| } | |
| .ft-info-epithet { | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| letter-spacing: 0.25em; | |
| text-transform: uppercase; | |
| color: var(--saffron); | |
| text-align: center; | |
| margin-bottom: 12px; | |
| } | |
| .ft-info-body { | |
| font-size: 13px; | |
| color: var(--text-secondary); | |
| line-height: 1.7; | |
| text-align: center; | |
| margin-bottom: 14px; | |
| } | |
| .ft-info-tags { | |
| display: flex; | |
| gap: 6px; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| } | |
| .ft-tag { | |
| background: rgba(255,107,0,0.1); | |
| border: 1px solid rgba(255,107,0,0.25); | |
| color: var(--saffron-light); | |
| padding: 3px 9px; | |
| border-radius: 12px; | |
| font-family: var(--font-heading); | |
| font-size: 9px; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| MANDALA COLORING TOOL | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .mandala-tool-wrapper { | |
| max-width: 980px; | |
| margin: 0 auto; | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| } | |
| .mc-controls { | |
| display: flex; | |
| align-items: flex-end; | |
| gap: 28px; | |
| flex-wrap: wrap; | |
| padding: 20px 28px; | |
| border-bottom: 1px solid var(--border-gold); | |
| background: rgba(0,0,0,0.25); | |
| } | |
| .mc-control-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .mc-label { | |
| font-family: var(--font-heading); | |
| font-size: 9px; | |
| font-weight: 700; | |
| letter-spacing: 0.3em; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| } | |
| .mc-mandala-btns { | |
| display: flex; | |
| gap: 6px; | |
| flex-wrap: wrap; | |
| } | |
| .mc-mtype { | |
| background: var(--bg-dark); | |
| border: 1px solid var(--border-gold); | |
| color: var(--text-secondary); | |
| padding: 6px 12px; | |
| border-radius: 3px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 600; | |
| letter-spacing: 0.1em; | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| white-space: nowrap; | |
| } | |
| .mc-mtype:hover, .mc-mtype.active { | |
| background: rgba(255,107,0,0.15); | |
| border-color: var(--saffron); | |
| color: var(--saffron-light); | |
| } | |
| .mc-range { | |
| -webkit-appearance: none; | |
| width: 120px; | |
| height: 4px; | |
| background: var(--border-gold); | |
| border-radius: 2px; | |
| outline: none; | |
| cursor: pointer; | |
| } | |
| .mc-range::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| width: 14px; height: 14px; | |
| background: var(--saffron); | |
| border-radius: 50%; | |
| cursor: pointer; | |
| } | |
| .mc-range-val { | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| color: var(--gold); | |
| min-width: 24px; | |
| } | |
| .mc-palette { | |
| display: flex; | |
| gap: 6px; | |
| flex-wrap: wrap; | |
| max-width: 220px; | |
| } | |
| .mc-color-swatch { | |
| width: 24px; height: 24px; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| border: 2px solid transparent; | |
| transition: transform 0.2s, border-color 0.2s; | |
| flex-shrink: 0; | |
| } | |
| .mc-color-swatch:hover { transform: scale(1.2); } | |
| .mc-color-swatch.active { border-color: #fff; transform: scale(1.15); } | |
| .mc-color-input { | |
| width: 28px; height: 28px; | |
| border: 1px solid var(--border-gold); | |
| border-radius: 50%; | |
| cursor: pointer; | |
| background: none; | |
| padding: 0; | |
| outline: none; | |
| } | |
| .mc-action-btns { | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .mc-action-btn { | |
| background: rgba(212,160,23,0.08); | |
| border: 1px solid var(--border-gold); | |
| color: var(--gold); | |
| padding: 7px 14px; | |
| border-radius: 3px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.1em; | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| white-space: nowrap; | |
| } | |
| .mc-action-btn:hover { background: rgba(212,160,23,0.2); } | |
| .mc-download-btn { | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)) ; | |
| color: var(--bg-dark) ; | |
| border-color: transparent ; | |
| } | |
| .mc-download-btn:hover { opacity: 0.85; } | |
| .mc-canvas-area { | |
| position: relative; | |
| background: #0e0800; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 28px; | |
| min-height: 520px; | |
| } | |
| .mc-bg-canvas, .mc-color-canvas { | |
| position: absolute; | |
| top: 50%; left: 50%; | |
| transform: translate(-50%, -50%); | |
| border-radius: 50%; | |
| } | |
| .mc-bg-canvas { pointer-events: none; } | |
| .mc-color-canvas { cursor: crosshair; } | |
| .mc-hint { | |
| text-align: center; | |
| padding: 12px; | |
| font-size: 12px; | |
| color: #eb7d0b; | |
| font-family: var(--font-heading); | |
| letter-spacing: 0.1em; | |
| border-top: 1px solid var(--border-gold); | |
| background: rgba(0,0,0,0.15); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| HINDU CALENDAR & FESTIVAL SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #calendar-section { | |
| background: radial-gradient(ellipse at 50% 0%, rgba(139,0,0,0.06) 0%, transparent 65%); | |
| } | |
| .calendar-layout { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: 1fr 1.4fr; | |
| gap: 36px; | |
| align-items: start; | |
| } | |
| .calendar-left-col { | |
| position: sticky; | |
| top: 90px; | |
| } | |
| /* โโ Panchang Widget โโ */ | |
| .panchang-card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| } | |
| .panchang-header { | |
| background: linear-gradient(135deg, rgba(255,107,0,0.15), rgba(212,160,23,0.1)); | |
| border-bottom: 1px solid var(--border-gold); | |
| padding: 20px 24px; | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| } | |
| .panchang-icon { font-size: 32px; filter: drop-shadow(0 0 8px var(--saffron)); } | |
| .panchang-header-text { flex: 1; } | |
| .panchang-title { | |
| font-family: var(--font-heading); | |
| font-size: 14px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| } | |
| .panchang-date-str { | |
| font-size: 12px; | |
| color: var(--text-muted); | |
| margin-top: 3px; | |
| } | |
| .panchang-body { padding: 20px 24px; } | |
| .panchang-row { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 13px 0; | |
| border-bottom: 1px solid rgba(212,160,23,0.1); | |
| gap: 10px; | |
| } | |
| .panchang-row:last-child { border-bottom: none; } | |
| .panchang-label { | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.3em; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .panchang-value { | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| text-align: right; | |
| } | |
| .panchang-badge { | |
| display: inline-block; | |
| padding: 3px 10px; | |
| border-radius: 20px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| } | |
| .badge-shukla { | |
| background: rgba(255,215,0,0.12); | |
| border: 1px solid rgba(255,215,0,0.35); | |
| color: var(--gold-bright); | |
| } | |
| .badge-krishna { | |
| background: rgba(139,0,0,0.15); | |
| border: 1px solid rgba(139,0,0,0.4); | |
| color: #ff8a8a; | |
| } | |
| .moon-phase-display { | |
| font-size: 28px; | |
| filter: drop-shadow(0 0 6px rgba(255,215,0,0.5)); | |
| } | |
| /* โโ Next Festival Countdown โโ */ | |
| .countdown-card { | |
| background: linear-gradient(135deg, rgba(255,107,0,0.08), rgba(212,160,23,0.05)); | |
| border: 1px solid var(--border-saffron); | |
| border-radius: 8px; | |
| padding: 24px 28px; | |
| margin-top: 20px; | |
| text-align: center; | |
| } | |
| .countdown-festival-name { | |
| font-family: var(--font-display); | |
| font-size: 18px; | |
| font-weight: 700; | |
| color: var(--saffron-light); | |
| margin-bottom: 4px; | |
| } | |
| .countdown-festival-icon { font-size: 36px; margin-bottom: 8px; display: block; } | |
| .countdown-label { | |
| font-family: var(--font-heading); | |
| font-size: 9px; | |
| font-weight: 600; | |
| letter-spacing: 0.4em; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| margin-bottom: 16px; | |
| } | |
| .countdown-grid { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 10px; | |
| } | |
| .countdown-unit { | |
| background: rgba(0,0,0,0.35); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 5px; | |
| padding: 12px 6px; | |
| } | |
| .countdown-num { | |
| font-family: var(--font-display); | |
| font-size: clamp(22px, 3vw, 32px); | |
| font-weight: 900; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold-bright)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| display: block; | |
| line-height: 1; | |
| margin-bottom: 4px; | |
| } | |
| .countdown-unit-label { | |
| font-family: var(--font-heading); | |
| font-size: 8px; | |
| font-weight: 700; | |
| letter-spacing: 0.25em; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| } | |
| .countdown-desc { | |
| font-size: 12px; | |
| color: #da8834; | |
| margin-top: 10px; | |
| font-style: italic; | |
| } | |
| /* โโ Upcoming Festivals List โโ */ | |
| .festivals-panel { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| } | |
| .festivals-panel-header { | |
| background: linear-gradient(135deg, rgba(212,160,23,0.1), rgba(255,107,0,0.06)); | |
| border-bottom: 1px solid var(--border-gold); | |
| padding: 18px 24px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| } | |
| .festivals-panel-title { | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| } | |
| .festivals-year-badge { | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| color: var(--saffron); | |
| background: rgba(255,107,0,0.1); | |
| border: 1px solid rgba(255,107,0,0.25); | |
| padding: 3px 10px; | |
| border-radius: 20px; | |
| } | |
| .festivals-list { padding: 8px 0; max-height: 520px; overflow-y: auto; } | |
| .festivals-list::-webkit-scrollbar { width: 3px; } | |
| .festivals-list::-webkit-scrollbar-thumb { background: var(--border-gold); } | |
| .festival-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| padding: 14px 24px; | |
| border-bottom: 1px solid rgba(212,160,23,0.06); | |
| transition: background var(--transition); | |
| position: relative; | |
| } | |
| .festival-item:last-child { border-bottom: none; } | |
| .festival-item:hover { background: rgba(255,107,0,0.04); } | |
| .festival-item.next-fest { | |
| background: rgba(255,107,0,0.07); | |
| border-left: 3px solid var(--saffron); | |
| } | |
| .festival-item.next-fest::before { | |
| content: 'NEXT'; | |
| position: absolute; | |
| right: 18px; top: 2px; | |
| font-family: var(--font-heading); | |
| font-size: 8px; | |
| font-weight: 700; | |
| letter-spacing: 0.3em; | |
| color: var(--saffron); | |
| background: rgba(255, 255, 255, 0.12); | |
| padding: 1px 7px; | |
| border-radius: 3px; | |
| } | |
| .festival-item.past-fest { opacity: 0.45; } | |
| .festival-emoji { font-size: 24px; flex-shrink: 0; width: 36px; text-align: center; } | |
| .festival-info { flex: 1; min-width: 0; } | |
| .festival-name { | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| margin-bottom: 2px; | |
| } | |
| .festival-desc { font-size: 11px; color: white; } | |
| .festival-date-block { text-align: right; flex-shrink: 0; } | |
| .festival-date { | |
| font-family: var(--font-heading); | |
| font-size: 12px; | |
| font-weight: 700; | |
| color: var(--saffron-light); | |
| } | |
| .festival-days-away { | |
| font-size: 10px; | |
| color: var(--text-muted); | |
| margin-top: 2px; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| END CUSTOM ADDITIONS | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| /* Responsive */ | |
| @media (max-width: 900px) { | |
| .calendar-layout { grid-template-columns: 1fr; } | |
| .calendar-left-col { position: static; } | |
| } | |
| @media (max-width: 600px) { | |
| .countdown-grid { grid-template-columns: repeat(2, 1fr); } | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| MISCONCEPTIONS SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #misconceptions { | |
| background: radial-gradient(ellipse at 50% 0%, rgba(139,0,0,0.07) 0%, transparent 65%); | |
| } | |
| .misconceptions-intro { | |
| max-width: 700px; | |
| margin: 0 auto 60px; | |
| text-align: center; | |
| color: var(--text-secondary); | |
| font-size: 17px; | |
| font-style: italic; | |
| line-height: 1.9; | |
| } | |
| .misc-grid { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); | |
| gap: 30px; | |
| } | |
| .misc-card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| transition: all var(--transition); | |
| position: relative; | |
| } | |
| .misc-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: 0; right: 0; | |
| height: 3px; | |
| background: linear-gradient(90deg, var(--crimson), var(--saffron), var(--gold-bright)); | |
| } | |
| .misc-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 50px rgba(255,107,0,0.15), var(--shadow-gold); | |
| border-color: var(--saffron); | |
| } | |
| .misc-card-header { | |
| padding: 22px 24px 14px; | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 14px; | |
| } | |
| .misc-icon { | |
| font-size: 32px; | |
| flex-shrink: 0; | |
| margin-top: 2px; | |
| filter: drop-shadow(0 0 8px var(--saffron)); | |
| } | |
| .misc-myth-label { | |
| font-family: var(--font-heading); | |
| font-size: 9px; | |
| font-weight: 700; | |
| letter-spacing: 0.4em; | |
| text-transform: uppercase; | |
| color: var(--crimson-soft); | |
| margin-bottom: 6px; | |
| display: block; | |
| } | |
| .misc-myth-text { | |
| font-family: var(--font-heading); | |
| font-size: 14px; | |
| font-weight: 700; | |
| color: #ff9999; | |
| line-height: 1.4; | |
| } | |
| .misc-divider { | |
| margin: 0 24px; | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, var(--border-gold), transparent); | |
| } | |
| .misc-card-body { | |
| padding: 16px 24px 24px; | |
| } | |
| .misc-fact-label { | |
| font-family: var(--font-heading); | |
| font-size: 9px; | |
| font-weight: 700; | |
| letter-spacing: 0.4em; | |
| text-transform: uppercase; | |
| color: var(--saffron); | |
| margin-bottom: 8px; | |
| display: block; | |
| } | |
| .misc-fact-text { | |
| font-size: 15px; | |
| color: var(--text-secondary); | |
| line-height: 1.75; | |
| } | |
| .misc-fact-text strong { | |
| color: var(--gold-pale); | |
| } | |
| .misc-source { | |
| margin-top: 12px; | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| color: var(--saffron); | |
| letter-spacing: 0.08em; | |
| font-style: italic; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PODCASTS SECTION | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #podcasts { | |
| background: linear-gradient(180deg, transparent, rgba(255,107,0,0.04), transparent); | |
| } | |
| .podcasts-grid { | |
| max-width: 1240px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); | |
| gap: 28px; | |
| } | |
| .podcast-card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 10px; | |
| overflow: hidden; | |
| transition: all var(--transition); | |
| position: relative; | |
| } | |
| .podcast-card:hover { | |
| transform: translateY(-6px); | |
| box-shadow: 0 24px 60px rgba(255,107,0,0.2), var(--shadow-gold); | |
| border-color: var(--saffron); | |
| } | |
| .podcast-thumb { | |
| position: relative; | |
| width: 100%; | |
| padding-top: 56.25%; /* 16:9 */ | |
| background: var(--bg-dark); | |
| overflow: hidden; | |
| cursor: pointer; | |
| } | |
| .podcast-thumb img { | |
| position: absolute; | |
| inset: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| transition: transform var(--transition); | |
| } | |
| .podcast-card:hover .podcast-thumb img { transform: scale(1.04); } | |
| .podcast-play-overlay { | |
| position: absolute; | |
| inset: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: rgba(0,0,0,0.35); | |
| transition: background var(--transition); | |
| } | |
| .podcast-card:hover .podcast-play-overlay { background: rgba(0,0,0,0.5); } | |
| .podcast-play-btn { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| background: rgba(255,0,0,0.88); | |
| border: 2px solid rgba(255,255,255,0.25); | |
| border-radius: 8px; | |
| padding: 10px 18px; | |
| color: #fff; | |
| font-size: 13px; | |
| font-family: var(--font-heading); | |
| font-weight: 700; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| box-shadow: 0 0 30px rgba(255,0,0,0.5); | |
| transition: all var(--transition); | |
| cursor: pointer; | |
| white-space: nowrap; | |
| } | |
| .podcast-play-btn .yt-icon { | |
| font-size: 28px; | |
| line-height: 1; | |
| margin-left: 5px; | |
| } | |
| .podcast-card:hover .podcast-play-btn { | |
| transform: scale(1.08); | |
| background: rgba(255,0,0,1); | |
| box-shadow: 0 0 50px rgba(255,0,0,0.8); | |
| } | |
| .podcast-duration-badge { | |
| position: absolute; | |
| bottom: 10px; | |
| right: 10px; | |
| background: rgba(0,0,0,0.82); | |
| color: #fff; | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| font-weight: 700; | |
| padding: 3px 8px; | |
| border-radius: 4px; | |
| letter-spacing: 0.05em; | |
| } | |
| .podcast-views-badge { | |
| position: absolute; | |
| top: 10px; | |
| left: 10px; | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); | |
| font-family: var(--font-heading); | |
| font-size: 9px; | |
| font-weight: 700; | |
| padding: 3px 10px; | |
| border-radius: 3px; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| } | |
| .podcast-info { | |
| padding: 18px 20px 20px; | |
| } | |
| .podcast-channel { | |
| font-family: var(--font-heading); | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.3em; | |
| text-transform: uppercase; | |
| color: var(--saffron-light); | |
| margin-bottom: 7px; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .podcast-title { | |
| font-family: var(--font-heading); | |
| font-size: 15px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| line-height: 1.4; | |
| margin-bottom: 10px; | |
| } | |
| .podcast-meta { | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| font-size: 12px; | |
| color: var(--text-muted); | |
| font-family: var(--font-heading); | |
| letter-spacing: 0.05em; | |
| } | |
| .podcast-meta span { display: flex; align-items: center; gap: 5px; } | |
| /* Podcast tabs */ | |
| .podcast-tabs { | |
| display: flex; | |
| justify-content: center; | |
| gap: 14px; | |
| margin: 0 auto 48px; | |
| } | |
| .ptab { | |
| font-family: var(--font-heading); | |
| font-size: 12px; | |
| font-weight: 700; | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| padding: 11px 28px; | |
| border-radius: 4px; | |
| border: 1px solid var(--border-gold); | |
| background: var(--bg-card); | |
| color: var(--text-secondary); | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| } | |
| .ptab:hover { border-color: var(--saffron); color: var(--saffron-light); } | |
| .ptab.active { | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); | |
| border-color: transparent; | |
| box-shadow: 0 4px 20px rgba(255,107,0,0.35); | |
| } | |
| /* Podcast error state */ | |
| .podcast-error { | |
| display: none; | |
| padding: 16px 20px; | |
| background: rgba(139,0,0,0.18); | |
| border-top: 1px solid rgba(139,0,0,0.3); | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| color: #ff9999; | |
| letter-spacing: 0.06em; | |
| text-align: center; | |
| } | |
| .podcast-error.visible { display: flex; align-items: center; justify-content: center; gap: 8px; } | |
| .podcast-error a { color: var(--saffron-light); text-decoration: underline; } | |
| /* YouTube note */ | |
| .podcast-yt-note { | |
| text-align: center; | |
| margin-top: 40px; | |
| font-family: var(--font-heading); | |
| font-size: 12px; | |
| letter-spacing: 0.08em; | |
| color: var(--text-muted); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PODCAST MODAL PLAYER | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #yt-modal { | |
| display: none; | |
| position: fixed; | |
| inset: 0; | |
| z-index: 9500; | |
| background: rgba(0,0,0,0.92); | |
| backdrop-filter: blur(8px); | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px; | |
| } | |
| #yt-modal.open { display: flex; } | |
| .yt-modal-inner { | |
| background: var(--bg-panel); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 10px; | |
| width: 100%; | |
| max-width: 860px; | |
| padding: 24px; | |
| position: relative; | |
| box-shadow: 0 0 80px rgba(255,107,0,0.25); | |
| animation: modalSlideIn 0.25s ease; | |
| } | |
| @keyframes modalSlideIn { | |
| from { transform: translateY(30px); opacity: 0; } | |
| to { transform: translateY(0); opacity: 1; } | |
| } | |
| .yt-modal-close { | |
| position: absolute; | |
| top: 14px; right: 14px; | |
| background: rgba(139,0,0,0.4); | |
| border: 1px solid rgba(139,0,0,0.6); | |
| color: #ff9999; | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| font-weight: 700; | |
| letter-spacing: 0.1em; | |
| padding: 6px 14px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| transition: all var(--transition); | |
| z-index: 2; | |
| } | |
| .yt-modal-close:hover { background: rgba(139,0,0,0.7); color: #fff; } | |
| .yt-modal-title { | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| font-weight: 700; | |
| color: var(--gold-pale); | |
| letter-spacing: 0.08em; | |
| margin-bottom: 16px; | |
| padding-right: 100px; | |
| line-height: 1.4; | |
| } | |
| .yt-modal-player { | |
| position: relative; | |
| width: 100%; | |
| padding-top: 56.25%; | |
| background: #000; | |
| border-radius: 6px; | |
| overflow: hidden; | |
| margin-bottom: 14px; | |
| } | |
| #yt-player-container { | |
| position: absolute; | |
| inset: 0; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| #yt-player-container iframe, | |
| #yt-player-container > div { | |
| width: 100% ; | |
| height: 100% ; | |
| } | |
| .yt-watch-btn { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| background: linear-gradient(135deg, #FF0000, #cc0000); | |
| color: #fff; | |
| font-family: var(--font-heading); | |
| font-size: 12px; | |
| font-weight: 700; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| text-decoration: none; | |
| padding: 10px 22px; | |
| border-radius: 4px; | |
| transition: all var(--transition); | |
| box-shadow: 0 4px 16px rgba(255,0,0,0.3); | |
| } | |
| .yt-watch-btn:hover { opacity: 0.88; transform: translateY(-2px); } | |
| /* Error box inside modal */ | |
| .yt-error-box { | |
| position: absolute; | |
| inset: 0; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 16px; | |
| background: linear-gradient(135deg, rgba(20,5,0,0.97), rgba(40,10,0,0.97)); | |
| padding: 30px; | |
| text-align: center; | |
| } | |
| .yt-error-icon { font-size: 48px; } | |
| .yt-error-msg { | |
| font-family: var(--font-heading); | |
| font-size: 14px; | |
| color: var(--text-secondary); | |
| letter-spacing: 0.05em; | |
| line-height: 1.6; | |
| max-width: 380px; | |
| } | |
| .yt-error-msg strong { color: #ff9999; } | |
| .yt-error-link { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| background: linear-gradient(135deg, #FF0000, #cc0000); | |
| color: #fff; | |
| font-family: var(--font-heading); | |
| font-size: 13px; | |
| font-weight: 700; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| text-decoration: none; | |
| padding: 12px 28px; | |
| border-radius: 4px; | |
| transition: all var(--transition); | |
| box-shadow: 0 4px 20px rgba(255,0,0,0.4); | |
| margin-top: 8px; | |
| } | |
| .yt-error-link:hover { opacity: 0.88; transform: translateY(-2px); box-shadow: 0 8px 30px rgba(255,0,0,0.5); } | |
| @media (max-width: 600px) { | |
| .yt-modal-inner { padding: 16px; } | |
| .yt-modal-title { font-size: 11px; padding-right: 80px; } | |
| } | |
| /* Embedded player */ | |
| .podcast-embed { | |
| display: none; | |
| position: relative; | |
| padding-top: 56.25%; | |
| } | |
| .podcast-embed.active { display: block; } | |
| .podcast-embed iframe { | |
| position: absolute; | |
| inset: 0; | |
| width: 100%; | |
| height: 100%; | |
| border: none; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FLOATING CONTACT BUTTONS | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| .floating-contacts { | |
| position: fixed; | |
| bottom: 15px; | |
| left: 10px; | |
| z-index: 999; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .float-btn { | |
| width: 45px; | |
| height: 45px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 22px; | |
| text-decoration: none; | |
| border: none; | |
| cursor: pointer; | |
| transition: all 0.3s cubic-bezier(0.4,0,0.2,1); | |
| box-shadow: 0 4px 18px rgba(0,0,0,0.5); | |
| position: relative; | |
| } | |
| .float-btn:hover { transform: scale(1.15) translateX(4px); } | |
| .float-btn-wa { | |
| background: linear-gradient(135deg, #25D366, #128C7E); | |
| color: #fff; | |
| box-shadow: 0 4px 18px rgba(37,211,102,0.4); | |
| } | |
| .float-btn-wa:hover { box-shadow: 0 8px 30px rgba(37,211,102,0.7); } | |
| .float-btn-gmail { | |
| background: linear-gradient(135deg, #be5626, #568f47, #177bb6); | |
| color: #fff; | |
| box-shadow: 0 4px 18px rgba(234,67,53,0.4); | |
| } | |
| .float-btn-gmail:hover { box-shadow: 0 8px 30px rgba(234,67,53,0.7); } | |
| .float-btn-tooltip { | |
| position: absolute; | |
| left: 62px; | |
| white-space: nowrap; | |
| background: var(--bg-panel); | |
| color: var(--text-primary); | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| font-weight: 600; | |
| letter-spacing: 0.1em; | |
| padding: 6px 12px; | |
| border-radius: 4px; | |
| border: 1px solid var(--border-gold); | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.2s; | |
| transform: translateX(-4px); | |
| } | |
| .float-btn:hover .float-btn-tooltip { opacity: 1; transform: translateX(0); } | |
| @media (max-width: 600px) { | |
| .misc-grid { grid-template-columns: 1fr; } | |
| .podcasts-grid { grid-template-columns: 1fr; } | |
| .floating-contacts { bottom: 80px; left: 14px; } | |
| .float-btn { width: 46px; height: 46px; font-size: 19px; } | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DEITY IMAGE POPUP | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| #deity-popup-overlay { | |
| display: none; | |
| position: fixed; | |
| inset: 0; | |
| background: rgba(0,0,0,0.72); | |
| z-index: 9998; | |
| backdrop-filter: blur(5px); | |
| } | |
| #deity-popup-overlay.open { display: block; } | |
| #deity-popup { | |
| display: none; | |
| position: fixed; | |
| z-index: 9999; | |
| top: 50%; left: 50%; | |
| transform: translate(-50%, -50%) scale(0.88); | |
| background: linear-gradient(160deg, var(--bg-panel) 0%, var(--bg-card) 100%); | |
| border: 1px solid var(--border-gold); | |
| border-radius: 14px; | |
| box-shadow: 0 0 60px rgba(212,160,23,0.35), 0 0 120px rgba(255,107,0,0.12), inset 0 1px 0 rgba(255,215,0,0.08); | |
| width: 280px; | |
| text-align: center; | |
| padding: 28px 22px 26px; | |
| opacity: 0; | |
| transition: opacity 0.3s ease, transform 0.32s cubic-bezier(0.34,1.56,0.64,1); | |
| } | |
| #deity-popup.open { | |
| display: block; | |
| opacity: 1; | |
| transform: translate(-50%, -50%) scale(1); | |
| } | |
| #deity-popup-img-wrap { | |
| width: 150px; | |
| height: 150px; | |
| border-radius: 50%; | |
| border: 3px solid var(--gold); | |
| box-shadow: 0 0 28px rgba(255,107,0,0.45), 0 0 60px rgba(212,160,23,0.2); | |
| margin: 0 auto 16px; | |
| overflow: hidden; | |
| background: var(--bg-mid); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| } | |
| #deity-popup-img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| border-radius: 50%; | |
| display: none; | |
| } | |
| #deity-popup-icon-fallback { | |
| font-size: 62px; | |
| filter: drop-shadow(0 0 10px rgba(255,107,0,0.6)); | |
| display: block; | |
| } | |
| #deity-popup-spinner { | |
| width: 36px; height: 36px; | |
| border: 3px solid rgba(212,160,23,0.2); | |
| border-top-color: var(--gold); | |
| border-radius: 50%; | |
| animation: dpSpin 0.8s linear infinite; | |
| position: absolute; | |
| } | |
| @keyframes dpSpin { to { transform: rotate(360deg); } } | |
| #deity-popup-name { | |
| font-family: var(--font-heading); | |
| font-size: 17px; | |
| font-weight: 700; | |
| color: var(--gold-bright); | |
| margin-bottom: 6px; | |
| letter-spacing: 0.08em; | |
| } | |
| #deity-popup-epithet { | |
| font-size: 13px; | |
| color: var(--text-muted); | |
| font-style: italic; | |
| margin-bottom: 20px; | |
| line-height: 1.5; | |
| } | |
| #deity-popup-close { | |
| background: linear-gradient(135deg, var(--saffron), var(--gold)); | |
| color: var(--bg-dark); | |
| border: none; | |
| border-radius: 5px; | |
| padding: 8px 26px; | |
| font-family: var(--font-heading); | |
| font-size: 11px; | |
| font-weight: 700; | |
| letter-spacing: 0.18em; | |
| cursor: pointer; | |
| text-transform: uppercase; | |
| transition: opacity 0.2s; | |
| } | |
| #deity-popup-close:hover { opacity: 0.85; } | |
| .deity-card { cursor: pointer; } | |
| .deity-hint { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px; | |
| margin-top: 10px; | |
| font-size: 10px; | |
| font-family: var(--font-heading); | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--saffron); | |
| opacity: 0.55; | |
| transition: opacity var(--transition); | |
| pointer-events: none; | |
| } | |
| .deity-card:hover .deity-hint { opacity: 1; } | |
| .deity-hint-dot { | |
| width: 5px; height: 5px; | |
| border-radius: 50%; | |
| background: var(--saffron); | |
| animation: hintPulse 1.4s ease-in-out infinite; | |
| flex-shrink: 0; | |
| } | |
| @keyframes hintPulse { | |
| 0%,100% { transform: scale(1); opacity: 0.6; } | |
| 50% { transform: scale(1.6); opacity: 1; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- โโโโโโโโโโโโโโโ PAGE LOADING OVERLAY โโโโโโโโโโโโโโโ --> | |
| <div id="page-loader" aria-label="Loading" role="status"> | |
| <div class="loader-om">เฅ</div> | |
| <div class="loader-title">Sanatan Dharma<br>Sacred Texts & Divine Wisdom</div> | |
| <div class="loader-spinner-ring"></div> | |
| <div class="loader-progress-wrap"> | |
| <div class="loader-progress-track"> | |
| <div class="loader-progress-bar" id="loader-bar"></div> | |
| </div> | |
| <div class="loader-status loader-dots" id="loader-status">Awakening the sacred space</div> | |
| </div> | |
| </div> | |
| <script> | |
| (function () { | |
| var bar = document.getElementById('loader-bar'); | |
| var statusEl = document.getElementById('loader-status'); | |
| var loader = document.getElementById('page-loader'); | |
| var progress = 0; | |
| var messages = [ | |
| 'Awakening the sacred space', | |
| 'Loading divine wisdom', | |
| 'Preparing sacred texts', | |
| 'Invoking the deities', | |
| 'Almost ready' | |
| ]; | |
| var msgIdx = 0; | |
| function setProgress(val) { | |
| progress = Math.min(val, 100); | |
| bar.style.width = progress + '%'; | |
| var mIdx = Math.min(Math.floor((progress / 100) * messages.length), messages.length - 1); | |
| if (mIdx !== msgIdx) { | |
| msgIdx = mIdx; | |
| statusEl.textContent = messages[msgIdx]; | |
| } | |
| } | |
| var tick = setInterval(function () { | |
| if (progress < 85) { | |
| setProgress(progress + (Math.random() * 6 + 2)); | |
| } else { | |
| clearInterval(tick); | |
| } | |
| }, 260); | |
| function dismissLoader() { | |
| clearInterval(tick); | |
| setProgress(100); | |
| statusEl.textContent = 'Welcome \uD83D\uDE4F'; | |
| statusEl.classList.remove('loader-dots'); | |
| setTimeout(function () { | |
| loader.classList.add('hidden'); | |
| }, 500); | |
| } | |
| if (document.readyState === 'complete') { | |
| dismissLoader(); | |
| } else { | |
| window.addEventListener('load', dismissLoader); | |
| setTimeout(dismissLoader, 8000); | |
| } | |
| })(); | |
| </script> | |
| <!-- โโโโโโโโโโโโโโโ CANVAS STARS โโโโโโโโโโโโโโโ --> | |
| <canvas id="cosmos-canvas"></canvas> | |
| <!-- โโโโโโโโโโโโโโโ PARTICLES โโโโโโโโโโโโโโโ --> | |
| <div id="particles-container"></div> | |
| <!-- โโโโโโโโโโโโโโโ NAVIGATION โโโโโโโโโโโโโโโ --> | |
| <nav id="main-nav"> | |
| <a href="#hero" class="nav-brand"> | |
| <span class="nav-om">เฅ</span> | |
| <span class="nav-title">Sanatan Dharma</span> | |
| </a> | |
| <button class="nav-toggle" id="nav-toggle" aria-label="Menu">โฐ</button> | |
| <ul class="nav-links" id="nav-links"> | |
| <li><a href="#texts">Sacred Texts</a></li> | |
| <li><a href="#deities">Deities</a></li> | |
| <li><a href="#philosophy">Philosophy</a></li> | |
| <li><a href="#calendar-section">Calendar</a></li> | |
| <li><a href="#family-tree">Family Tree</a></li> | |
| <li><a href="#misconceptions">Myths vs Facts</a></li> | |
| <li><a href="#podcasts">Podcasts</a></li> | |
| <li><a href="#shloka-section">Daily Shloka</a></li> | |
| <li><a href="#pilgrimage-map">Sacred Sites</a></li> | |
| <li><a href="#history-timeline">Timeline</a></li> | |
| <li><a href="#chatbot-section" class="nav-chat-btn" onclick="openGuruChat()">AI Guru</a></li> | |
| </ul> | |
| </nav> | |
| <!-- โโโโโโโโโโโโโโโ HERO โโโโโโโโโโโโโโโ --> | |
| <section id="hero"> | |
| <!-- Mandala SVGs --> | |
| <svg class="mandala-bg" viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg"> | |
| <g fill="none" stroke="#FF6B00" stroke-width="1"> | |
| <circle cx="400" cy="400" r="380"/> | |
| <circle cx="400" cy="400" r="340"/> | |
| <circle cx="400" cy="400" r="300"/> | |
| <circle cx="400" cy="400" r="260"/> | |
| <circle cx="400" cy="400" r="220"/> | |
| <circle cx="400" cy="400" r="180"/> | |
| <circle cx="400" cy="400" r="140"/> | |
| <circle cx="400" cy="400" r="100"/> | |
| <circle cx="400" cy="400" r="60"/> | |
| <!-- Spokes --> | |
| <line x1="400" y1="20" x2="400" y2="780"/> | |
| <line x1="20" y1="400" x2="780" y2="400"/> | |
| <line x1="131" y1="131" x2="669" y2="669"/> | |
| <line x1="669" y1="131" x2="131" y2="669"/> | |
| <line x1="68" y1="268" x2="732" y2="532"/> | |
| <line x1="732" y1="268" x2="68" y2="532"/> | |
| <line x1="268" y1="68" x2="532" y2="732"/> | |
| <line x1="532" y1="68" x2="268" y2="732"/> | |
| <!-- Lotus petals approximation --> | |
| <ellipse cx="400" cy="240" rx="30" ry="60"/> | |
| <ellipse cx="400" cy="560" rx="30" ry="60"/> | |
| <ellipse cx="240" cy="400" rx="60" ry="30"/> | |
| <ellipse cx="560" cy="400" rx="60" ry="30"/> | |
| <ellipse cx="286" cy="286" rx="30" ry="60" transform="rotate(45 286 286)"/> | |
| <ellipse cx="514" cy="286" rx="30" ry="60" transform="rotate(-45 514 286)"/> | |
| <ellipse cx="286" cy="514" rx="30" ry="60" transform="rotate(-45 286 514)"/> | |
| <ellipse cx="514" cy="514" rx="30" ry="60" transform="rotate(45 514 514)"/> | |
| </g> | |
| </svg> | |
| <svg class="mandala-bg mandala-bg-2" viewBox="0 0 600 600" xmlns="http://www.w3.org/2000/svg"> | |
| <g fill="none" stroke="#D4A017" stroke-width="1"> | |
| <polygon points="300,20 580,460 20,460"/> | |
| <polygon points="300,580 20,140 580,140"/> | |
| <circle cx="300" cy="300" r="280"/> | |
| <circle cx="300" cy="300" r="200"/> | |
| <circle cx="300" cy="300" r="120"/> | |
| <circle cx="300" cy="300" r="40"/> | |
| </g> | |
| </svg> | |
| <h1 class="hero-om">เฅ</h1> | |
| <h2 class="hero-title">Sanatan Dharma</h2> | |
| <p class="hero-subtitle">The Eternal Way of Life โ Sacred Wisdom Since Time Immemorial</p> | |
| <div class="hero-divider"> | |
| <span class="hero-divider-line"></span> | |
| <span class="hero-divider-sym">๐</span> | |
| <span class="hero-divider-sym">๐ฑ</span> | |
| <span class="hero-divider-sym">๐</span> | |
| <span class="hero-divider-line"></span> | |
| </div> | |
| <p class="hero-desc"> | |
| Explore the timeless scriptures, divine wisdom, and eternal philosophy of Hinduism โ | |
| the world's oldest living religious tradition. Read sacred texts, seek guidance from | |
| our AI Guru, and immerse in the divine knowledge of the Vedas, Upanishads & Puranas. | |
| </p> | |
| <div class="hero-cta-group"> | |
| <a href="#texts" class="btn-primary">๐ Explore Sacred Texts</a> | |
| <a href="#chatbot-section" class="btn-secondary" onclick="openGuruChat()">๐ค Consult AI Guru</a> | |
| </div> | |
| <div class="scroll-hint"> | |
| <span>Scroll</span> | |
| <div class="scroll-arrow"></div> | |
| </div> | |
| </section> | |
| <!-- โโโโโโโโโโโโโโโ STATS โโโโโโโโโโโโโโโ --> | |
| <div id="stats"> | |
| <div class="stats-inner"> | |
| <div class="stat-item reveal"> | |
| <span class="stat-num">10</span> | |
| <span class="stat-label">Sacred Texts</span> | |
| </div> | |
| <div class="stat-item reveal"> | |
| <span class="stat-num">5000+</span> | |
| <span class="stat-label">Years of Wisdom</span> | |
| </div> | |
| <div class="stat-item reveal"> | |
| <span class="stat-num">33</span> | |
| <span class="stat-label">Crore Devtas</span> | |
| </div> | |
| <div class="stat-item reveal"> | |
| <span class="stat-num">4</span> | |
| <span class="stat-label">Holy Vedas</span> | |
| </div> | |
| <div class="stat-item reveal"> | |
| <span class="stat-num">108</span> | |
| <span class="stat-label">Upanishads</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ SACRED TEXTS โโโโโโโโโโโโโโโ --> | |
| <section id="texts"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">Divine Knowledge</span> | |
| <h2 class="section-title">Sacred Texts of Sanatan Dharma</h2> | |
| <p style="color:var(--text-muted);max-width:580px;margin:0 auto;font-size:16px;"> | |
| Read, explore and download the holiest scriptures of Hinduism โ available to all seekers of divine truth. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span> | |
| <span class="underline-dot"></span> | |
| <span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="books-grid" id="books-grid"> | |
| <!-- Cards injected by JS --> | |
| </div> | |
| </section> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ DEITIES โโโโโโโโโโโโโโโ --> | |
| <section id="deities"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">The Divine Trinity & Beyond</span> | |
| <h2 class="section-title">Gods & Goddesses</h2> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span> | |
| <span class="underline-dot"></span> | |
| <span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="deities-grid"> | |
| <div class="deity-card reveal"><span class="deity-icon">๐ฑ</span><div class="deity-name">Lord Shiva</div><div class="deity-epithet">Mahadeva โ The Destroyer & Regenerator</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐</span><div class="deity-name">Lord Vishnu</div><div class="deity-epithet">Preserver of the Universe</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐</span><div class="deity-name">Brahma</div><div class="deity-epithet">The Creator of All Existence</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐ธ</span><div class="deity-name">Goddess Lakshmi</div><div class="deity-epithet">Bestower of Wealth & Fortune</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">โก</span><div class="deity-name">Goddess Durga</div><div class="deity-epithet">The Invincible Warrior Goddess</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐ต</span><div class="deity-name">Goddess Saraswati</div><div class="deity-epithet">Patroness of Art, Music & Learning</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐</span><div class="deity-name">Lord Ganesha</div><div class="deity-epithet">Remover of All Obstacles</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐บ</span><div class="deity-name">Lord Krishna</div><div class="deity-epithet">The Divine Flute Player</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐น</span><div class="deity-name">Lord Rama</div><div class="deity-epithet">The Ideal King, Maryada Purushottam</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐</span><div class="deity-name">Lord Surya</div><div class="deity-epithet">The Sun God, Source of Life</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐ฆ</span><div class="deity-name">Lord Kartikeya</div><div class="deity-epithet">God of War & Victory</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| <div class="deity-card reveal"><span class="deity-icon">๐</span><div class="deity-name">Hanuman Ji</div><div class="deity-epithet">The Devoted Servant of Rama</div><div class="deity-hint"><span class="deity-hint-dot"></span>Click to view</div></div> | |
| </div> | |
| </section> | |
| <!-- โโโโโโโโโโโโโโโ QUOTE TICKER โโโโโโโโโโโโโโโ --> | |
| <div id="quote-ticker"> | |
| <div class="ticker-inner"> | |
| <div class="ticker-label">เฅฅ เคถเฅเคฐเฅเคคเคฟ เฅฅ</div> | |
| <div class="ticker-track"> | |
| <div class="ticker-content" id="ticker-content"> | |
| <!-- injected by JS --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ PHILOSOPHY โโโโโโโโโโโโโโโ --> | |
| <section id="philosophy"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">Eternal Truths</span> | |
| <h2 class="section-title">Pillars of Sanatan Philosophy</h2> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span> | |
| <span class="underline-dot"></span> | |
| <span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="philosophy-grid"> | |
| <div class="pillar-card reveal"> | |
| <div class="pillar-num">01</div> | |
| <span class="pillar-icon">๐ต</span> | |
| <div class="pillar-title">Dharma</div> | |
| <p class="pillar-text">The cosmic order and moral law that governs all of existence. Right action, righteous conduct, and one's sacred duty in life. The foundation upon which a virtuous life is built.</p> | |
| </div> | |
| <div class="pillar-card reveal"> | |
| <div class="pillar-num">02</div> | |
| <span class="pillar-icon">โ๏ธ</span> | |
| <div class="pillar-title">Karma</div> | |
| <p class="pillar-text">The universal law of cause and effect. Every action creates a corresponding reaction across lifetimes. Pure actions generate positive karma, leading toward liberation.</p> | |
| </div> | |
| <div class="pillar-card reveal"> | |
| <div class="pillar-num">03</div> | |
| <span class="pillar-icon">๐</span> | |
| <div class="pillar-title">Samsara</div> | |
| <p class="pillar-text">The cycle of birth, death and rebirth that the soul traverses until it attains moksha. Each life provides an opportunity to evolve spiritually and grow closer to the divine.</p> | |
| </div> | |
| <div class="pillar-card reveal"> | |
| <div class="pillar-num">04</div> | |
| <span class="pillar-icon">๐ธ</span> | |
| <div class="pillar-title">Moksha</div> | |
| <p class="pillar-text">The ultimate liberation โ freedom from the cycle of rebirth. The merging of the individual soul (Atman) with the universal consciousness (Brahman). The highest goal of human existence.</p> | |
| </div> | |
| <div class="pillar-card reveal"> | |
| <div class="pillar-num">05</div> | |
| <span class="pillar-icon">โจ</span> | |
| <div class="pillar-title">Ahimsa</div> | |
| <p class="pillar-text">Non-violence and non-harming in thought, word and deed. Reverence for all living beings as manifestations of the divine. The cornerstone of Hindu ethics and spiritual practice.</p> | |
| </div> | |
| <div class="pillar-card reveal"> | |
| <div class="pillar-num">06</div> | |
| <span class="pillar-icon">๐</span> | |
| <div class="pillar-title">Bhakti</div> | |
| <p class="pillar-text">Devotional love and surrender to the divine. Through sincere worship and devotion, the devotee dissolves the ego and experiences the grace of the Supreme Being.</p> | |
| </div> | |
| </div> | |
| </section> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ HINDU CALENDAR & FESTIVALS โโโโโโโโโโโโโโโ --> | |
| <section id="calendar-section"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">Vedic Time Keeping</span> | |
| <h2 class="section-title">Hindu Panchang & Festivals</h2> | |
| <p style="color:var(--text-muted);max-width:580px;margin:0 auto;font-size:16px;"> | |
| Today's Tithi, Nakshatra & Paksha โ and the sacred festivals awaiting on the horizon. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span> | |
| <span class="underline-dot"></span> | |
| <span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="calendar-layout"> | |
| <!-- Left: Panchang Card + Countdown --> | |
| <div class="calendar-left-col"> | |
| <div class="panchang-card reveal"> | |
| <div class="panchang-header"> | |
| <span class="panchang-icon">๐</span> | |
| <div class="panchang-header-text"> | |
| <div class="panchang-title">Today's Panchang</div> | |
| <div class="panchang-date-str" id="panchang-date-str">Loadingโฆ</div> | |
| </div> | |
| <span class="moon-phase-display" id="moon-phase-emoji">๐</span> | |
| </div> | |
| <div class="panchang-body"> | |
| <div class="panchang-row"> | |
| <span class="panchang-label">๐ฑ Tithi</span> | |
| <span class="panchang-value" id="pc-tithi">โ</span> | |
| </div> | |
| <div class="panchang-row"> | |
| <span class="panchang-label">โจ Nakshatra</span> | |
| <span class="panchang-value" id="pc-nakshatra">โ</span> | |
| </div> | |
| <div class="panchang-row"> | |
| <span class="panchang-label">๐ Paksha</span> | |
| <span class="panchang-value" id="pc-paksha">โ</span> | |
| </div> | |
| <div class="panchang-row"> | |
| <span class="panchang-label">๐ Masa (Month)</span> | |
| <span class="panchang-value" id="pc-masa">โ</span> | |
| </div> | |
| <div class="panchang-row"> | |
| <span class="panchang-label">โ๏ธ Vara (Day)</span> | |
| <span class="panchang-value" id="pc-vara">โ</span> | |
| </div> | |
| <div class="panchang-row"> | |
| <span class="panchang-label">๐ Vikram Samvat</span> | |
| <span class="panchang-value" id="pc-samvat">โ</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Countdown --> | |
| <div class="countdown-card reveal"> | |
| <span class="countdown-festival-icon" id="cd-icon">๐ช</span> | |
| <div class="countdown-festival-name" id="cd-name">Loadingโฆ</div> | |
| <div class="countdown-label">Next Sacred Festival</div> | |
| <div class="countdown-grid"> | |
| <div class="countdown-unit"> | |
| <span class="countdown-num" id="cd-days">00</span> | |
| <span class="countdown-unit-label">Days</span> | |
| </div> | |
| <div class="countdown-unit"> | |
| <span class="countdown-num" id="cd-hours">00</span> | |
| <span class="countdown-unit-label">Hours</span> | |
| </div> | |
| <div class="countdown-unit"> | |
| <span class="countdown-num" id="cd-mins">00</span> | |
| <span class="countdown-unit-label">Mins</span> | |
| </div> | |
| <div class="countdown-unit"> | |
| <span class="countdown-num" id="cd-secs">00</span> | |
| <span class="countdown-unit-label">Secs</span> | |
| </div> | |
| </div> | |
| <div class="countdown-desc" id="cd-desc"></div> | |
| </div> | |
| </div> | |
| <!-- Right: Upcoming Festivals List --> | |
| <div class="festivals-panel reveal"> | |
| <div class="festivals-panel-header"> | |
| <span class="festivals-panel-title">๐ Upcoming Sacred Festivals. Check with Google.</span> | |
| <span class="festivals-year-badge" id="fest-year-badge">2026โ2027</span> | |
| </div> | |
| <div class="festivals-list" id="festivals-list"> | |
| <!-- Injected by JS --> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <div class="section-sep"></div> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ DEITY FAMILY TREE โโโโโโโโโโโโโโโ --> | |
| <section id="family-tree"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">Divine Lineage</span> | |
| <h2 class="section-title">Deity Family Tree</h2> | |
| <p style="color:var(--text-muted);max-width:580px;margin:0 auto;font-size:16px;"> | |
| Click any deity node to reveal their story, consort, and sacred connections. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span> | |
| <span class="underline-dot"></span> | |
| <span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="family-tree-wrapper reveal"> | |
| <div class="ft-legend"> | |
| <span class="ft-legend-item"><span class="ft-leg-dot" style="background:#FF6B00"></span>Trimurti / Supreme</span> | |
| <span class="ft-legend-item"><span class="ft-leg-dot" style="background:#D4A017"></span>Consort / Shakti</span> | |
| <span class="ft-legend-item"><span class="ft-leg-dot" style="background:#8B0000"></span>Avatar / Form</span> | |
| <span class="ft-legend-item"><span class="ft-leg-dot" style="background:#4a9eff"></span>Child / Progeny</span> | |
| <span class="ft-legend-item"><span class="ft-leg-dot" style="background:#c040c0"></span>Fierce / Shakti Form</span> | |
| <span class="ft-legend-item" style="font-size:9px;color:var(--text-muted);margin-left:auto;">โ Scroll to explore โ</span> | |
| <button onclick="downloadFamilyTree()" class="ft-download-btn" title="Download Family Tree as PNG"> | |
| <i class="fa fa-download"></i> Download | |
| </button> | |
| </div> | |
| <div class="ft-scroll-container"> | |
| <svg id="family-tree-svg" viewBox="0 0 2200 1060" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" style="min-width:2200px;height:auto;"></svg> | |
| </div> | |
| <!-- Deity info panel --> | |
| <div class="ft-info-panel" id="ft-info-panel"> | |
| <div class="ft-info-close" onclick="closeFTPanel()">โ</div> | |
| <div class="ft-info-icon" id="ft-info-icon">๐ฑ</div> | |
| <div class="ft-info-name" id="ft-info-name">Lord Shiva</div> | |
| <div class="ft-info-epithet" id="ft-info-epithet">Mahadeva</div> | |
| <div class="ft-info-body" id="ft-info-body">The supreme deity of destruction and regeneration.</div> | |
| <div class="ft-info-tags" id="ft-info-tags"></div> | |
| </div> | |
| </div> | |
| </section> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ MISCONCEPTIONS โโโโโโโโโโโโโโโ --> | |
| <section id="misconceptions"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">Truth vs Myth</span> | |
| <h2 class="section-title">Myths & Misconceptions โ Debunked</h2> | |
| <p style="color:var(--text-muted);max-width:640px;margin:0 auto;font-size:16px;"> | |
| Many misunderstand Sanatan Dharma due to incomplete knowledge. Here we illuminate the truth behind the most common misconceptions. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span> | |
| <span class="underline-dot"></span> | |
| <span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="misc-grid reveal"> | |
| <!-- 1 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"Hindus worship cows because they think the cow is God."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">Hindus <strong>revere</strong> the cow as a symbol of <strong>Ahimsa (non-violence), abundance, and motherhood</strong> โ not as a God. The cow selflessly gives milk, which nourishes all. She is called <em>Gau Mata</em> (Mother Cow) as a mark of gratitude, just as we honor our own mothers. The Rigveda (6.28) praises the cow as auspicious and life-sustaining.</p> | |
| <div class="misc-source">๐ Source: Rigveda 6.28, Atharva Veda 10.10</div> | |
| </div> | |
| </div> | |
| <!-- 2 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"Hinduism has 330 million Gods โ it is polytheistic."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">Sanatan Dharma is fundamentally <strong>Monistic</strong>. All deities are <strong>manifestations of one Supreme Reality โ Brahman</strong>. The Rigveda states: <em>"Ekam Sat Vipra Bahudha Vadanti"</em> โ Truth is one; the wise call it by many names. The 33 crore figure refers to 33 <em>koti</em> (types/categories) of divine powers, not 330 million separate Gods.</p> | |
| <div class="misc-source">๐ Source: Rigveda 1.164.46, Brihadaranyaka Upanishad 3.9</div> | |
| </div> | |
| </div> | |
| <!-- 3 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐บ</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"The caste system is a core teaching of Hinduism and was always birth-based."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">The original Varna system was based on <strong>Guna (qualities) and Karma (actions)</strong>, not birth. Lord Krishna in the Bhagavad Gita (4.13) says: <em>"Chaturvarnyam maya srishtam gunakarma vibhagashah"</em> โ the four Varnas are created by Me according to qualities and actions. Rigid birth-based caste discrimination is a <strong>social distortion</strong>, not a Vedic teaching.</p> | |
| <div class="misc-source">๐ Source: Bhagavad Gita 4.13, Manusmriti (original interpretation)</div> | |
| </div> | |
| </div> | |
| <!-- 4 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐ง</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"Hindus are fatalistic โ they believe everything is pre-destined."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">Sanatan Dharma strongly emphasizes <strong>Purushartha</strong> โ the four human pursuits requiring active effort: Dharma, Artha, Kama, and Moksha. The Bhagavad Gita's core teaching is <strong>Karma Yoga</strong> โ take right action without attachment to results. You create your own destiny through free will and righteous action. Fate is shaped by past Karma; the present is yours to act.</p> | |
| <div class="misc-source">๐ Source: Bhagavad Gita 3.19, Taittiriya Upanishad 1.11</div> | |
| </div> | |
| </div> | |
| <!-- 5 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">โ๏ธ</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"Hinduism oppresses women and treats them as inferior."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">Ancient Hinduism elevated women profoundly. <strong>Shakti</strong> (the Divine Feminine) is considered the supreme power of the universe. Vedic women like <strong>Gargi and Maitreyi</strong> debated philosophers in royal courts. The <em>Devi Suktam</em> of the Rigveda glorifies the Goddess as the source of all creation. Women held the sacred <em>Grihalakshmi</em> role โ the light of the household. Medieval social distortions must not be confused with Vedic teachings.</p> | |
| <div class="misc-source">๐ Source: Rigveda Devi Suktam, Brihadaranyaka Upanishad 3.6</div> | |
| </div> | |
| </div> | |
| <!-- 6 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"Hinduism is only for Indians / it is a regional religion."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">Sanatan Dharma means <strong>The Eternal Way</strong> โ it belongs to all of humanity and all of time. Its philosophy applies universally: <em>Vasudhaiva Kutumbakam</em> โ "The whole world is one family" (Maha Upanishad 6.71). Historically, Hindu civilization spread across Southeast Asia โ Angkor Wat (Cambodia), Bali, Thailand, and beyond. Anyone who seeks truth is welcome in Sanatan Dharma.</p> | |
| <div class="misc-source">๐ Source: Maha Upanishad 6.71, Rigveda 1.89.1</div> | |
| </div> | |
| </div> | |
| <!-- 7 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"All Hindus must be vegetarian. Eating meat is forbidden."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">While <strong>Ahimsa (non-violence)</strong> and vegetarianism are widely promoted, Hinduism does not universally forbid all meat consumption. Many Hindu communities, including Brahmins of Bengal and Kashmir, traditional warriors, and coastal communities, consume non-vegetarian food. The Vedas distinguish between sacred and permissible foods by context. Dietary choices are left to <strong>individual Dharma, community tradition, and spiritual path</strong>.</p> | |
| <div class="misc-source">๐ Source: Manusmriti (contextual), Mahabharata Anushasana Parva</div> | |
| </div> | |
| </div> | |
| <!-- 8 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"The Ramayana and Mahabharata are just myths/fairy tales."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">These epics are classified as <strong>Itihasa</strong> โ "thus it happened" โ historical narratives. Modern archaeology, geology, and astronomy increasingly support their accounts. The <strong>Saraswati River</strong> mentioned in the Rigveda has been confirmed by satellite imagery. Carbon dating of events described in the Mahabharata aligns with approximately <strong>3100 BCE</strong>. These texts encode profound cosmology, ethics, statecraft, and spiritual science.</p> | |
| <div class="misc-source">๐ Source: Valmiki Ramayana, Mahabharata (Vyasa), Archaeological Survey of India</div> | |
| </div> | |
| </div> | |
| <!-- 9 --> | |
| <div class="misc-card"> | |
| <div class="misc-card-header"> | |
| <span class="misc-icon">๐</span> | |
| <div> | |
| <span class="misc-myth-label">โ Misconception</span> | |
| <div class="misc-myth-text">"Idol worship is superstitious โ Hindus are worshipping stone."</div> | |
| </div> | |
| </div> | |
| <div class="misc-divider"></div> | |
| <div class="misc-card-body"> | |
| <span class="misc-fact-label">โ The Truth</span> | |
| <p class="misc-fact-text">In Hinduism, a <strong>Murti (sacred image)</strong> is not the deity โ it is a <em>Pratika</em> (symbol) that helps the mind concentrate on the <strong>formless Brahman</strong>. Just as a map represents a country but is not the country itself, a Murti is a meditative aid. This practice is called <strong>Saguna Upasana</strong> โ worship of the formless through form. Advanced practitioners move toward Nirguna (formless) meditation, as taught in the Upanishads.</p> | |
| <div class="misc-source">๐ Source: Agama Shastra, Yoga Sutras of Patanjali, Bhagavad Gita 12.2โ5</div> | |
| </div> | |
| </div> | |
| </div><!-- /.misc-grid --> | |
| </section> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ PODCASTS โโโโโโโโโโโโโโโ --> | |
| <section id="podcasts"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">Divine Discourse</span> | |
| <h2 class="section-title">Top Hindu Wisdom Podcasts</h2> | |
| <p style="color:var(--text-muted);max-width:640px;margin:0 auto;font-size:16px;"> | |
| The most-watched discourses on Sanatan Dharma โ Indian masters & Western scholars. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span><span class="underline-dot"></span><span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="podcast-tabs reveal"> | |
| <button class="ptab active" onclick="switchPodcastTab('indian',this)">๐ฎ๐ณ Indian Masters</button> | |
| <button class="ptab" onclick="switchPodcastTab('western',this)">๐ Western Voices</button> | |
| </div> | |
| <!-- FULLSCREEN MODAL PLAYER --> | |
| <div id="yt-modal" style="display:none"> | |
| <div class="yt-modal-inner"> | |
| <div class="yt-modal-player" id="yt-modal-player"> | |
| <div id="yt-player-container"></div> | |
| </div> | |
| <a class="yt-watch-btn" id="yt-watch-link" href="#" target="_blank" rel="noopener">โถ Watch on YouTube instead</a> | |
| </div> | |
| </div> | |
| <!-- INDIAN MASTERS TAB --> | |
| <div class="podcasts-grid reveal" id="ptab-indian"> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=AE8RJw9Mx6s','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/AE8RJw9Mx6s/hqdefault.jpg" alt="Sadhguru Explains Who is SHIVA to Joe Rogan" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ฅ Mystics of India</span> | |
| <span class="podcast-duration-badge">9:17</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Mystics of India</div> | |
| <div class="podcast-title">Sadhguru Explains Who is SHIVA to Joe Rogan</div> | |
| <div class="podcast-meta"><span>โฑ 9 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=I7QwxbImhZI','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/I7QwxbImhZI/hqdefault.jpg" alt="East vs west -- the myths that mystify | Devdutt Pattanaik" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ค TEDx Talk</span> | |
| <span class="podcast-duration-badge">19:55</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ TED</div> | |
| <div class="podcast-title">East vs west -- the myths that mystify | Devdutt Pattanaik</div> | |
| <div class="podcast-meta"><span>โฑ 20 min</span><span>๐ 4.8โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=in4s3bRiPuc','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/in4s3bRiPuc/hqdefault.jpg" alt="Bhagavad Gita Explained In 10 Minutes ft. @GaurGopalDas | TRS Clips" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ฅ Gaur Gopal Das</span> | |
| <span class="podcast-duration-badge">10:05</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Gaur Gopal Das</div> | |
| <div class="podcast-title">Bhagavad Gita Explained In 10 Minutes ft. @GaurGopalDas | TRS Clips</div> | |
| <div class="podcast-meta"><span>โฑ 10 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=2-inNDXoeVE','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/2-inNDXoeVE/hqdefault.jpg" alt="An Invaluable Lesson From The Bhagavad Gita For Your Life | Sadhguru" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Sadhguru</span> | |
| <span class="podcast-duration-badge">9:42</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Sadhguru</div> | |
| <div class="podcast-title">An Invaluable Lesson From The Bhagavad Gita For Your Life | Sadhguru</div> | |
| <div class="podcast-meta"><span>โฑ 9 min</span><span>๐ 4.8โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=8zVWVErAeyY','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/8zVWVErAeyY/hqdefault.jpg" alt="Sri Sri Ravi Shankar" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Gurudev</span> | |
| <span class="podcast-duration-badge">10:06</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Gurudev Sri Sri Ravi Shankar</div> | |
| <div class="podcast-title">THIS Is What Happens When YOU DIE! | Gurudev</div> | |
| <div class="podcast-meta"><span>โฑ 10 min</span><span>๐ 4.8โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=6BzVXl323vc','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/6BzVXl323vc/hqdefault.jpg" alt="Sadhguru Oxford" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Oxford</span> | |
| <span class="podcast-duration-badge">1:26:48</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ ๐ Sadhguru</div> | |
| <div class="podcast-title">Consciousness & the Nature of Reality | Oxford University Cosmologist Dr. Jude Currivan & Sadhguru</div> | |
| <div class="podcast-meta"><span>โฑ 1 hr 26 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| </div><!-- /#ptab-indian --> | |
| <!-- WESTERN VOICES TAB --> | |
| <div class="podcasts-grid reveal" id="ptab-western" style="display:none;"> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=8HAroiXlFqo','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/8HAroiXlFqo/hqdefault.jpg" alt="Alan Watts Hinduism" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Western Classic</span> | |
| <span class="podcast-duration-badge">16:00</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Wake Up Humanity</div> | |
| <div class="podcast-title">Bhagavad Gita: A Message To Modern Man - Alan watts</div> | |
| <div class="podcast-meta"><span>โฑ 16 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=BZA0FqNnQFs','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/BZA0FqNnQFs/hqdefault.jpg" alt="The Bhagavad Gita real Message" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Analyst</span> | |
| <span class="podcast-duration-badge">50:26</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Religion Camp</div> | |
| <div class="podcast-title">The Bhagavad Gita real Message</div> | |
| <div class="podcast-meta"><span>โฑ 50 min</span><span>๐ 4.7โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=4I3Z22lyESM','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/4I3Z22lyESM/hqdefault.jpg" alt="Rupert Spira Advaita" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Non-Duality</span> | |
| <span class="podcast-duration-badge">1:29:34</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Rupert Spira</div> | |
| <div class="podcast-title">Exploring the Essence of Advaita Vedanta</div> | |
| <div class="podcast-meta"><span>โฑ 1 hr 29 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=GMEsszfBYMo','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/GMEsszfBYMo/hqdefault.jpg" alt="Let's Talk Religion" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Western Scholar</span> | |
| <span class="podcast-duration-badge">44:53</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Let's Talk Religion</div> | |
| <div class="podcast-title">Shankara & Advaita Vedanta</div> | |
| <div class="podcast-meta"><span>โฑ 44 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=qtUQUMpjV44','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/qtUQUMpjV44/hqdefault.jpg" alt="VISHNU EXPLAINED: Hindu Mythology Of The Avatars" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Mythologist</span> | |
| <span class="podcast-duration-badge">41:58</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Religion Camp</div> | |
| <div class="podcast-title">VISHNU EXPLAINED: Hindu Mythology Of The Avatars</div> | |
| <div class="podcast-meta"><span>โฑ 41 min</span><span>๐ 4.8โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=4YRxwjOPAkQ','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/4YRxwjOPAkQ/hqdefault.jpg" alt="I studied Hinduism for 1 month. And I fell in love with Instagram." loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ฌ Science</span> | |
| <span class="podcast-duration-badge">10:26</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Nas Daily</div> | |
| <div class="podcast-title">I studied Hinduism for 1 month. And I fell in love with Instagram.</div> | |
| <div class="podcast-meta"><span>โฑ 10 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=9pv_d-evYJg','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/9pv_d-evYJg/hqdefault.jpg" alt="Raghunath Cappo Explains Karma, Dharma, and Rebirth" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Be Here Now</span> | |
| <span class="podcast-duration-badge">16:42</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ JRE Clips</div> | |
| <div class="podcast-title">Raghunath Cappo Explains Karma, Dharma, and Rebirth</div> | |
| <div class="podcast-meta"><span>โฑ 16 min</span><span>๐ 4.9โ </span></div> | |
| </div> | |
| </div> | |
| <div class="podcast-card" onclick="window.open('https://www.youtube.com/watch?v=_J_1wtXMGUg','_blank')"> | |
| <div class="podcast-thumb"> | |
| <img src="https://img.youtube.com/vi/_J_1wtXMGUg/hqdefault.jpg" alt="An Inquiry into Reality: Purusha, Prakriti, and Karmic Flow | Dr Robert Svoboda | Rahul Dewan" loading="lazy"/> | |
| <div class="podcast-play-overlay"><div class="podcast-play-btn"><span class="yt-icon">โถ</span></div></div> | |
| <span class="podcast-views-badge">๐ Karma</span> | |
| <span class="podcast-duration-badge">1:08:52</span> | |
| </div> | |
| <div class="podcast-info"> | |
| <div class="podcast-channel">๐ Sangam Talks</div> | |
| <div class="podcast-title">An Inquiry into Reality: Purusha, Prakriti, and Karmic Flow | Dr Robert Svoboda | Rahul Dewan</div> | |
| <div class="podcast-meta"><span>โฑ 1 hr 08 min</span><span>๐ 4.8โ </span></div> | |
| </div> | |
| </div> | |
| </div><!-- /#ptab-western --> | |
| </section> | |
| <!-- โโโโโโโโโโโโโโโ DAILY SHLOKA โโโโโโโโโโโโโโโ --> | |
| <div class="section-sep"></div> | |
| <section id="shloka-section"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">๐ค AI & Learning</span> | |
| <h2 class="section-title">Daily Shloka</h2> | |
| <p style="color:var(--text-muted);max-width:600px;margin:0 auto;font-size:16px;"> | |
| A sacred verse from the ancient scriptures โ uniquely selected each day by AI, with deep Groq-powered explanation of meaning, context & significance. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span><span class="underline-dot"></span><span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="shloka-outer reveal"> | |
| <div class="shloka-card" id="shloka-card"> | |
| <div class="shloka-day-badge" id="shloka-day-badge">Today's Verse</div> | |
| <div class="shloka-source-tag" id="shloka-source-tag"></div> | |
| <div class="shloka-sanskrit" id="shloka-sanskrit"></div> | |
| <div class="shloka-transliteration" id="shloka-transliteration"></div> | |
| <div class="shloka-translation" id="shloka-translation"></div> | |
| <div class="shloka-ai-panel" id="shloka-ai-panel"> | |
| <div class="shloka-ai-header"> | |
| <span class="shloka-ai-icon">๐ค</span> | |
| <span>Groq AI Explanation</span> | |
| <span class="shloka-ai-badge">Powered by Groq</span> | |
| </div> | |
| <div class="shloka-ai-body" id="shloka-ai-body"> | |
| <div id="shloka-ai-placeholder" class="shloka-ai-placeholder"> | |
| <p>Receive a deep breakdown of this verse โ its meaning, philosophical context, and timeless significance.</p> | |
| </div> | |
| <div id="shloka-ai-content" class="shloka-ai-content" style="display:none;"></div> | |
| <div id="shloka-ai-loading" class="shloka-ai-loading" style="display:none;"> | |
| <div class="shloka-loader-ring"></div> | |
| <span>Consulting the scriptures<span class="loader-dots"></span></span> | |
| </div> | |
| </div> | |
| <button class="shloka-explain-btn" id="shloka-explain-btn" onclick="explainShloka()"> | |
| โจ Reveal AI Explanation | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Navigation arrows --> | |
| <div class="shloka-nav"> | |
| <button class="shloka-nav-btn" onclick="changeShloka(-1)" title="Previous verse">โน</button> | |
| <button class="shloka-nav-btn" onclick="changeShloka(1)" title="Next verse">โบ</button> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- โโโโโโโโโโโโโโโ PILGRIMAGE MAP โโโโโโโโโโโโโโโ --> | |
| <div class="section-sep"></div> | |
| <section id="pilgrimage-map"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">๐บ๏ธ Exploration</span> | |
| <h2 class="section-title">Sacred Sites & Pilgrimage Map</h2> | |
| <p style="color:var(--text-muted);max-width:600px;margin:0 auto;font-size:16px;"> | |
| Explore the holiest sites of Sanatan Dharma โ across India and the entire world. Click any marker for details. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span><span class="underline-dot"></span><span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="map-outer reveal"> | |
| <div class="map-legend"> | |
| <div class="map-legend-item"><span class="legend-dot" style="background:#FF6B00;box-shadow:0 0 8px #FF6B00;"></span> Char Dham</div> | |
| <div class="map-legend-item"><span class="legend-dot" style="background:#FFD700;box-shadow:0 0 8px #FFD700;"></span> Jyotirlinga</div> | |
| <div class="map-legend-item"><span class="legend-dot" style="background:#C0392B;box-shadow:0 0 8px #C0392B;"></span> Shakti Peetha</div> | |
| <div class="map-legend-item"><span class="legend-dot" style="background:#7B68EE;box-shadow:0 0 8px #7B68EE;"></span> Global Sacred</div> | |
| <div class="map-legend-item"><span class="legend-dot" style="background:#6B8E23;box-shadow:0 0 8px #6B8E23;"></span> Other Sacred</div> | |
| </div> | |
| <div id="india-map-container"></div> | |
| <div id="site-info-panel" class="site-info-panel"> | |
| <button class="site-info-close" onclick="closeSitePanel()">โ</button> | |
| <div class="site-info-icon" id="site-icon"></div> | |
| <div class="site-info-name" id="site-name"></div> | |
| <div class="site-info-type" id="site-type"></div> | |
| <div class="site-info-state" id="site-state"></div> | |
| <p class="site-info-desc" id="site-desc"></p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- โโโโโโโโโโโโโโโ HISTORY TIMELINE โโโโโโโโโโโโโโโ --> | |
| <div class="section-sep"></div> | |
| <section id="history-timeline"> | |
| <div class="section-header reveal"> | |
| <span class="section-label">๐บ๏ธ Exploration</span> | |
| <h2 class="section-title">Timeline of Hindu History</h2> | |
| <p style="color:var(--text-muted);max-width:600px;margin:0 auto;font-size:16px;"> | |
| From the cosmic dawn of the Vedic Age to the present โ key epochs, sages, and events that shaped Sanatan Dharma. | |
| </p> | |
| <div class="section-underline"> | |
| <span class="underline-line"></span><span class="underline-dot"></span><span class="underline-line right"></span> | |
| </div> | |
| </div> | |
| <div class="timeline-scroll-hint">โ Scroll to explore โ</div> | |
| <div class="timeline-outer" id="timeline-outer"> | |
| <div class="timeline-track" id="timeline-track"> | |
| <!-- injected by JS --> | |
| </div> | |
| </div> | |
| <div class="timeline-scroll-bar"> | |
| <div class="timeline-scroll-thumb" id="timeline-thumb"></div> | |
| </div> | |
| </section> | |
| <!-- โโโโโโโโโโโโโโโ AI GURU FAB โโโโโโโโโโโโโโโ --> | |
| <!-- FAB trigger button --> | |
| <button id="guru-fab" aria-label="Open Dharma Guru AI" onclick="toggleGuruChat()"> | |
| <div class="guru-fab-pulse"></div> | |
| <span class="fab-open">เฅ</span> | |
| <span class="fab-close" style="font-size:20px;">โ</span> | |
| <span class="guru-fab-tooltip">Dharma Guru AI</span> | |
| </button> | |
| <!-- Floating chat panel --> | |
| <div id="guru-chat" role="dialog" aria-label="Dharma Guru AI"> | |
| <!-- Header --> | |
| <div class="guru-header"> | |
| <div class="guru-avatar">เฅ<div class="guru-status-dot"></div></div> | |
| <div class="guru-header-text"> | |
| <div class="guru-header-name">Dharma Guru AI</div> | |
| <div class="guru-header-sub" id="guru-sub-text">Vedas ยท Upanishads ยท Bhagavad Gita ยท Puranas</div> | |
| </div> | |
| <!-- Model switcher --> | |
| <button id="guru-switcher-btn" onclick="toggleGuruSwitcher(event)" title="Switch model"> | |
| <span class="guru-model-badge" id="guru-badge">Groq</span> | |
| <svg viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/></svg> | |
| </button> | |
| <div id="guru-switcher-menu"> | |
| <div class="guru-sw-title">Select Model</div> | |
| <button class="guru-sw-item active" id="sw-llama70b" onclick="switchGuruModel('groq:llama-3.3-70b-versatile','Llama 3.3 70B',this)"> | |
| <div class="guru-sw-dot" style="background:#f59e0b;"></div> | |
| <div class="guru-sw-info"> | |
| <div class="guru-sw-name">LLaMA 3.3 70B</div> | |
| <div class="guru-sw-model">Groq ยท Best quality</div> | |
| </div> | |
| <span class="guru-sw-status connected" id="sw-70b-status">Active</span> | |
| </button> | |
| <button class="guru-sw-item" id="sw-llama8b" onclick="switchGuruModel('groq:llama-3.1-8b-instant','Llama 3.1 8B',this)"> | |
| <div class="guru-sw-dot" style="background:#fbbf24;"></div> | |
| <div class="guru-sw-info"> | |
| <div class="guru-sw-name">LLaMA 3.1 8B</div> | |
| <div class="guru-sw-model">Groq ยท Fastest</div> | |
| </div> | |
| <span class="guru-sw-status disconnected" id="sw-8b-status">Ready</span> | |
| </button> | |
| <div class="guru-sw-divider"></div> | |
| <button class="guru-sw-manage" onclick="openGuruKeyModal()"> | |
| ๐ Manage Groq API Key | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Toggle row: Web Search + RAG --> | |
| <div class="guru-toggle-row"> | |
| <label class="guru-toggle" title="Enable web search for current information"> | |
| <input type="checkbox" class="gtoggle-cb" id="web-search-cb" checked /> | |
| <span class="gtoggle-sw"></span> | |
| <span class="gtoggle-label">๐ Web Search</span> | |
| </label> | |
| <label class="guru-toggle" title="Enable RAG: AI answers from your uploaded sacred text PDFs"> | |
| <input type="checkbox" class="gtoggle-cb" id="rag-cb" onchange="toggleRAG(this.checked)" /> | |
| <span class="gtoggle-sw"></span> | |
| <span class="gtoggle-label">๐ Sacred Texts RAG</span> | |
| </label> | |
| </div> | |
| <!-- RAG Status Bar --> | |
| <div id="rag-status-bar"> | |
| <span class="rag-badge">RAG ACTIVE</span> | |
| <span class="rag-text" id="rag-status-text">No texts indexed yet. Index a PDF to enable retrieval.</span> | |
| <div class="rag-book-pills" id="rag-book-pills"></div> | |
| <button class="rag-index-btn" id="rag-index-btn" onclick="openRAGIndexModal()">๏ผ Index PDF</button> | |
| <div id="rag-progress" style="width:100%"><div id="rag-progress-bar"></div></div> | |
| </div> | |
| <!-- Messages --> | |
| <div class="guru-msgs" id="chat-messages"> | |
| <div class="msg bot"> | |
| <div class="msg-avatar">เฅ</div> | |
| <div class="msg-body"> | |
| <div class="msg-bubble"> | |
| ๐ <strong>Namaste!</strong> I am <em>Dharma Guru</em>, your guide to the eternal wisdom of Sanatan Dharma.<br><br> | |
| Ask me anything about the Vedas, Upanishads, Bhagavad Gita, Ramayana, Mahabharata, Puranas, Hindu philosophy, mythology, and rituals.<br><br> | |
| How may I serve you today? | |
| </div> | |
| <div class="msg-time" id="init-time"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Suggestion chips --> | |
| <div class="guru-chips" id="guru-chips"> | |
| <button class="suggestion-chip" onclick="sendSuggestion(this)">Meaning of OM?</button> | |
| <button class="suggestion-chip" onclick="sendSuggestion(this)">Bhagavad Gita teachings</button> | |
| <button class="suggestion-chip" onclick="sendSuggestion(this)">The 4 Vedas</button> | |
| <button class="suggestion-chip" onclick="sendSuggestion(this)">What is Moksha?</button> | |
| <button class="suggestion-chip" onclick="sendSuggestion(this)">Lord Hanuman</button> | |
| <button class="suggestion-chip" onclick="sendSuggestion(this)">Ashta Dikpalakas</button> | |
| </div> | |
| <!-- Input row --> | |
| <div class="guru-input-row"> | |
| <textarea id="chat-input" rows="1" placeholder="Ask about Vedas, deities, rituals, philosophyโฆ" | |
| onkeydown="guruKeydown(event)" oninput="guruAutoGrow(this)"></textarea> | |
| <button id="send-btn" onclick="sendMessage()" title="Send"> | |
| <svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> | |
| </button> | |
| <button class="input-clear" id="input-clear" onclick="clearInput()" style="display:none"></button> | |
| </div> | |
| <div class="guru-footer-note">Sacred knowledge ยท Powered by Groq AI</div> | |
| </div> | |
| <!-- API Key Modal --> | |
| <div id="guru-key-modal" style="display:none;"> | |
| <div class="guru-key-box"> | |
| <button class="guru-modal-x" onclick="closeGuruKeyModal()" title="Close">โ</button> | |
| <h3>๐ Connect Groq AI</h3> | |
| <p class="guru-key-sub"> | |
| Get a <strong style="color:var(--gold-pale)">free Groq API key</strong> at | |
| <a href="https://console.groq.com/keys" target="_blank">console.groq.com/keys</a> โ no credit card needed. Your key is saved only in your browser. | |
| </p> | |
| <div class="guru-saved-badge" id="guru-saved-badge" style="display:none;">โ Key saved โ Guru is connected</div> | |
| <input id="guru-key-input" type="password" placeholder="gsk_xxxxxxxxxxxxxxxxxxxx" autocomplete="off" /> | |
| <div class="guru-key-btns"> | |
| <button id="guru-key-save" onclick="saveGuruKey()">Connect & Start</button> | |
| <button id="guru-key-skip" onclick="closeGuruKeyModal()">Close</button> | |
| </div> | |
| <p class="guru-modal-note">Key stored locally in your browser ยท Never sent to any server except Groq</p> | |
| </div> | |
| </div> | |
| <!-- โโโโโโโโโโโโโโโ FLOATING CONTACT BUTTONS โโโโโโโโโโโโโโโ --> | |
| <div class="floating-contacts" aria-label="Contact buttons"> | |
| <a class="float-btn float-btn-wa" href="https://wa.me/+8801719296601" target="_blank" rel="noopener" aria-label="Chat on WhatsApp"> | |
| <svg width="26" height="26" viewBox="0 0 32 32" fill="white" xmlns="http://www.w3.org/2000/svg"> | |
| <path d="M16 3C8.82 3 3 8.82 3 16c0 2.39.65 4.63 1.79 6.56L3 29l6.63-1.74A12.94 12.94 0 0016 29c7.18 0 13-5.82 13-13S23.18 3 16 3zm6.46 17.88c-.27.77-1.58 1.47-2.18 1.56-.56.09-1.27.13-2.04-.13-.47-.16-1.08-.37-1.85-.72-3.26-1.41-5.38-4.69-5.54-4.91-.16-.22-1.3-1.73-1.3-3.3s.82-2.34 1.11-2.66c.29-.32.63-.4.84-.4h.6c.19 0 .45-.07.7.54.27.63.91 2.23.99 2.39.08.16.13.35.03.56-.1.21-.16.34-.31.52-.16.18-.33.4-.47.54-.16.16-.32.33-.14.64.18.31.82 1.35 1.76 2.19 1.21 1.07 2.22 1.4 2.54 1.56.32.16.5.13.69-.08.19-.21.82-.96 1.04-1.28.22-.32.43-.27.72-.16.29.11 1.85.87 2.17 1.03.32.16.53.24.61.37.08.13.08.74-.19 1.51z"/> | |
| </svg> | |
| <span class="float-btn-tooltip">๐ฌ Chat on WhatsApp</span> | |
| </a> | |
| <!-- Gmail Button --> | |
| <a href="https://mail.google.com/mail/?view=cm&fs=1&to=shukdevdatta@gmail.com" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| class="float-btn float-btn-gmail" | |
| aria-label="Send Email via Gmail"> | |
| <span class="float-btn-tooltip">Send me an email</span> | |
| <svg class="btn-icon" viewBox="0 52 88 66" xmlns="http://www.w3.org/2000/svg" width="28" height="28"> | |
| <path fill="#4285F4" d="M58 108H30V75L4 55V112c0 3.3 2.7 6 6 6h72c3.3 0 6-2.7 6-6V55L62 75z"/> | |
| <path fill="#34A853" d="M62 108h26V55z"/> | |
| <path fill="#FBBC04" d="M4 55v53l26-33z"/> | |
| <path fill="#EA4335" d="M82 52H6L44 81z"/> | |
| <path fill="#C5221F" d="M6 52l24 23h28L82 52z"/> | |
| </svg> | |
| </a> | |
| </div> | |
| <div class="section-sep"></div> | |
| <!-- โโโโโโโโโโโโโโโ FOOTER โโโโโโโโโโโโโโโ --> | |
| <footer> | |
| <div class="footer-inner"> | |
| <div class="footer-top"> | |
| <div class="footer-brand"> | |
| <span class="nav-om">เฅ</span> | |
| <div class="footer-brand-name">Sanatan Dharma</div> | |
| <p class="footer-brand-text"> | |
| A digital sanctuary for seekers of eternal wisdom. Preserving and sharing the divine knowledge of Hinduism โ the world's oldest and most profound spiritual tradition. | |
| </p> | |
| </div> | |
| <div> | |
| <div class="footer-col-title">Sacred Texts</div> | |
| <ul class="footer-links"> | |
| <a href="#texts">Rig Veda</a> | |
| <a href="#texts">Bhagavad Gita</a> | |
| <a href="#texts">Ramayana</a> | |
| <a href="#texts">Mahabharata</a> | |
| <a href="#texts">Upanishads</a> | |
| </ul> | |
| </div> | |
| <div> | |
| <div class="footer-col-title">Explore</div> | |
| <ul class="footer-links"> | |
| <li><a href="#deities">Deities</a></li> | |
| <li><a href="#philosophy">Philosophy</a></li> | |
| <li><a href="#misconceptions">Myths vs Facts</a></li> | |
| <li><a href="#podcasts">Podcasts</a></li> | |
| <li><a href="#chatbot-section" onclick="openGuruChat()">AI Guru</a></li> | |
| <li><a href="#texts">All Texts</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <div class="footer-col-title">Resources</div> | |
| <ul class="footer-links"> | |
| <li><a href="https://www.sacred-texts.com/hin/" target="_blank">Sacred Texts Archive</a></li> | |
| <li><a href="https://www.gitasupersite.iitk.ac.in/" target="_blank">Gita Supersite IIT</a></li> | |
| <li><a href="https://vedabase.io/" target="_blank">Vedabase.io</a></li> | |
| <li><a href="https://www.hindupedia.com/" target="_blank">Hindupedia</a></li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="footer-bottom"> | |
| <div class="footer-copy">เฅฅ เคธเคฐเฅเคตเฅ เคญเคตเคจเฅเคคเฅ เคธเฅเคเคฟเคจเค เฅฅ โ May all beings be happy</div> | |
| <div class="footer-mantra">เฅ เคจเคฎเค เคถเคฟเคตเคพเคฏ | เฅ เคจเคฎเฅ เคจเคพเคฐเคพเคฏเคฃ</div> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- โโโโโโโโโโโโโโโ SCROLL TO TOP โโโโโโโโโโโโโโโ --> | |
| <button id="scroll-top-btn" onclick="window.scrollTo({top:0,behavior:'smooth'})" title="Back to top" aria-label="Scroll to top"> | |
| โ | |
| </button> | |
| <!-- โโโโโโโโโโโโโโโ PDF VIEWER MODAL โโโโโโโโโโโโโโโ --> | |
| <div id="pdf-modal" role="dialog" aria-modal="true"> | |
| <div class="pdf-modal-header"> | |
| <div class="pdf-modal-title" id="pdf-modal-title">Loadingโฆ</div> | |
| <div class="pdf-modal-controls"> | |
| <span class="pdf-page-info" id="pdf-page-info" style="font-size:11px;color:var(--text-muted);">๐ Use browser scrollbar to navigate</span> | |
| <a id="pdf-newtab-btn" class="pdf-ctrl-btn" target="_blank" title="Open in new tab" style="text-decoration:none;font-size:13px;">โ New Tab</a> | |
| <button class="pdf-close-btn" onclick="closePDFModal()" title="Close">โ</button> | |
| </div> | |
| </div> | |
| <div class="pdf-viewer-body"> | |
| <div id="pdf-canvas-container"> | |
| <div class="pdf-loading" id="pdf-loading"> | |
| <div class="pdf-spinner"></div> | |
| <span style="font-family:var(--font-heading);font-size:12px;letter-spacing:0.2em;color:var(--text-muted);margin-top:12px;">Opening Sacred Textโฆ</span> | |
| </div> | |
| <iframe id="pdf-iframe" | |
| style="display:none;width:100%;height:100%;min-height:75vh;border:none;flex:1;" | |
| allowfullscreen | |
| title="PDF Viewer"> | |
| </iframe> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- iframe-based PDF viewer โ no external library needed --> | |
| <script> | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| BOOK DATA | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const BOOKS = [ | |
| { | |
| title: "Rig Veda", | |
| file: "Rig Veda.pdf", | |
| symbol: "๐", | |
| category: "Veda", | |
| bg: "linear-gradient(135deg,#FF6B00,#D4A017)", | |
| desc: "The oldest and most revered of the four Vedas, containing 1,028 hymns of praise to the Vedic deities." | |
| }, | |
| { | |
| title: "Sam Veda", | |
| file: "Sam Veda.pdf", | |
| symbol: "๐ต", | |
| category: "Veda", | |
| bg: "linear-gradient(135deg,#8B0000,#D4A017)", | |
| desc: "The Veda of melodies and chants โ a liturgical collection of musical mantras for Vedic rituals." | |
| }, | |
| { | |
| title: "Yajur Veda", | |
| file: "Yajur Veda.pdf", | |
| symbol: "๐ฅ", | |
| category: "Veda", | |
| bg: "linear-gradient(135deg,#C0392B,#FF6B00)", | |
| desc: "The manual of sacrificial rites. Contains prose mantras for use in yajna (fire sacrifices)." | |
| }, | |
| { | |
| title: "Atharva Veda", | |
| file: "Atharva Veda.pdf", | |
| symbol: "๐ฟ", | |
| category: "Veda", | |
| bg: "linear-gradient(135deg,#1a7a1a,#D4A017)", | |
| desc: "The Veda of healing, magic and daily life wisdom โ containing hymns, spells, and philosophical speculations." | |
| }, | |
| { | |
| title: "Upanishads", | |
| file: "Upanishads.pdf", | |
| symbol: "๐ง", | |
| category: "Vedanta", | |
| bg: "linear-gradient(135deg,#4B0082,#D4A017)", | |
| desc: "The philosophical conclusions of the Vedas revealing the nature of Brahman, Atman, and the ultimate reality." | |
| }, | |
| { | |
| title: "Bhagavad Gita As It Is", | |
| file: "Bhagavad-gita-As-It-Is.pdf", | |
| symbol: "๐ง ", | |
| category: "Smriti", | |
| bg: "linear-gradient(135deg,#FF6B00,#4B0082)", | |
| desc: "The Song of God โ Lord Krishna's divine discourse to Arjuna on the battlefield of Kurukshetra." | |
| }, | |
| { | |
| title: "Ramayana", | |
| file: "Ramayana.pdf", | |
| symbol: "๐น", | |
| category: "Itihasa", | |
| bg: "linear-gradient(135deg,#006400,#D4A017)", | |
| desc: "The epic journey of Lord Rama โ the ideal man and king โ his devotion, sacrifice, and victory of righteousness." | |
| }, | |
| { | |
| title: "Mahabharata", | |
| file: "Mahabharata (Unabridged in English).pdf", | |
| symbol: "โ๏ธ", | |
| category: "Itihasa", | |
| bg: "linear-gradient(135deg,#8B0000,#333)", | |
| desc: "The longest epic ever written โ the great war of Kurukshetra and timeless dharmic wisdom of the Kuru dynasty." | |
| }, | |
| { | |
| title: "Puranas", | |
| file: "Puranas.pdf", | |
| symbol: "๐", | |
| category: "Purana", | |
| bg: "linear-gradient(135deg,#D4A017,#8B0000)", | |
| desc: "Ancient encyclopedic texts containing cosmology, genealogies of gods, heroes, sages and folk tales." | |
| }, | |
| { | |
| title: "Hanuman Chalisa", | |
| file: "Hanuman_Chalisa_in_English.pdf", | |
| symbol: "๐บ", | |
| category: "Stotra", | |
| bg: "linear-gradient(135deg,#FF6B00,#8B0000)", | |
| desc: "The forty verses in praise of Hanuman Ji by Tulsidas โ among the most recited Hindu devotional hymns." | |
| } | |
| ]; | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| RENDER BOOK CARDS | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| function renderBooks() { | |
| const grid = document.getElementById('books-grid'); | |
| BOOKS.forEach((book, i) => { | |
| const card = document.createElement('div'); | |
| card.className = 'book-card reveal'; | |
| card.innerHTML = ` | |
| <div class="book-cover" style="background:${book.bg}20;"> | |
| <div class="book-cover-art" style="background:${book.bg};"></div> | |
| <span class="book-symbol">${book.symbol}</span> | |
| <span class="book-category">${book.category}</span> | |
| </div> | |
| <div class="book-info"> | |
| <div class="book-title">${book.title}</div> | |
| <div class="book-desc">${book.desc}</div> | |
| <div class="book-actions"> | |
| <button class="btn-read" onclick="openPDF('${book.file}','${book.title}')"> | |
| ๐ Read | |
| </button> | |
| <button class="btn-read" onclick="indexBookForRAG('${book.file}','${book.title}')" style="background:rgba(75,0,130,0.3);border-color:rgba(75,0,130,0.5);color:#c084fc;font-size:10px;padding:6px 12px;" title="Index this PDF for AI RAG retrieval"> | |
| ๐ง Index for AI | |
| </button> | |
| </div> | |
| </div>`; | |
| grid.appendChild(card); | |
| }); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PDF VIEWER โ UNIVERSAL IFRAME APPROACH | |
| Works with: file://, localhost, GitHub Pages, any server. | |
| No PDF.js needed. Browser's native PDF renderer handles | |
| everything. Zoom, scroll, search all built-in. | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| let currentPDFFile = ''; | |
| let currentPDFTitle = ''; | |
| function openPDF(filename, title) { | |
| currentPDFFile = filename; | |
| currentPDFTitle = title; | |
| const modal = document.getElementById('pdf-modal'); | |
| const iframe = document.getElementById('pdf-iframe'); | |
| const loading = document.getElementById('pdf-loading'); | |
| const tabBtn = document.getElementById('pdf-newtab-btn'); | |
| /* Update header */ | |
| document.getElementById('pdf-modal-title').textContent = 'เฅฅ ' + title + ' เฅฅ'; | |
| tabBtn.href = filename; | |
| /* Show modal + spinner */ | |
| modal.classList.add('open'); | |
| document.body.style.overflow = 'hidden'; | |
| const scrollBtn = document.getElementById('scroll-top-btn'); | |
| if (scrollBtn) { scrollBtn.classList.remove('visible'); scrollBtn.style.display = 'none'; } | |
| loading.style.display = 'flex'; | |
| iframe.style.display = 'none'; | |
| iframe.src = ''; /* reset first to avoid stale content */ | |
| /* Small delay so the spinner renders, then load iframe */ | |
| setTimeout(() => { | |
| iframe.onload = () => { | |
| loading.style.display = 'none'; | |
| iframe.style.display = 'block'; | |
| }; | |
| iframe.onerror = () => { | |
| loading.style.display = 'none'; | |
| iframe.style.display = 'none'; | |
| showPDFError(filename, title); | |
| }; | |
| /* Set src โ works on file://, http://, https:// */ | |
| iframe.src = filename; | |
| /* Fallback: if onload hasn't fired in 4s, just show it anyway | |
| (some browsers don't fire onload for PDFs in iframes) */ | |
| setTimeout(() => { | |
| if (iframe.style.display === 'none') { | |
| loading.style.display = 'none'; | |
| iframe.style.display = 'block'; | |
| } | |
| }, 4000); | |
| }, 80); | |
| } | |
| function showPDFError(filename, title) { | |
| document.getElementById('pdf-loading').innerHTML = ` | |
| <div style="text-align:center;padding:40px 28px;max-width:560px;"> | |
| <div style="font-size:48px;margin-bottom:16px;">๐</div> | |
| <div style="font-family:var(--font-heading);font-size:15px;letter-spacing:0.1em; | |
| color:var(--gold-pale);margin-bottom:12px;">${title}</div> | |
| <p style="color:var(--text-muted);font-size:14px;line-height:1.9;margin-bottom:22px;"> | |
| Your browser blocked the PDF from loading inside the viewer.<br> | |
| Click <strong style="color:var(--saffron)">Open in New Tab</strong> in the header above, | |
| or download the file below. | |
| </p> | |
| <a href="${filename}" download | |
| style="display:inline-block;background:linear-gradient(135deg,var(--saffron),var(--gold)); | |
| color:var(--bg-dark);padding:11px 28px;border-radius:3px;text-decoration:none; | |
| font-family:var(--font-heading);font-size:11px;font-weight:700; | |
| letter-spacing:0.2em;text-transform:uppercase;margin-right:10px;"> | |
| โฌ Download PDF | |
| </a> | |
| <a href="${filename}" target="_blank" | |
| style="display:inline-block;border:1px solid var(--gold); | |
| color:var(--gold);padding:11px 28px;border-radius:3px;text-decoration:none; | |
| font-family:var(--font-heading);font-size:11px;font-weight:700; | |
| letter-spacing:0.2em;text-transform:uppercase;"> | |
| โ Open in New Tab | |
| </a> | |
| </div>`; | |
| document.getElementById('pdf-loading').style.display = 'flex'; | |
| } | |
| function closePDFModal() { | |
| const modal = document.getElementById('pdf-modal'); | |
| const iframe = document.getElementById('pdf-iframe'); | |
| modal.classList.remove('open'); | |
| document.body.style.overflow = ''; | |
| const scrollBtn = document.getElementById('scroll-top-btn'); | |
| if (scrollBtn) { | |
| scrollBtn.style.display = ''; | |
| if (window.scrollY > 400) scrollBtn.classList.add('visible'); | |
| else scrollBtn.classList.remove('visible'); | |
| } | |
| /* Stop the iframe from continuing to stream/render */ | |
| iframe.src = ''; | |
| iframe.style.display = 'none'; | |
| /* Restore spinner for next open */ | |
| const loading = document.getElementById('pdf-loading'); | |
| loading.style.display = 'flex'; | |
| loading.innerHTML = ` | |
| <div class="pdf-spinner"></div> | |
| <span style="font-family:var(--font-heading);font-size:12px;letter-spacing:0.2em; | |
| color:var(--text-muted);margin-top:12px;">Opening Sacred Textโฆ</span>`; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FORCE DOWNLOAD โ fetch โ Blob โ anchor click | |
| Bypasses browser's "open inline" behaviour for PDFs. | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| function downloadPDF(filename, title) { | |
| // Show a quick visual cue on the button | |
| const btns = document.querySelectorAll('.btn-download'); | |
| let clickedBtn = null; | |
| btns.forEach(b => { | |
| if (b.getAttribute('onclick') && b.getAttribute('onclick').includes(filename)) { | |
| clickedBtn = b; | |
| } | |
| }); | |
| if (clickedBtn) { | |
| const orig = clickedBtn.textContent; | |
| clickedBtn.textContent = 'โณ Preparingโฆ'; | |
| clickedBtn.disabled = true; | |
| fetch(filename) | |
| .then(res => { | |
| if (!res.ok) throw new Error('Network response was not ok'); | |
| return res.blob(); | |
| }) | |
| .then(blob => { | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| setTimeout(() => URL.revokeObjectURL(url), 10000); | |
| clickedBtn.textContent = 'โ Downloaded!'; | |
| setTimeout(() => { | |
| clickedBtn.textContent = orig; | |
| clickedBtn.disabled = false; | |
| }, 2000); | |
| }) | |
| .catch(() => { | |
| // Fallback: direct navigation with download attribute | |
| const a = document.createElement('a'); | |
| a.href = filename; | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| clickedBtn.textContent = orig; | |
| clickedBtn.disabled = false; | |
| }); | |
| } else { | |
| // Fallback if button not found | |
| fetch(filename) | |
| .then(res => res.blob()) | |
| .then(blob => { | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| }) | |
| .catch(() => { | |
| const a = document.createElement('a'); | |
| a.href = filename; | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| }); | |
| } | |
| } | |
| /* Stub nav functions (no-op for iframe mode, kept so old | |
| calls in keyboard handler don't throw ReferenceErrors) */ | |
| function pdfPrevPage() {} | |
| function pdfNextPage() {} | |
| function pdfZoomIn() {} | |
| function pdfZoomOut() {} | |
| /* Close modal on backdrop */ | |
| document.getElementById('pdf-modal').addEventListener('click', function(e) { | |
| if (e.target === this) closePDFModal(); | |
| }); | |
| /* Keyboard navigation */ | |
| document.addEventListener('keydown', function(e) { | |
| const modal = document.getElementById('pdf-modal'); | |
| if (!modal.classList.contains('open')) return; | |
| if (e.key === 'ArrowRight' || e.key === 'ArrowDown') pdfNextPage(); | |
| if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') pdfPrevPage(); | |
| if (e.key === 'Escape') closePDFModal(); | |
| }); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| AI CHATBOT | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const SYSTEM_PROMPT = `You are Dharma Guru, a deeply knowledgeable AI expert in Sanatan Dharma (Hinduism) and ancient Indian philosophy. | |
| You have profound expertise in: | |
| - The four Vedas: Rig Veda, Sam Veda, Yajur Veda, Atharva Veda | |
| - Upanishads (108 Upanishads, especially the major 13) | |
| - Bhagavad Gita โ all 18 chapters in depth | |
| - Ramayana (Valmiki Ramayana & Tulsidas Ramcharitmanas) | |
| - Mahabharata โ all 18 Parvas, the Pandavas, Kauravas, and Krishna's teachings | |
| - 18 Major Puranas and 18 Minor Puranas | |
| - Hanuman Chalisa and other stotras | |
| - Hindu philosophy: Advaita, Dvaita, Vishishtadvaita Vedanta | |
| - Yoga systems: Jnana, Bhakti, Karma, Raja Yoga | |
| - Hindu cosmology, deities, rituals, festivals, pilgrimage sites | |
| - Sanskrit terms, mantras, and their meanings | |
| - Dharma, Karma, Samsara, Moksha, Atman, Brahman | |
| Response style: | |
| - Begin with a respectful greeting like "Namaste" or "Jai Shri Ram" when appropriate | |
| - Use Sanskrit terms with their meanings in parentheses | |
| - Quote relevant shlokas/verses in Sanskrit (transliterated) with English translation | |
| - Be warm, respectful, and deeply knowledgeable | |
| - Provide nuanced, detailed answers | |
| - Reference specific books, chapters, and verses when possible | |
| - When web search results are provided, integrate them naturally and cite sources | |
| Always be respectful of all Hindu traditions and sampradayas. Avoid religious controversy.`; | |
| let chatHistory = []; | |
| const keys = { groq: '' }; | |
| function loadKeys() { | |
| keys.groq = localStorage.getItem('sd_groq_key') || ''; | |
| var badge = document.getElementById('guru-saved-badge'); | |
| var inp = document.getElementById('guru-key-input'); | |
| if (keys.groq) { | |
| if (badge) badge.style.display = 'block'; | |
| if (inp) inp.value = keys.groq; | |
| } | |
| } | |
| /* โโ Guru FAB / Panel โโ */ | |
| function toggleGuruChat() { | |
| var fab = document.getElementById('guru-fab'); | |
| var chat = document.getElementById('guru-chat'); | |
| var open = chat.classList.toggle('open'); | |
| fab.classList.toggle('open', open); | |
| if (open) { var inp = document.getElementById('chat-input'); if (inp) inp.focus(); } | |
| } | |
| function openGuruChat() { | |
| var fab = document.getElementById('guru-fab'); | |
| var chat = document.getElementById('guru-chat'); | |
| if (!chat.classList.contains('open')) { | |
| chat.classList.add('open'); | |
| fab.classList.add('open'); | |
| setTimeout(function() { var inp = document.getElementById('chat-input'); if (inp) inp.focus(); }, 350); | |
| } | |
| fab.scrollIntoView({ behavior: 'smooth', block: 'end' }); | |
| } | |
| function guruAutoGrow(el) { | |
| el.style.height = 'auto'; | |
| el.style.height = Math.min(el.scrollHeight, 90) + 'px'; | |
| } | |
| function guruKeydown(e) { | |
| if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } | |
| } | |
| /* โโ Model switcher โโ */ | |
| var currentGuruModel = 'groq:llama-3.3-70b-versatile'; | |
| function toggleGuruSwitcher(e) { | |
| e.stopPropagation(); | |
| var menu = document.getElementById('guru-switcher-menu'); | |
| if (!menu) return; | |
| var isOpen = menu.classList.toggle('open'); | |
| if (isOpen) { | |
| setTimeout(function() { document.addEventListener('click', closeGuruSwitcherOnce); }, 10); | |
| } | |
| } | |
| function closeGuruSwitcherOnce() { | |
| var menu = document.getElementById('guru-switcher-menu'); | |
| if (menu) menu.classList.remove('open'); | |
| document.removeEventListener('click', closeGuruSwitcherOnce); | |
| } | |
| function switchGuruModel(modelVal, label, btn) { | |
| currentGuruModel = modelVal; | |
| var badge = document.getElementById('guru-badge'); | |
| if (badge) badge.textContent = label; | |
| var items = document.querySelectorAll('.guru-sw-item'); | |
| for (var i = 0; i < items.length; i++) items[i].classList.remove('active'); | |
| if (btn) btn.classList.add('active'); | |
| closeGuruSwitcherOnce(); | |
| } | |
| /* โโ Key modal โโ */ | |
| function openGuruKeyModal() { | |
| closeGuruSwitcherOnce(); | |
| loadKeys(); | |
| var modal = document.getElementById('guru-key-modal'); | |
| if (modal) modal.style.display = 'flex'; | |
| } | |
| function closeGuruKeyModal() { | |
| var modal = document.getElementById('guru-key-modal'); | |
| if (modal) modal.style.display = 'none'; | |
| } | |
| function saveGuruKey() { | |
| var inp = document.getElementById('guru-key-input'); | |
| if (!inp) return; | |
| var val = inp.value.trim(); | |
| if (!val || !val.startsWith('gsk_') || val.length < 10) { | |
| inp.classList.add('error'); | |
| setTimeout(function() { inp.classList.remove('error'); }, 1200); | |
| return; | |
| } | |
| localStorage.setItem('sd_groq_key', val); | |
| keys.groq = val; | |
| var badge = document.getElementById('guru-saved-badge'); | |
| if (badge) badge.style.display = 'block'; | |
| showNotification('๐ Groq key saved! Guru is ready.'); | |
| closeGuruKeyModal(); | |
| // add greeting if no messages yet besides welcome | |
| var msgs = document.getElementById('chat-messages'); | |
| if (msgs && msgs.children.length <= 1) { | |
| addMessage('bot', '๐ Connected to Groq AI! I am ready to answer your questions about Sanatan Dharma. Ask me anything!'); | |
| } | |
| } | |
| /* Keep old saveKey as alias so nothing breaks */ | |
| function saveKey(provider) { | |
| if (provider === 'groq') saveGuruKey(); | |
| } | |
| function saveKey(provider) { | |
| const inputEl = document.getElementById(provider + '-key'); | |
| const val = inputEl.value.trim(); | |
| if (!val || val.startsWith('โ')) return; | |
| localStorage.setItem('sd_' + provider + '_key', val); | |
| keys[provider] = val; | |
| inputEl.value = 'โ'.repeat(20); | |
| showNotification('๐ ' + provider.toUpperCase() + ' key saved!'); | |
| } | |
| function showNotification(msg) { | |
| const n = document.createElement('div'); | |
| n.style.cssText = `position:fixed;bottom:24px;right:24px;background:linear-gradient(135deg,var(--saffron),var(--gold));color:var(--bg-dark);padding:12px 20px;border-radius:4px;font-family:var(--font-heading);font-size:12px;font-weight:700;letter-spacing:0.1em;z-index:9999;animation:msgSlide 0.3s ease;`; | |
| n.textContent = msg; | |
| document.body.appendChild(n); | |
| setTimeout(() => n.remove(), 3000); | |
| } | |
| function getTime() { | |
| return new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); | |
| } | |
| function addMessage(role, content, sources = [], ragChunks = []) { | |
| const msgs = document.getElementById('chat-messages'); | |
| const msgDiv = document.createElement('div'); | |
| msgDiv.className = 'msg ' + role; | |
| let sourcesHTML = ''; | |
| if (sources && sources.length > 0) { | |
| sourcesHTML = `<div class="sources-block"> | |
| <div class="sources-title">๐ Sources</div> | |
| ${sources.map(s => `<a class="source-link" href="${s.url || '#'}" target="_blank" rel="noopener">${s.title || s.url || 'Source'}</a>`).join('')} | |
| </div>`; | |
| } | |
| let ragHTML = ''; | |
| if (ragChunks && ragChunks.length > 0) { | |
| ragHTML = `<div class="rag-context-block"> | |
| <div class="rag-context-title">๐ Retrieved from Sacred Texts</div> | |
| ${ragChunks.map(c => ` | |
| <div class="rag-chunk-item">${c.text.substring(0, 200).replace(/</g,'<')}โฆ</div> | |
| <div class="rag-chunk-src">โ ${c.bookTitle} ยท relevance: ${(c.score * 100).toFixed(0)}%</div> | |
| `).join('')} | |
| </div>`; | |
| } | |
| const formattedContent = content | |
| .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') | |
| .replace(/\*(.*?)\*/g, '<em>$1</em>') | |
| .replace(/\n/g, '<br>'); | |
| msgDiv.innerHTML = ` | |
| <div class="msg-avatar">${role === 'bot' ? 'เฅ' : '๐ค'}</div> | |
| <div class="msg-body"> | |
| <div class="msg-bubble">${formattedContent}</div> | |
| ${sourcesHTML} | |
| ${ragHTML} | |
| <div class="msg-time">${getTime()}</div> | |
| </div>`; | |
| msgs.appendChild(msgDiv); | |
| msgs.scrollTop = msgs.scrollHeight; | |
| return msgDiv; | |
| } | |
| function addTyping() { | |
| const msgs = document.getElementById('chat-messages'); | |
| const div = document.createElement('div'); | |
| div.className = 'msg bot typing-indicator'; | |
| div.id = 'typing-msg'; | |
| div.innerHTML = ` | |
| <div class="msg-avatar">เฅ</div> | |
| <div class="msg-body"> | |
| <div class="msg-bubble"> | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| </div> | |
| </div>`; | |
| msgs.appendChild(div); | |
| msgs.scrollTop = msgs.scrollHeight; | |
| } | |
| function removeTyping() { | |
| const t = document.getElementById('typing-msg'); | |
| if (t) t.remove(); | |
| } | |
| // Simple web search via DuckDuckGo scrape or fallback | |
| async function searchWeb(query) { | |
| // Use a CORS proxy to search | |
| try { | |
| const res = await fetch(`https://api.duckduckgo.com/?q=${encodeURIComponent(query + ' Hinduism Sanatan Dharma')}&format=json&no_html=1&skip_disambig=1`); | |
| const data = await res.json(); | |
| const sources = []; | |
| if (data.RelatedTopics) { | |
| data.RelatedTopics.slice(0, 4).forEach(t => { | |
| if (t.FirstURL && t.Text) { | |
| sources.push({ url: t.FirstURL, title: t.Text.substring(0, 80) + 'โฆ' }); | |
| } | |
| }); | |
| } | |
| if (data.AbstractURL) sources.unshift({ url: data.AbstractURL, title: data.AbstractText ? data.AbstractText.substring(0, 80) + 'โฆ' : data.AbstractURL }); | |
| return { summary: data.AbstractText || '', sources }; | |
| } catch { | |
| return { summary: '', sources: [] }; | |
| } | |
| } | |
| async function callGroq(messages, modelName) { | |
| const key = localStorage.getItem('sd_groq_key') || keys.groq; | |
| if (!key) throw new Error('Groq API key not set'); | |
| const res = await fetch('https://api.groq.com/openai/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { 'Authorization': `Bearer ${key}`, 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ model: modelName, messages, max_tokens: 1800, temperature: 0.7 }) | |
| }); | |
| const data = await res.json(); | |
| if (!res.ok) throw new Error(data.error?.message || 'Groq API error'); | |
| return data.choices[0].message.content; | |
| } | |
| async function sendMessage() { | |
| const input = document.getElementById('chat-input'); | |
| const userMsg = input.value.trim(); | |
| if (!userMsg) return; | |
| input.value = ''; | |
| const clearBtn = document.getElementById('input-clear'); | |
| if (clearBtn) clearBtn.classList.remove('visible'); | |
| input.style.height = 'auto'; | |
| document.getElementById('send-btn').disabled = true; | |
| addMessage('user', userMsg); | |
| chatHistory.push({ role: 'user', content: userMsg }); | |
| // hide chips after first send | |
| var chips = document.getElementById('guru-chips'); | |
| if (chips) chips.style.display = 'none'; | |
| addTyping(); | |
| const modelVal = currentGuruModel; | |
| const [provider, ...modelParts] = modelVal.split(':'); | |
| const modelName = modelParts.join(':'); | |
| const useWebSearch = document.getElementById('web-search-cb').checked; | |
| let sources = []; | |
| let searchContext = ''; | |
| let ragChunks = []; | |
| try { | |
| // Web search | |
| if (useWebSearch) { | |
| const searchResult = await searchWeb(userMsg); | |
| sources = searchResult.sources; | |
| if (searchResult.summary) { | |
| searchContext = `\n\n[Web Search Context]: ${searchResult.summary}`; | |
| } | |
| } | |
| // RAG retrieval | |
| let ragContext = ''; | |
| if (RAG.enabled && RAG.index.length) { | |
| const ragResult = buildRAGContext(userMsg); | |
| ragContext = ragResult.context; | |
| ragChunks = ragResult.ragChunks; | |
| } | |
| const msgs = [ | |
| { role: 'system', content: SYSTEM_PROMPT + searchContext + ragContext }, | |
| ...chatHistory.slice(-10) | |
| ]; | |
| let reply = ''; | |
| if (provider === 'groq') { | |
| reply = await callGroq(msgs, modelName); | |
| } | |
| chatHistory.push({ role: 'assistant', content: reply }); | |
| removeTyping(); | |
| addMessage('bot', reply, sources, ragChunks); | |
| } catch (err) { | |
| removeTyping(); | |
| let errMsg = err.message || 'An error occurred.'; | |
| if (errMsg.includes('API key not set') || errMsg.includes('not set')) { | |
| errMsg = `๐ No Groq API key found. Click the badge in the chat header โ "Manage Groq API Key" to connect.`; | |
| } | |
| addMessage('bot', `๐ Apologies, dear seeker: ${errMsg}`); | |
| } finally { | |
| document.getElementById('send-btn').disabled = false; | |
| input.focus(); | |
| } | |
| } | |
| function sendSuggestion(el) { | |
| document.getElementById('chat-input').value = el.textContent; | |
| sendMessage(); | |
| } | |
| function clearInput() { | |
| const inp = document.getElementById('chat-input'); | |
| inp.value = ''; | |
| document.getElementById('input-clear').classList.remove('visible'); | |
| inp.style.height = 'auto'; | |
| inp.focus(); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| RAG ENGINE โ TF-IDF + COSINE SIMILARITY | |
| Indexes PDFs in the browser using PDF.js, stores chunks, | |
| and retrieves top-K relevant passages for every query. | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| // Configure PDF.js worker | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; | |
| /* --- RAG State ---------------------------------------- */ | |
| const RAG = { | |
| enabled: false, | |
| index: [], // [{ bookTitle, chunkIndex, text, tf }] | |
| idf: {}, // term -> idf score across all chunks | |
| books: {}, // bookTitle -> { chunkCount, color } | |
| stopWords: new Set(['the','a','an','and','or','but','in','on','at','to','for','of','with','by','from','is','are','was','were','be','been','being','have','has','had','do','does','did','will','would','could','should','may','might','shall','can','not','this','that','these','those','it','its','they','them','their','we','us','our','you','your','he','she','him','her','his','i','me','my','so','as','if','then','than','when','where','who','which','what','how','all','also','more','about','up','out','no','into','through','after','before','between','same','other','such','each','both','over','under','again','further','only','own','too','very','just','most','some','any','there','here','her','him','his','my','our','your']) | |
| }; | |
| /* --- Tokenize ------------------------------------------ */ | |
| function tokenize(text) { | |
| return text.toLowerCase() | |
| .replace(/[^a-z0-9\s]/g, ' ') | |
| .split(/\s+/) | |
| .filter(w => w.length > 2 && !RAG.stopWords.has(w)); | |
| } | |
| /* --- TF for one chunk ---------------------------------- */ | |
| function computeTF(tokens) { | |
| const tf = {}; | |
| tokens.forEach(t => { tf[t] = (tf[t] || 0) + 1; }); | |
| const len = tokens.length || 1; | |
| Object.keys(tf).forEach(t => { tf[t] /= len; }); | |
| return tf; | |
| } | |
| /* --- Rebuild IDF after adding new chunks --------------- */ | |
| function rebuildIDF() { | |
| const df = {}; | |
| RAG.index.forEach(chunk => { | |
| Object.keys(chunk.tf).forEach(t => { df[t] = (df[t] || 0) + 1; }); | |
| }); | |
| const N = RAG.index.length || 1; | |
| RAG.idf = {}; | |
| Object.keys(df).forEach(t => { | |
| RAG.idf[t] = Math.log((N + 1) / (df[t] + 1)) + 1; | |
| }); | |
| } | |
| /* --- TF-IDF vector for a chunk or query ---------------- */ | |
| function tfidfVector(tf) { | |
| const vec = {}; | |
| Object.keys(tf).forEach(t => { | |
| vec[t] = tf[t] * (RAG.idf[t] || 1); | |
| }); | |
| return vec; | |
| } | |
| /* --- Cosine similarity --------------------------------- */ | |
| function cosine(vecA, vecB) { | |
| const keysA = Object.keys(vecA); | |
| let dot = 0, normA = 0, normB = 0; | |
| keysA.forEach(t => { | |
| dot += (vecA[t] || 0) * (vecB[t] || 0); | |
| normA += vecA[t] ** 2; | |
| }); | |
| Object.values(vecB).forEach(v => { normB += v ** 2; }); | |
| if (!normA || !normB) return 0; | |
| return dot / (Math.sqrt(normA) * Math.sqrt(normB)); | |
| } | |
| /* --- Retrieve top-K chunks for a query ----------------- */ | |
| function retrieveChunks(query, topK = 4) { | |
| if (!RAG.index.length) return []; | |
| const qTokens = tokenize(query); | |
| const qTF = computeTF(qTokens); | |
| const qVec = tfidfVector(qTF); | |
| const scored = RAG.index.map(chunk => ({ | |
| ...chunk, | |
| score: cosine(qVec, tfidfVector(chunk.tf)) | |
| })); | |
| scored.sort((a, b) => b.score - a.score); | |
| return scored.slice(0, topK).filter(c => c.score > 0); | |
| } | |
| /* --- Extract text from a PDF (by URL or File) ---------- */ | |
| async function extractPDFText(source) { | |
| let pdfDoc; | |
| if (source instanceof File) { | |
| const ab = await source.arrayBuffer(); | |
| pdfDoc = await pdfjsLib.getDocument({ data: ab }).promise; | |
| } else { | |
| pdfDoc = await pdfjsLib.getDocument(source).promise; | |
| } | |
| const numPages = pdfDoc.numPages; | |
| const texts = []; | |
| for (let p = 1; p <= numPages; p++) { | |
| const page = await pdfDoc.getPage(p); | |
| const content = await page.getTextContent(); | |
| texts.push(content.items.map(i => i.str).join(' ')); | |
| // Update progress bar | |
| const pct = Math.round((p / numPages) * 100); | |
| setRAGProgress(pct); | |
| } | |
| return texts.join('\n'); | |
| } | |
| /* --- Chunk text into ~400-token chunks with overlap ---- */ | |
| function chunkText(text, chunkSize = 400, overlap = 80) { | |
| const words = text.split(/\s+/).filter(Boolean); | |
| const chunks = []; | |
| let i = 0; | |
| while (i < words.length) { | |
| chunks.push(words.slice(i, i + chunkSize).join(' ')); | |
| i += (chunkSize - overlap); | |
| } | |
| return chunks; | |
| } | |
| /* --- Index a single book ------------------------------- */ | |
| async function indexBook(source, bookTitle) { | |
| // Remove existing chunks for this book first | |
| RAG.index = RAG.index.filter(c => c.bookTitle !== bookTitle); | |
| showRAGProgress(); | |
| setRAGProgress(0); | |
| updateRAGStatusText(`Extracting text from "${bookTitle}"โฆ`); | |
| try { | |
| const rawText = await extractPDFText(source); | |
| const chunks = chunkText(rawText); | |
| updateRAGStatusText(`Indexing ${chunks.length} chunks from "${bookTitle}"โฆ`); | |
| chunks.forEach((text, idx) => { | |
| const tokens = tokenize(text); | |
| if (tokens.length < 10) return; // skip near-empty chunks | |
| const tf = computeTF(tokens); | |
| RAG.index.push({ bookTitle, chunkIndex: idx, text, tf }); | |
| }); | |
| rebuildIDF(); | |
| RAG.books[bookTitle] = { chunkCount: chunks.length }; | |
| updateRAGUI(); | |
| showNotification(`๐ "${bookTitle}" indexed โ ${RAG.index.filter(c=>c.bookTitle===bookTitle).length} chunks ready`); | |
| return true; | |
| } catch(e) { | |
| showNotification(`โ ๏ธ Could not index "${bookTitle}": ${e.message}`); | |
| return false; | |
| } finally { | |
| hideRAGProgress(); | |
| updateRAGStatusText(Object.keys(RAG.books).length | |
| ? `${RAG.index.length} chunks from ${Object.keys(RAG.books).length} text(s) indexed` | |
| : 'No texts indexed yet'); | |
| } | |
| } | |
| /* --- Index a book from the library ---------------------- | |
| PDFs live in the same folder as new_edition.html. | |
| On GitHub Pages / any HTTP server the relative path loads | |
| automatically. On file:// (local PC), browsers block | |
| relative fetches so we fall back to a file picker โ | |
| the user just selects the same file from their folder. | |
| The "Upload Your Own PDF" option handles extra PDFs. */ | |
| async function indexBookForRAG(filename, title) { | |
| if (!RAG.enabled) { | |
| document.getElementById('rag-cb').checked = true; | |
| toggleRAG(true); | |
| } | |
| // HTTP/HTTPS server (GitHub Pages, localhost, etc.) โ load directly | |
| if (location.protocol !== 'file:') { | |
| const ok = await indexBook(filename, title); | |
| if (ok) document.getElementById('chat').scrollIntoView({ behavior: 'smooth' }); | |
| return; | |
| } | |
| // file:// โ browser blocks relative fetches; use a hidden file picker instead. | |
| // Pre-store the book title so it indexes under the correct name regardless of filename chosen. | |
| window._pendingRAGBookTitle = title; | |
| let picker = document.getElementById('_rag-book-picker'); | |
| if (!picker) { | |
| picker = document.createElement('input'); | |
| picker.type = 'file'; | |
| picker.accept = '.pdf'; | |
| picker.id = '_rag-book-picker'; | |
| picker.style.display = 'none'; | |
| document.body.appendChild(picker); | |
| picker.addEventListener('change', function () { | |
| if (!this.files || !this.files[0]) return; | |
| const file = this.files[0]; | |
| const t = window._pendingRAGBookTitle || file.name.replace(/\.pdf$/i, ''); | |
| window._pendingRAGBookTitle = null; | |
| this.value = ''; // reset so same file can be picked again next time | |
| indexBook(file, t).then(ok => { | |
| if (ok) document.getElementById('chat').scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| }); | |
| } | |
| showNotification(`๐ Local mode โ select "${filename}" from your SANATANS folder`); | |
| picker.click(); | |
| } | |
| /* --- Toggle RAG on/off --------------------------------- */ | |
| function toggleRAG(enabled) { | |
| RAG.enabled = enabled; | |
| document.getElementById('rag-status-bar').classList.toggle('visible', enabled); | |
| if (enabled && Object.keys(RAG.books).length === 0) { | |
| updateRAGStatusText('No texts indexed yet. Click "๏ผ Index PDF" or use "๐ง Index for AI" on a book card.'); | |
| } | |
| } | |
| /* --- UI helpers ---------------------------------------- */ | |
| function setRAGProgress(pct) { | |
| document.getElementById('rag-progress-bar').style.width = pct + '%'; | |
| } | |
| function showRAGProgress() { | |
| document.getElementById('rag-progress').classList.add('visible'); | |
| document.getElementById('rag-index-btn').disabled = true; | |
| } | |
| function hideRAGProgress() { | |
| document.getElementById('rag-progress').classList.remove('visible'); | |
| document.getElementById('rag-index-btn').disabled = false; | |
| setRAGProgress(0); | |
| } | |
| function updateRAGStatusText(text) { | |
| document.getElementById('rag-status-text').textContent = text; | |
| } | |
| function updateRAGUI() { | |
| const pills = document.getElementById('rag-book-pills'); | |
| pills.innerHTML = ''; | |
| Object.keys(RAG.books).forEach(title => { | |
| const pill = document.createElement('span'); | |
| pill.className = 'rag-pill active'; | |
| pill.innerHTML = `๐ ${title} <span class="pill-x" onclick="removeRAGBook('${title}',event)">โ</span>`; | |
| pills.appendChild(pill); | |
| }); | |
| } | |
| function removeRAGBook(title, e) { | |
| e.stopPropagation(); | |
| RAG.index = RAG.index.filter(c => c.bookTitle !== title); | |
| delete RAG.books[title]; | |
| rebuildIDF(); | |
| updateRAGUI(); | |
| updateRAGStatusText(Object.keys(RAG.books).length | |
| ? `${RAG.index.length} chunks from ${Object.keys(RAG.books).length} text(s) indexed` | |
| : 'No texts indexed yet'); | |
| showNotification(`๐๏ธ "${title}" removed from RAG index`); | |
| } | |
| /* --- RAG Index Modal ----------------------------------- */ | |
| function openRAGIndexModal() { | |
| // Populate book list in modal | |
| const container = document.getElementById('rag-modal-books'); | |
| container.innerHTML = ''; | |
| BOOKS.forEach(book => { | |
| const indexed = !!RAG.books[book.title]; | |
| const btn = document.createElement('button'); | |
| btn.style.cssText = `background:${indexed ? 'rgba(75,0,130,0.25)' : 'var(--bg-card)'};border:1px solid ${indexed ? 'rgba(155,89,212,0.5)' : 'var(--border-gold)'};color:${indexed ? '#c084fc' : 'var(--text-secondary)'};padding:8px 12px;border-radius:4px;font-family:var(--font-heading);font-size:10px;letter-spacing:0.1em;cursor:pointer;text-align:left;transition:all 0.2s;`; | |
| btn.innerHTML = `${book.symbol} ${book.title}${indexed ? ' โ' : ''}`; | |
| btn.onclick = async () => { | |
| closeRAGModal(); | |
| await indexBookForRAG(book.file, book.title); | |
| }; | |
| container.appendChild(btn); | |
| }); | |
| document.getElementById('rag-modal-overlay').style.display = 'block'; | |
| document.getElementById('rag-modal').style.display = 'block'; | |
| } | |
| function closeRAGModal() { | |
| document.getElementById('rag-modal-overlay').style.display = 'none'; | |
| document.getElementById('rag-modal').style.display = 'none'; | |
| } | |
| function handleRAGFileSelect(input) { | |
| if (!input.files || !input.files[0]) return; | |
| const file = input.files[0]; | |
| const title = file.name.replace(/\.pdf$/i, ''); | |
| document.getElementById('rag-file-label').textContent = file.name; | |
| closeRAGModal(); | |
| indexBook(file, title); | |
| } | |
| /* --- Build RAG context string for prompt --------------- */ | |
| function buildRAGContext(query) { | |
| if (!RAG.enabled || !RAG.index.length) return { context: '', ragChunks: [] }; | |
| const chunks = retrieveChunks(query, 4); | |
| if (!chunks.length) return { context: '', ragChunks: [] }; | |
| const context = chunks.map((c, i) => | |
| `[Passage ${i+1} from "${c.bookTitle}"]: ${c.text.substring(0, 600)}` | |
| ).join('\n\n'); | |
| return { context: `\n\n[Sacred Text Passages โ use these as primary reference, cite the source]:\n${context}`, ragChunks: chunks }; | |
| } | |
| // Auto-resize is handled inline by guruAutoGrow / guruKeydown | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| CANVAS STARFIELD | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| (function() { | |
| const canvas = document.getElementById('cosmos-canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| let W, H, stars = []; | |
| function resize() { | |
| W = canvas.width = window.innerWidth; | |
| H = canvas.height = window.innerHeight; | |
| } | |
| function createStars(n) { | |
| stars = []; | |
| for (let i = 0; i < n; i++) { | |
| stars.push({ | |
| x: Math.random() * W, | |
| y: Math.random() * H, | |
| r: Math.random() * 1.5 + 0.2, | |
| speed: Math.random() * 0.2 + 0.05, | |
| opacity: Math.random() * 0.6 + 0.2, | |
| twinkle: Math.random() * Math.PI * 2 | |
| }); | |
| } | |
| } | |
| function draw() { | |
| ctx.clearRect(0, 0, W, H); | |
| const time = Date.now() / 1000; | |
| stars.forEach(s => { | |
| s.twinkle += 0.02; | |
| const alpha = s.opacity * (0.6 + 0.4 * Math.sin(s.twinkle)); | |
| ctx.beginPath(); | |
| ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); | |
| ctx.fillStyle = `rgba(255,215,100,${alpha})`; | |
| ctx.fill(); | |
| s.y -= s.speed; | |
| if (s.y < -5) { s.y = H + 5; s.x = Math.random() * W; } | |
| }); | |
| requestAnimationFrame(draw); | |
| } | |
| window.addEventListener('resize', () => { resize(); createStars(200); }); | |
| resize(); | |
| createStars(200); | |
| draw(); | |
| })(); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FLOATING PARTICLES | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| (function() { | |
| const container = document.getElementById('particles-container'); | |
| const symbols = ['เฅ', '๐ชท', '๐ธ', 'โฆ', '๊ฉ', 'โธ']; | |
| for (let i = 0; i < 18; i++) { | |
| const p = document.createElement('div'); | |
| const isSym = Math.random() > 0.5; | |
| p.className = 'particle'; | |
| const size = Math.random() * 12 + 4; | |
| const left = Math.random() * 100; | |
| const duration = Math.random() * 20 + 15; | |
| const delay = Math.random() * 20; | |
| p.style.cssText = ` | |
| left:${left}%; | |
| width:${size}px; height:${size}px; | |
| animation-duration:${duration}s; | |
| animation-delay:-${delay}s; | |
| font-size:${size * 1.4}px; | |
| ${isSym ? `background:none;border-radius:0;color:rgba(255,165,0,0.4);` : `background:rgba(255,165,0,0.3);`} | |
| `; | |
| p.textContent = isSym ? symbols[Math.floor(Math.random() * symbols.length)] : ''; | |
| container.appendChild(p); | |
| } | |
| })(); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| SCROLL REVEAL | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const revealObserver = new IntersectionObserver((entries) => { | |
| entries.forEach(e => { | |
| if (e.isIntersecting) { e.target.classList.add('visible'); } | |
| }); | |
| }, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' }); | |
| function observeReveal() { | |
| document.querySelectorAll('.reveal').forEach((el, i) => { | |
| el.style.transitionDelay = (i % 6) * 0.08 + 's'; | |
| revealObserver.observe(el); | |
| }); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| TICKER | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const QUOTES = [ | |
| '"Aham Brahmasmi" โ I am Brahman. (Brihadaranyaka Upanishad)', | |
| '"Tat Tvam Asi" โ Thou art That. (Chandogya Upanishad)', | |
| '"Vasudhaiva Kutumbakam" โ The whole world is one family.', | |
| '"Satyam Shivam Sundaram" โ Truth, Divinity, Beauty.', | |
| '"Dharmo Rakshati Rakshitah" โ Dharma protects those who protect it.', | |
| '"Sarve Bhavantu Sukhinah" โ May all beings be happy.', | |
| '"Yada Yada Hi Dharmasya..." โ Whenever dharma declines, I shall appear. (B.G. 4.7)', | |
| '"Karmanye Vadhikaraste..." โ You have a right to perform your prescribed duty. (B.G. 2.47)', | |
| '"Om Namah Shivaya" โ Salutations to the auspicious one.', | |
| '"Jai Shri Ram" โ Victory to Lord Rama.', | |
| '"Om Namo Narayanaya" โ Salutations to Lord Vishnu.', | |
| ]; | |
| function buildTicker() { | |
| const track = document.getElementById('ticker-content'); | |
| const doubled = [...QUOTES, ...QUOTES]; | |
| track.innerHTML = doubled.map(q => ` | |
| <span class="ticker-quote"> | |
| <span class="ticker-sep">เฅ</span> | |
| ${q} | |
| </span>`).join(''); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| NAV TOGGLE | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| document.getElementById('nav-toggle').addEventListener('click', () => { | |
| document.getElementById('nav-links').classList.toggle('open'); | |
| }); | |
| /* Close nav on link click */ | |
| document.querySelectorAll('.nav-links a').forEach(a => { | |
| a.addEventListener('click', () => document.getElementById('nav-links').classList.remove('open')); | |
| }); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| NAV SCROLL EFFECT | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| window.addEventListener('scroll', () => { | |
| const nav = document.getElementById('main-nav'); | |
| nav.style.background = window.scrollY > 50 | |
| ? 'rgba(10,4,0,0.99)' | |
| : 'linear-gradient(180deg,rgba(10,4,0,0.98) 0%,rgba(10,4,0,0.85) 100%)'; | |
| }); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| RIPPLE EFFECT | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| document.querySelectorAll('.btn-primary, .btn-secondary, .book-card').forEach(el => { | |
| el.addEventListener('click', function(e) { | |
| const r = document.createElement('span'); | |
| r.className = 'ripple'; | |
| this.style.position = 'relative'; | |
| this.style.overflow = 'hidden'; | |
| r.style.left = e.offsetX + 'px'; | |
| r.style.top = e.offsetY + 'px'; | |
| this.appendChild(r); | |
| setTimeout(() => r.remove(), 600); | |
| }); | |
| }); | |
| // Re-observe after dynamic content (secondary pass for late-rendered elements) | |
| setTimeout(observeReveal, 600); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| HINDU PANCHANG CALCULATOR | |
| Uses astronomical algorithms to compute Tithi, | |
| Nakshatra, Paksha, Masa & Vikram Samvat from | |
| Julian Day numbers. Accurate to ยฑ1 tithi/nakshatra. | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const TITHI_NAMES = [ | |
| 'Pratipada','Dwitiya','Tritiya','Chaturthi','Panchami', | |
| 'Shashthi','Saptami','Ashtami','Navami','Dashami', | |
| 'Ekadashi','Dwadashi','Trayodashi','Chaturdashi','Purnima', | |
| 'Pratipada','Dwitiya','Tritiya','Chaturthi','Panchami', | |
| 'Shashthi','Saptami','Ashtami','Navami','Dashami', | |
| 'Ekadashi','Dwadashi','Trayodashi','Chaturdashi','Amavasya' | |
| ]; | |
| const NAKSHATRA_NAMES = [ | |
| 'Ashwini','Bharani','Krittika','Rohini','Mrigashira','Ardra', | |
| 'Punarvasu','Pushya','Ashlesha','Magha','Purva Phalguni', | |
| 'Uttara Phalguni','Hasta','Chitra','Swati','Vishakha', | |
| 'Anuradha','Jyeshtha','Mula','Purva Ashadha','Uttara Ashadha', | |
| 'Shravana','Dhanishtha','Shatabhisha','Purva Bhadrapada', | |
| 'Uttara Bhadrapada','Revati' | |
| ]; | |
| const MASA_NAMES = [ | |
| 'Chaitra','Vaishakha','Jyeshtha','Ashadha','Shravana', | |
| 'Bhadrapada','Ashwin','Kartik','Margashirsha','Pausha','Magha','Phalguna' | |
| ]; | |
| const VARA_NAMES = [ | |
| 'Ravivara (Sunday)','Somvara (Monday)','Mangalvara (Tuesday)', | |
| 'Budhvara (Wednesday)','Guruvara (Thursday)', | |
| 'Shukravara (Friday)','Shanivara (Saturday)' | |
| ]; | |
| const MOON_PHASE_EMOJIS = ['๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','๐']; | |
| function julianDay(date) { | |
| const Y = date.getUTCFullYear(); | |
| const M = date.getUTCMonth() + 1; | |
| const D = date.getUTCDate() + (date.getUTCHours() + date.getUTCMinutes()/60) / 24; | |
| const A = Math.floor((14 - M) / 12); | |
| const Yr = Y + 4800 - A; | |
| const Mo = M + 12 * A - 3; | |
| return D + Math.floor((153*Mo+2)/5) + 365*Yr | |
| + Math.floor(Yr/4) - Math.floor(Yr/100) + Math.floor(Yr/400) - 32045; | |
| } | |
| function calcHinduPanchang() { | |
| const now = new Date(); | |
| const JD = julianDay(now); | |
| const T = (JD - 2451545.0) / 36525; // Julian centuries from J2000 | |
| /* โโ Sun longitude (apparent) โโ */ | |
| const M_sun = ((357.52911 + 35999.05029*T - 0.0001537*T*T) % 360 + 360) % 360; | |
| const M_rad = M_sun * Math.PI / 180; | |
| const C_sun = (1.914602 - 0.004817*T - 0.000014*T*T)*Math.sin(M_rad) | |
| + (0.019993 - 0.000101*T)*Math.sin(2*M_rad) | |
| + 0.000289*Math.sin(3*M_rad); | |
| let sunL = ((280.46646 + 36000.76983*T + C_sun) % 360 + 360) % 360; | |
| /* โโ Moon longitude (apparent) โโ */ | |
| const D_moon = ((297.85036 + 445267.111480*T) % 360 + 360) % 360; | |
| const M_moon_mean = ((357.52772 + 35999.050340*T) % 360 + 360) % 360; | |
| const M_m = ((134.96298 + 477198.867398*T) % 360 + 360) % 360; | |
| const F = ((93.27191 + 483202.017538*T) % 360 + 360) % 360; | |
| const Omega= ((125.04452 - 1934.136261*T) % 360 + 360) % 360; | |
| const D_r = D_moon * Math.PI/180; | |
| const M_r = M_moon_mean * Math.PI/180; | |
| const Mm_r= M_m * Math.PI/180; | |
| const F_r = F * Math.PI/180; | |
| const O_r = Omega* Math.PI/180; | |
| let moonL_corr = | |
| 6.288774*Math.sin(Mm_r) | |
| + 1.274027*Math.sin(2*D_r - Mm_r) | |
| + 0.658314*Math.sin(2*D_r) | |
| + 0.213618*Math.sin(2*Mm_r) | |
| - 0.185116*Math.sin(M_r) | |
| - 0.114332*Math.sin(2*F_r) | |
| + 0.058793*Math.sin(2*D_r - 2*Mm_r) | |
| + 0.057066*Math.sin(2*D_r - M_r - Mm_r) | |
| + 0.053322*Math.sin(2*D_r + Mm_r) | |
| + 0.045758*Math.sin(2*D_r - M_r) | |
| - 0.040923*Math.sin(M_r - Mm_r) | |
| - 0.034720*Math.sin(D_r) | |
| - 0.030383*Math.sin(M_r + Mm_r); | |
| let moonL_mean = ((218.3165 + 481267.8813*T) % 360 + 360) % 360; | |
| let moonL = (moonL_mean + moonL_corr + 360) % 360; | |
| /* โโ Tithi โโ */ | |
| let elongation = (moonL - sunL + 360) % 360; | |
| const tithiIdx = Math.floor(elongation / 12); // 0โ29 | |
| const tithiName = TITHI_NAMES[tithiIdx]; | |
| /* โโ Nakshatra โโ */ | |
| const nakshatraIdx = Math.floor(moonL / (360/27)) % 27; | |
| const nakshatraName = NAKSHATRA_NAMES[nakshatraIdx]; | |
| /* โโ Paksha โโ */ | |
| const isShukla = tithiIdx < 15; | |
| const paksha = isShukla ? 'Shukla Paksha (Waxing ๐)' : 'Krishna Paksha (Waning ๐)'; | |
| /* โโ Moon phase emoji โโ */ | |
| const phaseIdx = Math.min(Math.floor(elongation / (360/14)), 13); | |
| const moonEmoji = MOON_PHASE_EMOJIS[phaseIdx]; | |
| /* โโ Masa (approximate from sun longitude) โโ */ | |
| const masaIdx = Math.floor(sunL / 30) % 12; | |
| // Shift: Vernal equinox โ 0ยฐ โ Mesha (Chaitra starts ~Mar 22) | |
| const masaName = MASA_NAMES[masaIdx]; | |
| /* โโ Vikram Samvat (starts ~57 years before CE, adjusting for months) โโ */ | |
| const gregYear = now.getFullYear(); | |
| const gregMonth = now.getMonth(); // 0-based | |
| const samvat = gregYear + 57 + (gregMonth >= 3 ? 1 : 0); | |
| /* โโ Vara โโ */ | |
| const varaName = VARA_NAMES[now.getDay()]; | |
| return { tithiName, nakshatraName, paksha, isShukla, moonEmoji, masaName, samvat, varaName }; | |
| } | |
| function renderHinduCalendar() { | |
| const p = calcHinduPanchang(); | |
| const now = new Date(); | |
| document.getElementById('panchang-date-str').textContent = | |
| now.toLocaleDateString('en-IN', { weekday:'long', year:'numeric', month:'long', day:'numeric' }); | |
| document.getElementById('moon-phase-emoji').textContent = p.moonEmoji; | |
| document.getElementById('pc-tithi').textContent = p.tithiName; | |
| document.getElementById('pc-nakshatra').textContent = p.nakshatraName; | |
| const pakshaEl = document.getElementById('pc-paksha'); | |
| pakshaEl.innerHTML = `<span class="panchang-badge ${p.isShukla ? 'badge-shukla' : 'badge-krishna'}">${p.paksha}</span>`; | |
| document.getElementById('pc-masa').textContent = p.masaName + ' Masa'; | |
| document.getElementById('pc-vara').textContent = p.varaName; | |
| document.getElementById('pc-samvat').textContent = 'Samvat ' + p.samvat; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FULLY AUTOMATIC HINDU FESTIVAL ENGINE | |
| Calculates every festival astronomically for ANY year. | |
| No hardcoded dates โ works forever. | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| /* โโ Core: Sun & Moon longitude for any JS Date โโ */ | |
| function getSunMoonLon(date) { | |
| const JD = julianDay(date); | |
| const T = (JD - 2451545.0) / 36525; | |
| const M_sun = ((357.52911 + 35999.05029*T - 0.0001537*T*T) % 360 + 360) % 360; | |
| const Mr = M_sun * Math.PI / 180; | |
| const C_sun = (1.914602 - 0.004817*T - 0.000014*T*T)*Math.sin(Mr) | |
| + (0.019993 - 0.000101*T)*Math.sin(2*Mr) | |
| + 0.000289*Math.sin(3*Mr); | |
| const sunL = ((280.46646 + 36000.76983*T + C_sun) % 360 + 360) % 360; | |
| const D_moon = ((297.85036 + 445267.111480*T) % 360 + 360) % 360; | |
| const M_mm = ((357.52772 + 35999.050340*T) % 360 + 360) % 360; | |
| const M_m = ((134.96298 + 477198.867398*T) % 360 + 360) % 360; | |
| const F = ((93.27191 + 483202.017538*T) % 360 + 360) % 360; | |
| const Dr = D_moon*Math.PI/180, Mr2=M_mm*Math.PI/180; | |
| const Mmr=M_m*Math.PI/180, Fr=F*Math.PI/180; | |
| const corr = | |
| 6.288774*Math.sin(Mmr) | |
| + 1.274027*Math.sin(2*Dr - Mmr) | |
| + 0.658314*Math.sin(2*Dr) | |
| + 0.213618*Math.sin(2*Mmr) | |
| - 0.185116*Math.sin(Mr2) | |
| - 0.114332*Math.sin(2*Fr) | |
| + 0.058793*Math.sin(2*Dr - 2*Mmr) | |
| + 0.057066*Math.sin(2*Dr - Mr2 - Mmr) | |
| + 0.053322*Math.sin(2*Dr + Mmr) | |
| + 0.045758*Math.sin(2*Dr - Mr2) | |
| - 0.040923*Math.sin(Mr2 - Mmr) | |
| - 0.034720*Math.sin(Dr) | |
| - 0.030383*Math.sin(Mr2 + Mmr); | |
| const moonL = (((218.3165 + 481267.8813*T) % 360 + 360) % 360 + corr + 360) % 360; | |
| const elong = (moonL - sunL + 360) % 360; | |
| return { sunL, moonL, elong }; | |
| } | |
| /* โโ Binary search: find the date when elongation crosses a target value โโ */ | |
| function findElongationCrossing(targetElong, afterDate, searchDays) { | |
| let lo = new Date(afterDate); | |
| let hi = new Date(afterDate.getTime() + searchDays * 86400000); | |
| for (let i = 0; i < 48; i++) { | |
| const mid = new Date((lo.getTime() + hi.getTime()) / 2); | |
| const eNow = getSunMoonLon(mid).elong; | |
| const eTarget = targetElong; | |
| // handle wrap-around | |
| let eMid = eNow; | |
| let eCheck = (eMid - eTarget + 360) % 360; | |
| if (eCheck < 180) hi = mid; else lo = mid; | |
| } | |
| return new Date((lo.getTime() + hi.getTime()) / 2); | |
| } | |
| /* โโ Find the date when sun longitude crosses a target โโ */ | |
| function findSunLonCrossing(targetSunLon, afterDate, searchDays) { | |
| let lo = new Date(afterDate); | |
| let hi = new Date(afterDate.getTime() + searchDays * 86400000); | |
| for (let i = 0; i < 48; i++) { | |
| const mid = new Date((lo.getTime() + hi.getTime()) / 2); | |
| const sNow = getSunMoonLon(mid).sunL; | |
| let diff = (sNow - targetSunLon + 360) % 360; | |
| if (diff < 180) hi = mid; else lo = mid; | |
| } | |
| return new Date((lo.getTime() + hi.getTime()) / 2); | |
| } | |
| /* โโ Find n-th occurrence of a tithi (elongation band) on/after a date โโ */ | |
| function findTithi(tithiIndex, afterDate) { | |
| // Each tithi is a 12ยฐ band of elongation (0=Pratipada Shukla โฆ 29=Amavasya) | |
| const targetStart = tithiIndex * 12; | |
| return findElongationCrossing(targetStart, afterDate, 35); | |
| } | |
| /* โโ Find Purnima (Full Moon = elongation 180ยฐ) on/after a date โโ */ | |
| function findPurnima(afterDate) { | |
| return findElongationCrossing(180, afterDate, 35); | |
| } | |
| /* โโ Find Amavasya (New Moon = elongation 0ยฐ) on/after a date โโ */ | |
| function findAmavasya(afterDate) { | |
| return findElongationCrossing(0, afterDate, 35); | |
| } | |
| /* โโ Convert a Date to YYYY-MM-DD string (local) โโ */ | |
| function toYMD(d) { | |
| return d.getFullYear() + '-' | |
| + String(d.getMonth()+1).padStart(2,'0') + '-' | |
| + String(d.getDate()).padStart(2,'0'); | |
| } | |
| /* โโ Add days to a date โโ */ | |
| function addDays(d, n) { return new Date(d.getTime() + n*86400000); } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| COMPUTE ALL FESTIVALS FOR A GIVEN GREGORIAN YEAR | |
| Each festival is found by its astronomical definition: | |
| - Purnima / Amavasya / specific tithi in a lunar month | |
| - Or a specific Sun longitude (solar festivals) | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| function computeFestivalsForYear(year) { | |
| const results = []; | |
| /* Helper: search for a festival starting from a rough date */ | |
| function add(name, icon, desc, dateObj) { | |
| if (!dateObj) return; | |
| results.push({ name, icon, desc, date: toYMD(dateObj), _d: dateObj }); | |
| } | |
| /* โโ JanโFeb search start โโ */ | |
| const janStart = new Date(year, 0, 1); | |
| const febStart = new Date(year, 1, 1); | |
| const marStart = new Date(year, 2, 1); | |
| const aprStart = new Date(year, 3, 1); | |
| const mayStart = new Date(year, 4, 1); | |
| const julStart = new Date(year, 6, 1); | |
| const augStart = new Date(year, 7, 1); | |
| const sepStart = new Date(year, 8, 15); | |
| const octStart = new Date(year, 9, 1); | |
| const novStart = new Date(year, 10, 1); | |
| const decStart = new Date(year, 11, 1); | |
| /* โโ 1. Makar Sankranti โ Sun enters Capricorn (lon 270ยฐ) โโ */ | |
| add('Makar Sankranti','๐','The sun enters Capricorn โ harvest festival of India', | |
| findSunLonCrossing(270, janStart, 20)); | |
| /* โโ 2. Maha Shivratri โ Krishna Chaturdashi of Magha (tithiIdx=28) โโ */ | |
| // Magha = Jan/Feb; search from Jan 15 | |
| add('Maha Shivratri','๐ฑ','The great night of Lord Shiva โ night-long vigil and worship', | |
| findTithi(28, new Date(year, 0, 15))); | |
| /* โโ 3. Holi โ Purnima of Phalguna (Feb/Mar full moon) โโ */ | |
| add('Holi','๐','Festival of Colors & Joy โ triumph of good over evil', | |
| findPurnima(new Date(year, 1, 15))); | |
| /* โโ 4. Gudi Padwa / Ugadi โ Chaitra Shukla Pratipada โโ */ | |
| // First tithi (Pratipada Shukla) after March 15 | |
| add('Gudi Padwa / Ugadi','๐ช','Hindu New Year โ first day of Chaitra month', | |
| findTithi(0, new Date(year, 2, 15))); | |
| /* โโ 5. Ram Navami โ Chaitra Shukla Navami (tithiIdx=8) โโ */ | |
| add('Ram Navami','๐น','Birthday of Lord Rama โ 7th avatar of Vishnu', | |
| findTithi(8, new Date(year, 2, 20))); | |
| /* โโ 6. Hanuman Jayanti โ Chaitra Purnima (tithiIdx=14) โโ */ | |
| add('Hanuman Jayanti','๐บ','Birthday of Lord Hanuman โ the devoted servant of Rama', | |
| findTithi(14, new Date(year, 2, 25))); | |
| /* โโ 7. Akshaya Tritiya โ Vaishakha Shukla Tritiya (tithiIdx=2) โโ */ | |
| add('Akshaya Tritiya','๐','The imperishable auspicious day โ blessings never diminish', | |
| findTithi(2, new Date(year, 3, 10))); | |
| /* โโ 8. Buddha Purnima โ Vaishakha Purnima โโ */ | |
| add('Buddha Purnima','๐ง','Birth, Enlightenment & Nirvana of Gautama Buddha', | |
| findPurnima(new Date(year, 3, 20))); | |
| /* โโ 9. Guru Purnima โ Ashadha Purnima (Jun/Jul) โโ */ | |
| add('Guru Purnima','๐','Day to honour all spiritual and earthly teachers', | |
| findPurnima(new Date(year, 5, 20))); | |
| /* โโ 10. Nag Panchami โ Shravana Shukla Panchami (tithiIdx=4) โโ */ | |
| add('Nag Panchami','๐','Worship of the divine serpent โ offerings to Naga deities', | |
| findTithi(4, new Date(year, 6, 15))); | |
| /* โโ 11. Raksha Bandhan โ Shravana Purnima โโ */ | |
| add('Raksha Bandhan','๐บ','Festival of siblings โ sister ties a rakhi on her brother', | |
| findPurnima(new Date(year, 6, 20))); | |
| /* โโ 12. Janmashtami โ Krishna Ashtami (tithiIdx=22, Krishna Paksha Ashtami of Bhadrapada) โโ */ | |
| // Bhadrapada Krishna Ashtami = elongation index 22 | |
| add('Janmashtami','๐บ','Birth of Lord Krishna โ the divine flute player', | |
| findTithi(22, new Date(year, 7, 1))); | |
| /* โโ 13. Ganesh Chaturthi โ Bhadrapada Shukla Chaturthi (tithiIdx=3) โโ */ | |
| add('Ganesh Chaturthi','๐','Festival of Lord Ganesha โ remover of all obstacles', | |
| findTithi(3, new Date(year, 7, 15))); | |
| /* โโ 14. Navratri (Sharad) โ Ashwin Shukla Pratipada (tithiIdx=0) โโ */ | |
| add('Navratri (Sharad)','โก','Nine nights of the Divine Mother Durga', | |
| findTithi(0, new Date(year, 8, 15))); | |
| /* โโ 15. Dussehra (Vijayadashami) โ Ashwin Shukla Dashami (tithiIdx=9) โโ */ | |
| add('Dussehra','๐น','Victory of Rama over Ravana โ triumph of good over evil', | |
| findTithi(9, new Date(year, 8, 20))); | |
| /* โโ 16. Karva Chauth โ Krishna Chaturthi of Kartik (tithiIdx=19) โโ */ | |
| add('Karva Chauth','๐','Sacred fast by wives for the longevity of their husbands', | |
| findTithi(19, new Date(year, 9, 1))); | |
| /* โโ 17. Diwali โ Kartik Amavasya (new moon of Kartik) โโ */ | |
| const diwaliDate = findAmavasya(new Date(year, 9, 10)); | |
| add('Diwali','๐ช','Festival of Lights โ victory of light over darkness', diwaliDate); | |
| /* โโ 18. Govardhan Puja โ day after Diwali โโ */ | |
| if (diwaliDate) add('Govardhan Puja','๐ฟ','Worship of Govardhan Hill โ Krishna lifted it to shelter devotees', addDays(diwaliDate, 1)); | |
| /* โโ 19. Bhai Dooj โ 2 days after Diwali โโ */ | |
| if (diwaliDate) add('Bhai Dooj','โค๏ธ','The bond of siblings โ sisters welcome their brothers', addDays(diwaliDate, 2)); | |
| /* โโ 20. Dev Deepawali โ Kartik Purnima โโ */ | |
| add('Dev Deepawali','โจ','Diwali of the Gods โ Ganga ghats lit with countless lamps', | |
| findPurnima(new Date(year, 9, 20))); | |
| /* โโ 21. Vivah Panchami โ Margashirsha Shukla Panchami (tithiIdx=4) โโ */ | |
| add('Vivah Panchami','๐','Sacred marriage of Lord Rama and Goddess Sita', | |
| findTithi(4, new Date(year, 10, 15))); | |
| /* โโ Sort by date and return โโ */ | |
| results.sort((a, b) => a._d - b._d); | |
| return results; | |
| } | |
| /* โโ Build the rolling 12-month festival list โโ */ | |
| function buildFestivalList() { | |
| const today = new Date(); | |
| today.setHours(0,0,0,0); | |
| const yr = today.getFullYear(); | |
| // Compute this year + next year, deduplicate, then keep upcoming + last 2 passed | |
| const all = [ | |
| ...computeFestivalsForYear(yr), | |
| ...computeFestivalsForYear(yr + 1) | |
| ]; | |
| // Remove duplicates (same name within 20 days) | |
| const seen = new Set(); | |
| const deduped = all.filter(f => { | |
| const key = f.name + '_' + f.date.slice(0,7); | |
| if (seen.has(key)) return false; | |
| seen.add(key); | |
| return true; | |
| }); | |
| // Update year badge | |
| const badge = document.getElementById('fest-year-badge'); | |
| if (badge) badge.textContent = yr + 'โ' + (yr+1); | |
| return deduped; | |
| } | |
| let countdownInterval = null; | |
| function renderFestivals() { | |
| const list = document.getElementById('festivals-list'); | |
| const today = new Date(); | |
| today.setHours(0, 0, 0, 0); | |
| const FESTIVALS = buildFestivalList(); | |
| let nextFestival = null; | |
| let nextDiff = Infinity; | |
| list.innerHTML = FESTIVALS.map(f => { | |
| const fDate = new Date(f.date + 'T00:00:00'); | |
| const diff = fDate - today; | |
| const days = Math.floor(diff / 86400000); | |
| const isPast = diff < 0; | |
| if (!isPast && diff < nextDiff) { | |
| nextDiff = diff; | |
| nextFestival = f; | |
| } | |
| const daysLabel = isPast ? 'Passed' | |
| : days === 0 ? 'Today! ๐' | |
| : `In ${days} day${days===1?'':'s'}`; | |
| const dateStr = fDate.toLocaleDateString('en-IN', { day:'numeric', month:'short', year:'numeric' }); | |
| return ` | |
| <div class="festival-item${isPast?' past-fest':''}" id="fest-${f.date}"> | |
| <span class="festival-emoji">${f.icon}</span> | |
| <div class="festival-info"> | |
| <div class="festival-name">${f.name}</div> | |
| <div class="festival-desc">${f.desc}</div> | |
| </div> | |
| <div class="festival-date-block"> | |
| <div class="festival-date">${dateStr}</div> | |
| <div class="festival-days-away">${daysLabel}</div> | |
| </div> | |
| </div>`; | |
| }).join(''); | |
| if (nextFestival) { | |
| const el = document.getElementById('fest-' + nextFestival.date); | |
| if (el) el.classList.add('next-fest'); | |
| startCountdown(nextFestival); | |
| } | |
| } | |
| function startCountdown(festival) { | |
| document.getElementById('cd-icon').textContent = festival.icon; | |
| document.getElementById('cd-name').textContent = festival.name; | |
| document.getElementById('cd-desc').textContent = festival.desc; | |
| if (countdownInterval) clearInterval(countdownInterval); | |
| function tick() { | |
| const now = new Date(); | |
| const target = new Date(festival.date + 'T00:00:00'); | |
| const diff = target - now; | |
| if (diff <= 0) { | |
| // Festival just passed โ re-render to pick the next one | |
| clearInterval(countdownInterval); | |
| renderFestivals(); | |
| return; | |
| } | |
| const d = Math.floor(diff / 86400000); | |
| const h = Math.floor((diff % 86400000) / 3600000); | |
| const m = Math.floor((diff % 3600000) / 60000); | |
| const s = Math.floor((diff % 60000) / 1000); | |
| document.getElementById('cd-days').textContent = String(d).padStart(2,'0'); | |
| document.getElementById('cd-hours').textContent = String(h).padStart(2,'0'); | |
| document.getElementById('cd-mins').textContent = String(m).padStart(2,'0'); | |
| document.getElementById('cd-secs').textContent = String(s).padStart(2,'0'); | |
| } | |
| tick(); | |
| countdownInterval = setInterval(tick, 1000); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| SCROLL TO TOP BUTTON | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| (function() { | |
| const btn = document.getElementById('scroll-top-btn'); | |
| if (!btn) return; | |
| window.addEventListener('scroll', () => { | |
| if (window.scrollY > 400) btn.classList.add('visible'); | |
| else btn.classList.remove('visible'); | |
| }, { passive: true }); | |
| })(); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DEITY FAMILY TREE | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| COMPLETE DIVINE FAMILY TREE โ ALL THREE DEITIES | |
| Layout (x, y) โ SVG viewBox 0 0 2200 1400 | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| Row 0 y=70 : Trimurti (Brahma ยท Vishnu ยท Shiva) | |
| Row 1 y=240 : Consorts | |
| Row 2 y=420 : Avatars / Children / Major Forms | |
| Row 3 y=600 : Sub-avatars / Grandchildren / More Forms | |
| Row 4 y=780 : Further Progeny / Devotees / Manifestations | |
| Row 5 y=960 : Deep lineage (children of children) | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const FT_DEITIES = [ | |
| /* โโ TRIMURTI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| { | |
| id:'brahma', name:'Brahma', icon:'๐', color:'#FF6B00', | |
| epithet:'The Creator โ Svayambhu', | |
| desc:'The four-faced lord who emerged from the cosmic lotus growing from Vishnu\'s navel. He fashioned the universe, wrote the fate of all beings, and authored the four Vedas. He rides a white swan (Hamsa) and is rarely worshipped alone in temples because of a curse by Shiva.', | |
| tags:['Creator','Trimurti','Vedas','Four Faces','Lotus-born'], | |
| x:1100, y:70 | |
| }, | |
| { | |
| id:'vishnu', name:'Vishnu', icon:'๐', color:'#FF6B00', | |
| epithet:'The Preserver โ Narayana', | |
| desc:'The all-pervading sustainer who reclines on the cosmic serpent Adishesha in the Kshira Sagara (Milky Ocean). Whenever Dharma declines He descends as an avatar. He carries the Sudarshana Chakra, Panchajanya conch, Kaumodaki mace, and the Padma lotus. He is also called Narayana, Hari, and Vaikunthanatha.', | |
| tags:['Preserver','Trimurti','Dashavatara','Narayana','Vaikunta'], | |
| x:440, y:70 | |
| }, | |
| { | |
| id:'shiva', name:'Shiva', icon:'๐ฑ', color:'#FF6B00', | |
| epithet:'The Transformer โ Mahadeva', | |
| desc:'Mahadeva, the supreme deity of transformation, destruction and regeneration. He meditates on Mount Kailash, smears sacred ash on his body, wears a crescent moon, and the Ganga flows from his matted hair. As Nataraja he performs the cosmic dance of creation and dissolution. He is also Ardhanarishvara โ half male, half female.', | |
| tags:['Destroyer','Trimurti','Nataraja','Mahadeva','Kailash'], | |
| x:1760, y:70 | |
| }, | |
| /* โโ BRAHMA\'S LINE โโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| { | |
| id:'saraswati', name:'Saraswati', icon:'๐ต', color:'#D4A017', | |
| epithet:'Goddess of Knowledge & Arts', | |
| desc:'Consort of Brahma, born from his creative intellect. Vak Devi โ goddess of speech, learning, music, and wisdom. She rides a white swan, plays the veena, and holds a book and rosary. The patron of students and artists, she is worshipped at Vasant Panchami.', | |
| tags:['Knowledge','Music','Arts','Vak Devi','Vasant Panchami'], | |
| x:880, y:240 | |
| }, | |
| { | |
| id:'narada', name:'Narada Muni', icon:'๐ช', color:'#4a9eff', | |
| epithet:'Celestial Sage & Divine Messenger', | |
| desc:'A mind-born son (Manasputra) of Brahma. Narada travels the three worlds with his veena Mahati, chanting "Narayana Narayana." He is the first journalist of the cosmos โ carrying news between gods and humans โ and a master of Bhakti yoga.', | |
| tags:['Sage','Manasputra','Bhakti','Messenger'], | |
| x:680, y:420 | |
| }, | |
| { | |
| id:'daksha', name:'Daksha Prajapati', icon:'๐', color:'#4a9eff', | |
| epithet:'Lord of Progeny โ Prajapati', | |
| desc:'One of Brahma\'s ten Manasputras and the chief Prajapati (progenitor). Father of Sati (first wife of Shiva). His arrogant yajna (sacrifice) that excluded Shiva led to Sati\'s self-immolation and sparked Shiva\'s fury. He was later restored to life with a goat\'s head.', | |
| tags:['Prajapati','Progenitor','Father of Sati'], | |
| x:880, y:420 | |
| }, | |
| { | |
| id:'sati', name:'Sati Devi', icon:'๐ฅ', color:'#D4A017', | |
| epithet:'First Consort of Shiva โ Daksha\'s Daughter', | |
| desc:'Daughter of Daksha and the first wife of Shiva. She immolated herself in the sacrificial fire when her father insulted Shiva. Her grief-stricken husband carried her body across creation; the spots where her limbs fell became the 51 Shakti Peethas. She was reborn as Parvati.', | |
| tags:['Shakti Peetha','First Wife of Shiva','Self-Sacrifice'], | |
| x:1080, y:420 | |
| }, | |
| { | |
| id:'marichi', name:'Maharishi Marichi', icon:'โจ', color:'#4a9eff', | |
| epithet:'One of the Saptarishis', | |
| desc:'A Manasputra of Brahma and one of the seven great Sages (Saptarishi). Father of Kashyapa, the great progenitor. Marichi embodies the first cosmic ray of light emerging from creation.', | |
| tags:['Saptarishi','Manasputra','Cosmic Sage'], | |
| x:480, y:420 | |
| }, | |
| { | |
| id:'kashyapa', name:'Kashyapa Rishi', icon:'๐ฟ', color:'#4a9eff', | |
| epithet:'Father of the Universe\'s Beings', | |
| desc:'Son of Marichi and grandson of Brahma. Kashyapa is the progenitor of gods (Devas), demons (Asuras), humans, serpents (Nagas), birds (Garudas) and all living beings through his many wives, the daughters of Daksha.', | |
| tags:['Progenitor','Saptarishi','Father of Devas & Asuras'], | |
| x:300, y:600 | |
| }, | |
| { | |
| id:'aditi', name:'Aditi', icon:'๐ ', color:'#D4A017', | |
| epithet:'Mother of the Gods โ Mother of Adityas', | |
| desc:'Wife of Kashyapa and daughter of Daksha. She is the boundless sky-goddess and mother of the Adityas (twelve sun-gods) including Indra, Surya, Mitra, and Varuna. Her name means "the unbounded one."', | |
| tags:['Mother of Adityas','Sky Goddess','Daksha\'s Daughter'], | |
| x:580, y:600 | |
| }, | |
| { | |
| id:'indra', name:'Indra', icon:'โก', color:'#4a9eff', | |
| epithet:'King of Heaven โ Deva-Raja', | |
| desc:'Son of Kashyapa and Aditi, ruler of Svarga (heaven) and king of all the Devas. He wields the thunderbolt Vajra, rides the white elephant Airavata, and commands rain and storms. Often humbled by the devotion of great sages and avatars of Vishnu.', | |
| tags:['King of Heaven','Thunder','Vajra','Airavata'], | |
| x:120, y:780 | |
| }, | |
| { | |
| id:'surya', name:'Lord Surya', icon:'๐', color:'#FF6B00', | |
| epithet:'God of the Sun โ Aditya', | |
| desc:'Son of Kashyapa and Aditi, and chief among the twelve Adityas. He drives his golden chariot pulled by seven horses across the sky. Father of Manu (ancestor of humanity), Yama (god of death), Yami, Karna, Shani, and Ashwini Kumaras. The source of all light and vitality.', | |
| tags:['Sun God','Aditya','Father of Manu','Father of Yama'], | |
| x:300, y:780 | |
| }, | |
| /* โโ VISHNU\'S LINE โโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| { | |
| id:'lakshmi', name:'Lakshmi', icon:'๐ธ', color:'#D4A017', | |
| epithet:'Goddess of Fortune & Beauty', | |
| desc:'The eternal consort of Vishnu, born from the churning of the cosmic ocean (Samudra Manthan). Goddess of wealth, prosperity, beauty, and good fortune. She sits on a lotus and showers gold coins. In each of Vishnu\'s avatars she accompanies him โ as Sita with Rama, as Rukmini with Krishna.', | |
| tags:['Wealth','Fortune','Samudra Manthan','Lotus Goddess'], | |
| x:200, y:240 | |
| }, | |
| { | |
| id:'matsya', name:'Matsya', icon:'๐', color:'#8B0000', | |
| epithet:'1st Avatar โ The Fish', | |
| desc:'The first avatar of Vishnu, who took the form of a giant fish to save the Vedas from the demon Shankhasura and to rescue the sage Manu and all living creatures from the great cosmic deluge. The first of the Dashavatara.', | |
| tags:['Dashavatara','1st Avatar','Cosmic Flood','Vedas'], | |
| x:40, y:420 | |
| }, | |
| { | |
| id:'kurma', name:'Kurma', icon:'๐ข', color:'#8B0000', | |
| epithet:'2nd Avatar โ The Tortoise', | |
| desc:'Vishnu took the form of a giant tortoise to support Mount Mandara on his back during the churning of the cosmic ocean (Samudra Manthan), so the gods and demons could produce Amrita (the nectar of immortality).', | |
| tags:['Dashavatara','2nd Avatar','Samudra Manthan'], | |
| x:160, y:420 | |
| }, | |
| { | |
| id:'varaha', name:'Varaha', icon:'๐', color:'#8B0000', | |
| epithet:'3rd Avatar โ The Boar', | |
| desc:'Vishnu descended as a cosmic boar to rescue Bhudevi (Earth goddess) from the demon Hiranyaksha who had dragged her to the bottom of the primordial ocean. He lifted the Earth on his tusks and restored cosmic order.', | |
| tags:['Dashavatara','3rd Avatar','Earth Rescuer'], | |
| x:280, y:420 | |
| }, | |
| { | |
| id:'narasimha', name:'Narasimha', icon:'๐ฆ', color:'#8B0000', | |
| epithet:'4th Avatar โ Man-Lion', | |
| desc:'The half-man, half-lion avatar who appeared at twilight from a pillar to slay the demon king Hiranyakashipu, who had a boon making him immune to death by man or beast, by day or night, indoors or outdoors, on land or in the sky. He protected his devotee Prahlada.', | |
| tags:['Dashavatara','4th Avatar','Prahlada','Man-Lion'], | |
| x:400, y:420 | |
| }, | |
| { | |
| id:'vamana', name:'Vamana', icon:'๐ฃ', color:'#8B0000', | |
| epithet:'5th Avatar โ The Dwarf', | |
| desc:'Vishnu appeared as a dwarf brahmin boy and asked the generous demon king Bali for three paces of land. When granted, he grew to cosmic size and reclaimed the three worlds in two steps, placing the third on Bali\'s head, sending him to the netherworld.', | |
| tags:['Dashavatara','5th Avatar','Three Steps','Bali'], | |
| x:620, y:420 | |
| }, | |
| { | |
| id:'parashurama', name:'Parashurama', icon:'๐ช', color:'#8B0000', | |
| epithet:'6th Avatar โ The Warrior Sage', | |
| desc:'The brahmin warrior avatar who destroyed the corrupt Kshatriya (warrior) class twenty-one times to restore dharma. Son of Sage Jamadagni and Renuka. He carries an axe (Parashu) given by Shiva and is considered a Chiranjivi (immortal).', | |
| tags:['Dashavatara','6th Avatar','Warrior Brahmin','Immortal'], | |
| x:120, y:600 | |
| }, | |
| { | |
| id:'rama', name:'Lord Rama', icon:'๐น', color:'#8B0000', | |
| epithet:'7th Avatar โ Ideal King of Ayodhya', | |
| desc:'Maryada Purushottam โ the ideal man who exemplifies perfect righteousness, filial piety, and honour. Prince of Ayodhya, son of King Dasharatha and Kaushalya. The hero of the Ramayana who slew the demon king Ravana to rescue his wife Sita.', | |
| tags:['Dashavatara','7th Avatar','Ramayana','Ayodhya','Dharma'], | |
| x:400, y:600 | |
| }, | |
| { | |
| id:'krishna', name:'Lord Krishna', icon:'๐บ', color:'#8B0000', | |
| epithet:'8th Avatar โ The Complete Divine', | |
| desc:'The Purna Avatar โ the most complete manifestation of Vishnu. Divine flute player, charioteer, and philosopher-king who delivered the Bhagavad Gita. Son of Vasudeva and Devaki, raised by Nanda and Yashoda in Vrindavan. Husband of Rukmini and 16,108 wives.', | |
| tags:['Dashavatara','8th Avatar','Bhagavad Gita','Vrindavan','Purna Avatar'], | |
| x:500, y:600 | |
| }, | |
| { | |
| id:'balarama', name:'Balarama', icon:'๐พ', color:'#8B0000', | |
| epithet:'9th Avatar โ The Ploughman', | |
| desc:'Elder brother of Krishna and the ninth avatar of Vishnu. Carries the plough (Hala) and mace (Musala). Incredibly powerful โ he is said to have once dragged the river Yamuna with his plough when she refused to come to him. He taught wrestling to both Krishna and the Pandavas.', | |
| tags:['Dashavatara','9th Avatar','Elder Brother of Krishna','Plough'], | |
| x:680, y:600 | |
| }, | |
| { | |
| id:'kalki', name:'Kalki', icon:'๐', color:'#8B0000', | |
| epithet:'10th Avatar โ The Future Destroyer of Evil', | |
| desc:'The prophesied final avatar of Vishnu who has not yet appeared. He will ride a white horse, wielding a blazing sword, to end the Kali Yuga (the current age of darkness), destroy evil-doers, and usher in a new age of righteousness (Satya Yuga).', | |
| tags:['Dashavatara','10th Avatar','Future Avatar','Kali Yuga'], | |
| x:860, y:600 | |
| }, | |
| /* Krishna family */ | |
| { | |
| id:'rukmini', name:'Rukmini', icon:'๐', color:'#D4A017', | |
| epithet:'Chief Queen of Krishna โ Mahalakshmi Incarnate', | |
| desc:'The principal queen of Krishna, princess of Vidarbha, and an incarnation of Lakshmi. She wrote a secret letter to Krishna asking him to elope with her before her forced marriage to Shishupala, and Krishna carried her away in his chariot.', | |
| tags:['Queen of Krishna','Incarnation of Lakshmi','Dwarka'], | |
| x:460, y:780 | |
| }, | |
| { | |
| id:'radha', name:'Radha Rani', icon:'๐น', color:'#D4A017', | |
| epithet:'Supreme Goddess โ Eternal Soul of Krishna', | |
| desc:'In Vaishnavism, Radha is the supreme goddess and the very soul of Krishna. Her love for Krishna โ selfless, all-encompassing, and transcendent โ is the highest ideal of Bhakti. She presides at Barsana and Vrindavan. The name Radha-Krishna is inseparable in devotion.', | |
| tags:['Supreme Shakti','Divine Love','Bhakti','Vrindavan'], | |
| x:620, y:780 | |
| }, | |
| { | |
| id:'pradyumna', name:'Pradyumna', icon:'โ๏ธ', color:'#4a9eff', | |
| epithet:'Son of Krishna & Rukmini โ Reincarnation of Kamadeva', | |
| desc:'The eldest son of Krishna and Rukmini, and an incarnation of Kamadeva (the god of love). He was kidnapped at birth by the demon Shambara but was rescued and raised by Mayavati. He became a great warrior and hero.', | |
| tags:['Son of Krishna','Kamadeva Reincarnation','Hero'], | |
| x:360, y:960 | |
| }, | |
| { | |
| id:'aniruddha', name:'Aniruddha', icon:'๐ฏ', color:'#4a9eff', | |
| epithet:'Grandson of Krishna', | |
| desc:'Son of Pradyumna and grandson of Krishna and Rukmini. He fell in love with Usha, daughter of the demon king Bana, and was held captive. His rescue triggered a great battle between Krishna and Shiva.', | |
| tags:['Grandson of Krishna','Usha\'s Consort'], | |
| x:520, y:960 | |
| }, | |
| /* Rama family */ | |
| { | |
| id:'sita', name:'Goddess Sita', icon:'๐', color:'#D4A017', | |
| epithet:'Consort of Rama โ Daughter of Mother Earth', | |
| desc:'Born from the earth โ discovered in a furrow by King Janaka while ploughing a field. She is the incarnation of Lakshmi and the embodiment of purity, devotion, and sacrifice. Her abduction by Ravana and rescue by Rama form the heart of the Ramayana.', | |
| tags:['Consort of Rama','Incarnation of Lakshmi','Ramayana','Purity'], | |
| x:180, y:780 | |
| }, | |
| { | |
| id:'hanuman', name:'Hanuman Ji', icon:'๐', color:'#FF6B00', | |
| epithet:'Devoted Servant of Rama โ Chiranjeevi', | |
| desc:'Son of Vayu (wind god) and Anjana. The mighty monkey warrior whose devotion to Rama is unparalleled. He leapt across the ocean to Lanka, carried the Sanjeevani mountain, and burned Ravana\'s city. He is a Chiranjeevi (immortal) still present in the world, protecting devotees.', | |
| tags:['Son of Vayu','Devotion','Strength','Immortal','Bhakti'], | |
| x:40, y:780 | |
| }, | |
| { | |
| id:'lava', name:'Lava & Kusha', icon:'๐ฆ', color:'#4a9eff', | |
| epithet:'Twin Sons of Rama & Sita', | |
| desc:'The twin sons of Rama and Sita, born in the hermitage of Sage Valmiki after Sita\'s exile. Raised as warriors and scholars by Valmiki himself, they learned and sang the Ramayana. They confronted their own father\'s army without knowing his identity.', | |
| tags:['Sons of Rama','Sita\'s Twins','Ramayana Singers'], | |
| x:220, y:960 | |
| }, | |
| /* โโ SHIVA\'S LINE โโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| { | |
| id:'parvati', name:'Parvati', icon:'๐บ', color:'#D4A017', | |
| epithet:'The Divine Mother โ Adi Shakti Reborn', | |
| desc:'Daughter of Himavan (king of the Himalayas) and Mena, Parvati is the reincarnation of Sati and the second wife of Shiva. Through intense tapas (austerities) she won Shiva\'s heart. She is the gentle, nurturing face of Adi Shakti โ mother of Ganesha and Kartikeya, and the source of all Shakti manifestations.', | |
| tags:['Adi Shakti','Mother Goddess','Mountain Princess','Sati Reborn'], | |
| x:1620, y:240 | |
| }, | |
| { | |
| id:'ganesha', name:'Ganesha', icon:'๐', color:'#4a9eff', | |
| epithet:'Vighnaharta โ Remover of Obstacles', | |
| desc:'The elephant-headed son of Shiva and Parvati, and the first deity invoked before any sacred undertaking. He received his elephant head when Shiva, not recognising him, beheaded him and later replaced his head with that of an elephant. Lord of intellect, beginnings, and the arts. He rides a mouse (Mushika).', | |
| tags:['Remover of Obstacles','Beginnings','Intellect','Elephant Head'], | |
| x:1500, y:420 | |
| }, | |
| { | |
| id:'kartikeya', name:'Kartikeya', icon:'๐ฆ', color:'#4a9eff', | |
| epithet:'Skanda โ God of War & Commander of Divine Army', | |
| desc:'Also known as Murugan, Skanda, or Subrahmanya. The warrior son of Shiva and Parvati, created from the six sparks of Shiva\'s third eye and raised by the six Krittikas (Pleiades stars). He rides a peacock, wields the Vel spear, and is the commander-in-chief of the divine army who defeated the demon Tarakasura.', | |
| tags:['War','Victory','Peacock','Skanda','Murugan','Subrahmanya'], | |
| x:1700, y:420 | |
| }, | |
| { | |
| id:'riddhi', name:'Riddhi & Siddhi', icon:'๐ซ', color:'#D4A017', | |
| epithet:'Wives of Ganesha โ Prosperity & Achievement', | |
| desc:'Riddhi (prosperity and good fortune) and Siddhi (spiritual achievement and success) are the two wives of Ganesha, daughters of Vishwarupa or, in some traditions, of Brahma. They represent the dual blessings bestowed on devotees by Ganesha.', | |
| tags:['Prosperity','Siddhi','Wives of Ganesha'], | |
| x:1400, y:600 | |
| }, | |
| { | |
| id:'shubha_labha', name:'Shubha & Labha', icon:'๐', color:'#4a9eff', | |
| epithet:'Sons of Ganesha', | |
| desc:'Shubha (auspiciousness) and Labha (profit/gain) are the two sons of Ganesha born to Riddhi and Siddhi. They embody the blessings of good fortune and material success that flow from devotion to Ganesha.', | |
| tags:['Sons of Ganesha','Auspiciousness','Good Fortune'], | |
| x:1540, y:600 | |
| }, | |
| { | |
| id:'devasena', name:'Devasena', icon:'๐ผ', color:'#D4A017', | |
| epithet:'Wife of Kartikeya โ Daughter of Indra', | |
| desc:'Devasena (also called Devayani or Devayanai) is the wife of Kartikeya and the daughter of Indra and Sachidevi. She is the personification of the army of the gods. Her marriage to Kartikeya is celebrated in the Tamil tradition.', | |
| tags:['Wife of Kartikeya','Indra\'s Daughter','Divine Army'], | |
| x:1780, y:600 | |
| }, | |
| { | |
| id:'valli', name:'Valli', icon:'๐ฟ', color:'#D4A017', | |
| epithet:'Second Wife of Kartikeya โ Hunter Tribe Goddess', | |
| desc:'Valli is the second wife of Kartikeya (Murugan), born to a hunter chief. She represents the earthly, natural aspect of the divine feminine. Together with Devasena she flanks Murugan in all temples, representing the two dimensions of Shakti โ celestial and earthly.', | |
| tags:['Second Wife of Kartikeya','Murugan','Tamil Tradition'], | |
| x:1940, y:600 | |
| }, | |
| /* Shakti forms of Parvati */ | |
| { | |
| id:'durga', name:'Goddess Durga', icon:'๐ฆ', color:'#c040c0', | |
| epithet:'Mahishasuramardini โ Invincible Warrior', | |
| desc:'The warrior form of Parvati, created from the combined radiance of all the Devas to defeat the buffalo-demon Mahishasura. She rides a lion and carries weapons given by each god in her eighteen arms. The ten-day festival of Navratri celebrates her victory.', | |
| tags:['Warrior Goddess','Navratri','Mahishasura','Adi Shakti'], | |
| x:1320, y:420 | |
| }, | |
| { | |
| id:'kali', name:'Goddess Kali', icon:'๐', color:'#c040c0', | |
| epithet:'The Dark Mother โ Destroyer of Time', | |
| desc:'The fierce form of Parvati who emerged from Durga\'s forehead during the battle with the demons Chanda and Munda. She is the embodiment of Shakti\'s limitless, destructive power. Dark-complexioned, with a garland of skulls, she stands on Shiva\'s chest. She is also the loving mother who liberates souls from the cycle of rebirth.', | |
| tags:['Dark Mother','Fierce Shakti','Liberation','Time'], | |
| x:1460, y:780 | |
| }, | |
| { | |
| id:'chamunda', name:'Chamunda', icon:'๐', color:'#c040c0', | |
| epithet:'Slayer of Chanda & Munda', | |
| desc:'A fierce aspect of Kali or Durga who slew the demons Chanda and Munda, presenting their heads to Durga. She is depicted emaciated, wearing a tiger skin and a skull garland. She is one of the Saptamatrikas (seven divine mothers).', | |
| tags:['Fierce Goddess','Saptamatrika','Demon Slayer'], | |
| x:1600, y:960 | |
| }, | |
| { | |
| id:'bhairava', name:'Bhairava', icon:'๐', color:'#c040c0', | |
| epithet:'Fierce Form of Shiva โ Guardian of Kashi', | |
| desc:'A terrifying manifestation of Shiva associated with annihilation and the dissolution of the universe. Kala Bhairava is the guardian deity of Varanasi (Kashi). He is accompanied by a dog and carries a trident, noose, drum, and skull-cup. He is also worshipped as the master of time.', | |
| tags:['Fierce Shiva','Guardian of Kashi','Kala Bhairava'], | |
| x:2000, y:420 | |
| }, | |
| { | |
| id:'nandi', name:'Nandi', icon:'๐', color:'#4a9eff', | |
| epithet:'Sacred Bull โ Gatekeeper of Kailash', | |
| desc:'The divine bull and the primary gana (attendant) of Shiva. Nandi is the guardian of Kailash, the gatekeeper of Shiva\'s abode, and the chief of Shiva\'s army. He kneels perpetually before Shiva\'s lingam. Nandi symbolises strength, virility, and steadfast devotion.', | |
| tags:['Sacred Bull','Gatekeeper','Shiva\'s Vehicle','Gana'], | |
| x:1840, y:420 | |
| }, | |
| { | |
| id:'virabhadra', name:'Virabhadra', icon:'โ๏ธ', color:'#c040c0', | |
| epithet:'Warrior Form of Shiva โ Daksha\'s Nemesis', | |
| desc:'Created from Shiva\'s wrath and matted locks when he learned of Sati\'s death at Daksha\'s yajna. A fierce warrior who led the army of Shiva\'s ganas to destroy Daksha\'s sacrifice, beheading Daksha himself. He represents Shiva\'s capacity for righteous fury.', | |
| tags:['Warrior Form of Shiva','Daksha\'s Yajna','Righteous Fury'], | |
| x:1960, y:780 | |
| }, | |
| { | |
| id:'ayyappa', name:'Ayyappa', icon:'๐', color:'#4a9eff', | |
| epithet:'Harihara Putra โ Son of Vishnu & Shiva', | |
| desc:'Born of the union of Vishnu (in the form of Mohini) and Shiva. Also called Manikandan, he is the presiding deity of the Sabarimala temple in Kerala. He symbolises the harmony of Shaiva and Vaishnava traditions, and his devotees undertake a 41-day austerity before pilgrimage.', | |
| tags:['Harihara Putra','Sabarimala','Son of Vishnu & Shiva','Unity'], | |
| x:1760, y:780 | |
| }, | |
| /* Additional Shiva aspects */ | |
| { | |
| id:'ardhanarishvara', name:'Ardhanarishvara', icon:'โฏ๏ธ', color:'#FF6B00', | |
| epithet:'The Androgynous Form โ Shiva-Shakti United', | |
| desc:'The composite androgynous form that is half Shiva (right side) and half Parvati (left side). This iconography represents the inseparability of the male and female principles in creation, and the transcendence of all duality. It is the supreme symbol of cosmic oneness.', | |
| tags:['Androgynous','Cosmic Union','Non-duality','Shiva-Shakti'], | |
| x:2080, y:240 | |
| }, | |
| { | |
| id:'nataraja', name:'Nataraja', icon:'๐', color:'#FF6B00', | |
| epithet:'Lord of the Cosmic Dance', | |
| desc:'Shiva as the lord of dance, performing the Ananda Tandava (dance of bliss) within a ring of fire. His four arms hold a drum (Damaru โ the rhythm of creation), flame (the fire of dissolution), the Abhaya gesture (fear not), and a hand pointing to his raised foot (liberation). He stands on the dwarf of ignorance, Apasmara.', | |
| tags:['Cosmic Dance','Nataraja','Creation & Dissolution','Tandava'], | |
| x:2080, y:420 | |
| }, | |
| ]; | |
| const FT_EDGES = [ | |
| /* Trimurti base */ | |
| { from:'brahma', to:'saraswati', type:'consort' }, | |
| { from:'vishnu', to:'lakshmi', type:'consort' }, | |
| { from:'shiva', to:'parvati', type:'consort' }, | |
| { from:'shiva', to:'sati', type:'consort' }, | |
| { from:'shiva', to:'ardhanarishvara', type:'form' }, | |
| { from:'shiva', to:'nataraja', type:'form' }, | |
| { from:'shiva', to:'bhairava', type:'form' }, | |
| { from:'shiva', to:'virabhadra', type:'form' }, | |
| /* Brahma lineage */ | |
| { from:'brahma', to:'narada', type:'child' }, | |
| { from:'brahma', to:'daksha', type:'child' }, | |
| { from:'brahma', to:'marichi', type:'child' }, | |
| { from:'daksha', to:'sati', type:'child' }, | |
| { from:'daksha', to:'aditi', type:'child' }, | |
| { from:'marichi', to:'kashyapa', type:'child' }, | |
| { from:'kashyapa',to:'aditi', type:'consort' }, | |
| { from:'kashyapa',to:'indra', type:'child' }, | |
| { from:'kashyapa',to:'surya', type:'child' }, | |
| { from:'aditi', to:'indra', type:'child' }, | |
| { from:'aditi', to:'surya', type:'child' }, | |
| { from:'sati', to:'parvati', type:'form' }, | |
| /* Vishnu Dashavatara */ | |
| { from:'vishnu', to:'matsya', type:'avatar' }, | |
| { from:'vishnu', to:'kurma', type:'avatar' }, | |
| { from:'vishnu', to:'varaha', type:'avatar' }, | |
| { from:'vishnu', to:'narasimha', type:'avatar' }, | |
| { from:'vishnu', to:'vamana', type:'avatar' }, | |
| { from:'vishnu', to:'parashurama', type:'avatar' }, | |
| { from:'vishnu', to:'rama', type:'avatar' }, | |
| { from:'vishnu', to:'krishna', type:'avatar' }, | |
| { from:'vishnu', to:'balarama', type:'avatar' }, | |
| { from:'vishnu', to:'kalki', type:'avatar' }, | |
| { from:'lakshmi', to:'sita', type:'form' }, | |
| { from:'lakshmi', to:'rukmini', type:'form' }, | |
| { from:'lakshmi', to:'radha', type:'form' }, | |
| /* Rama family */ | |
| { from:'rama', to:'sita', type:'consort' }, | |
| { from:'rama', to:'hanuman', type:'devotee' }, | |
| { from:'rama', to:'lava', type:'child' }, | |
| { from:'sita', to:'lava', type:'child' }, | |
| /* Krishna family */ | |
| { from:'krishna', to:'rukmini', type:'consort' }, | |
| { from:'krishna', to:'radha', type:'consort' }, | |
| { from:'krishna', to:'pradyumna', type:'child' }, | |
| { from:'rukmini', to:'pradyumna', type:'child' }, | |
| { from:'pradyumna',to:'aniruddha', type:'child' }, | |
| /* Shiva family */ | |
| { from:'shiva', to:'ganesha', type:'child' }, | |
| { from:'shiva', to:'kartikeya', type:'child' }, | |
| { from:'shiva', to:'nandi', type:'devotee' }, | |
| { from:'parvati', to:'ganesha', type:'child' }, | |
| { from:'parvati', to:'kartikeya', type:'child' }, | |
| { from:'parvati', to:'durga', type:'form' }, | |
| { from:'parvati', to:'kali', type:'form' }, | |
| { from:'durga', to:'chamunda', type:'form' }, | |
| { from:'kali', to:'chamunda', type:'form' }, | |
| { from:'ganesha', to:'riddhi', type:'consort' }, | |
| { from:'ganesha', to:'shubha_labha', type:'child' }, | |
| { from:'riddhi', to:'shubha_labha', type:'child' }, | |
| { from:'kartikeya',to:'devasena', type:'consort' }, | |
| { from:'kartikeya',to:'valli', type:'consort' }, | |
| { from:'indra', to:'devasena', type:'child' }, | |
| /* Harihara Putra */ | |
| { from:'vishnu', to:'ayyappa', type:'avatar' }, | |
| { from:'shiva', to:'ayyappa', type:'child' }, | |
| ]; | |
| const EDGE_COLORS = { consort:'#D4A017', avatar:'#8B0000', child:'#4a9eff', devotee:'#FF6B00', form:'#c040c0', cosmic:'rgba(255,215,0,0.3)' }; | |
| function buildFamilyTree() { | |
| const svg = document.getElementById('family-tree-svg'); | |
| if (!svg) return; | |
| // Column header labels | |
| const headers = [ | |
| { x: 390, label: 'โฆ BRAHMA\'S LINEAGE โฆ', color: '#FF9A3C' }, | |
| { x: 500, label: 'โฆ VISHNU\'S LINEAGE โฆ', color: '#4a9eff' }, | |
| { x: 1770, label: 'โฆ SHIVA\'S LINEAGE โฆ', color: '#c040c0' }, | |
| ]; | |
| // Actually draw three vertical domain dividers | |
| const dividers = [ | |
| { x1:840, y1:30, x2:840, y2:1050, color:'rgba(255,107,0,0.12)' }, | |
| { x1:1380, y1:30, x2:1380, y2:1050, color:'rgba(255,107,0,0.12)' }, | |
| ]; | |
| dividers.forEach(d => { | |
| const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); | |
| line.setAttribute('x1', d.x1); line.setAttribute('y1', d.y1); | |
| line.setAttribute('x2', d.x2); line.setAttribute('y2', d.y2); | |
| line.setAttribute('stroke', d.color); | |
| line.setAttribute('stroke-width', '1'); | |
| line.setAttribute('stroke-dasharray', '6 4'); | |
| svg.appendChild(line); | |
| }); | |
| // Domain background rects | |
| const domains = [ | |
| { x:0, y:0, w:840, h:1060, color:'rgba(255,107,0,0.02)' }, | |
| { x:840, y:0, w:540, h:1060, color:'rgba(74,158,255,0.02)' }, | |
| { x:1380, y:0, w:820, h:1060, color:'rgba(192,64,192,0.02)' }, | |
| ]; | |
| domains.forEach(d => { | |
| const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); | |
| rect.setAttribute('x', d.x); rect.setAttribute('y', d.y); | |
| rect.setAttribute('width', d.w); rect.setAttribute('height', d.h); | |
| rect.setAttribute('fill', d.color); | |
| svg.insertBefore(rect, svg.firstChild); | |
| }); | |
| // Domain title labels | |
| const domainLabels = [ | |
| { x:425, y:16, text:'๐ VISHNU\'S LINEAGE', color:'#4a9eff' }, | |
| { x:1090, y:16, text:'๐ BRAHMA\'S LINEAGE', color:'#FF9A3C' }, | |
| { x:1745, y:16, text:'๐ฑ SHIVA\'S LINEAGE', color:'#c040c0' }, | |
| ]; | |
| domainLabels.forEach(lbl => { | |
| const t = document.createElementNS('http://www.w3.org/2000/svg', 'text'); | |
| t.setAttribute('x', lbl.x); | |
| t.setAttribute('y', lbl.y); | |
| t.setAttribute('text-anchor', 'middle'); | |
| t.setAttribute('fill', lbl.color); | |
| t.setAttribute('font-size', '11'); | |
| t.setAttribute('font-family', 'Cinzel, serif'); | |
| t.setAttribute('font-weight', '700'); | |
| t.setAttribute('letter-spacing', '3'); | |
| t.setAttribute('opacity', '0.75'); | |
| t.textContent = lbl.text; | |
| svg.appendChild(t); | |
| }); | |
| // Draw edges first | |
| FT_EDGES.forEach(edge => { | |
| const from = FT_DEITIES.find(d => d.id === edge.from); | |
| const to = FT_DEITIES.find(d => d.id === edge.to); | |
| if (!from || !to) return; | |
| const line = document.createElementNS('http://www.w3.org/2000/svg', 'path'); | |
| const mx = (from.x + to.x) / 2; | |
| const my = (from.y + to.y) / 2; | |
| // Use curved bezier for child/avatar lines, straight-ish for others | |
| const cpx = edge.type === 'child' || edge.type === 'avatar' ? mx : (from.x + to.x * 0.3) / 1.3; | |
| line.setAttribute('d', `M ${from.x} ${from.y} Q ${mx} ${from.y} ${to.x} ${to.y}`); | |
| line.setAttribute('fill', 'none'); | |
| line.setAttribute('stroke', EDGE_COLORS[edge.type] || '#555'); | |
| line.setAttribute('stroke-width', edge.type === 'cosmic' ? '1' : '1.5'); | |
| line.setAttribute('stroke-dasharray', edge.type === 'cosmic' ? '4 4' : (edge.type === 'form' ? '5 3' : 'none')); | |
| line.setAttribute('opacity', '0.45'); | |
| svg.appendChild(line); | |
| }); | |
| // Draw nodes | |
| FT_DEITIES.forEach(d => { | |
| // Trimurti nodes are larger | |
| const isTrimurti = ['brahma','vishnu','shiva'].includes(d.id); | |
| const isConsort = d.color === '#D4A017'; | |
| const r = isTrimurti ? 32 : 24; | |
| const rGlow = isTrimurti ? 44 : 34; | |
| const iconY = isTrimurti ? '-1' : '-1'; | |
| const iconSize = isTrimurti ? '24' : '18'; | |
| const labelY = r + 16; | |
| const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); | |
| g.setAttribute('class', 'ft-node'); | |
| g.setAttribute('transform', `translate(${d.x},${d.y})`); | |
| g.style.cursor = 'pointer'; | |
| // Glow circle | |
| const glow = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); | |
| glow.setAttribute('r', rGlow); | |
| glow.setAttribute('fill', d.color); | |
| glow.setAttribute('opacity', isTrimurti ? '0.16' : '0.1'); | |
| g.appendChild(glow); | |
| // Main circle | |
| const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); | |
| circle.setAttribute('r', r); | |
| circle.setAttribute('fill', `${d.color}22`); | |
| circle.setAttribute('stroke', d.color); | |
| circle.setAttribute('stroke-width', isTrimurti ? '2.5' : '1.5'); | |
| g.appendChild(circle); | |
| // Icon text | |
| const icon = document.createElementNS('http://www.w3.org/2000/svg', 'text'); | |
| icon.setAttribute('text-anchor', 'middle'); | |
| icon.setAttribute('dominant-baseline', 'central'); | |
| icon.setAttribute('font-size', iconSize); | |
| icon.setAttribute('y', iconY); | |
| icon.textContent = d.icon; | |
| g.appendChild(icon); | |
| // Name label (may need wrapping for long names) | |
| const nameParts = d.name.split(' '); | |
| if (nameParts.length <= 2 || d.name.length <= 14) { | |
| const label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); | |
| label.setAttribute('text-anchor', 'middle'); | |
| label.setAttribute('y', labelY); | |
| label.setAttribute('fill', '#F5E6A3'); | |
| label.setAttribute('font-size', isTrimurti ? '11' : '9'); | |
| label.setAttribute('font-family', 'Cinzel, serif'); | |
| label.setAttribute('font-weight', '700'); | |
| label.textContent = d.name; | |
| g.appendChild(label); | |
| } else { | |
| // Two-line label | |
| const mid = Math.ceil(nameParts.length / 2); | |
| const line1 = nameParts.slice(0, mid).join(' '); | |
| const line2 = nameParts.slice(mid).join(' '); | |
| [line1, line2].forEach((txt, i) => { | |
| const label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); | |
| label.setAttribute('text-anchor', 'middle'); | |
| label.setAttribute('y', labelY + i * 11); | |
| label.setAttribute('fill', '#F5E6A3'); | |
| label.setAttribute('font-size', '9'); | |
| label.setAttribute('font-family', 'Cinzel, serif'); | |
| label.setAttribute('font-weight', '700'); | |
| label.textContent = txt; | |
| g.appendChild(label); | |
| }); | |
| } | |
| // Hover effect | |
| g.addEventListener('mouseenter', () => { | |
| glow.setAttribute('opacity', isTrimurti ? '0.3' : '0.22'); | |
| circle.setAttribute('stroke-width', isTrimurti ? '3.5' : '2.5'); | |
| }); | |
| g.addEventListener('mouseleave', () => { | |
| glow.setAttribute('opacity', isTrimurti ? '0.16' : '0.1'); | |
| circle.setAttribute('stroke-width', isTrimurti ? '2.5' : '1.5'); | |
| }); | |
| g.addEventListener('click', () => openFTPanel(d)); | |
| svg.appendChild(g); | |
| }); | |
| } | |
| function openFTPanel(deity) { | |
| const panel = document.getElementById('ft-info-panel'); | |
| document.getElementById('ft-info-icon').textContent = deity.icon; | |
| document.getElementById('ft-info-name').textContent = deity.name; | |
| document.getElementById('ft-info-epithet').textContent = deity.epithet; | |
| document.getElementById('ft-info-body').textContent = deity.desc; | |
| const tags = document.getElementById('ft-info-tags'); | |
| tags.innerHTML = deity.tags.map(t => `<span class="ft-tag">${t}</span>`).join(''); | |
| panel.classList.add('open'); | |
| } | |
| function closeFTPanel() { | |
| document.getElementById('ft-info-panel').classList.remove('open'); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FAMILY TREE DRAG-TO-SCROLL | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| (function initFTDrag() { | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const container = document.querySelector('.ft-scroll-container'); | |
| if (!container) return; | |
| let isDown = false; | |
| let startX, startY, scrollLeft, scrollTop; | |
| container.addEventListener('mousedown', function(e) { | |
| // Don't hijack clicks on the SVG nodes | |
| if (e.target.closest('.ft-node')) return; | |
| isDown = true; | |
| startX = e.pageX - container.offsetLeft; | |
| startY = e.pageY - container.offsetTop; | |
| scrollLeft = container.scrollLeft; | |
| scrollTop = container.scrollTop; | |
| container.style.cursor = 'grabbing'; | |
| container.style.userSelect = 'none'; | |
| e.preventDefault(); | |
| }); | |
| document.addEventListener('mouseup', function() { | |
| if (!isDown) return; | |
| isDown = false; | |
| container.style.cursor = 'grab'; | |
| container.style.userSelect = ''; | |
| }); | |
| document.addEventListener('mousemove', function(e) { | |
| if (!isDown) return; | |
| e.preventDefault(); | |
| const x = e.pageX - container.offsetLeft; | |
| const y = e.pageY - container.offsetTop; | |
| const walkX = (x - startX) * 1.2; | |
| const walkY = (y - startY) * 1.2; | |
| container.scrollLeft = scrollLeft - walkX; | |
| container.scrollTop = scrollTop - walkY; | |
| }); | |
| // Touch support | |
| let touchStartX, touchStartY, touchScrollLeft, touchScrollTop; | |
| container.addEventListener('touchstart', function(e) { | |
| const t = e.touches[0]; | |
| touchStartX = t.pageX; | |
| touchStartY = t.pageY; | |
| touchScrollLeft = container.scrollLeft; | |
| touchScrollTop = container.scrollTop; | |
| }, { passive: true }); | |
| container.addEventListener('touchmove', function(e) { | |
| const t = e.touches[0]; | |
| container.scrollLeft = touchScrollLeft - (t.pageX - touchStartX); | |
| container.scrollTop = touchScrollTop - (t.pageY - touchStartY); | |
| }, { passive: true }); | |
| }); | |
| })(); | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| MANDALA COLORING TOOL | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const MC_PALETTE_COLORS = [ | |
| '#FF6B00','#D4A017','#FFD700','#FF9A3C','#FFEEAA', | |
| '#8B0000','#C0392B','#E91E63','#9C27B0','#3F51B5', | |
| '#00BCD4','#4CAF50','#8BC34A','#ffffff','#888888','#1a0800' | |
| ]; | |
| let mcCurrentColor = '#FF6B00'; | |
| let mcBrushSize = 22; | |
| let mcDrawing = false; | |
| let mcHistory = []; | |
| let mcMandalaType = 'lotus'; | |
| const MC_SIZE = 480; | |
| const MC_HALF = MC_SIZE / 2; | |
| function initMandala() { | |
| const bgC = document.getElementById('mandala-bg-canvas'); | |
| const colC = document.getElementById('mandala-color-canvas'); | |
| if (!bgC || !colC) return; | |
| bgC.width = colC.width = MC_SIZE; | |
| bgC.height = colC.height = MC_SIZE; | |
| // Palette | |
| const palette = document.getElementById('mc-palette'); | |
| if (palette) { | |
| palette.innerHTML = MC_PALETTE_COLORS.map((c,i) => | |
| `<div class="mc-color-swatch${i===0?' active':''}" style="background:${c}" onclick="setColor('${c}',this)" title="${c}"></div>` | |
| ).join(''); | |
| } | |
| drawMandalaBg(); | |
| } | |
| function drawMandalaBg() { | |
| const bgC = document.getElementById('mandala-bg-canvas'); | |
| if (!bgC) return; | |
| const ctx = bgC.getContext('2d'); | |
| ctx.clearRect(0, 0, MC_SIZE, MC_SIZE); | |
| ctx.save(); | |
| ctx.translate(MC_HALF, MC_HALF); | |
| const type = mcMandalaType; | |
| if (type === 'lotus') drawLotusPattern(ctx); | |
| else if (type === 'chakra') drawChakraPattern(ctx); | |
| else if (type === 'sri') drawSriYantraPattern(ctx); | |
| else if (type === 'sun') drawSolarPattern(ctx); | |
| ctx.restore(); | |
| } | |
| function drawLotusPattern(ctx) { | |
| const rings = [40, 80, 130, 180, 220]; | |
| const petalCounts = [8, 12, 16, 20, 24]; | |
| const col = 'rgba(212,160,23,0.35)'; | |
| // Centre | |
| ctx.beginPath(); ctx.arc(0,0,30,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(255,107,0,0.5)'; ctx.lineWidth=1; ctx.stroke(); | |
| rings.forEach((r, ri) => { | |
| const n = petalCounts[ri]; | |
| for (let i=0; i<n; i++) { | |
| const angle = (i / n) * Math.PI * 2; | |
| const px = Math.cos(angle) * r; | |
| const py = Math.sin(angle) * r; | |
| ctx.save(); | |
| ctx.translate(px, py); | |
| ctx.rotate(angle + Math.PI/2); | |
| ctx.beginPath(); | |
| ctx.ellipse(0, 0, r*0.18, r*0.35, 0, 0, Math.PI*2); | |
| ctx.strokeStyle = col; | |
| ctx.lineWidth = 0.8; | |
| ctx.stroke(); | |
| ctx.restore(); | |
| } | |
| ctx.beginPath(); ctx.arc(0,0,r,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(212,160,23,0.2)'; ctx.lineWidth=0.5; ctx.stroke(); | |
| }); | |
| // Spokes | |
| for (let i=0; i<24; i++) { | |
| const a = (i/24)*Math.PI*2; | |
| ctx.beginPath(); | |
| ctx.moveTo(0,0); | |
| ctx.lineTo(Math.cos(a)*220, Math.sin(a)*220); | |
| ctx.strokeStyle='rgba(255,107,0,0.1)'; ctx.lineWidth=0.5; ctx.stroke(); | |
| } | |
| } | |
| function drawChakraPattern(ctx) { | |
| const col = 'rgba(100,180,255,0.35)'; | |
| const spokes = [6,8,10,12,16]; | |
| const rings = [40,85,130,175,220]; | |
| rings.forEach((r,ri) => { | |
| const n = spokes[ri]; | |
| for (let i=0;i<n;i++) { | |
| const a = (i/n)*Math.PI*2; | |
| ctx.beginPath(); | |
| ctx.moveTo(Math.cos(a)*(r-22), Math.sin(a)*(r-22)); | |
| ctx.lineTo(Math.cos(a)*(r+0), Math.sin(a)*(r+0)); | |
| ctx.strokeStyle = col; ctx.lineWidth=1.2; ctx.stroke(); | |
| } | |
| ctx.beginPath(); ctx.arc(0,0,r,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(100,180,255,0.25)'; ctx.lineWidth=1; ctx.stroke(); | |
| }); | |
| // Centre dot | |
| ctx.beginPath(); ctx.arc(0,0,12,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(255,215,0,0.6)'; ctx.lineWidth=1.5; ctx.stroke(); | |
| } | |
| function drawSriYantraPattern(ctx) { | |
| const col = 'rgba(255,107,0,0.4)'; | |
| function tri(r, up, col2) { | |
| ctx.beginPath(); | |
| if (up) { | |
| ctx.moveTo(0, -r); | |
| ctx.lineTo( r*0.866, r*0.5); | |
| ctx.lineTo(-r*0.866, r*0.5); | |
| } else { | |
| ctx.moveTo(0, r); | |
| ctx.lineTo( r*0.866, -r*0.5); | |
| ctx.lineTo(-r*0.866, -r*0.5); | |
| } | |
| ctx.closePath(); | |
| ctx.strokeStyle = col2 || col; ctx.lineWidth=1; ctx.stroke(); | |
| } | |
| [220,190,160,130,100,70,40].forEach((r,i) => tri(r, i%2===0, i%2===0 ? col : 'rgba(212,160,23,0.4)')); | |
| ctx.beginPath(); ctx.arc(0,0,230,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(255,107,0,0.3)'; ctx.lineWidth=1; ctx.stroke(); | |
| ctx.beginPath(); ctx.arc(0,0,210,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(255,107,0,0.2)'; ctx.lineWidth=1; ctx.stroke(); | |
| // Dot in centre | |
| ctx.beginPath(); ctx.arc(0,0,8,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(255,215,0,0.7)'; ctx.lineWidth=1.5; ctx.stroke(); | |
| } | |
| function drawSolarPattern(ctx) { | |
| const rays = 16; | |
| for (let i=0;i<rays;i++) { | |
| const a = (i/rays)*Math.PI*2; | |
| ctx.save(); ctx.rotate(a); | |
| ctx.beginPath(); | |
| ctx.moveTo(0, -35); | |
| ctx.lineTo(15, -130); | |
| ctx.lineTo(-15, -130); | |
| ctx.closePath(); | |
| ctx.strokeStyle='rgba(255,215,0,0.3)'; ctx.lineWidth=0.8; ctx.stroke(); | |
| ctx.restore(); | |
| } | |
| [30,70,110,150,190,225].forEach(r => { | |
| ctx.beginPath(); ctx.arc(0,0,r,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(255,215,0,0.2)'; ctx.lineWidth=0.8; ctx.stroke(); | |
| }); | |
| ctx.beginPath(); ctx.arc(0,0,28,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(255,107,0,0.6)'; ctx.lineWidth=1.5; ctx.stroke(); | |
| } | |
| function setMandalaType(type, btn) { | |
| mcMandalaType = type; | |
| document.querySelectorAll('.mc-mtype').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| clearMandala(); | |
| drawMandalaBg(); | |
| } | |
| function setColor(color, el) { | |
| mcCurrentColor = color; | |
| document.querySelectorAll('.mc-color-swatch').forEach(s => s.classList.remove('active')); | |
| if (el) el.classList.add('active'); | |
| } | |
| function setCustomColor(color) { | |
| mcCurrentColor = color; | |
| document.querySelectorAll('.mc-color-swatch').forEach(s => s.classList.remove('active')); | |
| } | |
| function updateBrushSize(val) { | |
| mcBrushSize = parseInt(val); | |
| document.getElementById('mc-size-val').textContent = val; | |
| } | |
| function mcGetPos(e, canvas) { | |
| const rect = canvas.getBoundingClientRect(); | |
| const scaleX = canvas.width / rect.width; | |
| const scaleY = canvas.height / rect.height; | |
| return { | |
| x: (e.clientX - rect.left) * scaleX, | |
| y: (e.clientY - rect.top) * scaleY | |
| }; | |
| } | |
| function mcPaint(x, y) { | |
| const colC = document.getElementById('mandala-color-canvas'); | |
| const ctx = colC.getContext('2d'); | |
| const cx = x - MC_HALF; | |
| const cy = y - MC_HALF; | |
| const symmetry = mcMandalaType === 'lotus' ? 8 : | |
| mcMandalaType === 'chakra'? 12 : | |
| mcMandalaType === 'sri' ? 6 : | |
| mcMandalaType === 'sun' ? 16 : 8; | |
| ctx.fillStyle = mcCurrentColor; | |
| for (let i=0;i<symmetry;i++) { | |
| const angle = (i / symmetry) * Math.PI * 2; | |
| const rx = cx * Math.cos(angle) - cy * Math.sin(angle) + MC_HALF; | |
| const ry = cx * Math.sin(angle) + cy * Math.cos(angle) + MC_HALF; | |
| ctx.beginPath(); | |
| ctx.arc(rx, ry, mcBrushSize / 2, 0, Math.PI * 2); | |
| ctx.fill(); | |
| } | |
| } | |
| function mcStartDraw(e) { | |
| mcDrawing = true; | |
| const colC = document.getElementById('mandala-color-canvas'); | |
| // Save state | |
| const snapshot = colC.getContext('2d').getImageData(0,0,MC_SIZE,MC_SIZE); | |
| mcHistory.push(snapshot); | |
| if (mcHistory.length > 30) mcHistory.shift(); | |
| const pos = mcGetPos(e, colC); | |
| mcPaint(pos.x, pos.y); | |
| } | |
| function mcDraw(e) { | |
| if (!mcDrawing) return; | |
| const colC = document.getElementById('mandala-color-canvas'); | |
| const pos = mcGetPos(e, colC); | |
| mcPaint(pos.x, pos.y); | |
| } | |
| function mcStopDraw() { mcDrawing = false; } | |
| function mcTouchStart(e) { e.preventDefault(); mcStartDraw(e.touches[0]); } | |
| function mcTouchMove(e) { e.preventDefault(); if(mcDrawing) { const colC=document.getElementById('mandala-color-canvas'); const pos=mcGetPos(e.touches[0],colC); mcPaint(pos.x,pos.y); } } | |
| function undoMandala() { | |
| if (!mcHistory.length) return; | |
| const colC = document.getElementById('mandala-color-canvas'); | |
| const ctx = colC.getContext('2d'); | |
| ctx.putImageData(mcHistory.pop(), 0, 0); | |
| } | |
| function clearMandala() { | |
| const colC = document.getElementById('mandala-color-canvas'); | |
| if (!colC) return; | |
| colC.getContext('2d').clearRect(0, 0, MC_SIZE, MC_SIZE); | |
| mcHistory = []; | |
| } | |
| function downloadMandala() { | |
| const bgC = document.getElementById('mandala-bg-canvas'); | |
| const colC = document.getElementById('mandala-color-canvas'); | |
| const out = document.createElement('canvas'); | |
| out.width = MC_SIZE; out.height = MC_SIZE; | |
| const ctx = out.getContext('2d'); | |
| // Fill dark background | |
| ctx.fillStyle = '#0e0800'; | |
| ctx.fillRect(0, 0, MC_SIZE, MC_SIZE); | |
| ctx.drawImage(bgC, 0, 0); | |
| ctx.drawImage(colC, 0, 0); | |
| const a = document.createElement('a'); | |
| a.download = 'sacred-mandala.png'; | |
| a.href = out.toDataURL('image/png'); | |
| a.click(); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FAMILY TREE DOWNLOAD โ self-contained interactive HTML | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| function downloadFamilyTree() { | |
| const html = buildFamilyTreeHTML(); | |
| const blob = new Blob([html], { type: 'text/html;charset=utf-8' }); | |
| const a = document.createElement('a'); | |
| a.download = 'hindu-deity-family-tree.html'; | |
| a.href = URL.createObjectURL(blob); | |
| a.click(); | |
| URL.revokeObjectURL(a.href); | |
| } | |
| function buildFamilyTreeHTML() { | |
| const deityJSON = JSON.stringify(FT_DEITIES); | |
| const edgeJSON = JSON.stringify(FT_EDGES); | |
| return `<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <title>Hindu Deity Family Tree \u2014 Interactive</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700;900&family=Cinzel:wght@400;600;700&family=Crimson+Pro:ital,wght@0,300;0,400;0,600;1,300;1,400&display=swap" rel="stylesheet"/> | |
| <style> | |
| *,*::before,*::after{box-sizing:border-box;margin:0;padding:0} | |
| body{background:#0A0400;color:#FFF5E0;font-family:'Crimson Pro',serif;overflow:hidden;height:100vh;width:100vw} | |
| #app{position:relative;width:100vw;height:100vh;overflow:hidden} | |
| #svg-container{width:100%;height:100%;overflow:hidden;cursor:grab} | |
| #svg-container:active{cursor:grabbing} | |
| svg#ftsvg{display:block;user-select:none} | |
| .ft-node{cursor:pointer} | |
| #info-panel{position:fixed;top:80px;right:24px;width:290px;background:linear-gradient(145deg,#1A0A00,#130800);border:1px solid rgba(255,107,0,0.5);border-radius:10px;padding:24px 20px 20px;box-shadow:0 0 40px rgba(255,107,0,0.25);display:none;z-index:100;animation:fadeIn .25s ease;font-family:'Crimson Pro',serif} | |
| #info-panel.open{display:block} | |
| @keyframes fadeIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}} | |
| #info-close{position:absolute;top:10px;right:14px;cursor:pointer;color:rgba(255,200,100,.6);font-size:16px} | |
| #info-close:hover{color:#FF9A3C} | |
| #info-icon{font-size:36px;text-align:center;margin-bottom:10px} | |
| #info-name{font-family:'Cinzel Decorative',serif;font-size:17px;font-weight:700;color:#FFD700;text-align:center;margin-bottom:4px;line-height:1.3} | |
| #info-epithet{font-family:'Cinzel',serif;font-size:10px;font-weight:600;letter-spacing:.18em;text-transform:uppercase;color:#FF9A3C;text-align:center;margin-bottom:14px} | |
| #info-body{font-size:14px;line-height:1.7;color:#D4B896;margin-bottom:14px} | |
| #info-tags{display:flex;flex-wrap:wrap;gap:6px} | |
| .itag{background:rgba(255,107,0,.12);border:1px solid rgba(255,107,0,.3);border-radius:3px;padding:3px 8px;font-family:'Cinzel',serif;font-size:9px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:#FF9A3C} | |
| #legend{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);display:flex;gap:18px;flex-wrap:wrap;justify-content:center;background:rgba(10,4,0,.85);border:1px solid rgba(212,160,23,.25);border-radius:8px;padding:10px 20px;backdrop-filter:blur(10px);z-index:100} | |
| .leg-item{display:flex;align-items:center;gap:6px;font-family:'Cinzel',serif;font-size:9px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;color:#8B6B4A} | |
| .leg-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0} | |
| #titlebar{position:fixed;top:0;left:0;right:0;height:60px;z-index:100;background:linear-gradient(180deg,rgba(10,4,0,.97),rgba(10,4,0,.8));border-bottom:1px solid rgba(212,160,23,.2);display:flex;align-items:center;justify-content:center;gap:14px;backdrop-filter:blur(10px)} | |
| #titlebar h1{font-family:'Cinzel Decorative',serif;font-size:clamp(13px,2vw,18px);font-weight:700;color:#FFD700;letter-spacing:.12em} | |
| #titlebar span{color:#FF6B00;font-size:22px} | |
| #hint{position:fixed;top:70px;left:50%;transform:translateX(-50%);font-family:'Cinzel',serif;font-size:9px;letter-spacing:.3em;text-transform:uppercase;color:rgba(139,107,74,.7);pointer-events:none;z-index:99;white-space:nowrap} | |
| #zoom-controls{position:fixed;bottom:80px;right:24px;display:flex;flex-direction:column;gap:6px;z-index:100} | |
| .zoom-btn{width:34px;height:34px;border-radius:6px;border:1px solid rgba(212,160,23,.3);background:rgba(10,4,0,.85);color:#D4A017;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s} | |
| .zoom-btn:hover{background:rgba(212,160,23,.15);border-color:rgba(212,160,23,.6)} | |
| </style> | |
| </head> | |
| <body> | |
| <div id="app"> | |
| <div id="titlebar"><span>🔱</span><h1>Hindu Deity Family Tree</h1><span>🌀</span></div> | |
| <div id="hint">Drag to pan · Scroll to zoom · Click any deity to reveal their story</div> | |
| <div id="svg-container"> | |
| <svg id="ftsvg" viewBox="0 0 2200 1060" xmlns="http://www.w3.org/2000/svg" width="2200" height="1060"></svg> | |
| </div> | |
| <div id="info-panel"> | |
| <div id="info-close" onclick="closePanel()">✕</div> | |
| <div id="info-icon"></div><div id="info-name"></div> | |
| <div id="info-epithet"></div><div id="info-body"></div><div id="info-tags"></div> | |
| </div> | |
| <div id="legend"> | |
| <span class="leg-item"><span class="leg-dot" style="background:#FF6B00"></span>Trimurti / Supreme</span> | |
| <span class="leg-item"><span class="leg-dot" style="background:#D4A017"></span>Consort / Shakti</span> | |
| <span class="leg-item"><span class="leg-dot" style="background:#8B0000"></span>Avatar / Form</span> | |
| <span class="leg-item"><span class="leg-dot" style="background:#4a9eff"></span>Child / Progeny</span> | |
| <span class="leg-item"><span class="leg-dot" style="background:#c040c0"></span>Fierce / Shakti Form</span> | |
| </div> | |
| <div id="zoom-controls"> | |
| <button class="zoom-btn" onclick="zoom(1.2)">+</button> | |
| <button class="zoom-btn" onclick="zoom(0.833)">−</button> | |
| <button class="zoom-btn" onclick="resetView()" style="font-size:13px">⌂</button> | |
| </div> | |
| </div> | |
| <script> | |
| const FT_DEITIES=${deityJSON}; | |
| const FT_EDGES=${edgeJSON}; | |
| const EC={consort:'#D4A017',avatar:'#8B0000',child:'#4a9eff',devotee:'#FF6B00',form:'#c040c0',cosmic:'rgba(255,215,0,0.3)'}; | |
| const NS='http://www.w3.org/2000/svg'; | |
| const svg=document.getElementById('ftsvg'); | |
| function el(tag,a,p){const e=document.createElementNS(NS,tag);for(const[k,v]of Object.entries(a))e.setAttribute(k,v);if(p)p.appendChild(e);return e;} | |
| [{x:0,y:0,w:840,h:1060,f:'rgba(255,107,0,0.025)'},{x:840,y:0,w:540,h:1060,f:'rgba(74,158,255,0.025)'},{x:1380,y:0,w:820,h:1060,f:'rgba(192,64,192,0.025)'}].forEach(d=>el('rect',{x:d.x,y:d.y,width:d.w,height:d.h,fill:d.f},svg)); | |
| [{x:840},{x:1380}].forEach(d=>el('line',{x1:d.x,y1:30,x2:d.x,y2:1050,stroke:'rgba(255,107,0,0.12)','stroke-width':'1','stroke-dasharray':'6 4'},svg)); | |
| [{x:420,y:16,t:'VISHNU LINEAGE',f:'#4a9eff'},{x:1110,y:16,t:'BRAHMA LINEAGE',f:'#FF9A3C'},{x:1790,y:16,t:'SHIVA LINEAGE',f:'#c040c0'}].forEach(l=>{const n=el('text',{x:l.x,y:l.y,'text-anchor':'middle',fill:l.f,'font-size':'11','font-family':'Cinzel, serif','font-weight':'700','letter-spacing':'3',opacity:'0.7'},svg);n.textContent=l.t;}); | |
| FT_EDGES.forEach(edge=>{const from=FT_DEITIES.find(d=>d.id===edge.from),to=FT_DEITIES.find(d=>d.id===edge.to);if(!from||!to)return;const mx=(from.x+to.x)/2;el('path',{d:'M '+from.x+' '+from.y+' Q '+mx+' '+from.y+' '+to.x+' '+to.y,fill:'none',stroke:EC[edge.type]||'#555','stroke-width':edge.type==='cosmic'?'1':'1.5','stroke-dasharray':edge.type==='cosmic'?'4 4':(edge.type==='form'?'5 3':'none'),opacity:'0.45'},svg);}); | |
| FT_DEITIES.forEach(d=>{ | |
| const isT=['brahma','vishnu','shiva'].includes(d.id),r=isT?32:24,rG=isT?44:34,iS=isT?'24':'18',lY=r+16; | |
| const g=el('g',{class:'ft-node',transform:'translate('+d.x+','+d.y+')'},svg);g.style.cursor='pointer'; | |
| const glow=el('circle',{r:rG,fill:d.color,opacity:isT?'0.16':'0.1'},g); | |
| const circle=el('circle',{r:r,fill:d.color+'22',stroke:d.color,'stroke-width':isT?'2.5':'1.5'},g); | |
| const icon=el('text',{'text-anchor':'middle','dominant-baseline':'central','font-size':iS,y:'-1'},g);icon.textContent=d.icon; | |
| const pts=d.name.split(' '); | |
| if(pts.length<=2||d.name.length<=14){const lb=el('text',{'text-anchor':'middle',y:lY,fill:'#F5E6A3','font-size':isT?'11':'9','font-family':'Cinzel, serif','font-weight':'700'},g);lb.textContent=d.name;} | |
| else{const m=Math.ceil(pts.length/2);[pts.slice(0,m).join(' '),pts.slice(m).join(' ')].forEach((t,i)=>{const lb=el('text',{'text-anchor':'middle',y:lY+i*11,fill:'#F5E6A3','font-size':'9','font-family':'Cinzel, serif','font-weight':'700'},g);lb.textContent=t;});} | |
| g.addEventListener('mouseenter',()=>{glow.setAttribute('opacity',isT?'0.3':'0.22');circle.setAttribute('stroke-width',isT?'3.5':'2.5');}); | |
| g.addEventListener('mouseleave',()=>{glow.setAttribute('opacity',isT?'0.16':'0.1');circle.setAttribute('stroke-width',isT?'2.5':'1.5');}); | |
| g.addEventListener('click',e=>{e.stopPropagation();openPanel(d);}); | |
| }); | |
| function openPanel(d){document.getElementById('info-icon').textContent=d.icon;document.getElementById('info-name').textContent=d.name;document.getElementById('info-epithet').textContent=d.epithet;document.getElementById('info-body').textContent=d.desc;document.getElementById('info-tags').innerHTML=d.tags.map(t=>'<span class="itag">'+t+'</span>').join('');document.getElementById('info-panel').classList.add('open');} | |
| function closePanel(){document.getElementById('info-panel').classList.remove('open');} | |
| const container=document.getElementById('svg-container'); | |
| let scale=1,tx=0,ty=0,dragging=false,lastX=0,lastY=0,dragMoved=false; | |
| function applyT(){svg.style.transform='translate('+tx+'px,'+ty+'px) scale('+scale+')';svg.style.transformOrigin='0 0';} | |
| function resetView(){const vw=container.clientWidth,vh=container.clientHeight;scale=Math.min(vw/2200,vh/1060)*0.92;tx=(vw-2200*scale)/2;ty=(vh-1060*scale)/2+30;applyT();} | |
| resetView(); | |
| container.addEventListener('mousedown',e=>{if(e.target.closest('.ft-node'))return;dragging=true;dragMoved=false;lastX=e.clientX;lastY=e.clientY;e.preventDefault();}); | |
| window.addEventListener('mousemove',e=>{if(!dragging)return;const dx=e.clientX-lastX,dy=e.clientY-lastY;if(Math.abs(dx)+Math.abs(dy)>3)dragMoved=true;tx+=dx;ty+=dy;lastX=e.clientX;lastY=e.clientY;applyT();}); | |
| window.addEventListener('mouseup',()=>{dragging=false;}); | |
| container.addEventListener('wheel',e=>{e.preventDefault();const f=e.deltaY<0?1.1:0.9,rect=container.getBoundingClientRect(),mx=e.clientX-rect.left,my=e.clientY-rect.top;tx=mx-(mx-tx)*f;ty=my-(my-ty)*f;scale*=f;applyT();},{passive:false}); | |
| let t0=null; | |
| container.addEventListener('touchstart',e=>{if(e.touches.length===1)t0={x:e.touches[0].clientX,y:e.touches[0].clientY,tx,ty};},{passive:true}); | |
| container.addEventListener('touchmove',e=>{if(e.touches.length===1&&t0){tx=t0.tx+(e.touches[0].clientX-t0.x);ty=t0.ty+(e.touches[0].clientY-t0.y);applyT();}},{passive:true}); | |
| function zoom(f){const cx=container.clientWidth/2,cy=container.clientHeight/2;tx=cx-(cx-tx)*f;ty=cy-(cy-ty)*f;scale*=f;applyT();} | |
| container.addEventListener('click',e=>{if(!e.target.closest('.ft-node')&&!dragMoved)closePanel();}); | |
| <\/script> | |
| </body> | |
| </html>`; | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| INIT | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| document.addEventListener('DOMContentLoaded', () => { | |
| renderBooks(); | |
| buildTicker(); | |
| loadKeys(); | |
| observeReveal(); | |
| document.getElementById('init-time').textContent = getTime(); | |
| renderHinduCalendar(); | |
| renderFestivals(); | |
| buildFamilyTree(); | |
| initMandala(); | |
| // Show key modal on first load if no Groq key saved | |
| var savedKey = localStorage.getItem('sd_groq_key') || ''; | |
| if (!savedKey) { | |
| setTimeout(function() { | |
| var modal = document.getElementById('guru-key-modal'); | |
| if (modal) modal.style.display = 'flex'; | |
| }, 1200); | |
| } | |
| // Refresh panchang every 10 minutes (tithi can change during the day) | |
| setInterval(renderHinduCalendar, 600000); | |
| }); | |
| /* Podcast tab switcher */ | |
| function switchPodcastTab(tab, btn) { | |
| document.querySelectorAll('.ptab').forEach(function(b){ b.classList.remove('active'); }); | |
| btn.classList.add('active'); | |
| const indian = document.getElementById('ptab-indian'); | |
| const western = document.getElementById('ptab-western'); | |
| if (indian) indian.style.display = tab === 'indian' ? 'grid' : 'none'; | |
| if (western) western.style.display = tab === 'western' ? 'grid' : 'none'; | |
| } | |
| </script> | |
| <!-- โโโโโโโโโโโโโโโ RAG INDEX MODAL โโโโโโโโโโโโโโโ --> | |
| <div id="rag-modal-overlay" onclick="closeRAGModal()" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.75);z-index:10000;backdrop-filter:blur(4px);"></div> | |
| <div id="rag-modal" style="display:none;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10001;background:var(--bg-panel);border:1px solid var(--border-gold);border-radius:8px;padding:32px;max-width:520px;width:90%;max-height:80vh;overflow-y:auto;"> | |
| <div style="font-family:var(--font-display);font-size:18px;color:var(--gold-bright);margin-bottom:6px;">๐ Index Sacred Texts for RAG</div> | |
| <div style="font-family:var(--font-heading);font-size:10px;letter-spacing:0.2em;text-transform:uppercase;color:var(--text-muted);margin-bottom:20px;">Select PDFs to index โ the AI will retrieve relevant passages when answering</div> | |
| <div style="font-family:var(--font-heading);font-size:10px;letter-spacing:0.2em;text-transform:uppercase;color:var(--saffron);margin-bottom:10px;">From the Library</div> | |
| <div id="rag-modal-books" style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:20px;"></div> | |
| <div style="font-family:var(--font-heading);font-size:10px;letter-spacing:0.2em;text-transform:uppercase;color:var(--saffron);margin-bottom:10px;">Upload Your Own PDF</div> | |
| <label style="display:flex;align-items:center;gap:10px;background:var(--bg-card);border:1px dashed var(--border-gold);border-radius:4px;padding:12px 16px;cursor:pointer;color:var(--text-secondary);font-family:var(--font-heading);font-size:11px;"> | |
| <span>๐</span> | |
| <span id="rag-file-label">Choose a PDF fileโฆ</span> | |
| <input type="file" id="rag-file-input" accept=".pdf" style="display:none;" onchange="handleRAGFileSelect(this)" /> | |
| </label> | |
| <div style="display:flex;gap:10px;margin-top:20px;justify-content:flex-end;"> | |
| <button onclick="closeRAGModal()" style="background:transparent;border:1px solid var(--border-gold);color:var(--text-muted);padding:8px 20px;border-radius:3px;font-family:var(--font-heading);font-size:11px;cursor:pointer;">Cancel</button> | |
| </div> | |
| </div> | |
| <!-- โโโโโโโโโโโโโโโ DEITY IMAGE POPUP โโโโโโโโโโโโโโโ --> | |
| <div id="deity-popup-overlay" onclick="closeDeityPopup()"></div> | |
| <div id="deity-popup"> | |
| <div id="deity-popup-img-wrap"> | |
| <div id="deity-popup-spinner"></div> | |
| <img id="deity-popup-img" src="" alt="" /> | |
| <span id="deity-popup-icon-fallback"></span> | |
| </div> | |
| <div id="deity-popup-name"></div> | |
| <div id="deity-popup-epithet"></div> | |
| <button id="deity-popup-close" onclick="closeDeityPopup()">โ Close</button> | |
| </div> | |
| <script> | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DEITY POPUP โ uses Wikipedia pageimages API for live images | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| const DEITY_WIKI = { | |
| 'Lord Shiva': 'Shiva', | |
| 'Lord Vishnu': 'Vishnu', | |
| 'Brahma': 'Brahma', | |
| 'Goddess Lakshmi': 'Lakshmi', | |
| 'Goddess Durga': 'Durga', | |
| 'Goddess Saraswati':'Saraswati', | |
| 'Lord Ganesha': 'Ganesha', | |
| 'Lord Krishna': 'Krishna', | |
| 'Lord Rama': 'Rama', | |
| 'Lord Surya': 'Surya_(deity)', | |
| 'Lord Kartikeya': 'Kartikeya', | |
| 'Hanuman Ji': 'Hanuman' | |
| }; | |
| const deityImgCache = {}; | |
| async function fetchDeityImage(wikiTitle) { | |
| if (deityImgCache[wikiTitle]) return deityImgCache[wikiTitle]; | |
| try { | |
| const url = 'https://en.wikipedia.org/w/api.php?action=query&titles=' + encodeURIComponent(wikiTitle) + '&prop=pageimages&format=json&pithumbsize=300&origin=*'; | |
| const res = await fetch(url); | |
| const data = await res.json(); | |
| const pages = data.query.pages; | |
| const page = pages[Object.keys(pages)[0]]; | |
| const src = (page && page.thumbnail) ? page.thumbnail.source : null; | |
| deityImgCache[wikiTitle] = src; | |
| return src; | |
| } catch(e) { return null; } | |
| } | |
| function showDeityPopup() { | |
| document.getElementById('deity-popup-overlay').classList.add('open'); | |
| setTimeout(function(){ document.getElementById('deity-popup').classList.add('open'); }, 10); | |
| } | |
| function closeDeityPopup() { | |
| document.getElementById('deity-popup').classList.remove('open'); | |
| document.getElementById('deity-popup-overlay').classList.remove('open'); | |
| } | |
| async function openDeityPopup(name, epithet, icon) { | |
| document.getElementById('deity-popup-name').textContent = name; | |
| document.getElementById('deity-popup-epithet').textContent = epithet; | |
| var imgEl = document.getElementById('deity-popup-img'); | |
| var iconEl = document.getElementById('deity-popup-icon-fallback'); | |
| var spinnerEl = document.getElementById('deity-popup-spinner'); | |
| imgEl.style.display = 'none'; | |
| iconEl.style.display = 'none'; | |
| spinnerEl.style.display = 'block'; | |
| iconEl.textContent = icon; | |
| showDeityPopup(); | |
| var wikiTitle = DEITY_WIKI[name]; | |
| var src = wikiTitle ? await fetchDeityImage(wikiTitle) : null; | |
| spinnerEl.style.display = 'none'; | |
| if (src) { | |
| imgEl.src = src; | |
| imgEl.alt = name; | |
| imgEl.style.display = 'block'; | |
| imgEl.onerror = function() { | |
| imgEl.style.display = 'none'; | |
| iconEl.style.display = 'block'; | |
| }; | |
| } else { | |
| iconEl.style.display = 'block'; | |
| } | |
| } | |
| document.querySelectorAll('.deity-card').forEach(function(card) { | |
| card.addEventListener('click', function() { | |
| var name = (card.querySelector('.deity-name') || {}).textContent || ''; | |
| var epithet = (card.querySelector('.deity-epithet') || {}).textContent || ''; | |
| var icon = (card.querySelector('.deity-icon') || {}).textContent || '๐'; | |
| openDeityPopup(name.trim(), epithet.trim(), icon.trim()); | |
| }); | |
| }); | |
| document.addEventListener('keydown', function(e) { | |
| if (e.key === 'Escape') closeDeityPopup(); | |
| }); | |
| </script> | |
| <script> | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| DAILY SHLOKA โ GROQ AI (fully dynamic, day-based) | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| // Seed pool โ used as fallback & to derive the daily Groq prompt variety | |
| const SHLOKA_SEEDS = [ | |
| 'Bhagavad Gita 2.47','Bhagavad Gita 9.22','Rigveda 1.89.1','Bhagavad Gita 4.7', | |
| 'Ishavasya Upanishad 1','Bhagavad Gita 6.5','Chandogya Upanishad 6.8.7', | |
| 'Mandukya Upanishad 2','Bhagavad Gita 13.28','Bhagavad Gita 18.66', | |
| 'Taittiriya Upanishad 2.1','Mundaka Upanishad 3.2.9','Bhagavad Gita 2.20', | |
| 'Yoga Sutras 1.2','Maha Upanishad 6.71','Bhagavad Gita 3.27', | |
| 'Bhagavad Gita 10.20','Isha Upanishad 6','Kaivalya Upanishad 1', | |
| 'Kena Upanishad 1.3','Bhagavad Gita 9.26' | |
| ]; | |
| const SHLOKA_TOPICS = [ | |
| 'Karma and selfless action','Devotion and surrender to the Divine', | |
| 'The eternal nature of the soul (Atman)','Non-attachment and inner peace', | |
| 'Knowledge and wisdom (Jnana)','The nature of Brahman, the Supreme Reality', | |
| 'Dharma and righteous living','The unity of all beings', | |
| 'Meditation and the path of Yoga','Compassion and love (Bhakti)', | |
| 'The power of truth (Satya)','Liberation and Moksha', | |
| 'Time, creation, and cosmic cycles','The mind as friend and enemy', | |
| 'Service and humility','Divine grace and surrender', | |
| 'The path beyond sorrow','Equanimity in all circumstances', | |
| 'The sacred in the ordinary','Cosmic consciousness and Oneness', | |
| 'Faith and perseverance on the spiritual path' | |
| ]; | |
| let currentShlokaIndex = 0; | |
| let shlokaExplained = {}; | |
| let currentShlokaData = null; // holds the dynamically fetched shloka | |
| let dailyShlokaCache = {}; // keyed by date string | |
| function getDayKey() { | |
| var now = new Date(); | |
| return now.getFullYear() + '-' + (now.getMonth()+1) + '-' + now.getDate(); | |
| } | |
| function getDayOfYear() { | |
| var now = new Date(); | |
| var start = new Date(now.getFullYear(), 0, 0); | |
| return Math.floor((now - start) / 86400000); | |
| } | |
| async function fetchGroqDailyShloka(dayKey) { | |
| // Check localStorage cache first | |
| var cached = null; | |
| try { cached = JSON.parse(localStorage.getItem('sd_shloka_' + dayKey)); } catch(e) {} | |
| if (cached && cached.sanskrit) return cached; | |
| var dayOfYear = getDayOfYear(); | |
| var topicIdx = dayOfYear % SHLOKA_TOPICS.length; | |
| var topic = SHLOKA_TOPICS[topicIdx]; | |
| var seedRef = SHLOKA_SEEDS[dayOfYear % SHLOKA_SEEDS.length]; | |
| var groqKey = localStorage.getItem('sd_groq_key') || ''; | |
| if (!groqKey) return null; | |
| var prompt = `You are a learned Vedic scholar. Today's spiritual theme is: "${topic}". | |
| Please provide ONE authentic Sanskrit shloka related to this theme from the Vedas, Upanishads, Bhagavad Gita, or Puranas. Do NOT use ${seedRef} โ choose a different one. | |
| Respond ONLY with a valid JSON object in this exact format (no markdown, no extra text): | |
| { | |
| "source": "Scripture Name Chapter.Verse", | |
| "sanskrit": "Sanskrit text in Devanagari", | |
| "transliteration": "Roman transliteration with diacritics", | |
| "translation": "English translation in quotes" | |
| }`; | |
| try { | |
| var res = await fetch('https://api.groq.com/openai/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer ' + groqKey | |
| }, | |
| body: JSON.stringify({ | |
| model: 'llama-3.3-70b-versatile', | |
| max_tokens: 600, | |
| temperature: 0.7, | |
| messages: [{ role: 'user', content: prompt }] | |
| }) | |
| }); | |
| var data = await res.json(); | |
| var text = data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content; | |
| if (!text) return null; | |
| // Strip markdown code fences if present | |
| text = text.replace(/```json|```/g, '').trim(); | |
| var parsed = JSON.parse(text); | |
| if (parsed.sanskrit) { | |
| localStorage.setItem('sd_shloka_' + dayKey, JSON.stringify(parsed)); | |
| return parsed; | |
| } | |
| return null; | |
| } catch(e) { | |
| return null; | |
| } | |
| } | |
| // Fallback static shlokas | |
| const SHLOKAS = [ | |
| { source:'Bhagavad Gita 2.47', sanskrit:'เคเคฐเฅเคฎเคฃเฅเคฏเฅเคตเคพเคงเคฟเคเคพเคฐเคธเฅเคคเฅ เคฎเคพ เคซเคฒเฅเคทเฅ เคเคฆเคพเคเคจ เฅค\nเคฎเคพ เคเคฐเฅเคฎเคซเคฒเคนเฅเคคเฅเคฐเฅเคญเฅเคฐเฅเคฎเคพ เคคเฅ เคธเคเฅเคเฅเคฝเคธเฅเคคเฅเคตเคเคฐเฅเคฎเคฃเคฟ เฅฅ', transliteration:'Karmaแนyevฤdhikฤraste mฤ phaleแนฃu kadฤcana,\nMฤ karmaphalaheturbhลซr mฤ te saแน go\'stvakarmaแนi.', translation:'"You have a right to perform your duties, but you are not entitled to the fruits of your actions. Never consider yourself the cause of the results, nor be attached to inaction."' }, | |
| { source:'Bhagavad Gita 9.22', sanskrit:'เค เคจเคจเฅเคฏเคพเคถเฅเคเคฟเคจเฅเคคเคฏเคจเฅเคคเฅ เคฎเคพเค เคฏเฅ เคเคจเคพเค เคชเคฐเฅเคฏเฅเคชเคพเคธเคคเฅ เฅค\nเคคเฅเคทเคพเค เคจเคฟเคคเฅเคฏเคพเคญเคฟเคฏเฅเคเฅเคคเคพเคจเคพเค เคฏเฅเคเคเฅเคทเฅเคฎเค เคตเคนเคพเคฎเฅเคฏเคนเคฎเฅ เฅฅ', transliteration:'Ananyฤลhcintayanto mฤแน ye janฤแธฅ paryupฤsate,\nTeแนฃhฤแน nityฤbhiyuktฤnฤแน yoga-kแนฃhemaแน vahฤmyaham.', translation:'"For those who worship Me with devotion, meditating on My transcendental form, I carry what they lack and preserve what they have."' }, | |
| { source:'Rigveda 1.89.1', sanskrit:'เค เคจเฅ เคญเคฆเฅเคฐเคพเค เคเฅเคฐเคคเคตเฅ เคฏเคจเฅเคคเฅ เคตเคฟเคถเฅเคตเคคเค เฅค', transliteration:'ฤ no bhadrฤแธฅ kratavo yantu viลvataแธฅ.', translation:'"Let noble thoughts come to us from every direction."' }, | |
| { source:'Bhagavad Gita 4.7', sanskrit:'เคฏเคฆเคพ เคฏเคฆเคพ เคนเคฟ เคงเคฐเฅเคฎเคธเฅเคฏ เคเฅเคฒเคพเคจเคฟเคฐเฅเคญเคตเคคเคฟ เคญเคพเคฐเคค เฅค\nเค เคญเฅเคฏเฅเคคเฅเคฅเคพเคจเคฎเคงเคฐเฅเคฎเคธเฅเคฏ เคคเคฆเคพเคคเฅเคฎเคพเคจเค เคธเฅเคเคพเคฎเฅเคฏเคนเคฎเฅ เฅฅ', transliteration:'Yadฤ yadฤ hi dharmasya glฤnir bhavati Bhฤrata,\nAbhyutthฤnam adharmasya tadฤtmฤnaแน sแนjฤmyaham.', translation:'"Whenever there is a decline in righteousness, O Arjuna, and a rise of unrighteousness โ at that time I manifest Myself on earth."' }, | |
| { source:'Chandogya Upanishad 6.8.7', sanskrit:'เคคเคคเฅเคคเฅเคตเคฎเคธเคฟ เฅค', transliteration:'Tat tvam asi.', translation:'"Thou art That." โ The individual soul (Atman) is identical with the universal consciousness (Brahman).' }, | |
| { source:'Mundaka Upanishad 3.2.9', sanskrit:'เคธเคคเฅเคฏเคฎเฅเคต เคเคฏเคคเฅ เคจเคพเคจเฅเคคเคฎเฅ เฅค', transliteration:'Satyam eva jayate nฤnแนtam.', translation:'"Truth alone triumphs, not falsehood." โ The national motto of India.' }, | |
| { source:'Maha Upanishad 6.71', sanskrit:'เคตเคธเฅเคงเฅเคต เคเฅเคเฅเคฎเฅเคฌเคเคฎเฅ เฅค', transliteration:'Vasudhaiva kuแนญumbakam.', translation:'"The whole world is one family."' }, | |
| ]; | |
| async function initShloka() { | |
| var dayKey = getDayKey(); | |
| var dayOfYear = getDayOfYear(); | |
| // Set fallback shloka immediately (rotated by day) | |
| currentShlokaIndex = dayOfYear % SHLOKAS.length; | |
| currentShlokaData = SHLOKAS[currentShlokaIndex]; | |
| renderShloka(); | |
| // Try to load a Groq-generated shloka | |
| showShlokaLoading(true); | |
| var groqShloka = await fetchGroqDailyShloka(dayKey); | |
| showShlokaLoading(false); | |
| if (groqShloka) { | |
| currentShlokaData = groqShloka; | |
| dailyShlokaCache[dayKey] = groqShloka; | |
| renderShloka(); | |
| } | |
| } | |
| function showShlokaLoading(on) { | |
| var badge = document.getElementById('shloka-day-badge'); | |
| if (badge) badge.textContent = on ? 'โณ Fetching today\'s verseโฆ' : 'Today\'s Verse'; | |
| } | |
| function renderShloka() { | |
| var s = currentShlokaData || SHLOKAS[currentShlokaIndex]; | |
| document.getElementById('shloka-source-tag').textContent = '๐ ' + s.source; | |
| document.getElementById('shloka-sanskrit').textContent = s.sanskrit; | |
| document.getElementById('shloka-transliteration').textContent = s.transliteration; | |
| document.getElementById('shloka-translation').textContent = s.translation; | |
| document.getElementById('shloka-day-badge').textContent = 'Today\'s Verse'; | |
| // Reset AI panel | |
| var key = s.source + '_' + getDayKey(); | |
| document.getElementById('shloka-ai-placeholder').style.display = 'block'; | |
| document.getElementById('shloka-ai-content').style.display = 'none'; | |
| document.getElementById('shloka-ai-loading').style.display = 'none'; | |
| var btn = document.getElementById('shloka-explain-btn'); | |
| btn.disabled = false; | |
| btn.textContent = 'โจ Reveal AI Explanation'; | |
| if (shlokaExplained[key]) { | |
| document.getElementById('shloka-ai-placeholder').style.display = 'none'; | |
| document.getElementById('shloka-ai-content').style.display = 'block'; | |
| document.getElementById('shloka-ai-content').innerHTML = shlokaExplained[key]; | |
| btn.textContent = 'โ Explanation Loaded'; | |
| btn.disabled = true; | |
| } | |
| } | |
| function changeShloka(dir) { | |
| currentShlokaIndex = (currentShlokaIndex + dir + SHLOKAS.length) % SHLOKAS.length; | |
| currentShlokaData = SHLOKAS[currentShlokaIndex]; | |
| renderShloka(); | |
| } | |
| async function explainShloka() { | |
| var btn = document.getElementById('shloka-explain-btn'); | |
| var s = currentShlokaData || SHLOKAS[currentShlokaIndex]; | |
| var key = s.source + '_' + getDayKey(); | |
| btn.disabled = true; | |
| document.getElementById('shloka-ai-placeholder').style.display = 'none'; | |
| document.getElementById('shloka-ai-loading').style.display = 'flex'; | |
| var groqKey = localStorage.getItem('sd_groq_key') || ''; | |
| if (!groqKey) { | |
| document.getElementById('shloka-ai-loading').style.display = 'none'; | |
| document.getElementById('shloka-ai-placeholder').style.display = 'block'; | |
| document.getElementById('shloka-ai-placeholder').innerHTML = '<p style="color:var(--saffron-light)">๐ Please enter your <strong>Groq API key</strong> in the AI Guru section below to unlock AI explanations.</p>'; | |
| btn.disabled = false; | |
| btn.textContent = 'โจ Try Again'; | |
| return; | |
| } | |
| try { | |
| var res = await fetch('https://api.groq.com/openai/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer ' + groqKey | |
| }, | |
| body: JSON.stringify({ | |
| model: 'llama-3.3-70b-versatile', | |
| max_tokens: 900, | |
| messages: [ | |
| { | |
| role: 'system', | |
| content: 'You are a learned Hindu scholar and spiritual teacher. Explain Sanskrit shlokas in a reverent, accessible, and profound way. Structure your response with exactly these three labeled sections using HTML: <h4>Meaning</h4>, <h4>Context & Background</h4>, <h4>Spiritual Significance</h4>. Keep each section to 2-3 sentences. Use poetic yet clear language. Do not include any preamble or closing remarks.' | |
| }, | |
| { | |
| role: 'user', | |
| content: 'Explain this shloka from ' + s.source + ':\n\nSanskrit: ' + s.sanskrit + '\nTransliteration: ' + s.transliteration + '\nTranslation: ' + s.translation | |
| } | |
| ] | |
| }) | |
| }); | |
| var data = await res.json(); | |
| var html = data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content; | |
| if (!html) throw new Error('No content'); | |
| shlokaExplained[key] = html; | |
| document.getElementById('shloka-ai-loading').style.display = 'none'; | |
| document.getElementById('shloka-ai-content').innerHTML = html; | |
| document.getElementById('shloka-ai-content').style.display = 'block'; | |
| btn.textContent = 'โ Explanation Loaded'; | |
| } catch(e) { | |
| document.getElementById('shloka-ai-loading').style.display = 'none'; | |
| document.getElementById('shloka-ai-placeholder').style.display = 'block'; | |
| document.getElementById('shloka-ai-placeholder').innerHTML = '<p style="color:var(--crimson-soft)">โ ๏ธ Could not load Groq explanation. Check your API key or internet connection.</p>'; | |
| btn.disabled = false; | |
| btn.textContent = 'โจ Try Again'; | |
| } | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| PILGRIMAGE MAP โ LEAFLET | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| var SACRED_SITES = [ | |
| /* โโ INDIA โ Char Dham โโ */ | |
| { name:'Badrinath', type:'Char Dham', state:'Uttarakhand, India', icon:'๐๏ธ', lat:30.744, lng:79.493, desc:'One of the holiest shrines dedicated to Lord Vishnu, situated at 3,300 m in the Chamoli district. Part of both the Char Dham and Chota Char Dham pilgrimage circuits. The presiding deity is Badrinarayan.' }, | |
| { name:'Kedarnath', type:'Char Dham + Jyotirlinga', state:'Uttarakhand, India', icon:'๐ฑ', lat:30.735, lng:79.067, desc:'A sacred Jyotirlinga shrine of Lord Shiva in the Garhwal Himalayas at 3,583 m elevation. One of the 12 Jyotirlingas and a Char Dham site. The temple is open only six months a year due to heavy snowfall.' }, | |
| { name:'Gangotri', type:'Char Dham', state:'Uttarakhand, India', icon:'๐', lat:30.993, lng:78.940, desc:'The source of the sacred Ganga river, dedicated to Goddess Ganga. One of the four Char Dhams. The Ganga is said to have descended here at the request of King Bhagirath through his intense tapas.' }, | |
| { name:'Yamunotri', type:'Char Dham', state:'Uttarakhand, India', icon:'๐ง', lat:30.989, lng:78.461, desc:'Source of the Yamuna river and seat of Goddess Yamuna. The westernmost Char Dham shrine, situated at 3,293 m. Yamunotri also houses a hot water kund (spring) called Surya Kund.' }, | |
| /* โโ INDIA โ Jyotirlingas โโ */ | |
| { name:'Somnath', type:'Jyotirlinga', state:'Gujarat, India', icon:'๐', lat:20.888, lng:70.401, desc:'The first and foremost among the 12 Jyotirlingas, on the western coast of Gujarat. Destroyed and rebuilt multiple times, it stands as a symbol of the resilience of Sanatana Dharma. The current temple was rebuilt in 1951.' }, | |
| { name:'Mahakaleshwar', type:'Jyotirlinga', state:'Madhya Pradesh, India', icon:'๐๏ธ', lat:23.182, lng:75.768, desc:'The Mahakal temple in Ujjain on the Shipra river banks. Mahakal is the lord of time โ the only south-facing (Dakshinamukhi) Jyotirlinga. The Bhasma Aarti here, performed with sacred ash, is famous worldwide.' }, | |
| { name:'Omkareshwar', type:'Jyotirlinga', state:'Madhya Pradesh, India', icon:'เฅ', lat:22.246, lng:76.150, desc:'Located on an island in the Narmada river shaped like the sacred Om symbol. One of the 12 Jyotirlingas, uniquely situated where the Narmada and Kaveri rivers meet. The island itself is considered to be in the form of Om.' }, | |
| { name:'Bhimashankar', type:'Jyotirlinga', state:'Maharashtra, India', icon:'๐ฟ', lat:19.071, lng:73.535, desc:'Located in the Sahyadri hills near Pune. The Bhima river originates near this temple. The forest around it is now a wildlife sanctuary housing the endemic Indian Giant Squirrel.' }, | |
| { name:'Vishwanath (Kashi)', type:'Jyotirlinga', state:'Uttar Pradesh, India', icon:'๐๏ธ', lat:25.311, lng:83.010, desc:'The Golden Temple of Kashi (Varanasi), one of the most revered Jyotirlingas. Lord Shiva as Vishwanath is the Lord of the Universe. Varanasi is said to be the oldest continuously inhabited city on earth.' }, | |
| { name:'Trimbakeshwar', type:'Jyotirlinga', state:'Maharashtra, India', icon:'๐ฑ', lat:19.940, lng:73.530, desc:'Near Nashik on the banks of the Godavari. The Godavari originates here. The lingam has three faces representing Brahma, Vishnu, and Shiva. The Kumbh Mela is held here every 12 years.' }, | |
| { name:'Grishneshwar', type:'Jyotirlinga', state:'Maharashtra, India', icon:'โก', lat:20.022, lng:75.179, desc:'Last of the 12 Jyotirlingas, near the Ellora Caves in Aurangabad. The magnificent Maratha temple was renovated by Ahilyabai Holkar of Indore in the 18th century. The Ellora rock-cut temples are UNESCO World Heritage sites.' }, | |
| { name:'Rameshwaram', type:'Jyotirlinga + Char Dham', state:'Tamil Nadu, India', icon:'๐', lat:9.288, lng:79.317, desc:'On Pamban Island at the southernmost tip of India. Lord Rama worshipped Shiva here before crossing to Lanka. The Ramanathaswamy Temple has the longest corridor of any Hindu temple in the world.' }, | |
| { name:'Vaidyanath', type:'Jyotirlinga', state:'Jharkhand, India', icon:'๐', lat:24.492, lng:86.699, desc:'Also called Baidyanath Dham in Deoghar, this Jyotirlinga is one of the 51 Shakti Peethas too, making it doubly sacred. Millions of devotees carry holy Ganga water on foot from Sultanganj โ a 108 km walk โ to offer here.' }, | |
| { name:'Nageshwar', type:'Jyotirlinga', state:'Gujarat, India', icon:'๐', lat:22.339, lng:69.082, desc:'Located near Dwarka on the Gujarat coast, Nageshwar is known as the lord of serpents. It is among the most westerly Jyotirlingas and the 10th in the traditional listing. A massive 25-metre statue of Lord Shiva stands nearby.' }, | |
| { name:'Mallikarjuna', type:'Jyotirlinga', state:'Andhra Pradesh, India', icon:'โฐ๏ธ', lat:16.073, lng:78.868, desc:'Situated atop the Srisailam hill on the banks of the Krishna river, this is one of the most ancient Jyotirlingas. It is also a Shakti Peetha where Sati\'s neck is said to have fallen, making it doubly sacred.' }, | |
| /* โโ INDIA โ Shakti Peethas โโ */ | |
| { name:'Kamakhya', type:'Shakti Peetha', state:'Assam, India', icon:'๐บ', lat:26.166, lng:91.702, desc:'One of the most powerful Shakti Peethas on Nilachal Hill in Guwahati. The presiding deity is Kamakhya, goddess of desire and fertility. The annual Ambubachi Mela draws over 100,000 tantric practitioners and devotees.' }, | |
| { name:'Vaishno Devi', type:'Shakti Peetha', state:'Jammu & Kashmir, India', icon:'โฐ๏ธ', lat:33.030, lng:74.952, desc:'In the Trikuta Mountains of Jammu, this cave shrine is one of the most-visited religious sites in India with 8+ million visitors annually. The goddess is worshipped as three pindies (natural rock formations) โ Mahakali, Mahalakshmi, and Mahasaraswati.' }, | |
| { name:'Kalighat', type:'Shakti Peetha', state:'West Bengal, India', icon:'๐ฅ', lat:22.520, lng:88.343, desc:'One of the 51 Shakti Peethas and one of four Adi Shakti Peethas. Located in Kolkata on the banks of the Hooghly. The city of Kolkata derives its name from this temple โ Kali-Kshetra became Kalikata became Calcutta.' }, | |
| { name:'Hinglaj Mata', type:'Shakti Peetha', state:'Balochistan, Pakistan', icon:'๐', lat:25.515, lng:65.512, desc:'One of the most significant Shakti Peethas, where the crown of Sati is said to have fallen. Located in the Makran coastal range, it draws hundreds of thousands of Hindu pilgrims annually despite being in Pakistan.' }, | |
| { name:'Jwala Devi', type:'Shakti Peetha', state:'Himachal Pradesh, India', icon:'๐ฅ', lat:31.874, lng:76.338, desc:'The flame temple โ natural blue gas flames burn perpetually from cracks in the earth, worshipped as the goddess herself. Akbar, the Mughal emperor, was so impressed he offered a gold canopy here. It is one of the most unique Shakti Peethas.' }, | |
| /* โโ INDIA โ Sacred Temples & Towns โโ */ | |
| { name:'Tirupati Balaji', type:'Sacred Temple', state:'Andhra Pradesh, India', icon:'๐', lat:13.683, lng:79.347, desc:'The Venkateswara Temple atop Tirumala Hill is the world\'s most visited religious site, receiving 50,000+ pilgrims daily. Dedicated to Lord Venkateswara (Vishnu), it is also the wealthiest temple in the world with a treasury worth billions.' }, | |
| { name:'Puri Jagannath', type:'Sacred Temple', state:'Odisha, India', icon:'๐', lat:19.804, lng:85.818, desc:'Home to Lord Jagannath (form of Krishna/Vishnu), one of the Char Dhams. The Rath Yatra (chariot festival) draws 1+ million pilgrims annually. The kitchen of this temple is said to be the world\'s largest, cooking for 100,000 people daily.' }, | |
| { name:'Vrindavan', type:'Sacred Town', state:'Uttar Pradesh, India', icon:'๐บ', lat:27.581, lng:77.700, desc:'The sacred town of Lord Krishna\'s childhood pastimes (leelas). Contains 5,000+ temples including Banke Bihari and Govindadevji. Every lane is said to echo with the flute of Krishna; the town is a living sacred space for Vaishnavas worldwide.' }, | |
| { name:'Mathura', type:'Sacred Town', state:'Uttar Pradesh, India', icon:'๐ต', lat:27.492, lng:77.673, desc:'Birthplace of Lord Krishna, one of the Sapta Puri (seven sacred cities). The Krishna Janmabhoomi temple marks the exact birthplace. Mathura also served as the capital of the Surasena kingdom and was a major center of early Buddhist art.' }, | |
| { name:'Ayodhya', type:'Sacred Town', state:'Uttar Pradesh, India', icon:'๐น', lat:26.795, lng:82.198, desc:'Birthplace of Lord Rama, one of the seven sacred cities. The Ram Mandir, consecrated in January 2024, stands at the site traditionally identified as Rama\'s birthplace. Ayodhya sits on the sacred Sarayu river.' }, | |
| { name:'Haridwar', type:'Sacred Town', state:'Uttarakhand, India', icon:'๐', lat:29.945, lng:78.163, desc:'Gateway to the Gods โ where the Ganga descends from the Himalayas. One of the Sapta Puri. The Kumbh Mela here is the world\'s largest peaceful gathering of humanity. The Har Ki Pauri ghat aarti is witnessed by millions.' }, | |
| { name:'Kanchipuram', type:'Sacred Town', state:'Tamil Nadu, India', icon:'๐๏ธ', lat:12.839, lng:79.701, desc:'City of Thousand Temples and one of the Sapta Puri. Sacred to both Shaivites (Ekambareswara) and Vaishnavas (Varadaraja). Also the seat of the Kanchi Shankaracharya Matha, one of the four Mathas established by Adi Shankaracharya.' }, | |
| { name:'Dwarka', type:'Sacred Town', state:'Gujarat, India', icon:'๐', lat:22.238, lng:68.968, desc:'The sacred city of Lord Krishna, one of the Sapta Puri and a Char Dham. The ancient Dwarka is believed to lie submerged under the Arabian Sea โ marine archaeological surveys have found submerged structures offshore.' }, | |
| { name:'Ujjain (Avantika)', type:'Sacred Town', state:'Madhya Pradesh, India', icon:'โ๏ธ', lat:23.182, lng:75.777, desc:'One of the seven sacred cities (Sapta Puri) and the prime meridian of ancient Indian astronomy. The Kumbh Mela rotates to Ujjain every 12 years. It was the home of the legendary king Vikramaditya and the poet Kalidasa.' }, | |
| { name:'Nashik', type:'Sacred Town', state:'Maharashtra, India', icon:'๐ฟ', lat:19.992, lng:73.791, desc:'One of the four Kumbh Mela sites where Amrita fell from the celestial pot. Situated on the banks of the Godavari. Lord Rama is said to have spent part of his exile here in the Panchavati region.' }, | |
| { name:'Chidambaram', type:'Sacred Temple', state:'Tamil Nadu, India', icon:'๐', lat:11.400, lng:79.693, desc:'The Nataraja temple โ the cosmic dance hall of Lord Shiva as Nataraja, Lord of the Dance. One of the five Panchabhoota Stalas (representing the sky/space element). The Chidambaram Rahasyam (secret) behind the golden-roofed inner sanctum is among Shaivism\'s greatest mysteries.' }, | |
| /* โโ Bangladesh โโ */ | |
| { name:'Chandranath', type:'Shakti Peetha', state:'Chittagong, Bangladesh', icon:'๐', lat:22.618, lng:91.6617, desc:'The Chandranath Shaktipeeth, located atop Chandranath Hill in Sitakunda, Bangladesh, is a revered Hindu pilgrimage site (approx. 1152 ft altitude). It is believed to be the place where Goddess Sati\'s right arm fell, making it one of the 51 sacred Shakti Peethas. The temple is dedicated to Goddess Bhavani and Lord Chandrashekhar.' }, | |
| /* โโ NEPAL โโ */ | |
| { name:'Pashupatinath', type:'Global Sacred Site', state:'Kathmandu, Nepal', icon:'๐ฑ', lat:27.710, lng:85.349, desc:'The most sacred Shaivite temple in the world outside India. Located on the banks of the Bagmati river in Kathmandu. Dedicated to Pashupati (Shiva as lord of animals), it is a UNESCO World Heritage site and draws pilgrims from across South Asia.' }, | |
| { name:'Muktinath', type:'Global Sacred Site', state:'Mustang, Nepal', icon:'๐๏ธ', lat:28.817, lng:83.872, desc:'Sacred to both Hindus and Buddhists at 3,710 m in the Himalayas. One of the 108 Divya Desams for Vaishnavas and a sacred Shaivite site. 108 stone water spouts and eternal flame of natural gas make it one of the most mystical sites on earth.' }, | |
| { name:'Janakpur (Janakpurdham)', type:'Global Sacred Site', state:'Madhesh, Nepal', icon:'๐ธ', lat:26.727, lng:85.926, desc:'The birthplace of Sita Devi, consort of Lord Rama. The Janaki Mandir here is one of the most spectacular Hindu temples in the world. The Ram-Sita Vivah Panchami festival re-enacts their divine wedding each year.' }, | |
| /* โโ INDONESIA โโ */ | |
| { name:'Pura Besakih', type:'Global Sacred Site', state:'Bali, Indonesia', icon:'๐', lat:-8.374, lng:115.451, desc:'The Mother Temple of Bali โ the largest and holiest Hindu temple complex on the island. Built on the slopes of Mount Agung, an active volcano considered sacred by Balinese Hindus. Over 80 temples form this vast complex.' }, | |
| { name:'Prambanan', type:'Global Sacred Site', state:'Java, Indonesia', icon:'๐๏ธ', lat:-7.752, lng:110.491, desc:'The largest Hindu temple compound in Indonesia and the largest Trimurti temple in the world, dedicated to Brahma, Vishnu, and Shiva. A UNESCO World Heritage site built in the 9th century CE. The temples rise 47 metres to the sky.' }, | |
| /* โโ CAMBODIA โโ */ | |
| { name:'Angkor Wat', type:'Global Sacred Site', state:'Siem Reap, Cambodia', icon:'๐', lat:13.412, lng:103.867, desc:'The world\'s largest religious monument, originally built as a Hindu temple for Vishnu in the 12th century CE. Built by King Suryavarman II of the Khmer Empire. Its five towers represent Mount Meru, home of the devas (gods).' }, | |
| /* โโ MYANMAR โโ */ | |
| { name:'Shwedagon Pagoda', type:'Global Sacred Site', state:'Yangon, Myanmar', icon:'โจ', lat:16.798, lng:96.149, desc:'While primarily Buddhist, this golden pagoda houses sacred relics revered across Dharmic traditions. Built over 2,500 years ago, it is one of the oldest and most sacred monuments in the world. Hindu devas (planetary deities) grace the temple complex.' }, | |
| /* โโ SRI LANKA โโ */ | |
| { name:'Munneswaram', type:'Global Sacred Site', state:'North Western, Sri Lanka', icon:'๐', lat:7.944, lng:79.894, desc:'One of the ancient Pancha Ishwarams (five sacred Shiva temples) of Sri Lanka, believed to have been consecrated by Lord Rama himself. A major pilgrimage site for Tamil Hindus. The temple complex has shrines to Shiva, Ganesha, and Kali.' }, | |
| { name:'Nainativu Nagapooshani', type:'Global Sacred Site', state:'Northern, Sri Lanka', icon:'๐', lat:9.685, lng:79.872, desc:'The island temple of Goddess Nagapooshani (Parvati) accessible only by boat. Sacred to Tamil Hindus and associated with the serpent deity. The island itself is believed to be the place where Indra sought the blessings of the goddess.' }, | |
| /* โโ THAILAND โโ */ | |
| { name:'Erawan Shrine', type:'Global Sacred Site', state:'Bangkok, Thailand', icon:'๐', lat:13.744, lng:100.539, desc:'The sacred shrine of Brahma in the heart of Bangkok โ one of the most visited Hindu shrines outside South Asia. Thao Mahaprom (Lord Brahma with four faces) is venerated here. Thai Hindus and tourists offer flowers, incense, and dance performances daily.' }, | |
| { name:'Wat Phra Kaew (Brahma Shrine)', type:'Global Sacred Site', state:'Bangkok, Thailand', icon:'๐', lat:13.752, lng:100.491, desc:'Within the Grand Palace complex, the Emerald Buddha temple complex hosts significant Hindu iconography including shrines to Brahma, Vishnu, and Ganesha. Thailand\'s Brahmanical court traditions are deeply rooted in Vedic heritage.' }, | |
| /* โโ TRINIDAD & TOBAGO โโ */ | |
| { name:'Waterloo Temple in the Sea', type:'Global Sacred Site', state:'Carapichaima, Trinidad', icon:'๐', lat:10.546, lng:-61.393, desc:'The extraordinary Hindu temple built in the sea by Sewdass Sadhu, a devotee who carried every stone by bicycle over 25 years. A symbol of Hindu diaspora perseverance in the Caribbean. Dedicated to Lord Shiva, it attracts pilgrims worldwide.' }, | |
| /* โโ MAURITIUS โโ */ | |
| { name:'Ganga Talao (Grand Bassin)', type:'Global Sacred Site', state:'Savanne, Mauritius', icon:'๐ง', lat:-20.446, lng:57.488, desc:'A sacred crater lake considered the Ganges of Mauritius. During Maha Shivaratri, over 400,000 pilgrims walk barefoot for days to collect water and offer it at Shiva temples. One of the most remarkable Hindu pilgrimage events outside India.' }, | |
| /* โโ SURINAME โโ */ | |
| { name:'Arya Dewaker Mandir', type:'Global Sacred Site', state:'Paramaribo, Suriname', icon:'๐', lat:5.852, lng:-55.203, desc:'One of the oldest and most important Hindu temples in the Americas, serving the significant Indo-Surinamese community descended from indentured labourers. A living center of Vedic tradition transplanted across the ocean over 150 years ago.' }, | |
| /* โโ SOUTH AFRICA โโ */ | |
| { name:'Sri Siva Subramaniya Temple', type:'Global Sacred Site', state:'Pietermaritzburg, South Africa', icon:'๐ธ', lat:-29.621, lng:30.398, desc:'One of the oldest surviving Hindu temples in sub-Saharan Africa, testifying to the 19th-century Indian diaspora\'s spiritual resilience. South Africa\'s Tamil and Hindu communities maintain vibrant temple traditions inherited from Tamil Nadu.' }, | |
| /* โโ UNITED KINGDOM โโ */ | |
| { name:'BAPS Swaminarayan Mandir (Neasden)', type:'Global Sacred Site', state:'London, United Kingdom', icon:'๐๏ธ', lat:51.551, lng:-0.252, desc:'The first traditional Hindu mandir in Europe, crafted from 5,000 tonnes of Bulgarian limestone and Italian Carrara marble. Over a million hand-carved pieces were assembled by volunteers. A spiritual and architectural wonder of the Western world.' }, | |
| /* โโ USA โโ */ | |
| { name:'Malibu Hindu Temple', type:'Global Sacred Site', state:'California, USA', icon:'๐ ', lat:34.063, lng:-118.713, desc:'Perched in the Santa Monica mountains overlooking the Pacific, this South Indian-style temple is one of the most significant Hindu temples in North America. Dedicated to Venkateswara and built in authentic Agamic tradition by artisans from India.' }, | |
| { name:'Ganesha Temple (Flushing)', type:'Global Sacred Site', state:'New York, USA', icon:'๐', lat:40.735, lng:-73.831, desc:'The first traditional Hindu temple in the United States, founded in 1977. Built in authentic South Indian Agamic tradition with a consecrated Ganesha murti. It serves as a spiritual anchor for millions of Hindus across the northeastern USA.' }, | |
| ]; | |
| function initPilgrimageMap() { | |
| if (!window.L) return; | |
| var map = L.map('india-map-container', { | |
| center: [20.0, 80.0], zoom: 2, | |
| minZoom: 2, maxZoom: 10, | |
| }); | |
| L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { | |
| attribution: 'ยฉ OpenStreetMap contributors ยฉ CARTO', | |
| subdomains: 'abcd', maxZoom: 19 | |
| }).addTo(map); | |
| SACRED_SITES.forEach(function(site) { | |
| var color = site.type.includes('Char Dham') ? '#FF6B00' : site.type.includes('Jyotirlinga') ? '#FFD700' : site.type.includes('Shakti') ? '#C0392B' : site.type.includes('Global') ? '#7B68EE' : '#6B8E23'; | |
| var markerHtml = '<div style="width:14px;height:14px;border-radius:50%;background:'+color+';border:2px solid rgba(255,255,255,0.6);box-shadow:0 0 12px '+color+';cursor:pointer;transition:transform 0.2s;"></div>'; | |
| var icon = L.divIcon({ html: markerHtml, className: '', iconSize: [14, 14], iconAnchor: [7, 7] }); | |
| var marker = L.marker([site.lat, site.lng], { icon: icon }).addTo(map); | |
| marker.bindTooltip(site.name, { permanent: false, direction: 'top', className: 'leaflet-tooltip-custom' }); | |
| marker.on('click', function() { openSitePanel(site); }); | |
| }); | |
| } | |
| function openSitePanel(site) { | |
| document.getElementById('site-icon').textContent = site.icon; | |
| document.getElementById('site-name').textContent = site.name; | |
| document.getElementById('site-type').textContent = site.type; | |
| document.getElementById('site-state').textContent = '๐ ' + site.state; | |
| document.getElementById('site-desc').textContent = site.desc; | |
| document.getElementById('site-info-panel').classList.add('open'); | |
| } | |
| function closeSitePanel() { document.getElementById('site-info-panel').classList.remove('open'); } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| HISTORY TIMELINE | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| var TIMELINE_EVENTS = [ | |
| { year:'~7000 BCE', era:'Pre-Vedic', title:'Mehrgahr Settlement', icon:'๐บ', text:'Early agricultural communities appear in the Indus-Saraswati region, ancestral to Vedic civilization.' }, | |
| { year:'~5000 BCE', era:'Vedic Dawn', title:'Rigveda Composed', icon:'๐', text:'The oldest scripture of humanity โ the Rigveda โ is revealed to Rishis in the Saraswati River valley.' }, | |
| { year:'~3100 BCE', era:'Vedic Age', title:'Mahabharata War', icon:'โ๏ธ', text:'The great Kurukshetra war takes place. The Bhagavad Gita is revealed by Lord Krishna to Arjuna on the battlefield.' }, | |
| { year:'~2600 BCE', era:'Indus Valley', title:'Harappan Peak', icon:'๐๏ธ', text:'Mohenjo-daro and Harappa reach their zenith โ planned cities with sanitation more advanced than many modern towns.' }, | |
| { year:'~800 BCE', era:'Upanishadic Age', title:'Principal Upanishads', icon:'๐ง', text:'The Brihadaranyaka, Chandogya, and other major Upanishads explore Brahman, Atman, and the path to liberation.' }, | |
| { year:'~600 BCE', era:'Darshana Age', title:'Six Darshanas Codified', icon:'โญ', text:'The six schools of Hindu philosophy โ Nyaya, Vaisheshika, Sankhya, Yoga, Mimamsa, Vedanta โ take their classical forms.' }, | |
| { year:'~500 BCE', era:'Classical', title:'Valmiki Composes Ramayana', icon:'๐น', text:'The 24,000-verse Ramayana is composed, depicting the life of Lord Rama โ the ideal man and king.' }, | |
| { year:'~400 BCE', era:'Classical', title:'Panini\'s Grammar', icon:'๐ค', text:'Panini creates the Ashtadhyayi โ the world\'s first generative grammar, a masterwork of Sanskrit linguistic science.' }, | |
| { year:'~300 BCE', era:'Maurya Period', title:'Chandragupta Maurya', icon:'โ๏ธ', text:'The Maurya Empire unifies the Indian subcontinent. Kautilya\'s Arthashastra codifies Dharmic statecraft.' }, | |
| { year:'~200 BCE', era:'Classical', title:'Yoga Sutras of Patanjali', icon:'๐ง', text:'Patanjali codifies 196 Yoga Sutras, systematizing the philosophy and eight-limbed practice of classical Yoga.' }, | |
| { year:'~320 CE', era:'Gupta Empire', title:'Golden Age Begins', icon:'๐', text:'The Gupta dynasty ushers in India\'s Golden Age. Kalidasa, Aryabhata, Varahamihira produce timeless works in art, mathematics, and astronomy.' }, | |
| { year:'~788 CE', era:'Medieval', title:'Adi Shankaracharya', icon:'๐๏ธ', text:'Shankara is born. By 32, he writes commentaries on the Prasthanatrayi, establishes four Mathas, and revives Advaita Vedanta across India.' }, | |
| { year:'~1000 CE', era:'Medieval', title:'Ramanuja & Bhakti', icon:'๐ธ', text:'Sri Ramanuja propounds Vishishtadvaita, reviving the devotional tradition of the Alvars and Bhakti movement.' }, | |
| { year:'~1200 CE', era:'Bhakti Age', title:'Bhakti Movement', icon:'โค๏ธ', text:'Saints like Kabir, Mirabai, Tukaram, and Chaitanya inspire millions with devotional poetry, songs, and direct God-experience.' }, | |
| { year:'~1238 CE', era:'Bhakti Age', title:'Madhvacharya Born', icon:'๐', text:'Sri Madhvacharya, founder of Dvaita Vedanta, is born in coastal Karnataka. His philosophical system posits an eternal distinction between the individual soul and Brahman, and establishes the Udupi Krishna Matha tradition.' }, | |
| { year:'~1268 CE', era:'Bhakti Age', title:'Jnaneshwar & Warkari Sampradaya', icon:'๐ฟ', text:'The young saint-philosopher Jnaneshwar composes the Jnaneshwari โ a Marathi commentary on the Bhagavad Gita โ at age 16. He and Namdev found the Warkari pilgrimage tradition to Pandharpur, uniting Maharashtra in devotion to Vitthal (Krishna).' }, | |
| { year:'~1300 CE', era:'Bhakti Age', title:'Namdev\'s Devotional Songs', icon:'๐ต', text:'Sant Namdev, the divine tailor-poet of Maharashtra, composes thousands of abhangas (devotional poems) that cross caste lines. His verses appear in the Guru Granth Sahib, showing how Bhakti transcended religious boundaries across northern India.' }, | |
| { year:'~1336 CE', era:'Vijayanagara Empire', title:'Vijayanagara Empire Founded', icon:'๐', text:'Harihara I and Bukka Raya I found the Vijayanagara Empire in modern Karnataka. For over two centuries it becomes the last great Hindu empire of South India โ a bastion of Sanatan Dharma, Sanskrit learning, and temple art patronage.' }, | |
| { year:'~1356 CE', era:'Bhakti Age', title:'Vedanta Desika\'s Works', icon:'๐๏ธ', text:'The great Vaishnava theologian Vedanta Desika, disciple in the Ramanuja lineage, produces towering works synthesizing Vishishtadvaita philosophy, poetry, and drama in Sanskrit and Manipravalam โ a hallmark of the flowering of South Indian Bhakti.' }, | |
| { year:'~1398 CE', era:'Bhakti Age', title:'Kabir Das Born', icon:'๐ฟ', text:'The mystic weaver-poet Kabir is born in Varanasi. His dohas (couplets) and pads (songs) dissolve the boundaries between Hindu and Sufi mysticism, pointing directly to the formless Brahman. His verses are treasured in both the Guru Granth Sahib and Hindu Bhakti tradition.' }, | |
| { year:'~1440 CE', era:'Bhakti Age', title:'Ramananda & Northern Bhakti', icon:'๐', text:'Sri Ramananda, the pioneering Vaishnava saint of Varanasi, opens the Bhakti tradition to all castes and genders. His twelve disciples include Kabir, Ravidas, and Dhanna โ a revolutionary circle that shaped the face of northern Indian spirituality.' }, | |
| { year:'~1479 CE', era:'Bhakti Age', title:'Vallabhacharya Born', icon:'๐ธ', text:'Sri Vallabhacharya is born. He founds the Pushti Marg (Path of Grace), a joyful devotional philosophy centred on Krishna as Shrinathji. The Srinathji temple at Nathdwara in Rajasthan and the Pushtimarg community continue his tradition to this day.' }, | |
| { year:'~1486 CE', era:'Bhakti Age', title:'Chaitanya Mahaprabhu Born', icon:'โจ', text:'Sri Chaitanya Mahaprabhu is born in Navadvipa, Bengal. He establishes the Gaudiya Vaishnava tradition centered on congregational chanting (Sankirtan) of the names of Krishna. His ecstatic love for Krishna transformed Bengal and Vrindavan, and his tradition later reached the entire world.' }, | |
| { year:'~1498 CE', era:'Bhakti Age', title:'Mirabai Born', icon:'๐', text:'The princess-saint Mirabai is born in Rajasthan. Devoted utterly to Lord Krishna as her husband, she renounces royal life and composes hundreds of bhajans of intense longing and love. Her songs remain among the most beloved devotional poetry in the Hindi and Rajasthani traditions.' }, | |
| { year:'~1530 CE', era:'Bhakti Age', title:'Tulsidas & Ramcharitmanas', icon:'๐น', text:'Sant Tulsidas is born. He will compose the Ramcharitmanas โ the retelling of the Ramayana in Awadhi (vernacular Hindi) โ making the epic accessible to millions. It becomes arguably the most widely read religious text in northern India.' }, | |
| { year:'~1534 CE', era:'Bhakti Age', title:'Surdas Composes Sursagar', icon:'๐ถ', text:'The blind poet-saint Surdas composes the Sursagar โ over 100,000 verses in Braj Bhasha devoted to the childhood and youth of Krishna. His vivid descriptions of Krishna\'s leelas, especially in Brindavan, deeply shaped the Bhakti aesthetic.' }, | |
| { year:'~1556 CE', era:'Vijayanagara Empire', title:'Battle of Talikota', icon:'โ๏ธ', text:'The Battle of Talikota sees the Vijayanagara Empire defeated by a Deccan Sultanate alliance. Though the empire persists in weakened form, this marks a turning point. The resulting cultural dispersal spreads Vijayanagara temple arts and scholarship across South India.' }, | |
| { year:'~1608 CE', era:'Bhakti Age', title:'Tukaram Born', icon:'๐ผ', text:'Sant Tukaram, the greatest poet-saint of Maharashtra, is born near Pune. His abhangas (devotional poems) to Vitthal of Pandharpur are masterpieces of spiritual literature. Tukaram, a common shopkeeper by birth, became one of the most revered saints in the Warkari tradition.' }, | |
| { year:'1630 CE', era:'Maratha Age', title:'Chhatrapati Shivaji', icon:'โ๏ธ', text:'Shivaji Maharaj establishes the Maratha Empire, reviving Hindu Swarajya (self-rule) and protecting Dharmic values.' }, | |
| { year:'1836 CE', era:'Modern', title:'Birth of Ramakrishna', icon:'๐', text:'Mystic saint Ramakrishna Paramahamsa is born. His direct God-realization across all paths inspires a global spiritual renaissance.' }, | |
| { year:'1863 CE', era:'Modern', title:'Swami Vivekananda Born', icon:'โจ', text:'Vivekananda is born. In 1893 he electrifies the Parliament of World\'s Religions in Chicago, presenting Vedanta to the Western world.' }, | |
| { year:'1869 CE', era:'Modern', title:'Mahatma Gandhi Born', icon:'๐๏ธ', text:'Gandhi is born. He will use Ahimsa (non-violence) and Satyagraha (truth-force) โ ancient Hindu ideals โ to free a nation.' }, | |
| { year:'1947 CE', era:'Contemporary', title:'Indian Independence', icon:'๐ฎ๐ณ', text:'India gains independence. The Constitution embodies Dharmic ideals: Satyameva Jayate (Truth Alone Triumphs) is the national motto.' }, | |
| { year:'Present', era:'Contemporary', title:'Global Dharmic Renaissance', icon:'๐', text:'Yoga, Vedanta, and Hindu philosophy spread worldwide. Hundreds of millions practice Sanatan Dharma across every continent.' }, | |
| ]; | |
| function buildTimeline() { | |
| var track = document.getElementById('timeline-track'); | |
| if (!track) return; | |
| TIMELINE_EVENTS.forEach(function(ev, i) { | |
| var above = (i % 2 === 0); | |
| var item = document.createElement('div'); | |
| item.className = 'tl-item ' + (above ? 'above' : 'below'); | |
| var card = document.createElement('div'); card.className = 'tl-card'; | |
| card.innerHTML = '<div class="tl-icon">' + ev.icon + '</div><div class="tl-year">' + ev.year + '</div><div class="tl-era">' + ev.era + '</div><div class="tl-title">' + ev.title + '</div><div class="tl-text">' + ev.text + '</div>'; | |
| var dot = document.createElement('div'); dot.className = 'tl-dot'; | |
| var conn = document.createElement('div'); conn.className = 'tl-connector'; conn.style.height = '44px'; | |
| if (above) { item.appendChild(card); item.appendChild(conn); item.appendChild(dot); } | |
| else { item.appendChild(dot); item.appendChild(conn); item.appendChild(card); } | |
| track.appendChild(item); | |
| }); | |
| // Sync scroll thumb | |
| var outer = document.getElementById('timeline-outer'); | |
| var thumb = document.getElementById('timeline-thumb'); | |
| if (!outer || !thumb) return; | |
| outer.addEventListener('scroll', function() { | |
| var pct = outer.scrollLeft / (outer.scrollWidth - outer.clientWidth) || 0; | |
| var w = Math.max(10, (outer.clientWidth / outer.scrollWidth * 100)); | |
| thumb.style.width = w + '%'; | |
| thumb.style.marginLeft = (pct * (100 - w)) + '%'; | |
| }); | |
| // Drag-scroll | |
| var isDragging = false, startX, scrollLeft; | |
| outer.addEventListener('mousedown', function(e) { isDragging = true; startX = e.pageX - outer.offsetLeft; scrollLeft = outer.scrollLeft; outer.style.cursor = 'grabbing'; }); | |
| window.addEventListener('mouseup', function() { isDragging = false; outer.style.cursor = 'grab'; }); | |
| outer.addEventListener('mousemove', function(e) { | |
| if (!isDragging) return; e.preventDefault(); | |
| outer.scrollLeft = scrollLeft - (e.pageX - outer.offsetLeft - startX); | |
| }); | |
| } | |
| /* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| INIT โ call on DOMContentLoaded | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */ | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initShloka(); | |
| buildTimeline(); | |
| setTimeout(function() { | |
| if (window.L) { initPilgrimageMap(); } | |
| }, 300); | |
| }); | |
| </script> | |
| </body> | |
| </html> |