1<!DOCTYPE html> 2<html lang="en"> 3<head> 4<meta charset="UTF-8"> 5<meta name="viewport" content="width=device-width, initial-scale=1.0"> 6<title>The Golden Path Library</title> 7<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png"> 8<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png"> 9<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> 10<link rel="preconnect" href="https://fonts.googleapis.com"> 11<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Lora:ital,wght@0,400;0,600;1,400&family=Marcellus&family=Great+Vibes&display=swap" rel="stylesheet">
12<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
12 13<style> 14:root { 15 --gold: #C5A55A; 16 --gold-light: #D4BA7A; 17 --gold-pale: #F0E6CC; 18 --plum: #8B2252; 19 --plum-deep: #6B1A42; 20 --berry: #940E4B; 21 --cream: #FBF8F3; 22 --cream-warm: #F7F0E5; 23 --charcoal: #2E2E2E; 24} 25 26* { margin: 0; padding: 0; box-sizing: border-box; } 27 28body { 29 font-family: 'Lora', serif; 30 background: var(--cream); 31 color: var(--charcoal); 32 min-height: 100vh; 33} 34 35/* ============================================ 36 PASSWORD SCREEN 37 ============================================ */ 38#password-screen { 39 min-height: 100vh; 40 display: flex; 41 flex-direction: column; 42 align-items: center; 43 justify-content: center; 44 background: var(--cream); 45 position: relative; 46} 47 48#password-screen::before { 49 content: ''; 50 position: absolute; 51 top: 0; 52 left: 0; 53 right: 0; 54 height: 200px; 55 background: var(--plum); 56} 57 58.password-card { 59 position: relative; 60 background: var(--cream); 61 border: 1px solid var(--gold-light); 62 border-radius: 12px; 63 padding: 60px 50px 50px; 64 max-width: 460px; 65 width: 90%; 66 text-align: center; 67 box-shadow: 0 8px 40px rgba(139, 34, 82, 0.08); 68 margin-top: -40px; 69} 70 71.password-logo { 72 width: 60px; 73 height: 60px; 74 margin-bottom: 20px; 75} 76 77.password-title { 78 font-family: 'Marcellus', serif; 79 font-size: 13px; 80 letter-spacing: 4px; 81 color: var(--gold); 82 text-transform: uppercase; 83 margin-bottom: 8px; 84} 85 86.password-heading { 87 font-family: 'Cormorant Garamond', serif; 88 font-size: 32px; 89 color: var(--plum-deep); 90 margin-bottom: 6px; 91 font-weight: 400; 92} 93 94.password-subtitle { 95 font-family: 'Cormorant Garamond', serif; 96 font-style: italic; 97 font-size: 16px; 98 color: var(--berry); 99 margin-bottom: 30px; 100} 101 102.password-divider { 103 display: flex; 104 align-items: center; 105 gap: 12px; 106 margin-bottom: 30px; 107} 108 109.password-divider::before, 110.password-divider::after { 111 content: ''; 112 flex: 1; 113 height: 1px; 114 background: var(--gold-light); 115} 116 117.password-divider span { 118 color: var(--gold); 119 font-size: 14px; 120} 121 122.password-prompt { 123 font-family: 'Cormorant Garamond', serif; 124 font-style: italic; 125 font-size: 15px; 126 color: var(--plum); 127 margin-bottom: 20px; 128} 129 130.password-input { 131 width: 100%; 132 padding: 14px 20px; 133 border: 1px solid var(--gold-light); 134 border-radius: 8px; 135 background: white; 136 font-family: 'Lora', serif; 137 font-size: 14px; 138 color: var(--charcoal); 139 text-align: center; 140 letter-spacing: 2px; 141 outline: none; 142 transition: border-color 0.3s; 143 margin-bottom: 16px; 144} 145 146.password-input:focus { 147 border-color: var(--gold); 148 box-shadow: 0 0 0 3px rgba(197, 165, 90, 0.15); 149} 150 151.password-input::placeholder { 152 color: #bbb; 153 letter-spacing: 1px; 154} 155 156.password-btn { 157 width: 100%; 158 padding: 14px; 159 background: var(--plum); 160 color: var(--gold); 161 border: none; 162 border-radius: 8px; 163 font-family: 'Marcellus', serif; 164 font-size: 13px; 165 letter-spacing: 3px; 166 text-transform: uppercase; 167 cursor: pointer; 168 transition: all 0.3s; 169} 170 171.password-btn:hover { 172 background: var(--plum-deep); 173 box-shadow: 0 4px 16px rgba(107, 26, 66, 0.3); 174} 175 176.password-error { 177 color: var(--berry); 178 font-size: 13px; 179 font-style: italic; 180 margin-top: 12px; 181 opacity: 0; 182 transition: opacity 0.3s; 183} 184 185.password-error.show { opacity: 1; } 186 187/* ============================================ 188 LIBRARY (hidden until authenticated) 189 ============================================ */ 190#library { 191 display: none; 192 min-height: 100vh; 193} 194 195/* Top banner */ 196.back-to-hub { 197 position: absolute; 198 top: 16px; 199 left: 20px; 200 display: inline-flex; 201 align-items: center; 202 gap: 6px; 203 padding: 8px 18px; 204 background: rgba(107, 26, 66, 0.7); 205 border: 1px solid rgba(197, 165, 90, 0.4); 206 border-radius: 6px; 207 color: var(--gold-light); 208 font-family: 'Marcellus', serif; 209 font-size: 10px; 210 letter-spacing: 2px; 211 text-transform: uppercase; 212 text-decoration: none; 213 transition: all 0.3s; 214 z-index: 10; 215} 216 217.back-to-hub:hover { 218 background: rgba(107, 26, 66, 0.95); 219 border-color: var(--gold); 220 color: var(--gold); 221} 222 223.library-banner { 224 background: var(--plum); 225 padding: 40px 20px 36px; 226 text-align: center; 227 position: relative; 228} 229 230.library-banner::after {
231 content: ''; 232 position: absolute; 233 bottom: -3px; 234 left: 10%; 235 right: 10%; 236 height: 3px; 237 background: var(--gold); 238} 239 240.banner-eyebrow { 241 font-family: 'Marcellus', serif; 242 font-size: 12px; 243 letter-spacing: 5px; 244 color: var(--gold); 245 text-transform: uppercase; 246 margin-bottom: 10px; 247} 248 249.banner-title { 250 font-family: 'Cormorant Garamond', serif; 251 font-size: 38px; 252 color: white; 253 font-weight: 400; 254 margin-bottom: 4px; 255} 256 257.banner-subtitle { 258 font-family: 'Cormorant Garamond', serif; 259 font-style: italic; 260 font-size: 16px; 261 color: var(--gold-light); 262} 263 264/* Welcome section */ 265.welcome-section { 266 max-width: 720px; 267 margin: 0 auto; 268 padding: 50px 30px 30px; 269 text-align: center; 270} 271 272.welcome-text { 273 font-family: 'Cormorant Garamond', serif; 274 font-style: italic; 275 font-size: 18px; 276 line-height: 1.7; 277 color: var(--plum); 278 margin-bottom: 8px; 279} 280 281.welcome-signature { 282 font-family: 'Great Vibes', cursive; 283 font-size: 28px; 284 color: var(--gold); 285} 286 287/* Diamond divider */ 288.diamond-divider { 289 display: flex; 290 align-items: center; 291 justify-content: center; 292 gap: 14px; 293 padding: 10px 0 30px; 294 max-width: 500px; 295 margin: 0 auto; 296} 297 298.diamond-divider::before, 299.diamond-divider::after { 300 content: ''; 301 flex: 1; 302 height: 1px; 303 background: var(--gold-light); 304} 305 306.diamond-divider span { 307 color: var(--gold); 308 font-size: 10px; 309} 310 311/* Teachings grid */ 312.teachings-container { 313 max-width: 800px; 314 margin: 0 auto; 315 padding: 0 30px 60px; 316} 317 318.teaching-card { 319 display: flex; 320 align-items: flex-start; 321 gap: 16px; 322 padding: 22px 24px; 323 margin-bottom: 12px; 324 background: white; 325 border-radius: 10px; 326 border: 1px solid rgba(197, 165, 90, 0.2); 327 transition: all 0.3s; 328 text-decoration: none; 329 color: inherit; 330 cursor: pointer; 331} 332 333.teaching-card:hover { 334 border-color: var(--gold); 335 box-shadow: 0 4px 20px rgba(197, 165, 90, 0.15); 336 transform: translateY(-1px); 337} 338 339.teaching-number { 340 font-family: 'Marcellus', serif; 341 font-size: 12px; 342 color: var(--gold); 343 background: var(--cream-warm); 344 width: 32px; 345 height: 32px; 346 border-radius: 50%; 347 display: flex; 348 align-items: center; 349 justify-content: center; 350 flex-shrink: 0; 351 margin-top: 2px; 352} 353 354.teaching-info { flex: 1; } 355 356.teaching-title { 357 font-family: 'Cormorant Garamond', serif; 358 font-size: 19px; 359 color: var(--plum-deep); 360 margin-bottom: 3px; 361 font-weight: 600; 362} 363 364.teaching-subtitle { 365 font-family: 'Lora', serif; 366 font-size: 13.5px; 367 color: var(--berry); 368 font-style: italic; 369 opacity: 0.95; 370} 371 372.teaching-download { 373 font-family: 'Marcellus', serif; 374 font-size: 11px; 375 letter-spacing: 1.5px; 376 color: var(--berry); 377 text-transform: uppercase; 378 align-self: center; 379 flex-shrink: 0; 380 opacity: 0.85; 381 transition: opacity 0.3s; 382 font-weight: 600; 383} 384 385.teaching-card:hover .teaching-download { 386 opacity: 1; 387} 388 389/* Bookmark button â berry outline for legibility on cream */ 390.bookmark-btn { 391 background: none; 392 border: none; 393 cursor: pointer; 394 font-size: 24px; 395 color: var(--berry); 396 opacity: 0.55; 397 padding: 4px 8px; 398 align-self: center; 399 flex-shrink: 0; 400 transition: all 0.2s; 401 line-height: 1; 402} 403.bookmark-btn:hover { 404 color: var(--berry); 405 opacity: 1; 406 transform: scale(1.15); 407} 408.bookmark-btn.bookmarked { 409 color: var(--berry); 410 opacity: 1; 411} 412 413/* Favorites filter row */ 414.library-toolbar { 415 max-width: 760px; 416 margin: 0 auto 18px; 417 padding: 0 24px; 418 display: flex; 419 align-items: center; 420 justify-content: flex-end; 421 gap: 10px; 422 flex-wrap: wrap; 423} 424.fav-filter-btn { 425 display: inline-flex; 426 align-items: center; 427 gap: 6px; 428 padding: 9px 18px; 429 background: white; 430 border: 1px solid var(--gold); 431 border-radius: 18px; 432 font-family: 'Marcellus', serif; 433 font-size: 11px; 434 letter-spacing: 1.5px; 435 text-transform: uppercase; 436 color: var(--berry); 437 font-weight: 600; 438 cursor: pointer; 439 transition: all 0.3s; 440} 441.fav-filter-btn:hover { 442 border-color: var(--berry); 443 color: var(--berry); 444} 445.fav-filter-btn.active { 446 background: var(--berry); 447 color: white; 448 border-color: var(--berry); 449} 450.teaching-card.hidden-by-filter { display: none !important; } 451 452/* Start Here card */ 453.start-here-card { 454 background: var(--cream-warm); 455 border: 2px solid var(--gold); 456 box-shadow: 0 4px 20px rgba(197, 165, 90, 0.15); 457} 458 459.start-here-card:hover { 460 box-shadow: 0 6px 28px rgba(197, 165, 90, 0.25); 461} 462 463.start-here-icon { 464 color: var(--gold); 465 font-size: 24px; 466 flex-shrink: 0; 467 width: 32px; 468 text-align: center; 469 margin-top: 2px; 470} 471 472/* Bonus section */ 473.bonus-section { 474 max-width: 800px; 475 margin: 0 auto;
476 padding: 0 30px 50px; 477} 478 479.bonus-heading { 480 font-family: 'Marcellus', serif; 481 font-size: 12px; 482 letter-spacing: 4px; 483 color: var(--gold); 484 text-transform: uppercase; 485 text-align: center; 486 margin-bottom: 20px; 487} 488 489.bonus-card { 490 display: flex; 491 align-items: center; 492 gap: 16px; 493 padding: 18px 24px; 494 margin-bottom: 10px; 495 background: var(--cream-warm); 496 border-radius: 10px; 497 border: 1px solid rgba(197, 165, 90, 0.15); 498 transition: all 0.3s; 499 text-decoration: none; 500 color: inherit; 501 cursor: pointer; 502} 503 504.bonus-card:hover { 505 border-color: var(--gold); 506 box-shadow: 0 4px 16px rgba(197, 165, 90, 0.12); 507} 508 509.bonus-icon { 510 color: var(--gold); 511 font-size: 18px; 512 flex-shrink: 0; 513} 514 515.bonus-title { 516 font-family: 'Cormorant Garamond', serif; 517 font-size: 17px; 518 color: var(--plum-deep); 519 font-weight: 600; 520} 521 522.bonus-subtitle { 523 font-size: 12px; 524 color: var(--charcoal); 525 opacity: 0.7; 526} 527 528/* Footer */ 529.library-footer { 530 text-align: center; 531 padding: 30px 20px 40px; 532 border-top: 1px solid var(--gold-light); 533 position: relative; 534} 535 536.library-footer::before { 537 content: '\2666\FE0E'; 538 position: absolute; 539 top: -8px; 540 left: 50%; 541 transform: translateX(-50%); 542 background: var(--cream); 543 padding: 0 12px; 544 color: var(--gold); 545 font-size: 10px; 546} 547 548.footer-logo { 549 width: 36px; 550 height: 36px; 551 margin-bottom: 8px; 552} 553 554.footer-brand { 555 font-family: 'Marcellus', serif; 556 font-size: 9px; 557 letter-spacing: 4px; 558 color: var(--gold); 559 text-transform: uppercase; 560 margin-bottom: 6px; 561} 562 563.footer-copy { 564 font-size: 9px; 565 color: var(--gold); 566 opacity: 0.7; 567 max-width: 500px; 568 margin: 0 auto; 569 line-height: 1.5; 570} 571 572/* Responsive */ 573@media (max-width: 600px) { 574 .password-card { padding: 40px 30px 36px; } 575 .banner-title { font-size: 28px; } 576 .teaching-card { padding: 16px; } 577 .teaching-title { font-size: 17px; } 578 .teaching-download { display: none; } 579} 580 581 582/* iOS Safari auto-zoom fix: inputs must be at least 16px to prevent zoom-on-focus */ 583@media (max-width: 768px) { 584 input[type="text"], 585 input[type="email"], 586 input[type="password"], 587 input[type="search"], 588 input[type="number"], 589 input[type="tel"], 590 input[type="url"], 591 textarea, 592 select { 593 font-size: 16px !important; 594 } 595} 596</style> 597</head> 598<body> 599 600<!-- ============================================ 601 PASSWORD SCREEN 602 ============================================ --> 603<div id="password-screen"> 604 <a class='back-to-hub' href='/' style='position:fixed;top:16px;left:20px;'>← Return to Main Page</a> 605 <div class="password-card"> 606 <img src="feminine-rising-logo.png" alt="Feminine Rising" class="password-logo"> 607 <div class="password-title">The Golden Path</div> 608 <h1 class="password-heading">Library</h1> 609 <div class="password-subtitle">Your Sacred Resource Collection</div> 610 611 <div class="password-divider"><span>♦︎</span></div> 612 613 <p class="password-prompt">Please enter your access code to continue</p> 614 <input type="text" id="password-input" class="password-input" placeholder="Access Code" autofocus> 615 <button onclick="checkPassword()" class="password-btn">Continue</button> 616 <p id="password-error" class="password-error">The access code you entered is not recognized. Please try again or contact [email protected] if you have questions.</p> 617 </div> 618</div> 619 620<!-- ============================================ 621 AUTH SCREEN (Email + Password) â appears AFTER access code 622 ============================================ --> 623<div id="auth-screen" style="display:none;min-height:100vh;align-items:center;justify-content:center;background:var(--cream);position:relative;flex-direction:column;"> 624 <a class='back-to-hub' href='/' style='position:fixed;top:16px;left:20px;'>← Return to Main Page</a> 625 <div class="password-card" style="max-width:460px;"> 626 <img src="feminine-rising-logo.png" alt="Feminine Rising" class="password-logo"> 627 <div class="password-title">The Golden Path</div> 628 <h1 class="password-heading" id="auth-heading">Welcome, Sister</h1> 629 <p class="password-subtitle" id="auth-subtitle">Create your account to enter the library.</p> 630
631 <div class="password-divider"><span>♦︎</span></div> 632 633 <p style="font-family:'Cormorant Garamond',serif;font-style:italic;font-size:14px;color:var(--plum);margin-bottom:18px;line-height:1.5;"> 634 If you're already in the Private Mentorship hub, use the same email & password you use there. 635 </p> 636 637 <input type="email" id="auth-email" class="password-input" placeholder="[email protected]" autocomplete="email" autocapitalize="none" autocorrect="off" spellcheck="false" style="text-align:left;letter-spacing:0;"> 638 <div style="position:relative;"> 639 <input type="password" id="auth-password" class="password-input" placeholder="Create a password" autocomplete="current-password" autocapitalize="none" autocorrect="off" spellcheck="false" style="text-align:left;letter-spacing:0;padding-right:48px;"> 640 <button type="button" onclick="toggleAuthPasswordVisibility()" aria-label="Show or hide password" style="position:absolute;right:12px;top:50%;transform:translateY(-65%);background:none;border:none;cursor:pointer;font-size:18px;color:var(--gold);padding:4px 8px;line-height:1;"> 641 <span id="auth-eye-icon">👁</span> 642 </button> 643 </div> 644 <button id="auth-send-btn" onclick="authSubmit()" class="password-btn">Create My Account</button> 645 <p id="auth-message" class="password-error" style="opacity:0;transition:opacity 0.3s;"></p> 646 <div style="margin-top:18px;"> 647 <button id="auth-toggle" onclick="toggleAuthMode()" style="background:none;border:none;color:var(--plum);font-family:'Cormorant Garamond',serif;font-size:14px;font-style:italic;cursor:pointer;letter-spacing:0.3px;opacity:0.85;transition:opacity 0.2s;" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.85'">Already have an account? Sign in</button> 648 </div> 649 <div id="forgot-row" style="margin-top:10px;display:none;"> 650 <button onclick="sendPasswordReset()" style="background:none;border:none;color:var(--gold);font-family:'Marcellus',serif;font-size:10px;letter-spacing:2px;cursor:pointer;text-transform:uppercase;opacity:0.8;transition:opacity 0.2s;" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.8'">Forgot password?</button> 651 </div> 652 </div> 653</div> 654 655<!-- ============================================ 656 RESET PASSWORD SCREEN (after clicking email link) 657 ============================================ --> 658<div id="reset-screen" style="display:none;min-height:100vh;align-items:center;justify-content:center;background:var(--cream);position:relative;flex-direction:column;"> 659 <div class="password-card" style="max-width:460px;"> 660 <img src="feminine-rising-logo.png" alt="Feminine Rising" class="password-logo"> 661 <h1 class="password-heading">Set a New Password</h1> 662 <p class="password-subtitle">Choose a new password for your account.</p> 663 <div class="password-divider"><span>♦︎</span></div> 664 <input type="password" id="reset-password" class="password-input" placeholder="New password (8+ characters)" autocomplete="new-password" autocapitalize="none" autocorrect="off" spellcheck="false" style="text-align:left;letter-spacing:0;"> 665 <input type="password" id="reset-password-confirm" class="password-input" placeholder="Confirm new password" autocomplete="new-password" autocapitalize="none" autocorrect="off" spellcheck="false" style="text-align:left;letter-spacing:0;"> 666 <button id="reset-submit-btn" onclick="updatePasswordSubmit()" class="password-btn">Update Password</button> 667 <p id="reset-message" class="password-error" style="opacity:0;transition:opacity 0.3s;"></p> 668 </div> 669</div> 670 671<!-- ============================================ 672 LIBRARY 673 ============================================ --> 674<div id="library"> 675 676 <!-- Banner --> 677 <header class="library-banner"> 678 <a class='back-to-hub' href='/'>← Return to Main Page</a> 679 <div class="banner-eyebrow">The Golden Path</div> 680 <h1 class="banner-title">Library</h1> 681 <p class="banner-subtitle">Your Sacred Resource Collection ~ 2026</p> 682 </header> 683 684 <!-- Welcome --> 685 <section class="welcome-section"> 686 <p class="welcome-text"> 687 Welcome to your Golden Path Library. These teachings are yours to return to, 688 revisit, and carry forward as living resources on your journey. 689 </p> 690 </section> 691 692 <div class="diamond-divider"><span>♦︎</span></div> 693 694 <!-- Library Toolbar --> 695 <div class="library-toolbar"> 696 <button class="fav-filter-btn" id="fav-filter-btn" onclick="toggleFavoritesFilter()">
697 <span id="fav-filter-icon">♡</span> <span id="fav-filter-label">Show Favorites</span> 698 </button> 699 </div> 700 701 <!-- Teachings --> 702 <section class="teachings-container"> 703 704 <!-- Start Here --> 705 <a class='teaching-card start-here-card' href='/teaching-00'> 706 <div class="start-here-icon">✦</div> 707 <div class="teaching-info"> 708 <div class="teaching-title" style="color: var(--berry);">Start Here</div> 709 <div class="teaching-subtitle">Leveraging This Resource</div> 710 </div> 711 <div class="teaching-download">View →</div> 712 </a> 713 714 <div style="height: 8px;"></div> 715 716 <a class='teaching-card' href='/teaching-01'> 717 <div class="teaching-number">1</div> 718 <div class="teaching-info"> 719 <div class="teaching-title">Insights into Survival, Trauma & Regulation</div> 720 <div class="teaching-subtitle">Understanding the Language of Your Body</div> 721 </div> 722 <div class="teaching-download">View →</div> 723 </a> 724 725 <a class='teaching-card' href='/teaching-02'> 726 <div class="teaching-number">2</div> 727 <div class="teaching-info"> 728 <div class="teaching-title">Stories & Beliefs</div> 729 <div class="teaching-subtitle">How Meaning Making Impacts Us</div> 730 </div> 731 <div class="teaching-download">View →</div> 732 </a> 733 734 <a class='teaching-card' href='/teaching-03'> 735 <div class="teaching-number">3</div> 736 <div class="teaching-info"> 737 <div class="teaching-title">Consciousness Is Primary</div> 738 <div class="teaching-subtitle">The Foundation of Sovereign Choice</div> 739 </div> 740 <div class="teaching-download">View →</div> 741 </a> 742 743 <a class='teaching-card' href='/teaching-04'> 744 <div class="teaching-number">4</div> 745 <div class="teaching-info"> 746 <div class="teaching-title">80/20</div> 747 <div class="teaching-subtitle">Returning to Center</div> 748 </div> 749 <div class="teaching-download">View →</div> 750 </a> 751 752 <a class='teaching-card' href='/teaching-05'> 753 <div class="teaching-number">5</div> 754 <div class="teaching-info"> 755 <div class="teaching-title">Accepting What Is</div> 756 <div class="teaching-subtitle">From Resistance to Clarity</div> 757 </div> 758 <div class="teaching-download">View →</div> 759 </a> 760 761 <a class='teaching-card' href='/teaching-06'> 762 <div class="teaching-number">6</div> 763 <div class="teaching-info"> 764 <div class="teaching-title">Automatic, Conscious, Simple</div> 765 <div class="teaching-subtitle">Three Ways of Meeting the Moment</div> 766 </div> 767 <div class="teaching-download">View →</div> 768 </a> 769 770 <a class='teaching-card' href='/teaching-07'> 771 <div class="teaching-number">7</div> 772 <div class="teaching-info"> 773 <div class="teaching-title">Opening Our Sight</div> 774 <div class="teaching-subtitle">Expanding the Aperture of Experience</div> 775 </div> 776 <div class="teaching-download">View →</div> 777 </a> 778 779 <a class='teaching-card' href='/teaching-09'> 780 <div class="teaching-number">8</div> 781 <div class="teaching-info"> 782 <div class="teaching-title">Rewriting the Code</div> 783 <div class="teaching-subtitle">Choosing New Patterns on Purpose</div> 784 </div> 785 <div class="teaching-download">View →</div> 786 </a> 787 788 <a class='teaching-card' href='/teaching-10'> 789 <div class="teaching-number">9</div> 790 <div class="teaching-info"> 791 <div class="teaching-title">The S Practice</div> 792 <div class="teaching-subtitle">A Feminine Ecosystem for Being</div> 793 </div> 794 <div class="teaching-download">View →</div> 795 </a> 796 797 </section> 798 799 <!-- Bonus Resources --> 800 <div class="diamond-divider"><span>♦︎</span></div> 801 802 <section class="bonus-section"> 803 <div class="bonus-heading">Bonus Resources</div> 804 805 <a class='bonus-card' href='/completion-resources'> 806 <div class="bonus-icon">✦</div> 807 <div style="flex:1;"> 808 <div class="bonus-title">Completion & Ongoing Resources</div> 809 <div class="bonus-subtitle">What's Next & How to Stay Connected</div> 810 </div> 811 <div class="teaching-download">View →</div> 812 </a> 813 814 </section> 815 816 <!-- Footer --> 817 <footer class="library-footer"> 818 <img src="feminine-rising-logo.png" alt="Feminine Rising" class="footer-logo"><br> 819 <div class="footer-brand">The Golden Path</div> 820 <p class="footer-copy">© 2026 The Golden Path, brought to you by Feminine Rising. All rights reserved.</p> 821 </footer> 822 823</div> 824
825<script> 826// ============================================================ 827// SUPABASE CLIENT (shared with the Mentorship Hub) 828// ============================================================ 829const SUPABASE_URL = 'https://ttvjonqfljiinmkjbqvj.supabase.co'; 830const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InR0dmpvbnFmbGppaW5ta2picXZqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzYyNTkyNDQsImV4cCI6MjA5MTgzNTI0NH0.mQ9NUsw4cFMf2uRxPWoLl2ig04O0lHbqjx7N3hfG3iU'; 831const sbClient = window.supabase ? window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY) : null; 832let currentUser = null; 833 834// ============================================================ 835// STEP 1 â ACCESS CODE (server-validated) 836// ============================================================ 837async function checkPassword() { 838 const inputEl = document.getElementById('password-input'); 839 const error = document.getElementById('password-error'); 840 const btn = document.querySelector('#password-screen .password-btn'); 841 const code = inputEl.value; 842 843 if (btn) { btn.disabled = true; btn.dataset.original = btn.textContent; btn.textContent = 'One moment...'; } 844 845 try { 846 const res = await fetch('/.netlify/functions/check-library-code', { 847 method: 'POST', 848 headers: { 'Content-Type': 'application/json' }, 849 body: JSON.stringify({ code }) 850 }); 851 const data = await res.json(); 852 853 if (data.ok && data.token) { 854 sessionStorage.setItem('gp_authenticated', data.token); 855 showAuthScreen(); 856 } else { 857 error.classList.add('show'); 858 inputEl.value = ''; 859 inputEl.focus(); 860 } 861 } catch (e) { 862 error.classList.add('show'); 863 } finally { 864 if (btn) { btn.disabled = false; btn.textContent = btn.dataset.original || 'Continue'; } 865 } 866} 867 868document.getElementById('password-input').addEventListener('keypress', function(e) { 869 if (e.key === 'Enter') checkPassword(); 870}); 871 872// ============================================================ 873// STEP 2 â EMAIL + PASSWORD AUTH (Supabase) 874// ============================================================ 875let authMode = 'signup'; // 'signup' or 'signin' 876 877function showAuthScreen() { 878 document.getElementById('password-screen').style.display = 'none'; 879 document.getElementById('reset-screen').style.display = 'none'; 880 document.getElementById('library').style.display = 'none'; 881 const auth = document.getElementById('auth-screen'); 882 auth.style.display = 'flex'; 883} 884 885function toggleAuthMode() { 886 const btn = document.getElementById('auth-send-btn'); 887 const toggle = document.getElementById('auth-toggle'); 888 const heading = document.getElementById('auth-heading'); 889 const subtitle = document.getElementById('auth-subtitle'); 890 const passInput = document.getElementById('auth-password'); 891 892 if (authMode === 'signup') { 893 authMode = 'signin'; 894 btn.textContent = 'Sign In'; 895 toggle.textContent = 'New here? Create an account'; 896 heading.textContent = 'Welcome Back'; 897 subtitle.textContent = 'Sign in to enter the library.'; 898 passInput.placeholder = 'Your password'; 899 document.getElementById('forgot-row').style.display = 'block'; 900 } else { 901 authMode = 'signup'; 902 btn.textContent = 'Create My Account'; 903 toggle.textContent = 'Already have an account? Sign in'; 904 heading.textContent = 'Welcome, Sister'; 905 subtitle.textContent = 'Create your account to enter the library.'; 906 passInput.placeholder = 'Create a password'; 907 document.getElementById('forgot-row').style.display = 'none'; 908 } 909 910 document.getElementById('auth-message').classList.remove('show'); 911} 912 913function toggleAuthPasswordVisibility() { 914 const input = document.getElementById('auth-password'); 915 const icon = document.getElementById('auth-eye-icon'); 916 if (!input) return; 917 const shown = input.type === 'text'; 918 input.type = shown ? 'password' : 'text'; 919 if (icon) icon.innerHTML = shown ? '👁' : '🙈'; 920} 921 922async function authSubmit() { 923 if (!sbClient) { 924 showAuthMessage('Connection error. Please refresh and try again.'); 925 return; 926 } 927 const email = document.getElementById('auth-email').value.trim(); 928 const password = document.getElementById('auth-password').value; 929 const btn = document.getElementById('auth-send-btn'); 930 931 if (!email) { showAuthMessage('Please enter your email.'); return; } 932 if (!password || password.length < 6) { showAuthMessage('Password must be at least 6 characters.'); return; } 933 934 btn.disabled = true; 935 const originalText = btn.textContent; 936 btn.textContent = 'One moment...'; 937 938 let result; 939 if (authMode === 'signup') { 940 result = await sbClient.auth.signUp({ email, password }); 941 if (result.error && (result.error.message.includes('already registered') || result.error.message.includes('already been registered'))) { 942 result = await sbClient.auth.signInWithPassword({ email, password }); 943 } 944 } else { 945 result = await sbClient.auth.signInWithPassword({ email, password }); 946 } 947 948 if (result.error) { 949 showAuthMessage(result.error.message); 950 btn.disabled = false;
951 btn.textContent = originalText; 952 } else if (result.data?.user) { 953 currentUser = result.data.user; 954 // Log this library sign-in for the teacher's analytics (once per browser session) 955 sessionStorage.setItem('lib_session_logged', '1'); 956 sbClient.from('library_sessions').insert({ 957 user_id: currentUser.id, 958 user_email: currentUser.email 959 }).then(() => {}); 960 showLibrary(); 961 } 962} 963 964function showAuthMessage(msg) { 965 const el = document.getElementById('auth-message'); 966 el.textContent = msg; 967 el.classList.add('show'); 968 el.style.opacity = '1'; 969} 970 971async function sendPasswordReset() { 972 if (!sbClient) return; 973 const email = document.getElementById('auth-email').value.trim(); 974 if (!email) { 975 showAuthMessage('Please enter your email above first, then tap Forgot password.'); 976 return; 977 } 978 showAuthMessage('Sending reset link...'); 979 try { 980 const { error } = await sbClient.auth.resetPasswordForEmail(email, { 981 redirectTo: window.location.origin + '/library.html' 982 }); 983 if (error) { 984 showAuthMessage(error.message); 985 } else { 986 const el = document.getElementById('auth-message'); 987 el.textContent = 'Check your email for a password reset link.'; 988 el.style.opacity = '1'; 989 el.style.color = 'var(--plum)'; 990 } 991 } catch (e) { 992 showAuthMessage('Could not send reset link. Please try again.'); 993 } 994} 995 996document.getElementById('auth-email').addEventListener('keypress', function(e) { 997 if (e.key === 'Enter') document.getElementById('auth-password').focus(); 998}); 999document.getElementById('auth-password').addEventListener('keypress', function(e) { 1000 if (e.key === 'Enter') authSubmit(); 1001}); 1002 1003// ============================================================ 1004// PASSWORD RESET FLOW (after clicking email link) 1005// ============================================================ 1006function showResetScreen() { 1007 document.getElementById('password-screen').style.display = 'none'; 1008 document.getElementById('auth-screen').style.display = 'none'; 1009 document.getElementById('library').style.display = 'none'; 1010 document.getElementById('reset-screen').style.display = 'flex'; 1011} 1012 1013async function updatePasswordSubmit() { 1014 const pw = document.getElementById('reset-password').value; 1015 const pw2 = document.getElementById('reset-password-confirm').value; 1016 const msg = document.getElementById('reset-message'); 1017 const btn = document.getElementById('reset-submit-btn'); 1018 1019 if (!pw || pw.length < 8) { msg.textContent = 'Password must be at least 8 characters.'; msg.style.opacity = '1'; return; } 1020 if (pw !== pw2) { msg.textContent = 'Passwords do not match. Please re-type.'; msg.style.opacity = '1'; return; } 1021 1022 btn.disabled = true; 1023 btn.textContent = 'Updating...'; 1024 1025 try { 1026 const { error } = await sbClient.auth.updateUser({ password: pw }); 1027 if (error) { 1028 msg.textContent = error.message; 1029 msg.style.opacity = '1'; 1030 btn.disabled = false; 1031 btn.textContent = 'Update Password'; 1032 return; 1033 } 1034 msg.textContent = 'Password updated! Taking you to the library...'; 1035 msg.style.opacity = '1'; 1036 msg.style.color = 'var(--plum)'; 1037 const { data: { user } } = await sbClient.auth.getUser(); 1038 setTimeout(() => { 1039 if (window.history.replaceState) window.history.replaceState(null, '', window.location.pathname); 1040 if (user) { currentUser = user; showLibrary(); } 1041 }, 900); 1042 } catch (e) { 1043 msg.textContent = 'Could not update password. Please try again.'; 1044 msg.style.opacity = '1'; 1045 btn.disabled = false; 1046 btn.textContent = 'Update Password'; 1047 } 1048} 1049 1050// ============================================================ 1051// SHOW LIBRARY (after both gates pass) 1052// ============================================================ 1053async function showLibrary() { 1054 document.getElementById('password-screen').style.display = 'none'; 1055 document.getElementById('auth-screen').style.display = 'none'; 1056 document.getElementById('reset-screen').style.display = 'none'; 1057 document.getElementById('library').style.display = 'block'; 1058 // Load bookmarks from Supabase, then inject UI 1059 await loadBookmarks(); 1060 injectBookmarkButtons(); 1061} 1062 1063// ============================================================ 1064// SESSION RESTORE / RECOVERY DETECTION 1065// ============================================================
1066(async function init() { 1067 // Detect URL-based recovery flow first 1068 const urlHash = window.location.hash || ''; 1069 const urlSearch = window.location.search || ''; 1070 const isRecoveryUrl = 1071 urlHash.indexOf('type=recovery') !== -1 || 1072 urlSearch.indexOf('type=recovery') !== -1 || 1073 (urlSearch.indexOf('code=') !== -1 && urlSearch.indexOf('error') === -1); 1074 1075 if (isRecoveryUrl && sbClient) { 1076 sessionStorage.setItem('gp_authenticated', sessionStorage.getItem('gp_authenticated') || 'true'); 1077 sbClient.auth.onAuthStateChange((event, session) => { 1078 if (event === 'PASSWORD_RECOVERY' || (event === 'SIGNED_IN' && session)) { 1079 showResetScreen(); 1080 } 1081 }); 1082 if (urlSearch.indexOf('code=') !== -1) { 1083 try { 1084 const params = new URLSearchParams(urlSearch); 1085 const code = params.get('code'); 1086 if (code) await sbClient.auth.exchangeCodeForSession(code); 1087 } catch (e) {} 1088 } 1089 setTimeout(() => { 1090 if (document.getElementById('reset-screen').style.display !== 'flex') showResetScreen(); 1091 }, 600); 1092 return; 1093 } 1094 1095 // Try to restore an existing Supabase session 1096 if (sbClient) { 1097 const { data: { session } } = await sbClient.auth.getSession(); 1098 if (session) { 1099 currentUser = session.user; 1100 // Already signed in â but still need access code for this session 1101 const existingToken = sessionStorage.getItem('gp_authenticated'); 1102 if (existingToken && (existingToken === 'true' || existingToken.startsWith('gp-'))) { 1103 // Also log this session (returning visitor) â but only once per browser-session 1104 if (!sessionStorage.getItem('lib_session_logged')) { 1105 sessionStorage.setItem('lib_session_logged', '1'); 1106 sbClient.from('library_sessions').insert({ 1107 user_id: currentUser.id, 1108 user_email: currentUser.email 1109 }).then(() => {}); 1110 } 1111 showLibrary(); 1112 return; 1113 } 1114 } 1115 } 1116 1117 // Check if access code was already entered this session 1118 const existingToken = sessionStorage.getItem('gp_authenticated'); 1119 if (existingToken && (existingToken === 'true' || existingToken.startsWith('gp-'))) { 1120 // Code is good â go to auth (or library if already signed in) 1121 if (currentUser) { 1122 showLibrary(); 1123 } else { 1124 showAuthScreen(); 1125 } 1126 } 1127 // Otherwise the password-screen is already visible by default 1128})(); 1129 1130// ============================================================ 1131// BOOKMARKS / FAVORITES (per-user, stored in Supabase) 1132// ============================================================ 1133let favorites = {}; // map: { teaching_key: true } 1134let favoritesOnly = false; 1135 1136async function loadBookmarks() { 1137 favorites = {}; 1138 if (!sbClient || !currentUser) return; 1139 const { data } = await sbClient 1140 .from('library_bookmarks') 1141 .select('teaching_key') 1142 .eq('user_id', currentUser.id); 1143 if (data) data.forEach(b => { favorites[b.teaching_key] = true; }); 1144} 1145 1146async function toggleBookmark(event, key) { 1147 event.preventDefault(); 1148 event.stopPropagation(); 1149 if (!sbClient || !currentUser) return; 1150 1151 const isCurrentlyFav = !!favorites[key]; 1152 const btn = event.currentTarget; 1153 1154 // Optimistic UI update 1155 if (isCurrentlyFav) { 1156 delete favorites[key]; 1157 if (btn) { 1158 btn.classList.remove('bookmarked'); 1159 btn.innerHTML = '♡'; 1160 btn.setAttribute('aria-label', 'Add to favorites'); 1161 btn.setAttribute('title', 'Add to favorites'); 1162 } 1163 await sbClient.from('library_bookmarks').delete() 1164 .eq('user_id', currentUser.id) 1165 .eq('teaching_key', key); 1166 } else { 1167 favorites[key] = true; 1168 if (btn) { 1169 btn.classList.add('bookmarked'); 1170 btn.innerHTML = '♥'; 1171 btn.setAttribute('aria-label', 'Remove from favorites'); 1172 btn.setAttribute('title', 'Remove from favorites'); 1173 } 1174 await sbClient.from('library_bookmarks').insert({ 1175 user_id: currentUser.id, 1176 teaching_key: key 1177 }); 1178 } 1179 1180 if (favoritesOnly) applyFavoritesFilter(); 1181} 1182 1183function injectBookmarkButtons() { 1184 document.querySelectorAll('.teaching-card').forEach(card => { 1185 if (card.querySelector('.bookmark-btn')) return; 1186 const href = card.getAttribute('href') || '';
1187 const key = href.replace(/[^a-z0-9]/gi, '_'); 1188 if (!key) return; 1189 const isFav = !!favorites[key]; 1190 1191 const btn = document.createElement('button'); 1192 btn.className = 'bookmark-btn' + (isFav ? ' bookmarked' : ''); 1193 btn.innerHTML = isFav ? '♥' : '♡'; 1194 btn.setAttribute('aria-label', isFav ? 'Remove from favorites' : 'Add to favorites'); 1195 btn.setAttribute('title', isFav ? 'Remove from favorites' : 'Add to favorites'); 1196 btn.onclick = function(e) { toggleBookmark(e, key); }; 1197 card.dataset.favKey = key; 1198 const dl = card.querySelector('.teaching-download'); 1199 if (dl) { 1200 card.insertBefore(btn, dl); 1201 } else { 1202 card.appendChild(btn); 1203 } 1204 }); 1205} 1206 1207function toggleFavoritesFilter() { 1208 favoritesOnly = !favoritesOnly; 1209 const btn = document.getElementById('fav-filter-btn'); 1210 const icon = document.getElementById('fav-filter-icon'); 1211 const label = document.getElementById('fav-filter-label'); 1212 if (favoritesOnly) { 1213 btn.classList.add('active'); 1214 icon.innerHTML = '♥'; 1215 label.textContent = 'Showing Favorites'; 1216 } else { 1217 btn.classList.remove('active'); 1218 icon.innerHTML = '♡'; 1219 label.textContent = 'Show Favorites'; 1220 } 1221 applyFavoritesFilter(); 1222} 1223 1224function applyFavoritesFilter() { 1225 document.querySelectorAll('.teaching-card').forEach(card => { 1226 const key = card.dataset.favKey; 1227 if (!favoritesOnly) { 1228 card.classList.remove('hidden-by-filter'); 1229 } else { 1230 if (key && favorites[key]) { 1231 card.classList.remove('hidden-by-filter'); 1232 } else { 1233 card.classList.add('hidden-by-filter'); 1234 } 1235 } 1236 }); 1237} 1238</script>
1238 1239 1240</body> 1241</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.