diff --git a/client.js b/client.js index aa382fb..3b687c7 100644 --- a/client.js +++ b/client.js @@ -1398,49 +1398,268 @@ } // -------------------------------------------------------------------------- - // 8. VOICE SEARCH, CLEAR BUTTON & ACCESSIBLE SEARCH UX (YouTube Style) // -------------------------------------------------------------------------- - var activeRecognition = null; - var isVoiceListening = false; + // 8. YOUTUBE-IDENTICAL SEARCH BAR & VOICE MODAL ENGINE (v1.2.0) + // -------------------------------------------------------------------------- + var activeVoiceRecognition = null; + var isVoiceListeningActive = false; + var modalTargetInput = null; - function initVoiceSearch(searchInp, searchWrap) { - var SpeechRec = window.SpeechRecognition || window.webkitSpeechRecognition; - var voiceBtn = searchWrap.querySelector('#yaktube-voice-search-btn'); + function createYouTubeVoiceModal() { + var existingModal = document.getElementById('yaktube-voice-modal'); + if (existingModal) return existingModal; - if (!voiceBtn) { - voiceBtn = document.createElement('button'); - voiceBtn.id = 'yaktube-voice-search-btn'; - voiceBtn.type = 'button'; - voiceBtn.className = 'yaktube-voice-search-btn'; - voiceBtn.setAttribute('aria-label', 'Sesle Ara (Shift+V)'); - voiceBtn.setAttribute('title', 'Sesle Ara (Shift+V)'); - voiceBtn.innerHTML = '🎙️'; - voiceBtn.style.cssText = - 'background: rgba(255, 143, 55, 0.12); color: #ff8f37; border: 1px solid rgba(255, 143, 55, 0.35); border-radius: 50%; width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center; font-size: 16px; cursor: pointer; margin-left: 6px; transition: all 0.2s ease; flex-shrink: 0; box-shadow: 0 2px 6px rgba(0,0,0,0.2);'; + var modal = document.createElement('div'); + modal.id = 'yaktube-voice-modal'; + modal.className = 'yaktube-voice-modal-overlay'; + modal.setAttribute('role', 'dialog'); + modal.setAttribute('aria-modal', 'true'); + modal.setAttribute('aria-labelledby', 'yaktube-voice-modal-title'); + modal.style.display = 'none'; - searchWrap.appendChild(voiceBtn); + modal.innerHTML = + '
' + + '
' + + '

Sesle arama yapın

' + + '' + + '
' + + '
' + + '
Dinleniyor...
' + + '
' + + '
' + + '' + + '
' + + '
' + + '
Aramak istediğiniz sanatçıyı, şarkıyı veya konuyu söyleyin.
' + + '
' + + '
'; - voiceBtn.addEventListener('click', function (e) { - e.preventDefault(); - e.stopPropagation(); - toggleVoiceSearch(searchInp, voiceBtn); + document.body.appendChild(modal); + + var closeBtn = modal.querySelector('#yaktube-voice-modal-close'); + if (closeBtn) { + closeBtn.addEventListener('click', function () { + closeYouTubeVoiceModal(); }); } - // Clear Button (✖️) - var clearBtn = searchWrap.querySelector('#yaktube-clear-search-btn'); - if (!clearBtn) { - clearBtn = document.createElement('button'); - clearBtn.id = 'yaktube-clear-search-btn'; + modal.addEventListener('click', function (e) { + if (e.target === modal) { + closeYouTubeVoiceModal(); + } + }); + + var pulseBtn = modal.querySelector('#yaktube-voice-mic-pulse-btn'); + if (pulseBtn) { + pulseBtn.addEventListener('click', function () { + if (isVoiceListeningActive) { + stopVoiceRecognition(); + } else { + startVoiceRecognition(); + } + }); + } + + return modal; + } + + function openYouTubeVoiceModal(targetInput) { + var SpeechRec = window.SpeechRecognition || window.webkitSpeechRecognition; + if (!SpeechRec) { + announce( + 'Tarayıcınız sesli arama özelliğini desteklemiyor. Lütfen Chrome, Edge veya güncel bir mobil tarayıcı kullanın.' + ); + return; + } + + var modal = createYouTubeVoiceModal(); + modal.style.display = 'flex'; + announce('Sesli arama penceresi açıldı. Dinleniyor, lütfen aramak istediğiniz videoyu söyleyin.', true); + + modalTargetInput = targetInput; + startVoiceRecognition(); + + var closeBtn = modal.querySelector('#yaktube-voice-modal-close'); + if (closeBtn) closeBtn.focus(); + } + + function closeYouTubeVoiceModal() { + var modal = document.getElementById('yaktube-voice-modal'); + if (modal) modal.style.display = 'none'; + stopVoiceRecognition(); + announce('Sesli arama kapatıldı.'); + } + + function startVoiceRecognition() { + var SpeechRec = window.SpeechRecognition || window.webkitSpeechRecognition; + if (!SpeechRec) return; + + if (activeVoiceRecognition) { + try { + activeVoiceRecognition.stop(); + } catch (e) {} + } + + var modal = document.getElementById('yaktube-voice-modal'); + var statusText = modal ? modal.querySelector('#yaktube-voice-status-text') : null; + var transcriptPreview = modal ? modal.querySelector('#yaktube-voice-transcript-preview') : null; + var micBtn = modal ? modal.querySelector('#yaktube-voice-mic-pulse-btn') : null; + var ripple = modal ? modal.querySelector('#yaktube-voice-ripple') : null; + + if (statusText) statusText.textContent = 'Dinleniyor...'; + if (transcriptPreview) transcriptPreview.textContent = ''; + if (micBtn) micBtn.classList.remove('yaktube-idle-mic'); + if (ripple) ripple.style.display = 'block'; + + try { + activeVoiceRecognition = new SpeechRec(); + activeVoiceRecognition.lang = navigator.language || 'tr-TR'; + activeVoiceRecognition.continuous = false; + activeVoiceRecognition.interimResults = true; + activeVoiceRecognition.maxAlternatives = 1; + + isVoiceListeningActive = true; + + activeVoiceRecognition.onresult = function (ev) { + if (ev.results && ev.results[0]) { + var transcript = ev.results[0][0].transcript.trim(); + if (transcriptPreview) transcriptPreview.textContent = '"' + transcript + '"'; + + if (ev.results[0].isFinal) { + announce('"' + transcript + '" için arama başlatılıyor...', true); + if (modalTargetInput) { + modalTargetInput.value = transcript; + modalTargetInput.dispatchEvent(new Event('input', { bubbles: true })); + modalTargetInput.dispatchEvent(new Event('change', { bubbles: true })); + + setTimeout(function () { + closeYouTubeVoiceModal(); + var form = modalTargetInput.closest('form'); + if (form) { + form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); + } else { + window.location.href = '/search/videos?search=' + encodeURIComponent(transcript); + } + }, 400); + } + } + } + }; + + activeVoiceRecognition.onerror = function (ev) { + console.warn('[YakTube] Voice modal error:', ev.error); + if (statusText) { + if (ev.error === 'not-allowed') { + statusText.textContent = 'Mikrofon İzni Gerekli'; + announce('Mikrofon izni verilmedi.'); + } else if (ev.error === 'no-speech') { + statusText.textContent = 'Ses Algılanamadı'; + announce('Ses algılanamadı, tekrar denemek için mikrofona dokunun.'); + } else { + statusText.textContent = 'Bir sorun oluştu'; + } + } + stopVoiceRecognition(); + }; + + activeVoiceRecognition.onend = function () { + if (isVoiceListeningActive) { + stopVoiceRecognition(); + } + }; + + activeVoiceRecognition.start(); + } catch (err) { + console.error('[YakTube] SpeechRec start failed:', err); + stopVoiceRecognition(); + } + } + + function stopVoiceRecognition() { + isVoiceListeningActive = false; + if (activeVoiceRecognition) { + try { + activeVoiceRecognition.stop(); + } catch (e) {} + activeVoiceRecognition = null; + } + + var modal = document.getElementById('yaktube-voice-modal'); + if (modal) { + var micBtn = modal.querySelector('#yaktube-voice-mic-pulse-btn'); + var ripple = modal.querySelector('#yaktube-voice-ripple'); + var statusText = modal.querySelector('#yaktube-voice-status-text'); + + if (micBtn) micBtn.classList.add('yaktube-idle-mic'); + if (ripple) ripple.style.display = 'none'; + if (statusText && statusText.textContent === 'Dinleniyor...') { + statusText.textContent = 'Mikrofona dokunup konuşun'; + } + } + } + + function transformToYouTubeSearchBar(searchInp, searchContainer) { + if (searchInp.hasAttribute('data-yaktube-yt-styled')) return; + searchInp.setAttribute('data-yaktube-yt-styled', 'true'); + + // 1. Back button for Mobile Navigation (←) + var backBtn = searchContainer.querySelector('.yaktube-search-back-btn'); + if (!backBtn) { + backBtn = document.createElement('button'); + backBtn.type = 'button'; + backBtn.className = 'yaktube-search-back-btn'; + backBtn.setAttribute('aria-label', 'Aramadan Çık (Geri)'); + backBtn.innerHTML = '←'; + searchContainer.prepend(backBtn); + + backBtn.addEventListener('click', function (e) { + e.preventDefault(); + document.body.classList.remove('yaktube-mobile-search-active'); + searchInp.blur(); + announce('Arama kapatıldı.'); + }); + } + + // 2. Wrap input inside YouTube Pill Container + var pillWrapper = searchInp.closest('.yaktube-search-pill-wrapper'); + if (!pillWrapper) { + pillWrapper = document.createElement('div'); + pillWrapper.className = 'yaktube-search-pill-wrapper'; + + searchInp.parentNode.insertBefore(pillWrapper, searchInp); + pillWrapper.appendChild(searchInp); + + // Add Clear Button (✕) + var clearBtn = document.createElement('button'); clearBtn.type = 'button'; clearBtn.className = 'yaktube-clear-search-btn'; clearBtn.setAttribute('aria-label', 'Arama Metnini Temizle (Escape)'); - clearBtn.setAttribute('title', 'Arama Metnini Temizle (Escape)'); clearBtn.innerHTML = '✕'; - clearBtn.style.cssText = - 'background: rgba(255, 255, 255, 0.12); color: #cbd5e1; border: none; border-radius: 50%; width: 22px; height: 22px; display: none; align-items: center; justify-content: center; font-size: 11px; font-weight: bold; cursor: pointer; margin-right: 4px; transition: all 0.2s ease; flex-shrink: 0;'; + pillWrapper.appendChild(clearBtn); - searchInp.insertAdjacentElement('afterend', clearBtn); + // Add Search Submit Button (🔍) + var submitBtn = document.createElement('button'); + submitBtn.type = 'submit'; + submitBtn.className = 'yaktube-search-submit-btn'; + submitBtn.setAttribute('aria-label', 'Ara'); + submitBtn.innerHTML = '🔍'; + pillWrapper.appendChild(submitBtn); + + function updateClearState() { + if (searchInp.value && searchInp.value.trim().length > 0) { + clearBtn.style.display = 'inline-flex'; + } else { + clearBtn.style.display = 'none'; + } + } + + searchInp.addEventListener('input', updateClearState); + searchInp.addEventListener('focus', function () { + updateClearState(); + document.body.classList.add('yaktube-mobile-search-active'); + }); clearBtn.addEventListener('click', function (e) { e.preventDefault(); @@ -1450,31 +1669,50 @@ searchInp.dispatchEvent(new Event('change', { bubbles: true })); clearBtn.style.display = 'none'; searchInp.focus(); - announce('Arama metni temizlendi.'); + announce('Arama kutusu temizlendi.'); + }); + + submitBtn.addEventListener('click', function (e) { + var query = (searchInp.value || '').trim(); + if (query) { + var form = searchInp.closest('form'); + if (form) { + form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); + } else { + window.location.href = '/search/videos?search=' + encodeURIComponent(query); + } + } }); } - function updateClearBtnVisibility() { - if (searchInp.value && searchInp.value.trim().length > 0) { - clearBtn.style.display = 'inline-flex'; - } else { - clearBtn.style.display = 'none'; - } + // 3. YouTube Circular Voice Button (🎙️) Beside the Pill Box + var voiceBtn = searchContainer.querySelector('.yaktube-voice-search-btn'); + if (!voiceBtn) { + voiceBtn = document.createElement('button'); + voiceBtn.type = 'button'; + voiceBtn.className = 'yaktube-voice-search-btn'; + voiceBtn.setAttribute('aria-label', 'Sesle Ara (Shift+V)'); + voiceBtn.setAttribute('title', 'Sesle Ara (Shift+V)'); + voiceBtn.innerHTML = '🎙️'; + + searchContainer.appendChild(voiceBtn); + + voiceBtn.addEventListener('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + openYouTubeVoiceModal(searchInp); + }); } - searchInp.addEventListener('input', updateClearBtnVisibility); - searchInp.addEventListener('focus', updateClearBtnVisibility); - updateClearBtnVisibility(); - - // Escape Key Exit + // 4. Escape key handling searchInp.addEventListener('keydown', function (e) { if (e.key === 'Escape' || e.keyCode === 27) { if (searchInp.value && searchInp.value.trim().length > 0) { searchInp.value = ''; searchInp.dispatchEvent(new Event('input', { bubbles: true })); - clearBtn.style.display = 'none'; announce('Arama metni temizlendi.'); } else { + document.body.classList.remove('yaktube-mobile-search-active'); searchInp.blur(); announce('Arama kutusundan çıkıldı.'); } @@ -1482,135 +1720,54 @@ }); } - function toggleVoiceSearch(searchInp, voiceBtn) { - var SpeechRec = window.SpeechRecognition || window.webkitSpeechRecognition; - if (!SpeechRec) { - announce('Tarayıcınız sesli arama özelliğini desteklemiyor. Lütfen Chrome veya Edge kullanın.'); - return; - } - - if (isVoiceListening) { - if (activeRecognition) { - try { - activeRecognition.stop(); - } catch (err) {} - } - stopVoiceState(voiceBtn); - announce('Sesli arama durduruldu.'); - return; - } - - try { - activeRecognition = new SpeechRec(); - activeRecognition.lang = navigator.language || 'tr-TR'; - activeRecognition.continuous = false; - activeRecognition.interimResults = false; - activeRecognition.maxAlternatives = 1; - - isVoiceListening = true; - voiceBtn.classList.add('yaktube-voice-recording'); - voiceBtn.innerHTML = '🔴'; - voiceBtn.setAttribute('aria-label', 'Dinleniyor... Konuşun (Durdurmak için tıklayın)'); - announce('Sesli arama dinleniyor, lütfen aramak istediğiniz videoyu söyleyin...', true); - - activeRecognition.onresult = function (ev) { - if (ev.results && ev.results[0] && ev.results[0][0]) { - var transcript = ev.results[0][0].transcript.trim(); - if (transcript) { - searchInp.value = transcript; - searchInp.dispatchEvent(new Event('input', { bubbles: true })); - searchInp.dispatchEvent(new Event('change', { bubbles: true })); - - announce('"' + transcript + '" için aranıyor...', true); - - // Trigger search submission - var form = searchInp.closest('form'); - if (form) { - form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); - } else { - window.location.href = '/search/videos?search=' + encodeURIComponent(transcript); - } - } - } - }; - - activeRecognition.onerror = function (ev) { - console.warn('[YakTube] Voice search error:', ev.error); - if (ev.error === 'not-allowed') { - announce('Mikrofon izni verilmedi. Lütfen tarayıcı ayarlarından mikrofona izin verin.'); - } else if (ev.error === 'no-speech') { - announce('Ses algılanamadı, lütfen tekrar deneyin.'); - } else { - announce('Sesli arama sırasında bir sorun oluştu: ' + ev.error); - } - stopVoiceState(voiceBtn); - }; - - activeRecognition.onend = function () { - stopVoiceState(voiceBtn); - }; - - activeRecognition.start(); - } catch (err) { - console.error('[YakTube] Failed to start speech recognition:', err); - announce('Sesli arama başlatılamadı.'); - stopVoiceState(voiceBtn); - } - } - - function stopVoiceState(voiceBtn) { - isVoiceListening = false; - if (voiceBtn) { - voiceBtn.classList.remove('yaktube-voice-recording'); - voiceBtn.innerHTML = '🎙️'; - voiceBtn.setAttribute('aria-label', 'Sesle Ara (Shift+V)'); - } - } - function injectSearchEnhancements() { var searchContainers = document.querySelectorAll( 'my-search-typeahead, .search-container, .header-search, my-header .search' ); searchContainers.forEach(function (container) { var searchInp = container.querySelector('input[type="search"], input[type="text"], input'); - if (searchInp && !searchInp.hasAttribute('data-yaktube-enhanced')) { - searchInp.setAttribute('data-yaktube-enhanced', 'true'); - initVoiceSearch(searchInp, container); + if (searchInp) { + transformToYouTubeSearchBar(searchInp, container); } }); - // Also check generic header inputs - var headerInputs = document.querySelectorAll('my-header input:not([data-yaktube-enhanced])'); + var headerInputs = document.querySelectorAll('my-header input'); headerInputs.forEach(function (inp) { - var parent = inp.parentElement; + var parent = inp.closest('my-search-typeahead') || inp.parentElement; if ( parent && (inp.type === 'search' || inp.type === 'text' || - inp.placeholder.toLowerCase().includes('ara') || - inp.placeholder.toLowerCase().includes('search')) + (inp.placeholder && inp.placeholder.toLowerCase().includes('ara'))) ) { - inp.setAttribute('data-yaktube-enhanced', 'true'); - initVoiceSearch(inp, parent); + transformToYouTubeSearchBar(inp, parent); } }); } - // Global Keyboard Shortcut: Shift+V for Voice Search, / for search focus + // Global Keyboard Shortcuts document.addEventListener('keydown', function (e) { var activeTag = document.activeElement ? document.activeElement.tagName.toLowerCase() : ''; var isInputActive = activeTag === 'input' || activeTag === 'textarea'; + // Shift + V: Open YouTube Voice Modal if (e.shiftKey && (e.key === 'V' || e.key === 'v') && !isInputActive) { e.preventDefault(); - var mainVoiceBtn = document.querySelector('#yaktube-voice-search-btn'); var searchInp = document.querySelector( 'my-search-typeahead input, my-header input[type="search"], input[type="search"]' ); - if (mainVoiceBtn && searchInp) { - toggleVoiceSearch(searchInp, mainVoiceBtn); + openYouTubeVoiceModal(searchInp); + } + // Escape: Close Voice Modal if open + else if (e.key === 'Escape' || e.keyCode === 27) { + var modal = document.getElementById('yaktube-voice-modal'); + if (modal && modal.style.display === 'flex') { + e.preventDefault(); + closeYouTubeVoiceModal(); } - } else if (e.key === '/' && !isInputActive && !e.ctrlKey && !e.metaKey) { + } + // / : Focus Search Input + else if (e.key === '/' && !isInputActive && !e.ctrlKey && !e.metaKey) { e.preventDefault(); var searchInp = document.querySelector( 'my-search-typeahead input, my-header input[type="search"], input[type="search"]' diff --git a/package.json b/package.json index 4974075..5236234 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "peertube-plugin-yaktube-ondemand", - "version": "1.1.0", + "version": "1.2.0", "description": "Accessible On-Demand YouTube Import, Live Streams, Smart Recommendations and A11y Engine for PeerTube", "main": "main.js", "clientScripts": [ diff --git a/style.css b/style.css index 544a009..2f319c7 100644 --- a/style.css +++ b/style.css @@ -405,3 +405,306 @@ my-header .action-buttons { .yaktube-admin-login-link a:hover { text-decoration: underline; } + +/* -------------------------------------------------------------------------- */ +/* YOUTUBE-STYLE SEARCH BAR & VOICE SEARCH MODAL (v1.2.0) */ +/* -------------------------------------------------------------------------- */ + +/* Header Search Container Pill Styling */ +my-search-typeahead, +.search-container, +.header-search { + display: flex !important; + align-items: center !important; + position: relative !important; +} + +.yaktube-search-pill-wrapper { + display: flex !important; + align-items: center !important; + background: #121212 !important; + border: 1px solid #303030 !important; + border-radius: 40px !important; + padding: 2px 6px 2px 14px !important; + transition: all 0.2s ease !important; + width: 100% !important; + max-width: 580px !important; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.4) !important; +} + +.yaktube-search-pill-wrapper:focus-within { + border-color: #ff8f37 !important; + box-shadow: + 0 0 0 1px #ff8f37, + 0 0 12px rgba(255, 143, 55, 0.3) !important; + background: #0f0f0f !important; +} + +.yaktube-search-pill-wrapper input { + background: transparent !important; + border: none !important; + color: #f1f1f1 !important; + font-size: 15px !important; + padding: 8px 6px !important; + width: 100% !important; + outline: none !important; + box-shadow: none !important; +} + +.yaktube-search-pill-wrapper input::placeholder { + color: #888 !important; + font-size: 14px !important; +} + +/* Clear Button Inside Pill */ +.yaktube-clear-search-btn { + background: rgba(255, 255, 255, 0.12) !important; + color: #aaa !important; + border: none !important; + border-radius: 50% !important; + width: 24px !important; + height: 24px !important; + display: none; + align-items: center !important; + justify-content: center !important; + font-size: 12px !important; + font-weight: 700 !important; + cursor: pointer !important; + margin: 0 6px !important; + transition: all 0.2s ease !important; + flex-shrink: 0 !important; +} + +.yaktube-clear-search-btn:hover { + background: rgba(255, 255, 255, 0.25) !important; + color: #fff !important; +} + +/* Search Submit Button on Right of Pill */ +.yaktube-search-submit-btn { + background: #222222 !important; + color: #f1f1f1 !important; + border: none !important; + border-left: 1px solid #303030 !important; + border-radius: 0 40px 40px 0 !important; + padding: 8px 18px !important; + cursor: pointer !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 14px !important; + transition: all 0.2s ease !important; + margin: -2px -6px -2px 0 !important; +} + +.yaktube-search-submit-btn:hover { + background: #272727 !important; + color: #ff8f37 !important; +} + +/* YouTube Circular Voice Button Beside Search Box */ +.yaktube-voice-search-btn { + background: #1f1f1f !important; + color: #f1f1f1 !important; + border: 1px solid rgba(255, 255, 255, 0.08) !important; + border-radius: 50% !important; + width: 40px !important; + height: 40px !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 18px !important; + cursor: pointer !important; + margin-left: 10px !important; + transition: all 0.25s ease !important; + flex-shrink: 0 !important; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important; +} + +.yaktube-voice-search-btn:hover { + background: #2a2a2a !important; + border-color: #ff8f37 !important; + color: #ff8f37 !important; + transform: scale(1.06) !important; +} + +/* YouTube-Style Voice Search Modal Overlay */ +.yaktube-voice-modal-overlay { + position: fixed !important; + top: 0 !important; + left: 0 !important; + width: 100vw !important; + height: 100vh !important; + background: rgba(0, 0, 0, 0.75) !important; + backdrop-filter: blur(8px) !important; + z-index: 99999 !important; + display: flex !important; + align-items: flex-start !important; + justify-content: center !important; + padding-top: 80px !important; + animation: yaktube-fade-in 0.2s ease !important; +} + +.yaktube-voice-modal-card { + background: #212121 !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; + border-radius: 16px !important; + width: 90% !important; + max-width: 560px !important; + padding: 24px 28px !important; + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.7) !important; + color: #fff !important; + position: relative !important; +} + +.yaktube-voice-modal-header { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + margin-bottom: 24px !important; +} + +.yaktube-voice-modal-title { + font-size: 20px !important; + font-weight: 500 !important; + margin: 0 !important; + color: #f1f1f1 !important; +} + +.yaktube-voice-modal-close-btn { + background: transparent !important; + border: none !important; + color: #aaa !important; + font-size: 20px !important; + cursor: pointer !important; + width: 36px !important; + height: 36px !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + transition: all 0.2s ease !important; +} + +.yaktube-voice-modal-close-btn:hover { + background: rgba(255, 255, 255, 0.1) !important; + color: #fff !important; +} + +.yaktube-voice-modal-body { + text-align: center !important; + padding: 10px 0 20px 0 !important; +} + +.yaktube-voice-status-text { + font-size: 22px !important; + font-weight: 600 !important; + color: #f1f1f1 !important; + margin-bottom: 12px !important; + min-height: 30px !important; +} + +.yaktube-voice-transcript-preview { + font-size: 16px !important; + color: #ff8f37 !important; + font-weight: 500 !important; + margin-bottom: 30px !important; + min-height: 24px !important; + word-break: break-word !important; +} + +/* Pulsing Mic Circle Animation */ +.yaktube-voice-mic-wrapper { + position: relative !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + margin: 10px 0 25px 0 !important; +} + +.yaktube-voice-mic-pulse-btn { + background: #cc0000 !important; + color: #fff !important; + border: none !important; + border-radius: 50% !important; + width: 76px !important; + height: 76px !important; + font-size: 34px !important; + cursor: pointer !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + box-shadow: 0 4px 20px rgba(204, 0, 0, 0.5) !important; + z-index: 2 !important; + transition: all 0.2s ease !important; +} + +.yaktube-voice-mic-pulse-btn:hover { + transform: scale(1.05) !important; +} + +.yaktube-voice-mic-pulse-btn.yaktube-idle-mic { + background: #333 !important; + box-shadow: none !important; +} + +.yaktube-voice-ripple { + position: absolute !important; + width: 76px !important; + height: 76px !important; + border-radius: 50% !important; + background: rgba(204, 0, 0, 0.4) !important; + animation: yaktube-ripple-wave 1.6s infinite ease-out !important; + z-index: 1 !important; +} + +@keyframes yaktube-ripple-wave { + 0% { + transform: scale(1); + opacity: 0.8; + } + 100% { + transform: scale(2.2); + opacity: 0; + } +} + +.yaktube-voice-hint-text { + font-size: 13px !important; + color: #888 !important; + margin-top: 10px !important; +} + +/* Mobile Search Overlay Mode */ +@media (max-width: 768px) { + body.yaktube-mobile-search-active my-header .header-left, + body.yaktube-mobile-search-active my-header .header-right, + body.yaktube-mobile-search-active my-header .user-menu { + display: none !important; + } + body.yaktube-mobile-search-active my-search-typeahead, + body.yaktube-mobile-search-active .search-container { + width: 100% !important; + max-width: 100% !important; + display: flex !important; + } + .yaktube-search-back-btn { + display: inline-flex !important; + } +} + +.yaktube-search-back-btn { + display: none; + background: transparent !important; + border: none !important; + color: #fff !important; + font-size: 20px !important; + width: 36px !important; + height: 36px !important; + border-radius: 50% !important; + align-items: center !important; + justify-content: center !important; + cursor: pointer !important; + margin-right: 8px !important; + flex-shrink: 0 !important; +}