Update index.html
Browse files- index.html +23 -32
index.html
CHANGED
|
@@ -37,7 +37,7 @@
|
|
| 37 |
@keyframes pulse-soft { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
|
| 38 |
@keyframes bounce { 0%, 20%, 50%, 80%, 100% {transform: translateY(0);} 40% {transform: translateY(-15px);} 60% {transform: translateY(-7px);} }
|
| 39 |
|
| 40 |
-
/* PODCAST STUDIO VIEW
|
| 41 |
.podcast-studio-container { width: 100%; animation: fadeIn 0.5s ease-out; }
|
| 42 |
.podcast-studio-container .form-group-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.2rem; }
|
| 43 |
.podcast-studio-container #project-speakers-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 1.5rem; text-align: center; }
|
|
@@ -94,7 +94,6 @@
|
|
| 94 |
.podcast-studio-container .loading-state-wrapper .loading-text { font-size: 0.85em; font-weight: 600; color: var(--accent-primary); white-space: nowrap; transition: all 0.3s; }
|
| 95 |
.podcast-studio-container .turn-retry-btn { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; background: none; border: 1px solid var(--input-border); color: var(--text-secondary); padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); cursor: pointer; transition: var(--transition-smooth); margin-right: auto; display: flex; align-items: center; gap: 6px; }
|
| 96 |
.podcast-studio-container .turn-retry-btn:hover { border-color: var(--accent-secondary); color: var(--accent-secondary); background: var(--accent-secondary-glow); }
|
| 97 |
-
.podcast-studio-container .turn-retry-btn svg { width: 16px; height: 16px; fill: currentColor; }
|
| 98 |
.podcast-studio-container .replace-success-message, .podcast-studio-container .main-update-message { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); white-space: nowrap; display: none; align-items: center; justify-content: center; margin-right: auto; }
|
| 99 |
.podcast-studio-container .replace-success-message { background-color: var(--accent-secondary-glow); color: var(--accent-secondary); }
|
| 100 |
.podcast-studio-container .main-update-message { background: linear-gradient(90deg, #e6fffa, #b2f5ea); color: #2c7a7b; border: 1px solid #81e6d9; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
|
|
@@ -272,7 +271,7 @@
|
|
| 272 |
<div class="form-group">
|
| 273 |
<label for="text-input-standard">📝 متن اصلی</label>
|
| 274 |
<textarea id="text-input-standard" rows="5" placeholder="متن خود را برای تبدیل به گفتار اینجا وارد کنید..."></textarea>
|
| 275 |
-
<div class="char-counter-wrapper"><span id="char-count">0</span> نویسه</div>
|
| 276 |
</div>
|
| 277 |
<div class="form-group">
|
| 278 |
<label for="prompt-input-standard">🗣️ توصیف لحن و احساس (اختیاری)</label>
|
|
@@ -416,7 +415,7 @@
|
|
| 416 |
</div>
|
| 417 |
</section>
|
| 418 |
|
| 419 |
-
<footer class="site-footer"><p>© 1404 - تمام حقوق برای Ai Sada محفوظ است.</p></footer>
|
| 420 |
</div>
|
| 421 |
|
| 422 |
<!-- ======================================================================= -->
|
|
@@ -443,7 +442,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 443 |
let isMainSelectorAction = false;
|
| 444 |
let currentPodcastMode = null;
|
| 445 |
|
| 446 |
-
//
|
| 447 |
const allSpeakers =[
|
| 448 |
{ id: "Charon", name: "شهاب (مرد)", gender: "male", desc: "صدایی قدرمند و رسا", imgUrl: "https://uploadkon.ir/uploads/a18705_25IMG-۲۰۲۵۰۷۰۵-۱۱۰۵۴۹.jpg", samples:["https://uploadkon.ir/uploads/c17c06_26شهاب-یک-2-.mp3", "https://uploadkon.ir/uploads/af0d06_26شهاب-دو-2-.mp3", "https://uploadkon.ir/uploads/9e2806_26شهاب-سه-2-.mp3", "https://uploadkon.ir/uploads/35bd06_26شهاب-چهار-2-.mp3"], sampleTexts:["", "", "هاهاها ﺗﺒﺪﯾﻞ متن (خنده زیاد) شما ﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(خنده)", "ﺗﺒﺪﯾﻞ ﻣﺘﻦ ﺷﻤﺎﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(متن با صدای ترس و لرز بخون)"] },
|
| 449 |
{ id: "Zephyr", name: "آوا (زن)", gender: "female", desc: "لطیف و دلنشین", imgUrl: "https://uploadkon.ir/uploads/029605_25IMG-۲۰۲۵۰۷۰۵-۱۱۱۲۵۲.jpg", samples:["https://uploadkon.ir/uploads/920e06_26آوا-یک-2-.mp3", "https://uploadkon.ir/uploads/f50c06_26آوا-دو-2-.mp3", "https://uploadkon.ir/uploads/bc8e06_26آوا-سه-2-.mp3", "https://uploadkon.ir/uploads/1b2e06_26آوا-چهار-2-.mp3"], sampleTexts:["", "", "هاهاها ﺗﺒﺪﯾﻞ ﻣﺘﻦ ﺷﻤﺎ(خنده زیاد) ﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(خنده)", "وای ﺗﺒﺪﯾﻞ ﻣﺘﻦ ﺷﻤﺎﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(با تعجب و هیجان زیاد)"] },
|
|
@@ -763,7 +762,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 763 |
</div>
|
| 764 |
<button type="button" id="clear-history-btn-podcast">
|
| 765 |
<svg viewBox="0 0 24 24"><path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"></path></svg>
|
| 766 |
-
حذف کامل پروژه و ش
|
| 767 |
</button>
|
| 768 |
`;
|
| 769 |
container.appendChild(studioContainer);
|
|
@@ -821,36 +820,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 821 |
const turnDiv = document.createElement('div');
|
| 822 |
turnDiv.className = 'script-turn';
|
| 823 |
const turnIndex = container.children.length;
|
| 824 |
-
|
| 825 |
turnDiv.innerHTML = `
|
| 826 |
-
<div class="turn-speaker-selector">
|
| 827 |
-
<div class="custom-select-container" data-selected-id="${speakerId}">
|
| 828 |
-
<div class="custom-select-trigger"></div>
|
| 829 |
-
<div class="custom-select-options"></div>
|
| 830 |
-
</div>
|
| 831 |
-
</div>
|
| 832 |
<div class="turn-content">
|
| 833 |
<textarea>${text || ''}</textarea>
|
| 834 |
<div class="turn-player-container">
|
| 835 |
-
<button type="button" class="turn-play-btn">
|
| 836 |
-
<svg viewBox="0 0 24 24" class="play-icon"><path d="M8 5v14l11-7z"></path></svg>
|
| 837 |
-
<svg viewBox="0 0 24 24" class="pause-icon"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path></svg>
|
| 838 |
-
</button>
|
| 839 |
<audio class="turn-audio" style="display:none;"></audio>
|
| 840 |
-
<div class="loading-state-wrapper" style="display:none;">
|
| 841 |
-
|
| 842 |
-
|
| 843 |
-
</div>
|
| 844 |
-
<button type="button" class="turn-retry-btn">
|
| 845 |
-
<svg viewBox="0 0 24 24"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></svg>
|
| 846 |
-
جایگزین قطعه
|
| 847 |
-
</button>
|
| 848 |
-
<span class="replace-success-message">فایل جدید جایگزین شد</span>
|
| 849 |
<span class="main-update-message">فایل اصلی بروز شد</span>
|
| 850 |
</div>
|
| 851 |
</div>
|
| 852 |
<button type="button" class="remove-turn-btn" title="حذف نوبت">×</button>`;
|
| 853 |
-
|
| 854 |
container.appendChild(turnDiv);
|
| 855 |
|
| 856 |
turnDiv.querySelector('textarea').addEventListener('input', triggerSave);
|
|
@@ -890,7 +873,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 890 |
async function initiateAiPodcastGeneration(userText) {
|
| 891 |
hideModal(longTextModal);
|
| 892 |
await clearProjectState();
|
| 893 |
-
activePodcastSpeakers =
|
| 894 |
podcastMasterAudioBlobs =[];
|
| 895 |
|
| 896 |
showLoadingState(mainOutputSection, "در حال اتصال به هوش مصنوعی...");
|
|
@@ -1303,9 +1286,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1303 |
|
| 1304 |
(function() {
|
| 1305 |
const form = document.getElementById('standard-tts-form'); if (!form) return;
|
| 1306 |
-
const textInput = form.querySelector('#text-input-standard'), promptInput = form.querySelector('#prompt-input-standard'), generateBtn = form.querySelector('#generate-btn-standard'), btnText = generateBtn.querySelector('.btn-text'), btnSpinner = generateBtn.querySelector('.spinner'), outputSection = document.getElementById('output-section-standard'), statusMessage = outputSection.querySelector('#status-message-standard'), loadingAnimation = outputSection.querySelector('#loading-animation-wrapper-standard'), playerContent = outputSection.querySelector('#audio-player-content-standard'), charCount = form.querySelector('#char-count');
|
| 1307 |
|
| 1308 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1309 |
|
| 1310 |
const showResultState = (isSuccess, msg = '') => {
|
| 1311 |
loadingAnimation.style.display = 'none';
|
|
@@ -1355,7 +1345,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1355 |
})();
|
| 1356 |
|
| 1357 |
// Events
|
| 1358 |
-
function setupEventListeners() {
|
|
|
|
| 1359 |
document.querySelectorAll('.modal-overlay').forEach(o => o.addEventListener('click', (e) => (e.target === o) && hideModal(o)));
|
| 1360 |
document.querySelectorAll('.close-modal-btn').forEach(b => b.addEventListener('click', () => hideModal(b.closest('.modal-overlay'))));
|
| 1361 |
|
|
@@ -1410,4 +1401,4 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1410 |
});
|
| 1411 |
</script>
|
| 1412 |
</body>
|
| 1413 |
-
</html>
|
|
|
|
| 37 |
@keyframes pulse-soft { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
|
| 38 |
@keyframes bounce { 0%, 20%, 50%, 80%, 100% {transform: translateY(0);} 40% {transform: translateY(-15px);} 60% {transform: translateY(-7px);} }
|
| 39 |
|
| 40 |
+
/* PODCAST STUDIO VIEW */
|
| 41 |
.podcast-studio-container { width: 100%; animation: fadeIn 0.5s ease-out; }
|
| 42 |
.podcast-studio-container .form-group-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.2rem; }
|
| 43 |
.podcast-studio-container #project-speakers-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 1.5rem; text-align: center; }
|
|
|
|
| 94 |
.podcast-studio-container .loading-state-wrapper .loading-text { font-size: 0.85em; font-weight: 600; color: var(--accent-primary); white-space: nowrap; transition: all 0.3s; }
|
| 95 |
.podcast-studio-container .turn-retry-btn { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; background: none; border: 1px solid var(--input-border); color: var(--text-secondary); padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); cursor: pointer; transition: var(--transition-smooth); margin-right: auto; display: flex; align-items: center; gap: 6px; }
|
| 96 |
.podcast-studio-container .turn-retry-btn:hover { border-color: var(--accent-secondary); color: var(--accent-secondary); background: var(--accent-secondary-glow); }
|
|
|
|
| 97 |
.podcast-studio-container .replace-success-message, .podcast-studio-container .main-update-message { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); white-space: nowrap; display: none; align-items: center; justify-content: center; margin-right: auto; }
|
| 98 |
.podcast-studio-container .replace-success-message { background-color: var(--accent-secondary-glow); color: var(--accent-secondary); }
|
| 99 |
.podcast-studio-container .main-update-message { background: linear-gradient(90deg, #e6fffa, #b2f5ea); color: #2c7a7b; border: 1px solid #81e6d9; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
|
|
|
|
| 271 |
<div class="form-group">
|
| 272 |
<label for="text-input-standard">📝 متن اصلی</label>
|
| 273 |
<textarea id="text-input-standard" rows="5" placeholder="متن خود را برای تبدیل به گفتار اینجا وارد کنید..."></textarea>
|
| 274 |
+
<div class="char-counter-wrapper"><span id="char-count">0</span> / <span id="char-max">80000</span> نویسه</div>
|
| 275 |
</div>
|
| 276 |
<div class="form-group">
|
| 277 |
<label for="prompt-input-standard">🗣️ توصیف لحن و احساس (اختیاری)</label>
|
|
|
|
| 415 |
</div>
|
| 416 |
</section>
|
| 417 |
|
| 418 |
+
<footer class="site-footer"><p>© 1404 - تمام حقوق برای <a href="#">Ai Sada</a> محفوظ است.</p></footer>
|
| 419 |
</div>
|
| 420 |
|
| 421 |
<!-- ======================================================================= -->
|
|
|
|
| 442 |
let isMainSelectorAction = false;
|
| 443 |
let currentPodcastMode = null;
|
| 444 |
|
| 445 |
+
// دریافت کامل آرایه اسپیکرها با ۴ فایل و ۴ متن از کد اصلی
|
| 446 |
const allSpeakers =[
|
| 447 |
{ id: "Charon", name: "شهاب (مرد)", gender: "male", desc: "صدایی قدرمند و رسا", imgUrl: "https://uploadkon.ir/uploads/a18705_25IMG-۲۰۲۵۰۷۰۵-۱۱۰۵۴۹.jpg", samples:["https://uploadkon.ir/uploads/c17c06_26شهاب-یک-2-.mp3", "https://uploadkon.ir/uploads/af0d06_26شهاب-دو-2-.mp3", "https://uploadkon.ir/uploads/9e2806_26شهاب-سه-2-.mp3", "https://uploadkon.ir/uploads/35bd06_26شهاب-چهار-2-.mp3"], sampleTexts:["", "", "هاهاها ﺗﺒﺪﯾﻞ متن (خنده زیاد) شما ﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(خنده)", "ﺗﺒﺪﯾﻞ ﻣﺘﻦ ﺷﻤﺎﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(متن با صدای ترس و لرز بخون)"] },
|
| 448 |
{ id: "Zephyr", name: "آوا (زن)", gender: "female", desc: "لطیف و دلنشین", imgUrl: "https://uploadkon.ir/uploads/029605_25IMG-۲۰۲۵۰۷۰۵-۱۱۱۲۵۲.jpg", samples:["https://uploadkon.ir/uploads/920e06_26آوا-یک-2-.mp3", "https://uploadkon.ir/uploads/f50c06_26آوا-دو-2-.mp3", "https://uploadkon.ir/uploads/bc8e06_26آوا-سه-2-.mp3", "https://uploadkon.ir/uploads/1b2e06_26آوا-چهار-2-.mp3"], sampleTexts:["", "", "هاهاها ﺗﺒﺪﯾﻞ ﻣﺘﻦ ﺷﻤﺎ(خنده زیاد) ﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(خنده)", "وای ﺗﺒﺪﯾﻞ ﻣﺘﻦ ﺷﻤﺎﺑﻪ ﺻﺪﺍﯾﯽ ﮐﻪ ﻫﻤﺪﻟﯽ ﻭ ﺩﺭﮎ ﺭﺍ ﺑﺮﻣﯽﺍﻧﮕﯿﺰﺩ.(با تعجب و هیجان زیاد)"] },
|
|
|
|
| 762 |
</div>
|
| 763 |
<button type="button" id="clear-history-btn-podcast">
|
| 764 |
<svg viewBox="0 0 24 24"><path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"></path></svg>
|
| 765 |
+
حذف کامل پروژه و بازگشت
|
| 766 |
</button>
|
| 767 |
`;
|
| 768 |
container.appendChild(studioContainer);
|
|
|
|
| 820 |
const turnDiv = document.createElement('div');
|
| 821 |
turnDiv.className = 'script-turn';
|
| 822 |
const turnIndex = container.children.length;
|
|
|
|
| 823 |
turnDiv.innerHTML = `
|
| 824 |
+
<div class="turn-speaker-selector"><div class="custom-select-container" data-selected-id="${speakerId}"><div class="custom-select-trigger"></div><div class="custom-select-options"></div></div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 825 |
<div class="turn-content">
|
| 826 |
<textarea>${text || ''}</textarea>
|
| 827 |
<div class="turn-player-container">
|
| 828 |
+
<button type="button" class="turn-play-btn"><svg viewBox="0 0 24 24" class="play-icon"><path d="M8 5v14l11-7z"></path></svg><svg viewBox="0 0 24 24" class="pause-icon"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path></svg></button>
|
|
|
|
|
|
|
|
|
|
| 829 |
<audio class="turn-audio" style="display:none;"></audio>
|
| 830 |
+
<div class="loading-state-wrapper" style="display:none;"><div class="spinner"></div><span class="loading-text">در حال جایگزینی...</span></div>
|
| 831 |
+
<button type="button" class="turn-retry-btn"><svg viewBox="0 0 24 24"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></svg> جایگزین قطعه</button>
|
| 832 |
+
<span class="replace-success-message">جایگزین شد</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 833 |
<span class="main-update-message">فایل اصلی بروز شد</span>
|
| 834 |
</div>
|
| 835 |
</div>
|
| 836 |
<button type="button" class="remove-turn-btn" title="حذف نوبت">×</button>`;
|
|
|
|
| 837 |
container.appendChild(turnDiv);
|
| 838 |
|
| 839 |
turnDiv.querySelector('textarea').addEventListener('input', triggerSave);
|
|
|
|
| 873 |
async function initiateAiPodcastGeneration(userText) {
|
| 874 |
hideModal(longTextModal);
|
| 875 |
await clearProjectState();
|
| 876 |
+
activePodcastSpeakers =[];
|
| 877 |
podcastMasterAudioBlobs =[];
|
| 878 |
|
| 879 |
showLoadingState(mainOutputSection, "در حال اتصال به هوش مصنوعی...");
|
|
|
|
| 1286 |
|
| 1287 |
(function() {
|
| 1288 |
const form = document.getElementById('standard-tts-form'); if (!form) return;
|
| 1289 |
+
const textInput = form.querySelector('#text-input-standard'), promptInput = form.querySelector('#prompt-input-standard'), generateBtn = form.querySelector('#generate-btn-standard'), btnText = generateBtn.querySelector('.btn-text'), btnSpinner = generateBtn.querySelector('.spinner'), outputSection = document.getElementById('output-section-standard'), statusMessage = outputSection.querySelector('#status-message-standard'), loadingAnimation = outputSection.querySelector('#loading-animation-wrapper-standard'), playerContent = outputSection.querySelector('#audio-player-content-standard'), charCount = form.querySelector('#char-count'), charMax = form.querySelector('#char-max');
|
| 1290 |
|
| 1291 |
+
const MAX_CHARS = 80000;
|
| 1292 |
+
if(charMax) charMax.textContent = MAX_CHARS.toLocaleString('fa-IR');
|
| 1293 |
+
|
| 1294 |
+
textInput.addEventListener('input', () => {
|
| 1295 |
+
const len = textInput.value.length;
|
| 1296 |
+
charCount.textContent = len.toLocaleString('fa-IR');
|
| 1297 |
+
charCount.style.color = len > MAX_CHARS ? 'red' : 'var(--accent-primary)';
|
| 1298 |
+
});
|
| 1299 |
|
| 1300 |
const showResultState = (isSuccess, msg = '') => {
|
| 1301 |
loadingAnimation.style.display = 'none';
|
|
|
|
| 1345 |
})();
|
| 1346 |
|
| 1347 |
// Events
|
| 1348 |
+
function setupEventListeners() {
|
| 1349 |
+
[changeSpeakerBtn, selectedSpeakerCard].forEach(el => el && el.addEventListener('click', () => { isMainSelectorAction = true; createSpeakerCardsInModal(); showModal(speakerModal); }));
|
| 1350 |
document.querySelectorAll('.modal-overlay').forEach(o => o.addEventListener('click', (e) => (e.target === o) && hideModal(o)));
|
| 1351 |
document.querySelectorAll('.close-modal-btn').forEach(b => b.addEventListener('click', () => hideModal(b.closest('.modal-overlay'))));
|
| 1352 |
|
|
|
|
| 1401 |
});
|
| 1402 |
</script>
|
| 1403 |
</body>
|
| 1404 |
+
</html>
|