PageSourceSearch

https://radio93portofeliz.com.br/equipe

html radio93portofeliz.com.br collected 2026-10-02 00:44:22 UTC 185,457 bytes, 4,030 lines download raw bytes

1<!DOCTYPE html>
2<html lang="pt-BR">
3
4<head>
5    <meta charset="UTF-8">
6    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
7        <title>Nossa Equipe - Rádio 93 FM — Rádio 93 FM</title>
8    <meta name="description"  content="A primeira em Porto Feliz!">
9    <meta name="keywords" content="radio, online, musica, streaming">
10            <meta name="robots"       content="index, follow">
11    
12        <link rel="canonical" href="https://radio93portofeliz.com.br/equipe">
13    
14    <!-- Open Graph (WhatsApp, Facebook, Telegram, LinkedIn, Discord, Teams, iMessage) -->
15    <meta property="og:type"        content="website">
16    <meta property="og:title"       content="Nossa Equipe - Rádio 93 FM">
17    <meta property="og:description" content="A primeira em Porto Feliz!">
18    <meta property="og:url"         content="https://radio93portofeliz.com.br/equipe">
19    <meta property="og:site_name"   content="Rádio 93 FM">
20    <meta property="og:locale"      content="pt_BR">
21    <meta property="og:image"       content="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
22    <meta property="og:image:secure_url" content="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
23    <meta property="og:image:alt"   content="Nossa Equipe - Rádio 93 FM">
24    <meta property="og:image:width"  content="512">
25    <meta property="og:image:height" content="512">
26    <meta property="og:image:type"   content="image/png">
27    
28    <!-- Twitter / X -->
29    <meta name="twitter:card"        content="summary_large_image">
30    <meta name="twitter:title"       content="Nossa Equipe - Rádio 93 FM">
31    <meta name="twitter:description" content="A primeira em Porto Feliz!">
32    <meta name="twitter:image"       content="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
33    <meta name="twitter:image:alt"   content="Nossa Equipe - Rádio 93 FM">
34
35    <!-- LCP Preload (Logo) com WebP fallback -->
36                    <link rel="preload" as="image" href="https://radio93portofeliz.com.br/uploads/webp/media/6a05dc8e13c73_1778769038.webp">
37    
38    <link rel="stylesheet" href="https://radio93portofeliz.com.br/themes/neon/style.css?v=1790901861">
39
40
41    <!-- Custom CSS (Admin) -->
42    
43    <!-- External Fonts (Google Fonts) -->
44    <link rel="preconnect" href="https://fonts.googleapis.com">
45    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
46    <link
47        href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@400;500;700&family=Roboto:ital,wght@0,300;0,400;0,500;0,700;1,400&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,700;1,9..40,400&display=swap"
48        rel="stylesheet">
49    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
50
51    <!-- Lucide Icons (Ajuste 8 — ícones modernos) -->
52    
52<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js" defer></script>
52
53
54    <!-- Global CSS — Seções e Cookie Banner (Ajustes 6 e 3) -->
55    <link rel="stylesheet" href="https://radio93portofeliz.com.br/assets/css/section-header.css?v=1790901861">
56    <link rel="stylesheet" href="https://radio93portofeliz.com.br/assets/css/cookie-banner.css?v=1790901861">
57    <!-- Global Slider CSS — carregado para todos os temas -->
58    <link rel="stylesheet" href="https://radio93portofeliz.com.br/assets/css/slider.css?v=1784221298">
59    <!-- Top Músicas: marquee de título/artista. Depois do CSS de tema de
60         propósito — precisa vencer o `text-overflow: ellipsis` dos temas. -->
61    <link rel="stylesheet" href="https://radio93portofeliz.com.br/assets/css/chart-marquee.css?v=1790785403">
62        <!-- App Rádio 3.0: camada de segurança escopada em html.radio30-app. Carregada
63         sempre, porque a classe também é injetada pelo próprio app — cobre o HTML
64         servido sem a detecção no servidor (cache, service worker). -->
65    <link rel="stylesheet" href="https://radio93portofeliz.com.br/assets/css/radio30-app.css?v=1790785403">
66    
67    <!-- Favicon -->
68            <link rel="icon" type="image/png" sizes="32x32" href="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
69        <link rel="shortcut icon" href="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
70    
71    <!-- PWA Meta Tags -->
72    <link rel="manifest" href="/?manifest=1">
73        <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
74    <meta name="theme-color" content="#000000"  media="(prefers-color-scheme: dark)">
75        <meta name="theme-color" content="#ffffff">
76    
77    <meta name="mobile-web-app-capable" content="yes">
78    <meta name="apple-mobile-web-app-capable" content="yes">
79    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
80    <meta name="apple-mobile-web-app-title" content="Rádio 93 FM">
81
82    <!-- Apple Touch Icons — usa o ícone de Configurações Gerais; PWA como fallback -->
83        <link rel="apple-touch-icon" href="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
84    <link rel="apple-touch-icon" sizes="152x152" href="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
85    <link rel="apple-touch-icon" sizes="180x180" href="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
86    <link rel="apple-touch-icon" sizes="167x167" href="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png">
87    
88    
88<script>
89    const SITE_URL = "https://radio93portofeliz.com.br";
90    const STREAM_URL = ""; /* carregado via /api/stream-config.php */
91    const SITE_NAME = "Rádio 93 FM";
92    const PLAYER_DEFAULT_IMAGE = "https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png";
93</script>
93
94
95<!-- Font Awesome -->
96<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
97<!-- Material Icons + Symbols (Stitch uses Material Symbols Outlined) -->
98<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
99<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,[email protected],0..1&display=swap" rel="stylesheet">
100<!-- Space Grotesk — Fonte principal do design Stitch NEON -->
101<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet">
102
103<link rel="preconnect" href="https://fonts.googleapis.com">
104<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
105<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Poppins:wght@300;400;500;600;700;800&family=Space+Grotesk:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
106    <!-- Dynamic Theme CSS — carregado APÓS o head.php do tema para garantir que o admin sempre prevaleça -->
107    <style>
108        @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
109
110            :root {
111                --primary-color: #ff9500;
112                --secondary-color: #234783;
113                --tertiary-color: #234783;
114                --accent-color: #ff00ea;
115                --primary-rgb: 255, 149, 0;
116                --secondary-rgb: 35, 71, 131;
117                --tertiary-rgb: 35, 71, 131;
118                --accent-rgb: 255, 0, 234;
119                /* Aliases semânticos */
120                --color-primary: #ff9500;
121                --color-secondary: #234783;
122                --color-tertiary: #234783;
123            }
124        
125            :root {
126                /* Light Theme - Background Colors */
127                --bg-primary: #ffffff;
128                --bg-primary-rgb: 255, 255, 255;
129                --bg-secondary: #f5f5f7;
130                --bg-secondary-rgb: 245, 245, 247;
131                --bg-tertiary: #e9ecef;
132                --bg-card: #ffffff;
133                --bg-card-rgb: 255, 255, 255;
134                --bg-card-hover: #f8f9fa;
135                --bg-input: #ffffff;
136
137                /* === Header/Nav background (sincronizado com theme_color do PWA) === */
138                --site-header-bg: #ffffff;
139                --header-bg: var(--site-header-bg);
140
141                --glass-bg: rgba(255, 255, 255, 0.95);
142                --glass-border: rgba(0, 0, 0, 0.08);
143                --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
144
145                --text-primary: #1a1a2e;
146                --text-primary-rgb: 26, 26, 46;
147                --text-secondary: #4a4a5a;
148                --text-muted: #6c757d;
149
150                --border-color: rgba(0, 0, 0, 0.08);
151                --border-light: rgba(0, 0, 0, 0.05);
152
153                --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
154                --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08);
155                --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.1);
156                --shadow-glow: 0 0 30px rgba(255, 149, 0, 0.12);
157
158                --gradient-dark: linear-gradient(180deg, #ffffff 0%, #f5f5f7 100%);
159
160                /* === WAVE Brutalism — Borda/Sombra (Light) === */
161                --brut-border-color: #000000;
162                --brut-border-light: #333333;
163                --brut-shadow-color:  #000000;
164                --brut-shadow-hover:  #000000;
165            }
166            
167            :root {
168                --nav-font-family: 'Inter', sans-serif;
169                --nav-font-size: 14px;
170                --nav-font-weight: normal;
171                --nav-font-style: normal;
172                --nav-color: #111111;
173            }
174        
175                .nav, .nav-link, .header nav, .header .nav-link, nav a,
176                #mainNav a, #mainNav a.nav-link {
177                    font-family: var(--nav-font-family);
178                    font-size: var(--nav-font-size);
179                    font-weight: var(--nav-font-weight);
180                    font-style: var(--nav-font-style);
181                    color: var(--nav-color);
182                }
183            
184            :root {
185                --page-title-font-family: 'Inter', sans-serif;
186                --page-title-font-weight: bold;
187                --page-title-font-style: normal;
188                --page-title-color: #1a1a1a;
189                --page-title-font-size-base: 39px;
190
191                /* Aliases legados — mantém compatibilidade com head.php dos temas */
192                --title-font-family: 'Inter', sans-serif;
193                --title-font-weight: bold;
194                --title-font-style: normal;
195                --title-color: #1a1a1a;
196                --title-font-size-base: 39px;
197            }
198        
199                /* === FONTE DOS TÍTULOS DAS PÁGINAS (somente front-end) === */
200                body h1, body h2, body h3, body h4, body h5, body h6,
201                body .section-title, body .page-title, body .pd-page-hero__title,
202                /* Single pages — títulos principais de página */
203                body .ns-title,
204                body .pr-title,
205                body .ep-title,
206                body .on-air-title,
207                body .page-hero__title {
208                    font-family: var(--page-title-font-family) !important;
209                    font-weight: var(--page-title-font-weight) !important;
210                    font-style: var(--page-title-font-style) !important;
211                    color: var(--page-title-color) !important;
212                }
213                body h1, body .ns-title, body .pr-title, body .ep-title, body .on-air-title, body .page-hero__title { font-size: var(--page-title-font-size-base) !important; }
214                body h2 { font-size: calc(var(--page-title-font-size-base) * 0.85) !important; }
215                body h3 { font-size: calc(var(--page-title-font-size-base) * 0.7) !important; }
216                body h4, body h5, body h6 { font-size: calc(var(--page-title-font-size-base) * 0.6) !important; }
217            
218            :root {
219                --card-title-font-family: 'Inter', sans-serif;
220                --card-title-font-weight: bold;
221                --card-title-font-style: normal;
222                --card-title-color: #222222;
223                --card-title-font-size: 20px;
224            }
225        
226                /* === FONTE DOS CARDS (home + páginas internas + single pages) (somente front-end) === */
227                /* ── Notícias ── */
228                body .hn-card__title, body .hn-hero__title, body .d5-news-card__title,
229                body .d5-news-featured__title, body .s-news-card__title, body .news-title,
230                /* ── Sidebar de notícia single ── */
231                body .ns-sidebar-card-title, body .ns-sidebar-title,
232                body .pulse-sidebar-card-title, body .pulse-sidebar-title,
233                /* ── Eventos ── */
234                body .d5-event-card__title, body .s-event-card__title, body .event-title,
235                /* ── Promoções ── */
236                body .d5-promo-card__title, body .s-promo-card__title, body .promo-title,
237                /* ── Podcasts / Episódios ── */
238                body .pod-card__title, body .sp-title, body .ep-sidebar-title, body .ep-more-title, body .podcast-title,
239                /* ── Programação / Shows ── */
240                body .d5-prog-card__name, body .schedule-name, body .on-air-name, body .spsc-name, body .s-show-card__name,
241                /* ── Equipe ── */
242                body .team-name, body .team-member-name,
243                /* ── Listagens ── */
244                body .s-section-title,
245                /* ── Genéricos ── */
246                body .card-title, body .font-display {
247                    font-family: var(--card-title-font-family) !important;
248                    font-weight: var(--card-title-font-weight) !important;
249                    font-style: var(--card-title-font-style) !important;
250                    color: var(--card-title-color) !important;
251                    font-size: var(--card-title-font-size) !important;
252                }
253            
254            :root {
255                --body-font-family: 'Poppins', sans-serif;
256                --body-font-weight: normal;
257                --body-font-style: normal;
258                --body-font-color: #333333;
259                --body-font-size: 15px;
260            }
261        
262                /* ── Parágrafos e listas genéricas (somente front-end) ── */
263                p:not(.font-preview), li {
264                    font-family: var(--body-font-family) !important;
265                    font-weight: var(--body-font-weight) !important;
266                    font-style: var(--body-font-style) !important;
267                    font-size: var(--body-font-size) !important;
268                    color: var(--body-font-color) !important;
269                }
270
271                /* ── Single de Notícia (ns-*) ── */
272                .ns-body-content, .ns-body-content p, .ns-body-content li, .ns-lead {
273                    font-family: var(--body-font-family) !important;
274                    font-weight: var(--body-font-weight) !important;
275                    font-style: var(--body-font-style) !important;
276                    font-size: var(--body-font-size) !important;
277                    color: var(--body-font-color) !important;
278                }
279
280                /* ── Single de Evento (ev-*) ── */
281                .ev-description, .ev-description p, .ev-description li {
282                    font-family: var(--body-font-family) !important;
283                    font-weight: var(--body-font-weight) !important;
284                    font-style: var(--body-font-style) !important;
285                    font-size: var(--body-font-size) !important;
286                    color: var(--body-font-color) !important;
287                }
288
289                /* ── Single de Promoção (pr-*) ── */
290                .pr-description, .pr-description p, .pr-description li,
291                .pr-rules-body, .pr-rules-body p, .pr-rules-body li {
292                    font-family: var(--body-font-family) !important;
293                    font-weight: var(--body-font-weight) !important;
294                    font-style: var(--body-font-style) !important;
295                    font-size: var(--body-font-size) !important;
296                    color: var(--body-font-color) !important;
297                }
298
299                /* ── Single de Podcast (ep-*) ── */
300                .ep-desc-content, .ep-desc-content p, .ep-desc-content li {
301                    font-family: var(--body-font-family) !important;
302                    font-weight: var(--body-font-weight) !important;
303                    font-style: var(--body-font-style) !important;
304                    font-size: var(--body-font-size) !important;
305                    color: var(--body-font-color) !important;
306                }
307
308                /* ── Page Builder / Páginas dinâmicas ── */
309                .bloco-texto, .bloco-texto p, .bloco-texto li,
310                .pd-compat-text, .pd-compat-rich, .pd-compat-rich p, .pd-compat-rich li,
311                .pd-page-content p, .pd-page-content li {
312                    font-family: var(--body-font-family) !important;
313                    font-weight: var(--body-font-weight) !important;
314                    font-style: var(--body-font-style) !important;
315                    font-size: var(--body-font-size) !important;
316                    color: var(--body-font-color) !important;
317                }
318
319                /* ── Cards e seções da Home ── */
320                .card-description, .card-text, .card-excerpt,
321                .news-excerpt, .news-description,
322                .event-description, .event-excerpt,
323                .team-description, .team-bio,
324                .promo-description, .promotion-description,
325                .podcast-description, .podcast-excerpt,
326                .section-description, .section-text,
327                .about-text, .about-description,
328                .hero-description, .hero-text,
329                .slider-description, .slide-text {
330                    font-family: var(--body-font-family) !important;
331                    font-weight: var(--body-font-weight) !important;
332                    font-style: var(--body-font-style) !important;
333                    font-size: var(--body-font-size) !important;
334                    color: var(--body-font-color) !important;
335                }
336            
337            :root {
338                --player-bg: linear-gradient(135deg, #ff9500 0%, #ff5900 100%);
339                --player-text-color: #ffffff;
340            }
341
342            /* =========================================================
343               GLOBAL PLAYER CONTROLS (SOLID ROUND BUTTONS)
344               ========================================================= */
345            /* Garante herança da cor base do texto no container */
346            #player, .d5-player, .neon-player {
347                color: var(--player-text-color, #ffffff) !important;
348            }
349
350            /* Textos padronizados */
351            #playerArtist, #playerTitle, #playerStatusText, .live-text, .neon-player__title, .neon-player__artist,
352            .d5-player-title, .d5-player-artist {
353                color: var(--player-text-color, #ffffff) !important;
354                -webkit-text-stroke: 0 !important;
355                text-shadow: none !important;
356            }
357            .neon-player__artist, .d5-player-artist {
358                opacity: 0.8 !important;
359            }
360
361            /* 
362               BOTÕES SÓLIDOS (Exemplo Anexo)
363               O fundo do botão herda a Cor dos Elementos.
364               O ícone corta a cor, usando a cor de fundo original do player (ou escala de cinza/preta).
365            */
366            #playBtn, #liveBtn, #videoBtn, #volumeBtn,
367            .d5-play-btn, .neon-player__play-btn, .neon-player__live-btn, .neon-player__vol-btn {
368                background-color: var(--player-text-color, #ffffff) !important;
369                border: none !important;
370                box-shadow: 0 4px 10px rgba(0,0,0,0.15) !important;
371                display: flex !important;
372                align-items: center !important;
373                justify-content: center !important;
374                border-radius: 50% !important; /* Sempre redondo */
375            }
376
377            /* Ajuste de Preenchimento para garantir que todos os botões fiquem proporcionais */
378            #liveBtn, #videoBtn, #volumeBtn, .neon-player__live-btn, .neon-player__vol-btn {
379                width: 36px !important;
380                height: 36px !important;
381                min-width: 36px !important;
382            }
383            #playBtn, .d5-play-btn, .neon-player__play-btn {
384                width: 48px !important;
385                height: 48px !important;
386            }
387
388            /* Ícones dentro dos botões (Cor contrastante) */
389            #playBtn i, #liveBtn i, #volumeBtn i, #videoBtn i, .d5-vol-icon,
390            #playBtn svg, #liveBtn svg, #volumeBtn svg, #videoBtn svg,
391            #svgPlay, #svgPause,
392            .d5-play-btn i, .d5-play-btn svg, .d5-vol-icon svg,
393            .neon-player__play-btn svg, .neon-player__controls button svg, .neon-player__vol-btn svg,
394            .neon-player__controls button i {
395                color: var(--player-bg, #222) !important;
396                fill: var(--player-bg, #222) !important;
397                filter: none !important;
398                -webkit-text-stroke: 0 !important;
399                text-shadow: none !important;
400                /* Adicionando um mixin de preto para garantir contraste na inversão */
401                color: color-mix(in srgb, var(--player-bg, #222) 80%, black) !important;
402                fill: color-mix(in srgb, var(--player-bg, #222) 80%, black) !important;
403            }
404            /* Exceção para o spinner de loading preservar stroke e não ter fill */
405            #svgLoading {
406                color: color-mix(in srgb, var(--player-bg, #222) 80%, black) !important;
407                fill: none !important;
408            }
409
410            /* Remove margens estranhas dos ícones centralizados que alguns SVGs tem por padrão */
411            .neon-player__play-btn svg, .d5-play-btn i {
412                margin: 0 !important;
413                transform: translateX(2px); /* Ajuste visual do triângulo de play ficar centralizado visualmente */
414            }
415            .neon-player__controls button svg {
416                transform: none;
417            }
418
419            /* Trilho do Slider de Volume */
420            .d5-volume-slider, .neon-player__vol-slider {
421                background: color-mix(in srgb, var(--player-text-color, #ffffff) 20%, transparent) !important;
422            }
423
424            /* Controle de Volume: Destaque na bolinha (Thumb) combinando com o botão sólido */
425            #volumeSlider::-webkit-slider-thumb, 
426            .d5-volume-slider::-webkit-slider-thumb,
427            .neon-player__vol-slider::-webkit-slider-thumb {
428                background: var(--player-text-color, #ffffff) !important;
429                box-shadow: 0 2px 5px rgba(0,0,0,0.3) !important;
430                border: none !important;
431            }
432            #volumeSlider::-moz-range-thumb, 
433            .d5-volume-slider::-moz-range-thumb,
434            .neon-player__vol-slider::-moz-range-thumb {
435                background: var(--player-text-color, #ffffff) !important;
436                box-shadow: 0 2px 5px rgba(0,0,0,0.3) !important;
437                border: none !important;
438            }
439
440            /* Elementos secundários (Badges informativos) */
441            #playerArtwork, .d5-player-artwork {
442                background-color: color-mix(in srgb, var(--player-text-color, #ffffff) 10%, transparent) !important;
443                border: 1px solid color-mix(in srgb, var(--player-text-color, #ffffff) 15%, transparent) !important;
444                border-radius: 8px !important;
445            }
446            .live-badge {
447                background-color: color-mix(in srgb, var(--player-text-color, #ffffff) 15%, transparent) !important;
448                border: 1px solid color-mix(in srgb, var(--player-text-color, #ffffff) 30%, transparent) !important;
449                color: var(--player-text-color, #ffffff) !important;
450            }
451            .live-dot, .neon-player__live-dot {
452                background-color: var(--player-text-color, #ffffff) !important;
453            }
454        
455            /* =========================================================
456               PADRONIZAÇÃO TIPOGRÁFICA MOBILE (FIXED SIZES)
457               As definições abaixo forçam tamanhos fixos no mobile,
458               ignorando os tamanhos definidos pelo painel para desktop.
459               Escala baseada em boas práticas para UI Mobile.
460               ========================================================= */
461            @media (max-width: 768px) {
462                :root {
463                    --page-title-font-size-base: 24px !important;
464                    --card-title-font-size: 18px !important;
465                    --body-font-size: 14px !important;
466                    --nav-font-size: 16px !important;
467                }
468
469                /* Título Principal (H1) */
470                body h1, body .ns-title, body .pr-title, body .ep-title, body .on-air-title, body .page-hero__title { 
471                    font-size: 24px !important; 
472                    line-height: 1.4 !important;
473                }
474
475                /* Título Secundário (H2) */
476                body h2, body .section-title { 
477                    font-size: 20px !important; 
478                    line-height: 1.4 !important;
479                }
480
481                /* Título Terciário (H3) / Cards Principais */
482                body h3, 
483                body .hn-card__title, body .hn-hero__title, body .d5-news-card__title,
484                body .d5-news-featured__title, body .s-news-card__title, body .news-title,
485                body .ns-sidebar-card-title, body .ns-sidebar-title,
486                body .pulse-sidebar-card-title, body .pulse-sidebar-title,
487                body .d5-event-card__title, body .s-event-card__title, body .event-title,
488                body .d5-promo-card__title, body .s-promo-card__title, body .promo-title,
489                body .pod-card__title, body .sp-title, body .ep-sidebar-title, body .ep-more-title, body .podcast-title,
490                body .d5-prog-card__name, body .schedule-name, body .on-air-name, body .spsc-name, body .s-show-card__name,
491                body .tea
491m-name, body .team-member-name,
492                body .s-section-title,
493                body .card-title, body .font-display,
494                body .neon-news-header__title {
495                    font-size: 18px !important;
496                    line-height: 1.4 !important;
497                }
498
499                /* Subtítulo (H4-H6 / Nav) */
500                body h4, body h5, body h6,
501                body .nav, body .nav-link, body .header nav, body .header .nav-link, body nav a,
502                body #mainNav a, body #mainNav a.nav-link { 
503                    font-size: 16px !important; 
504                    line-height: 1.4 !important;
505                }
506
507                /* Corpo do Texto (Body) */
508                body p:not(.font-preview), body li,
509                body .ns-body-content, body .ns-body-content p, body .ns-body-content li, body .ns-lead,
510                body .ev-description, body .ev-description p, body .ev-description li,
511                body .pr-description, body .pr-description p, body .pr-description li,
512                body .pr-rules-body, body .pr-rules-body p, body .pr-rules-body li,
513                body .ep-desc-content, body .ep-desc-content p, body .ep-desc-content li,
514                body .bloco-texto, body .bloco-texto p, body .bloco-texto li,
515                body .pd-compat-text, body .pd-compat-rich, body .pd-compat-rich p, body .pd-compat-rich li,
516                body .pd-page-content p, body .pd-page-content li {
517                    font-size: 14px !important;
518                    line-height: 1.6 !important;
519                }
520
521                /* Descrições / Texto Auxiliar */
522                body .card-description, body .card-text, body .card-excerpt,
523                body .news-excerpt, body .news-description,
524                body .event-description, body .event-excerpt,
525                body .team-description, body .team-bio,
526                body .promo-description, body .promotion-description,
527                body .podcast-description, body .podcast-excerpt,
528                body .section-description, body .section-text,
529                body .about-text, body .about-description,
530                body .hero-description, body .hero-text,
531                body .slider-description, body .slide-text,
532                body .neon-slide__desc {
533                    font-size: 12px !important;
534                    line-height: 1.5 !important;
535                }
536
537                /* Legendas / Caption */
538                body figcaption, body .caption, body .date-badge, body .d5-news-date, body .category-badge, 
539                body .neon-news-header__label span, body .neon-news-header__label {
540                    font-size: 11px !important;
541                    line-height: 1.4 !important;
542                }
543            }
544            
545/* === CONTROLE DE TAMANHO DO LOGOTIPO === */
546@media (min-width: 769px) {
547    .site-logo img, .navbar-brand img, .navbar-logo img, header .logo img, nav .logo img, header img[src*='uploads/media'], header img[src*='uploads/logo'], header a[href='./'] > img, .wave-header__logo img, .pulse-header__logo img, .neon-header__logo img, .header-logo img {
548        width: 110px !important;
549        max-width: 100% !important;
550        height: auto !important;
551        object-fit: contain !important;
552        display: block !important;
553        transition: width 0.2s, height 0.2s !important;
554    }
555    header, .site-header, nav.navbar, .navbar, .navbar-inner, .header-inner, .wave-header, .pulse-header, .neon-header {
556        height: auto !important;
557        min-height: 65px !important;
558        padding-top: 16px !important;
559        padding-bottom: 16px !important;
560        display: flex !important;
561        align-items: center !important;
562        box-sizing: border-box !important;
563    }
564}
565@media (max-width: 768px) {
566    .site-logo img, .navbar-brand img, .navbar-logo img, header .logo img, nav .logo img, header img[src*='uploads/media'], header img[src*='uploads/logo'], header a[href='./'] > img, .wave-header__logo img, .pulse-header__logo img, .neon-header__logo img, .header-logo img, #mobileNav img[src*='uploads/media'], #mobileNav img[src*='uploads/logo'], #mobileNav a[href='./'] > img, [data-ag-drawer] img[src*='uploads/media'], [data-ag-drawer] img[src*='uploads/logo'], [data-ag-drawer] a[href='./'] > img, .wave-mobile__logo img, .pulse-mobile__logo img, .neon-mobile__logo img, .mobile-logo img {
567        width: 50px !important;
568        max-width: 100% !important;
569        height: auto !important;
570        object-fit: contain !important;
571        display: block !important;
572        transition: width 0.2s, height 0.2s !important;
573    }
574    header, .site-header, nav.navbar, .navbar, .navbar-inner, .header-inner, .wave-header, .pulse-header, .neon-header {
575        height: auto !important;
576        min-height: 39px !important;
577        padding-top: 12px !important;
578        padding-bottom: 12px !important;
579    }
580}
581.navbar-brand, .site-logo, nav .logo, .wave-header__logo, .pulse-header__logo, .neon-header__logo, .header-logo {
582    display: flex !important;
583    align-items: center !important;
584    overflow: visible !important;
585    flex-shrink: 0 !important;
586    height: auto !important;
587}
588    </style>
589
590    <!-- Google Analytics -->
591        
592    <!-- Google Search Console -->
593        
594    <!-- Código Personalizado — <head> (raw, escopo do tenant; ver /?page=integrations) -->
595        </head>
596
597<body class="theme-light">
598
599
600    <!-- Código Personalizado — início do <body> (raw, escopo do tenant) -->
601        
602    
603<header class="neon-header" id="header">
604    <div class="neon-header__inner">
605        <!-- Logo -->
606        <a href="./" class="neon-header__logo">
607                            <img src="https://radio93portofeliz.com.br/uploads/media/6a05e3548b2fc_1778770772.png"
608                     alt="Rádio 93 FM">
609                    </a>
610
611        <!-- Desktop Nav -->
612                <nav class="neon-nav" id="mainNav">
613                            <a href="https://radio93portofeliz.com.br/"                   class="neon-nav__link ">
614                    Início                </a>
615                            <a href="a-radio"                   class="neon-nav__link ">
616                    A rádio                </a>
617                            <a href="https://www.youtube.com/radio93portofeliz"                   class="neon-nav__link ">
618                    Estúdio                </a>
619                            <a href="equipe"                   class="neon-nav__link neon-nav__link--active">
620                    Equipe                </a>
621                            <a href="programacao"                   class="neon-nav__link ">
622                    Programação                </a>
623                            <a href="promocoes"                   class="neon-nav__link ">
624                    Promoções                </a>
625                            <a href="contato"                   class="neon-nav__link ">
626                    Contato                </a>
627                    </nav>
628        
629        <!-- Right Actions -->
630        <div class="neon-header__actions">
631            <!-- Seletor de Praça (multi-praça; inerte com <2 praças) -->
632            
633            <!-- On Air Widget -->
634            
635            <!-- Search -->
636            <form action="https://radio93portofeliz.com.br/" method="GET" class="neon-search" id="neonDesktopSearchForm">
637                <input type="hidden" name="page" value="busca">
638                <button type="submit" class="neon-search__btn">
639                    <!-- Lucide: search -->
640                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
641                </button>
642                <input type="text" name="q" placeholder="Buscar..." class="neon-search__input" id="neonDesktopSearchInput" autocomplete="off">
643                <div class="neon-search-spinner" style="display: none;">
644                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewB
644ox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="spin"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
645                </div>
646                <!-- Resultado Desktop -->
647                <div class="neon-live-search-results" id="neonDesktopSearchDropdown"></div>
648            </form>
649
650        </div>
651
652        <!-- NEW: Mobile Actions (App Style Spotify) -->
653        <div class="neon-header__mobile-actions">
654            <!-- Mobile Toggle (drawer) -->
655            <button type="button" class="neon-header__mobile-btn neon-header__mobile-action" id="neonMobileBtn" data-ag-drawer-toggle aria-label="Abrir menu" aria-expanded="false" aria-controls="neonMobile">
656                <!-- Lucide: menu -->
657                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>
658            </button>
659                        <a href="https://wa.me/5515996090935" target="_blank" rel="noopener" class="neon-header__mobile-action header-icon-whatsapp" aria-label="WhatsApp" style="display: flex; align-items: center;">
660                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" style="width: 22px; height: 22px; min-width: 22px; min-height: 22px;">
661                    <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
662                </svg>
663            </a>
664                        <button type="button" class="neon-header__mobile-action" onclick="NeonMobileSearch.open()" aria-label="Buscar" style="display: flex; align-items: center;">
665                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 22px; height: 22px; min-width: 22px; min-height: 22px;"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
666            </button>
667        </div>
668    </div>
669</header>
670
671<!-- Mobile Expandable Search -->
672<div class="neon-mobile-search-overlay" id="neonMobileSearchOverlay">
673    <div class="neon-mobile-search-dialog">
674        <div class="neon-mobile-search-topbar">
675            <h3 class="neon-mobile-search-title">Pesquisar</h3>
676            <button type="button" class="neon-mobile-search-close" onclick="NeonMobileSearch.close()" aria-label="Fechar Busca">
677                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
678            </button>
679        </div>
680        <form action="https://radio93portofeliz.com.br/" method="GET" class="neon-mobile-search-form" id="neonMobileSearchForm">
681            <input type="hidden" name="page" value="busca">
682            <button type="submit" class="neon-mobile-search-submit">
683                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
684            </button>
685            <input type="text" name="q" placeholder="O que você quer ouvir?" class="neon-mobile-search-input" id="neonMobileSearchInput" autocomplete="off">
686            <div class="neon-search-spinner" id="neonMobileSearchSpinner" style="display: none;">
687                <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="spin"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
688            </div>
689        </form>
690        <!-- Resultado Mobile -->
691        <div class="neon-mobile-live-results" id="neonMobileSearchDropdown"></div>
692    </div>
693</div>
694
695<!-- Menu Mobile Drawer (partial compartilhado — markup+JS únicos) -->
696
697<!-- Backdrop / Overlay do Drawer -->
698<div class="neon-mobile-overlay" id="neonOverlay" data-ag-drawer-overlay></div>
699
700<!-- Drawer / Painel Lateral -->
701<aside class="neon-mobile" id="neonMobile" data-ag-drawer aria-hidden="true">
702    <div class="neon-mobile__header">
703        <div class="neon-mobile__logo">
704                            <img src="https://radio93portofeliz.com.br/uploads/media/6a05e3548b2fc_1778770772.png"
705                     alt="Rádio 93 FM">
706                    </div>
707        <button type="button" class="neon-mobile__close" data-ag-drawer-close aria-label="Fechar menu">
708            <!-- Lucide: x -->
709            <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
710        </button>
711    </div>
712
713    
714    
715    <nav class="neon-mobile__nav">
716                    <a href="https://radio93portofeliz.com.br/" data-ag-drawer-link
717               class="neon-mobile__link ">
718                Início                <!-- Lucide: chevron-right -->
719                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewB
719ox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
720            </a>
721                    <a href="a-radio" data-ag-drawer-link
722               class="neon-mobile__link ">
723                A rádio                <!-- Lucide: chevron-right -->
724                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
725            </a>
726                    <a href="https://www.youtube.com/radio93portofeliz" data-ag-drawer-link
727               class="neon-mobile__link ">
728                Estúdio                <!-- Lucide: chevron-right -->
729                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
730            </a>
731                    <a href="equipe" data-ag-drawer-link
732               class="neon-mobile__link neon-mobile__link--active">
733                Equipe                <!-- Lucide: chevron-right -->
734                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
735            </a>
736                    <a href="programacao" data-ag-drawer-link
737               class="neon-mobile__link ">
738                Programação                <!-- Lucide: chevron-right -->
739                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
740            </a>
741                    <a href="promocoes" data-ag-drawer-link
742               class="neon-mobile__link ">
743                Promoções                <!-- Lucide: chevron-right -->
744                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
745            </a>
746                    <a href="contato" data-ag-drawer-link
747               class="neon-mobile__link ">
748                Contato                <!-- Lucide: chevron-right -->
749                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
750            </a>
751            </nav>
752
753    <div class="neon-mobile__footer">
754        <p>Rádio 93 FM</p>
755    </div>
756</aside>
757
758<script>
759/* Controlador ÚNICO do drawer mobile (compartilhado por Pulse/Wave/Neon/Glow).
760   Como só um tema renderiza por página, há um único [data-ag-drawer]. */
761(function () {
762    if (window.AgDrawer) return;
763
764    const panel = document.querySelector('[data-ag-drawer]');
765    if (!panel) return;
766    const overlay = document.querySelector('[data-ag-drawer-overlay]');
767    const toggles = document.querySelectorAll('[data-ag-drawer-toggle]');
768    let lastFocus = null;
769
770    const AgDrawer = {
771        open() {
772            panel.classList.add('is-open');
773            if (overlay) overlay.classList.add('is-open');
774            document.body.style.overflow = 'hidden';
775            panel.setAttribute('aria-hidden', 'false');
776            toggles.forEach(t => t.setAttribute('aria-expanded', 'true'));
777            // Acessibilidade: guarda o foco anterior e move para o botão de fechar.
778            lastFocus = document.activeElement;
779            const closeBtn = panel.querySelector('[data-ag-drawer-close]');
780            if (closeBtn) closeBtn.focus();
781        },
782        close() {
783            panel.classList.remove('is-open');
784            if (overlay) overlay.classList.remove('is-open');
785            document.body.style.overflow = '';
786            panel.setAttribute('aria-hidden', 'true');
787            toggles.forEach(t => t.setAttribute('aria-expanded', 'false'));
788            // Devolve o foco a quem abriu o drawer.
789            if (lastFocus && typeof lastFocus.focus === 'function') lastFocus.focus();
790        },
791        toggle() {
792            panel.classList.contains('is-open') ? this.close() : this.open();
793        }
794    };
795    window.AgDrawer = AgDrawer;
796
797    toggles.forEach(t => {
798        t.setAttribute('aria-expanded', 'false');
799        t.addEventListener('click', (e) => { e.preventDefault(); AgDrawer.toggle(); });
800    });
801    if (overlay) overlay.addEventListener('click', () => AgDrawer.close());
802    panel.querySelectorAll('[data-ag-drawer-close]').forEach(b => b.addEventListener('click', () => AgDrawer.close()));
803    panel.querySelectorAll('[data-ag-drawer-link]').forEach(a => a.addEventListener('click', () => AgDrawer.close()));
804    document.addEventListener('keydown', (e) => {
805        if (e.key === 'Escape' && panel.classList.contains('is-open')) AgDrawer.close();
806    });
807})();
808</script>
808
809
810<!-- Header spacer removido: #mainContent já tem padding-top:72px via style.css -->
811
812<script>
813// ── Active nav state sync (JS) ──────────────────────────────────────────
814function syncNeonNavActive() {
815    const raw = window.location.pathname.replace(/^\//, '').replace(/\/$/, '');
816    const currentPage = raw || 'home';
817    const normalizeSlug = (s) => s.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9-]/g, '');
818    const getSlug = (href) => {
819        try {
820            const url = new URL(href, window.location.origin);
821            const sp = url.searchParams.get('page');
822            if (sp) return sp;
823            const p = url.pathname.replace(/^\//, '').replace(/\/$/, '');
824            return p ? p.split('/').pop() : 'home';
825        } catch(e) { return href.replace(/^\//, '').replace(/\/$/, '') || 'home'; }
826    };
827    const pn = normalizeSlug(currentPage);
828    // Menu de ícones: o componente mantém o próprio sync (.is-active no <li> +
829    // aria-current no link). Menu de texto: a lista abaixo fica vazia quando o
830    // estilo de ícones está ativo, então as duas linhas convivem.
831    if (typeof window.agEicSyncActive === 'function') window.agEicSyncActive();
832    document.querySelectorAll('.neon-nav__link').forEach(l => l.classList.toggle('neon-nav__link--active', normalizeSlug(getSlug(l.getAttribute('href')||'')) === pn));
833    document.querySelectorAll('.neon-mobile__link').forEach(l => l.classList.toggle('neon-mobile__link--active', normalizeSlug(getSlug(l.getAttribute('href')||'')) === pn));
834}
835syncNeonNavActive();
836window.addEventListener('pageshow', (e) => { if (e.persisted) syncNeonNavActive(); });
837window.addEventListener('popstate', syncNeonNavActive);
838// ────────────────────────────────────────────────────────────────────────
839
840// ESC fecha a busca mobile. O drawer é controlado pelo AgDrawer (partial compartilhado).
841document.addEventListener('keydown', e => {
842    if (e.key === 'Escape' && typeof NeonMobileSearch !== 'undefined') NeonMobileSearch.close();
843});
844
845const NeonMobileSearch = {
846    open() {
847        document.getElementById('neonMobileSearchOverlay').classList.add('is-active');
848        document.body.style.overflow = 'hidden';
849        setTimeout(() => {
850            const input = document.getElementById('neonMobileSearchInput');
851            if(input) input.focus();
852        }, 300);
853    },
854    close() {
855        document.getElementById('neonMobileSearchOverlay').classList.remove('is-active');
856        document.body.style.overflow = '';
857    },
858    toggle() {
859        document.getElementById('neonMobileSearchOverlay').classList.contains('is-active') ? this.close() : this.open();
860    }
861};
862</script>
862
863    <!-- Page Content -->
864    <div id="mainContent">
865                
866
867<style>
868/* ──────────────────────────────────────────
869   PAGE HEADER — Padrão global de listagens
870   Usa CSS variables para herdar light/dark mode automaticamente
871   ────────────────────────────────────────── */
872.page-header {
873    padding: 32px 0 16px;
874    border-bottom: 1px solid var(--border-color);
875    margin-bottom: 24px;
876    max-width: 1200px;
877    margin-left: auto;
878    margin-right: auto;
879    padding-left: 40px;
880    padding-right: 40px;
881}
882
883.page-header .breadcrumb {
884    display: flex;
885    align-items: center;
886    gap: 6px;
887    font-size: 12px;
888    color: var(--text-muted);
889    margin-bottom: 16px;
890    opacity: 0.75;
891}
892
893/* "Início" — apagado, página pai */
894.page-header .breadcrumb a {
895    color: var(--text-muted);
896    text-decoration: none;
897    transition: color 0.15s;
898    opacity: 0.7;
899}
900
901.page-header .breadcrumb a:hover {
902    color: var(--text-secondary);
903    opacity: 1;
904}
905
906/* Separador "/" */
907.page-header .breadcrumb .bc-sep {
908    color: var(--border-color);
909    font-size: 12px;
910    user-select: none;
911}
912
913/* Página atual — destaque sutil em âmbar */
914.page-header .breadcrumb .bc-current {
915    color: var(--primary-color, #f5a623);
916    font-weight: 500;
917    opacity: 1;
918}
919
920.page-header h1 {
921    font-size: 1.6rem;
922    font-weight: 600;
923    color: var(--text-primary);
924    line-height: 1.2;
925    margin: 0;
926}
927
928.page-header .subtitle {
929    font-size: 0.95rem;
930    color: var(--text-secondary);
931    margin-top: 8px;
932    margin-bottom: 0;
933}
934
935/* Decoração lateral: barra única, levemente mais fina */
936.page-header h1::before {
937    content: '';
938    display: inline-block;
939    width: 3px;
940    height: 0.9em;
941    background: var(--primary-color);
942    border-radius: 2px;
943    margin-right: 12px;
944    vertical-align: middle;
945}
946
947@media (max-width: 768px) {
948    .page-header {
949        padding-left: 20px;
950        padding-right: 20px;
951        padding-top: 24px;
952    }
953
954    .page-header h1 {
955        font-size: 1.35rem;
956    }
957}
958</style>
959
960<div class="page-header">
961    <nav class="breadcrumb" aria-label="Breadcrumb">
962        <a href="https://radio93portofeliz.com.br/">Início</a>
963        <span class="bc-sep" aria-hidden="true">/</span>
964        <span class="bc-current">Equipe</span>
965    </nav>
966    <h1>Equipe</h1>
967    </div>
968
969
970<div class="container">
971    <div class="page-body">
972                
973                    <div class="team-grid">
974                                    <article class="team-card"
975                         data-nome="93 FM"
976                         data-cargo="93 FM"
977                         data-descricao="A primeira em todo lugar!"
978                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a0cec6dbaece_1779231853.png"
979                         data-facebook="https://www.facebook.com/radio93portofeliz/"
980                         data-instagram="https://www.instagram.com/radio93portofeliz/"
981                         data-twitter=""
982                         data-tiktok="">
983                        <!-- Capa 1920×800 -->
984                        <div class="team-cover">
985                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a0cec6dbaece_1779231853.png"
986                                    alt="93 FM">
987                                                    </div>
988
989                        <!-- Informações -->
990                        <div class="team-card__body">
991                            <h2 class="team-name">93 FM</h2>
992
993                                                            <span class="team-role">93 FM</span>
994                            
995                                                            <p class="team-bio">
996                                    A primeira em todo lugar!                                </p>
997                            
998                            <div class="team-social">
999                                                                    <sp
999an><i class="fab fa-instagram"></i> Instagram</span>
1000                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1001                            </div>
1002                        </div>
1003                    </article>
1004                                    <article class="team-card"
1005                         data-nome="Adilson Giuli"
1006                         data-cargo="Locutor"
1007                         data-descricao="Adilson comanda todos os começos de manhã com muita alegria e música sertaneja, de segunda a sexta, das 05h às 08h, além de ser no nosso narrador esportivo oficial."
1008                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a05ecb89c971_1778773176.png"
1009                         data-facebook=""
1010                         data-instagram="https://www.instagram.com/locutoradilsongiuli/"
1011                         data-twitter=""
1012                         data-tiktok="">
1013                        <!-- Capa 1920×800 -->
1014                        <div class="team-cover">
1015                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a05ecb89c971_1778773176.png"
1016                                    alt="Adilson Giuli">
1017                                                    </div>
1018
1019                        <!-- Informações -->
1020                        <div class="team-card__body">
1021                            <h2 class="team-name">Adilson Giuli</h2>
1022
1023                                                            <span class="team-role">Locutor</span>
1024                            
1025                                                            <p class="team-bio">
1026                                    Adilson comanda todos os começos de manhã com muita alegria e música sertaneja, de segunda a sexta,...                                </p>
1027                            
1028                            <div class="team-social">
1029                                                                    <span><i class="fab fa-instagram"></i> Instagram</span>
1030                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1031                            </div>
1032                        </div>
1033                    </article>
1034                                    <article class="team-card"
1035                         data-nome="Bruno Mendonça"
1036                         data-cargo="Apresentador"
1037                         data-descricao="O Bruno é integrante do Programa Corneteiros, de segunda à sexta, das 18h às 19h!!!"
1038                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a078bcf74df9_1778879439.png"
1039                         data-facebook=""
1040                         data-instagram="https://www.instagram.com/brunomendonca.oficial/"
1041                         data-twitter=""
1042                         data-tiktok="">
1043                        <!-- Capa 1920×800 -->
1044                        <div class="team-cover">
1045                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a078bcf74df9_1778879439.png"
1046                                    alt="Bruno Mendonça">
1047                                                    </div>
1048
1049                        <!-- Informações -->
1050                        <div class="team-card__body">
1051                            <h2 class="team-name">Bruno Mendonça</h2>
1052
1053                                                            <span class="team-role">Apresentador</span>
1054                            
1055                                                            <p class="team-bio">
1056                                    O Bruno é integrante do Programa Corneteiros, de segunda à sexta, das 18h às 19h!!!                                </p>
1057                            
1058                            <div class="team-social">
1059                                                                    <span><i class="fab fa-instagram"></i> Instagram</span>
1060                                                                <sp
1060an><i class="fas fa-microphone-alt"></i> Locutor</span>
1061                            </div>
1062                        </div>
1063                    </article>
1064                                    <article class="team-card"
1065                         data-nome="Douglas Torres"
1066                         data-cargo="Apresentador"
1067                         data-descricao="Apresenta o programa Corneteiros, de segunda a sexta, das 18h às 19h."
1068                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a14f99bd2c49_1779759515.png"
1069                         data-facebook=""
1070                         data-instagram="https://www.instagram.com/douglastorresradio/"
1071                         data-twitter=""
1072                         data-tiktok="">
1073                        <!-- Capa 1920×800 -->
1074                        <div class="team-cover">
1075                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a14f99bd2c49_1779759515.png"
1076                                    alt="Douglas Torres">
1077                                                    </div>
1078
1079                        <!-- Informações -->
1080                        <div class="team-card__body">
1081                            <h2 class="team-name">Douglas Torres</h2>
1082
1083                                                            <span class="team-role">Apresentador</span>
1084                            
1085                                                            <p class="team-bio">
1086                                    Apresenta o programa Corneteiros, de segunda a sexta, das 18h às 19h.                                </p>
1087                            
1088                            <div class="team-social">
1089                                                                    <span><i class="fab fa-instagram"></i> Instagram</span>
1090                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1091                            </div>
1092                        </div>
1093                    </article>
1094                                    <article class="team-card"
1095                         data-nome="Isa Nascimento"
1096                         data-cargo="Locutora"
1097                         data-descricao="A Isa comanda as nossas tardes, de segunda a sexta, a partir das 13h até às 18h, além de ser a produtora das fofocas da 93 FM."
1098                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a05f05b5b952_1778774107.png"
1099                         data-facebook=""
1100                         data-instagram="https://www.instagram.com/euaisanascimento/"
1101                         data-twitter=""
1102                         data-tiktok="">
1103                        <!-- Capa 1920×800 -->
1104                        <div class="team-cover">
1105                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a05f05b5b952_1778774107.png"
1106                                    alt="Isa Nascimento">
1107                                                    </div>
1108
1109                        <!-- Informações -->
1110                        <div class="team-card__body">
1111                            <h2 class="team-name">Isa Nascimento</h2>
1112
1113                                                            <span class="team-role">Locutora</span>
1114                            
1115                                                            <p class="team-bio">
1116                                    A Isa comanda as nossas tardes, de segunda a sexta, a partir das 13h até às 18h, além de ser a...                                </p>
1117                            
1118                            <div class="team-social">
1119                                                                    <span><i class="fab fa-instagram"></i> Instagram</span>
1120                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1121                            </div>
1122                        </div>
1123                    </article>
1124                                    <article class="team-card"
1125                         data-nome="JB"
1126                         data-cargo="Jornalista"
1127                         data-descricao="O JB é jornalista e editor do Bom Dia 93, que vai ao ar de segunda a sexta, das 9h as 11h."
1128                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a05f0a88b485_1778774184.png"
1129                         data-facebook=""
1130                         data-instagram="https://www.instagram.com/euojb/"
1131                         data-twitter=""
1132                         data-tiktok="">
1133                        <!-- Capa 1920×800 -->
1134                        <div class="team-cover">
1135                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a05f0a88b485_1778774184.png"
1136                                    alt="JB">
1137                                                    </div>
1138
1139                        <!-- Informações -->
1140                        <div class="team-card__body">
1141                            <h2 class="team-name">JB</h2>
1142
1143                                                            <span class="team-role">Jornalista</span>
1144                            
1145                                                            <p class="team-bio">
1146                                    O JB é jornalista e editor do Bom Dia 93, que vai ao ar de segunda a sexta, das 9h as 11h.                                </p>
1147                            
1148                            <div class="team-social">
1149                                                                    <span><i class="fab fa-instagram"></i> Instagram</span>
1150                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1151                            </div>
1152                        </div>
1153                    </article>
1154                                    <article class="team-card"
1155                         data-nome="Luciano Veiga"
1156                         data-cargo="Diretor Artístico"
1157                         data-descricao="O Luciano comanda o programa Corneteiros, de segunda a sexta, das 18h às 19h, além de ser o nosso diretor artístico, cuidando de tudo que você ouve aqui na 93 FM."
1158                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a14fb21c308a_1779759905.png"
1159                         data-facebook=""
1160                         data-instagram=""
1161                         data-twitter=""
1162                         data-tiktok="">
1163                        <!-- Capa 1920×800 -->
1164                        <div class="team-cover">
1165                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a14fb21c308a_1779759905.png"
1166                                    alt="Luciano Veiga">
1167                                                    </div>
1168
1169                        <!-- Informações -->
1170                        <div class="team-card__body">
1171                            <h2 class="team-name">Luciano Veiga</h2>
1172
1173                                                            <span class="team-role">Diretor Artístico</span>
1174                            
1175                                                            <p class="team-bio">
1176                                    O Luciano comanda o programa Corneteiros, de segunda a sexta, das 18h às 19h, além de ser o nosso...                                </p>
1177                            
1178                            <div class="team-social">
1179                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1180                            </div>
1181                        </div>
1182                    </article>
1183                                    <article class="team-card"
1184                         data-nome="Ricardo Ribeiro"
1185                         data-cargo="Apresentador"
1186                         data-descricao="Ricardo traz as principais notícias da manhã no “Bom Dia 93”, de segunda a sexta, das 9h às 11h."
1187                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a05f12cf386c_1778774316.png"
1188                         data-facebook=""
1189                         data-instagram="https://www.instagram.com/ricardo_inacio_ribeiro/"
1190                         data-twitter=""
1191                         data-tiktok="">
1192                        <!-- Capa 1920×800 -->
1193                        <div class="team-cover">
1194                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a05f12cf386c_1778774316.png"
1195                                    alt="Ricardo Ribeiro">
1196                                                    </div>
1197
1198                        <!-- Informações -->
1199                        <div class="team-card__body">
1200                            <h2 class="team-name">Ricardo Ribeiro</h2>
1201
1202                                                            <span class="team-role">Apresentador</span>
1203                            
1204                                                            <p class="team-bio">
1205                                    Ricardo traz as principais notícias da manhã no “Bom Dia 93”, de segunda a sexta, das 9h às 11h.                                </p>
1206                            
1207                            <div class="team-social">
1208                                                                    <sp
1208an><i class="fab fa-instagram"></i> Instagram</span>
1209                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1210                            </div>
1211                        </div>
1212                    </article>
1213                                    <article class="team-card"
1214                         data-nome="Rodrigo Ribeiro"
1215                         data-cargo="Locutor"
1216                         data-descricao="Rodrigo comanda as nossas manhãs, de segunda a sexta-feira das 08h às 13h e nos sábados das 07 às 12h."
1217                         data-foto="https://radio93portofeliz.com.br/uploads/media/6a05f165cd49f_1778774373.png"
1218                         data-facebook=""
1219                         data-instagram="https://www.instagram.com/rodrigoribeirolocutor/"
1220                         data-twitter=""
1221                         data-tiktok="">
1222                        <!-- Capa 1920×800 -->
1223                        <div class="team-cover">
1224                                                            <img src="https://radio93portofeliz.com.br/uploads/media/6a05f165cd49f_1778774373.png"
1225                                    alt="Rodrigo Ribeiro">
1226                                                    </div>
1227
1228                        <!-- Informações -->
1229                        <div class="team-card__body">
1230                            <h2 class="team-name">Rodrigo Ribeiro</h2>
1231
1232                                                            <span class="team-role">Locutor</span>
1233                            
1234                                                            <p class="team-bio">
1235                                    Rodrigo comanda as nossas manhãs, de segunda a sexta-feira das 08h às 13h e nos sábados das 07 às...                                </p>
1236                            
1237                            <div class="team-social">
1238                                                                    <span><i class="fab fa-instagram"></i> Instagram</span>
1239                                                                <span><i class="fas fa-microphone-alt"></i> Locutor</span>
1240                            </div>
1241                        </div>
1242                    </article>
1243                            </div>
1244                
1245            </div>
1246</div>
1247
1248<style>
1249    /* =============================
1250       GRID DE EQUIPE
1251    ============================= */
1252    .team-grid {
1253        display: grid;
1254        grid-template-columns: repeat(4, 1fr);
1255        gap: 24px;
1256        margin-top: 8px;
1257    }
1258
1259    @media (max-width: 1024px) {
1260        .team-grid { grid-template-columns: repeat(3, 1fr); }
1261    }
1262
1263    @media (max-width: 768px) {
1264        .team-grid { grid-template-columns: repeat(2, 1fr); gap: 16px; }
1265    }
1266
1267    @media (max-width: 480px) {
1268        .team-grid { grid-template-columns: 1fr; }
1269    }
1270
1271    /* =============================
1272       CARD DE MEMBRO
1273    ============================= */
1274    .team-card {
1275        background: var(--bg-card, var(--card-bg));
1276        border-radius: 16px;
1277        overflow: hidden;
1278        box-shadow: 0 2px 12px rgba(0, 0, 0, 0.07);
1279        transition: transform 0.2s ease, box-shadow 0.2s ease;
1280        display: flex;
1281        flex-direction: column;
1282    }
1283
1284    .team-card:hover {
1285        transform: translateY(-4px);
1286        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
1287    }
1288
1289    /* Capa 1920×800 (object-fit cover) */
1290    .team-cover {
1291        width: 100%;
1292        aspect-ratio: 1920 / 800;
1293        overflow: hidden;
1294        background: var(--bg-tertiary);
1295        flex-shrink: 0;
1296    }
1297
1298    .team-cover img {
1299        width: 100%;
1300        height: 100%;
1301        object-fit: cover;
1302        object-position: center;
1303        display: block;
1304    }
1305
1306    .team-cover-placeholder {
1307        width: 100%;
1308        height: 100%;
1309        background: linear-gradient(135deg, var(--primary-color), var(--secondary-color, var(--primary-color)));
1310        display: flex;
1311        align-items: center;
1312        justify-content: center;
1313    }
1314
1315    .team-cover-placeholder i {
1316        font-size: 2.5rem;
1317        color: rgba(255, 255, 255, 0.6);
1318    }
1319
1320    /* Corpo do card */
1321    .team-card__body {
1322        padding: 20px 16px;
1323        text-align: center;
1324        display: flex;
1325        flex-direction: column;
1326        align-items: center;
1327        flex: 1;
1328    }
1329
1330    /* Nome */
1331    .team-name {
1332        font-size: 1rem;
1333        font-weight: 700;
1334        color: var(--text-primary);
1335        margin: 0 0 6px;
1336        line-height: 1.3;
1337    }
1338
1339    /* Cargo */
1340    .team-role {
1341        font-size: 0.85rem;
1342        color: var(--cor-destaque, var(--primary-color));
1343        font-weight: 600;
1344        margin-bottom: 10px;
1345        display: block;
1346    }
1347
1348    /* Bio */
1349    .team-bio {
1350        font-size: 0.825rem;
1351        color: var(--text-secondary);
1352        line-height: 1.5;
1353        margin-bottom: 12px;
1354    }
1355
1356    /* Redes sociais */
1357    .team-social {
1358        display: flex;
1359        gap: 12px;
1360        flex-wrap: wrap;
1361        justify-content: center;
1362        font-size: 0.75rem;
1363        color: var(--text-muted);
1364        margin-top: auto;
1365    }
1366</style>
1367<script src="https://radio93portofeliz.com.br/assets/js/team-module.js?v=1790901861"></script>
1367    </div>
1368
1369    <!-- Persistent Audio Player -->
1370    <!-- Theme: NEON — Glassmorphism + Neon Glow — Audio Player -->
1371<div id="player" class="neon-player">
1372    <div class="neon-player__inner">
1373
1374        <!-- ──── GRUPO ESQUERDO: Artwork + Info + Controles + Volume ──── -->
1375        <div class="neon-player__left-group">
1376
1377            <!-- Artwork + Info -->
1378            <div class="neon-player__info">
1379                <div class="neon-player__artwork" id="playerArtwork">
1380                    <img src="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png"
1381                         alt="Album Art"
1382                         onerror="this.src = PLAYER_DEFAULT_IMAGE || 'https://radio93portofeliz.com.br/assets/img/default-cover.png'">
1383                </div>
1384                <div class="neon-player__meta">
1385                    <div class="neon-player__status" data-player-status>
1386                    </div>
1387                    <div class="neon-player__title" id="playerTitle">Carregando...</div>
1388                    <div class="neon-player__artist" id="playerArtist">Rádio 93 FM</div>
1389                </div>
1390            </div>
1391
1392            <!-- NOVOS CONTROLES (NATIVOS) -->
1393            <div class="zmp-player-controls-wrapper">
1394
1395                <!-- PLAY / PAUSE -->
1396                <button id="playBtn" class="zmp-btn zmp-play-size" title="Play/Pause">
1397                    <svg id="playIcon" class="zmp-svg zmp-show" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" style="max-width: 22px; max-height: 22px;">
1398                        <path fill="currentColor" d="M6 3.5a1.5 1.5 0 0 1 2.3-.8l11.5 8a1.5 1.5 0 0 1 0 2.6l-11.5 8A1.5 1.5 0 0 1 6 20.5v-17z"/>
1399                    </svg>
1400                </button>
1401
1402                <!-- AO VIVO -->
1403                <button id="zmpLiveBtn" class="zmp-btn zmp-aux-size" title="Voltar ao Vivo" onclick="if(window.radioPlayer) window.radioPlayer.returnToLive()">
1404                    <svg class="zmp-svg zmp-show" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="currentColor" style="max-width: 18px; max-height: 18px;">
1405                        <circle cx="12" cy="12" r="2.5" />
1406                        <path d="M7.74 16.26a6 6 0 0 1 0-8.52l1.42 1.42a4 4 0 0 0 0 5.68z" />
1407                        <path d="M16.26 16.26a6 6 0 0 0 0-8.52l-1.42 1.42a4 4 0 0 1 0 5.68z" />
1408                        <path d="M4.91 19.09a10 10 0 0 1 0-14.18l1.42 1.42a8 8 0 0 0 0 11.34z" />
1409                        <path d="M19.09 19.09a10 10 0 0 0 0-14.18l-1.42 1.42a8 8 0 0 1 0 11.34z" />
1410                    </svg>
1411                </button>
1412
1413                <!-- VOLUME -->
1414                <button id="volumeBtn" class="zmp-btn zmp-aux-size" title="Volume">
1415                    <svg id="volumeIcon" class="zmp-svg zmp-show" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
1416                        <path fill="currentColor" d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
1417                    </svg>
1418                </button>
1419                <input type="range" id="volumeSlider" class="zmp-volume-slider" min="0" max="100" value="90" />
1420            
1421            </div> <!-- /zmp-player-controls-wrapper -->
1422
1423            <!-- CSS BLINDADO DOS CONTROLES -->
1424            <style>
1425            .zmp-player-controls-wrapper {
1426                display: flex;
1427                align-items: center;
1428                gap: 16px;
1429                margin-left: 20px;
1430            }
1431            .zmp-btn {
1432                appearance: none !important;
1433                background: transparent !important;
1434                color: var(--player-text-color, #ffffff);
1435                border: none !important;
1436                border-radius: 50% !important;
1437                display: flex !important;
1438                align-items: center !important;
1439                justify-content: center !important;
1440                cursor: pointer;
1441                box-shadow: none !important;
1442                transition: opacity 0.2s ease, transform 0.15s ease;
1443                padding: 0 !important;
1444            }
1445            .zmp-btn:hover {
1446                opacity: 0.7;
1447                transform: scale(1.08);
1448            }
1449            .zmp-play-size {
1450                width: 48px !important;
1451                height: 48px !important;
1452                min-width: 48px !important;
1453            }
1454            .zmp-aux-size {
1455                width: 36px !important;
1456                height: 36px !important;
1457                min-width: 36px !important;
1458            }
1459            /* ── BLINDAGEM MÁXIMA PARA SVG E DESIGN NEON ──
1460               Garante que os controles respondam à "COR DOS ELEMENTOS" do painel,
1461               independentemente do fundo, restaurando o estilo Neon sem fills hardcoded. */
1462               
1463            /* 1. Cores e Fills dos Ícones (SVGs e FontAwesome) */
1464            html body #player #playBtn svg,
1465            html body #player #zmpLiveBtn svg,
1466            html body #player #volumeBtn svg,
1467            html body #player .zmp-svg,
1468            html body #player .zmp-btn i {
1469                display: block;
1470                color: var(--player-text-color, #ffffff) !important;
1471                fill: var(--player-text-color, #ffffff) !important;
1472                stroke: none !important;
1473                margin: 0 auto !important;
1474                pointer-events: none;
1475                width: 20px !important;
1476                height: auto !important;
1477                filter: none !important;
1478            }
1479            
1480            /* Ajuste de tamanho específico para Live e Volume */
1481            html body #player #zmpLiveBtn svg,
1482            html body #player #volumeBtn svg,
1483            html body #player #volumeBtn i,
1484            html body #player #zmpLiveBtn i {
1485                width: 18px !important;
1486            }
1487            
1488            /* Textos padronizados de AO VIVO */
1489            html body #player .neon-player__live-text,
1490            html body #player .neon-watch-label {
1491                color: var(--player-text-color, #ffffff) !important;
1492            }
1493            html body #player .neon-player__live-dot {
1494                background: var(--player-text-color, #ffffff) !important;
1495                box-shadow: 0 0 6px color-mix(in srgb, var(--player-text-color, #ffffff) 60%, transparent) !important;
1496            }
1497
1498            html body #player #zmpIconLoad {
1499                fill: none !important;
1500                stroke: currentColor !important;
1501            }
1502
1503            /* 2. Formato e Glow dos Botões Principais (Restaurando o Neon) */
1504            html body #player, html body .neon-player {
1505                background: var(--player-bg, rgba(20, 16, 33, 0.95)) !important;
1506            }
1507
1508            html body #player #playBtn,
1509            html body #player #zmpLiveBtn,
1510            html body #player #volumeBtn,
1511            html body #player .zmp-btn {
1512                background-color: transparent !important;
1513                box-shadow: none !important;
1514                display: flex;
1515                align-items: center;
1516                justify-content: center;
1517                cursor: pointer;
1518                transition: all 0.3s ease;
1519                border: none !important; /* Limpa bordas extras e heranças do themes/neon/style.css */
1520            }
1521
1522            /* Play Button - Anel Neon Principal */
1523            html body #player #playBtn {
1524                width: 48px !important;
1525                height: 48px !important;
1526                border-radius: 50% !important;
1527                border: 1.5px solid color-mix(in srgb, var(--player-text-color, #ffffff) 50%, transparent) !important;
1528                background: color-mix(in srgb, var(--player-text-color, #ffffff) 5%, transparent) !important;
1529            }
1530            html body #player #playBtn:hover {
1531                background: color-mix(in srgb, var(--player-text-color, #ffffff) 15%, transparent) !important;
1532                border-color: var(--player-text-color, #ffffff) !important;
1533                transform: scale(1.05);
1534                box-shadow: 0 0 15px color-mix(in srgb, var(--player-text-color, #ffffff) 30%, transparent) !important;
1535            }
1536
1537            /* Correção visual do play center gap (o triângulo engana o olho) */
1538            html body #player #playBtn svg {
1539                transform: translateX(1.5px);
1540            }
1541            /* Quando está pausado, o SVG muda para pause/spinner, então removemos o shift */
1542            html body #player #playBtn svg.animate-spin,
1543            html body #player #playBtn svg[class*="fa-pause"] {
1544                transform: translateX(0);
1545            }
1546
1547            /* Live/Volume Buttons - Anéis Menores */
1548            html body #player #zmpLiveBtn,
1549            html body #player #volumeBtn {
1550                width: 36px !important;
1551                height: 36px !important;
1552                border-radius: 50% !important;
1553                border: 1.5px solid color-mix(in srgb, var(--player-text-color, #ffffff) 30%, transparent) !important;
1554                background: transparent !important;
1555            }
1556            html body #player #zmpLiveBtn:hover,
1557            html body #player #volumeBtn:hover {
1558                border-color: color-mix(in srgb, var(--player-text-color, #ffffff) 80%, transparent) !important;
1559                background: color-mix(in srgb, var(--player-text-color, #ffffff) 10%, transparent) !important;
1560            }
1561
1562            /* Slider de Volume */
1563            .zmp-volume-slider {
1564                width: 80px;
1565                height: 4px;
1566                border-radius: 4px;
1567                appearance: none;
1568                background: color-mix(in srgb, var(--player-text-color, #ffffff) 20%, transparent);
1569                outline: none;
1570                cursor: pointer;
1571            }
1572            .zmp-volume-slider::-webkit-slider-thumb {
1573                appearance: none;
1574                width: 12px;
1575                height: 12px;
1576                border-radius: 50%;
1577                background: var(--player-text-color, #ffffff);
1578                cursor: pointer;
1579                box-shadow: 0 1px 4px rgba(0,0,0,0.3);
1580            }
1581            
1582            /* Responsive Hiding se a tela for mto pequena pro volume (inclui celular em paisagem) */
1583            @media (max-width: 1024px), (pointer: coarse) and (max-width: 1366px) {
1584                #zmpVolBtn, .zmp-volume-slider { display: none !important; }
1585                .zmp-player-controls-wrapper { margin-left: auto; gap: 12px; }
1586            }
1587            </style>
1588
1589
1590        </div><!-- /.neon-player__left-group -->
1591
1592        <!-- ──── GRUPO DIREITO: Botão ASSISTIR AO VIVO ──── -->
1593        <!-- Aparece apenas quando o widget de vídeo existe no DOM (JS controla) -->
1594        <div class="neon-player__video-btn-wrap" id="neonVideoWatchBtn" style="display:none;">
1595            <button class="neon-player__watch-btn" id="btnAssistirAoVivo" title="Assistir transmissão ao vivo">
1596                <span class="neon-watch-dot"></span>
1597                <span class="neon-watch-label">ASSISTIR AO VIVO</span>
1598            </button>
1599        </div>
1600
1601    </div><!-- /.neon-player__inner -->
1602    <div id="visualizer" style="display:none;"></div>
1603</div>
1604
1605<style>
1606/* ════════════════════════════════════════════════
1607   NEON PLAYER — Layout: Left-group + Right-button
1608   ════════════════════════════════════════════════ */
1609
1610/* O inner agora é: left-group flex-1 | btn-wrap flex-shrink-0 */
1611.neon-player__inner {
1612    display        : flex;
1613    align-items    : center;
1614    justify-content: space-between;
1615    max-width      : 1440px;
1616    margin         : 0 auto;
1617    padding        : 12px 32px;
1618    gap            : 24px;
1619}
1620
1621/* Grupo esquerdo: agrupa artwork, controles e volume numa fileira */
1622.neon-player__left-group {
1623    display    : flex;
1624    align-items: center;
1625    gap        : 24px;
1626    flex       : 1;
1627    min-width  : 0;
1628}
1629
1630/* Fix: info não cresce mais (ocupa apenas o que precisa) */
1631.neon-player__info {
1632    display    : flex;
1633    align-items: center;
1634    gap        : 14px;
1635    min-width  : 0;
1636    /* Remove o flex:1 antigo — agora o left-group controla o crescimento */
1637}
1638
1639/* ── Botão ASSISTIR AO VIVO (direita) ── */
1640.neon-player__video-btn-wrap {
1641    flex-shrink: 0;
1642    margin-left: auto; /* empurra para a extrema direita */
1643}
1644
1645.neon-player__watch-btn {
1646    display        : flex;
1647    align-items    : center;
1648    gap            : 8px;
1649    background     : transparent;
1650    color          : var(--player-text-color, #ffffff);
1651    border         : 1px solid color-mix(in srgb, var(--player-text-color, #ffffff) 40%, transparent);
1652    font-size      : 11px;
1653    font-weight    : 800;
1654    letter-spacing : 0.8px;
1655    text-transform : uppercase;
1656    padding        : 9px 20px;
1657    border-radius  : 999px;
1658    cursor         : pointer;
1659    white-space    : nowrap;
1660    transition     : all 0.2s ease;
1661    font-family    : 'Space Grotesk', 'Inter', sans-serif;
1662    box-shadow     : none;
1663}
1664
1665.neon-player__watch-btn:hover {
1666    background: color-mix(in srgb, var(--player-text-color, #ffffff) 10%, transparent);
1667    transform: scale(1.02);
1668}
1669
1670.neon-watch-dot {
1671    width        : 7px;
1672    height       : 7px;
1673    border-radius: 50%;
1674    background   : var(--player-text-color, #ffffff);
1675    flex-shrink  : 0;
1676    animation    : neon-watch-pulse 1.5s ease-in-out infinite;
1677}
1678
1679@keyframes neon-watch-pulse {
1680    0%, 100% { opacity: 1;   transform: scale(1); }
1681    50%      { opacity: 0.3; transform: scale(0.85); }
1682}
1683
1684/* Estado: vídeo aberto → botão muda para "FECHAR VÍDEO" */
1685.neon-player__watch-btn.is-open {
1686    background : color-mix(in srgb, var(--player-text-color, #ffffff) 15%, transparent);
1687    color      : var(--player-text-color, #ffffff);
1688    border     : 1px solid color-mix(in srgb, var(--player-text-color, #ffffff) 40%, transparent);
1689    box-shadow : none;
1690}
1691
1692.neon-player__watch-btn.is-open .neon-watch-dot {
1693    animation: none;
1694    opacity  : 1;
1695}
1696
1697/* Mobile: ocultar botão de vídeo (não aplicável) — inclui celular em paisagem */
1698@media (max-width: 1024px), (pointer: coarse) and (max-width: 1366px) {
1699    .neon-player__video-btn-wrap { display: none !important; }
1700    .neon-player__inner { padding: 10px 16px; gap: 12px; }
1701    .neon-player__left-group { gap: 12px; }
1702}
1703</style>
1704
1705<script>
1706(function() {
1707    function initWatchBtn() {
1708        var btn     = document.getElementById('btnAssistirAoVivo');
1709        var btnWrap = document.getElementById('neonVideoWatchBtn');
1710        var widget  = document.getElementById('vmpWidget');
1711        var label   = btn ? btn.querySelector('.neon-watch-label') : null;
1712
1713        // Só exibe o botão se o widget de vídeo existir no DOM
1714        if (!widget || !btn || !label) return;
1715        btnWrap.style.display = '';
1716
1717        function syncLabel() {
1718            var isOpen = widget.style.display !== 'none';
1719            btn.classList.toggle('is-open', isOpen);
1720            label.textContent = isOpen ? 'FECHAR VÍDEO' : 'ASSISTIR AO VIVO';
1721        }
1722
1723        btn.addEventListener('click', function() {
1724            var isOpen = widget.style.display !== 'none';
1725            widget.style.display = isOpen ? 'none' : 'block';
1726            syncLabel();
1727        });
1728
1729        // Sincronizar quando o botão × interno do mini player for clicado
1730        var closeBtn = document.getElementById('vmpCloseBtn');
1731        if (closeBtn) {
1732            closeBtn.addEventListener('click', function() {
1733                setTimeout(syncLabel, 50);
1734            });
1735        }
1736
1737        syncLabel(); // estado inicial
1738    }
1739
1740    if (document.readyState === 'loading') {
1741        document.addEventListener('DOMContentLoaded', initWatchBtn);
1742    } else {
1743        setTimeout(initWatchBtn, 200);
1744    }
1745
1746})();
1747</script>
1747
1748    <!-- Video Player Global: movido para dentro do #mainContent (topo) — Fix 3 -->
1749    <!-- O singleton define('VIDEO_PLAYER_GLOBAL_RENDERED') previne dupla renderização -->
1750
1751    
1752<footer class="neon-footer">
1753    <div class="neon-footer__accent-line"></div>
1754    <div class="neon-footer__inner">
1755
1756        <!-- Col 1: Marca / About -->
1757        <div class="neon-footer__col">
1758            <a href="home" class="neon-footer__logo">
1759                                    <img src="https://radio93portofeliz.com.br/uploads/media/6a05e3548b2fc_1778770772.png"
1760                         alt="Rádio 93 FM">
1761                            </a>
1762            <p class="neon-footer__site-name">Rádio 93 FM</p>
1763            <p class="neon-footer__desc">A primeira em Porto Feliz!</p>
1764            <div class="neon-footer__social">
1765                                    <a href="https://www.instagram.com/RADIO93PORTOFELIZ" target="_blank" rel="noopener" class="neon-footer__social-btn" title="Instagram">
1766                        <i class="fab fa-instagram"></i>
1767                    </a>
1768                                                    <a href="https://www.facebook.com/radio93portofeliz" target="_blank" rel="noopener" class="neon-footer__social-btn" title="Facebook">
1769                        <i class="fab fa-facebook-f"></i>
1770                    </a>
1771                                                    <a href="https://www.youtube.com/radio93portofeliz" target="_blank" rel="noopener" class="neon-footer__social-btn" title="YouTube">
1772                        <i class="fab fa-youtube"></i>
1773                    </a>
1774                                                                                                    <a href="https://play.google.com/store/apps/details?id=radio93portofeliz.uhost.br&amp;hl=pt_BR" target="_blank" rel="noopener" class="neon-footer__social-btn" title="Android">
1775                        <i class="fab fa-android"></i>
1776                    </a>
1777                                                    <a href="https://apps.apple.com/br/app/r%C3%A1dio-93-porto-feliz/id6773632501" target="_blank" rel="noopener" class="neon-footer__social-btn" title="iOS">
1778                        <i class="fab fa-apple"></i>
1779                    </a>
1780                                                    <a href="https://wa.me/5515996090935" target="_blank" rel="noopener" class="neon-footer__social-btn" title="WhatsApp">
1781                        <i class="fab fa-whatsapp"></i>
1782                    </a>
1783                            </div>
1784        </div>
1785
1786        <!-- Col 2 e 3: Navegação (2 colunas) -->
1787        <div class="neon-footer__col neon-footer__col--nav">
1788            <h4 class="neon-footer__heading">Navegação</h4>
1789            <ul class="neon-footer__links neon-footer__links--two-cols">
1790                                    <li><a href="a-radio">A rádio</a></li>
1791                                    <li><a href="equipe">Equipe</a></li>
1792                                    <li><a href="programacao">Programação</a></li>
1793                                    <li><a href="promocoes">Promoções</a></li>
1794                                    <li><a href="contato">Contato</a></li>
1795                            </ul>
1796        </div>
1797
1798        <!-- Col 4: Contato -->
1799        <div class="neon-footer__col">
1800            <h4 class="neon-footer__heading">Contato</h4>
1801            <ul class="neon-footer__contact">
1802                                    <li>
1803                        <!-- WhatsApp (mantido FA Brands) -->
1804                        <i class="fab fa-whatsapp"></i>
1805                        <a href="https://wa.me/5515996090935" target="_blank" rel="noopener">
1806                            (15) 996090935                        </a>
1807                    </li>
1808                                                                        <li>
1809                        <!-- Lucide: phone -->
1810                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.69 12a19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 3.56 1.2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
1811                        <span>(15) 3237-1955</span>
1812                    </li>
1813                                                                                            <li>
1814                        <!-- Lucide: mail -->
1815                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
1816                        <a href="mailto:[email protected]">[email protected]</a>
1817                    </li>
1818                                                                            <li>
1819                        <!-- Lucide: mail -->
1820                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
1821                        <a href="mailto:[email protected]">[email protected]</a>
1822                    </li>
1823                                                                            <li>
1824                        <!-- Lucide: mail -->
1825                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
1826                        <a href="mailto:[email protected]">[email protected]</a>
1827                    </li>
1828                                                                </ul>
1829        </div>
1830    </div>
1831
1832    <!-- Barra de Copyright -->
1833    <div class="neon-footer__bottom">
1834        <div class="neon-footer__bottom-inner">
1835            <div class="neon-footer__b-left">
1836                <p class="neon-footer__copyright">
1837                    Todos os Direito Reservados - Rádio 93 FM                    &middot; <a href="politica-de-privacidade" class="neon-footer__privacy-link">Política de Privacidade</a>
1838                </p>
1839            </div>
1840            <div class="neon-footer__b-right">
1841                                    <a href="https://uhost.com.br/" target="_blank" rel="noopener" class="neon-footer__dev-link">
1842                        <img src="https://radio93portofeliz.com.br/uploads/media/6a1501d3687d1_1779761619.png"
1843                             alt="Desenvolvido pela UHOST"
1844                             class="neon-footer__dev-logo"
1845                             onerror="this.closest('.neon-footer__dev-link').style.display='none'">
1846                    </a>
1847                            </div>
1848        </div>
1849    </div>
1850</footer>
1851
1852<script>
1853document.addEventListener('DOMContentLoaded', () => {
1854    const handleShelves = () => {
1855        if (window.innerWidth > 768) {
1856            document.querySelectorAll('.shelf-dots').forEach(d => d.style.display = 'none');
1857            return;
1858        } else {
1859            document.querySelectorAll('.shelf-dots').forEach(d => d.style.display = 'flex');
1860        }
1861
1862        const shelves = document.querySelectorAll('.shelf-row');
1863        shelves.forEach(shelf => {
1864            // Touch handler aplicado SEMPRE — independente de haver dots
1865            let _tx = 0, _tl = 0;
1866            shelf.addEventListener('touchstart', e => {
1867                _tx = e.touches[0].clientX;
1868                _tl = shelf.scrollLeft;
1869            }, { passive: true });
1870            shelf.addEventListener('touchmove', e => {
1871                shelf.scrollLeft = _tl + (_tx - e.touches[0].clientX);
1872            }, { passive: true });
1873
1874            // Encontra o container dos dots (pode estar fora do layout-wrapper, dentro do .container)
1875            const dotContainer = shelf.closest('.container')?.querySelector('.shelf-dots') ?? null;
1876
1877            if (!dotContainer) return;
1878
1879            const cards = Array.from(shelf.children);
1880            if (cards.length === 0) return;
1881
1882            // Evitar duplicações
1883            if (dotContainer.children.length === cards.length) return;
1884
1885            // Criar dots
1886            dotContainer.innerHTML = '';
1887            cards.forEach((_, i) => {
1888                const dot = document.createElement('div');
1889                dot.classList.add('dot');
1890                if (i === 0) dot.classList.add('active');
1891                dotContainer.appendChild(dot);
1892            });
1893
1894            const dots = Array.from(dotContainer.children);
1895
1896            // Observer para atualizar estado ativo
1897            const observer = new IntersectionObserver((entries) => {
1898                entries.forEach(entry => {
1899                    if (entry.isIntersecting) {
1900                        const index = cards.indexOf(entry.target);
1901                        if (index !== -1 && dots[index]) {
1902                            dots.forEach(d => d.classList.remove('active'));
1903                            dots[index].classList.add('active');
1904                        }
1905                    }
1906                });
1907            }, {
1908                root: shelf,
1909                threshold: 0.6 // pelo menos 60% visível para ativar
1910            });
1911
1912            cards.forEach(card => observer.observe(card));
1913        });
1914    };
1915
1916    handleShelves();
1917    window.addEventListener('resize', handleShelves);
1918});
1919</script>
1919
1920
1921    
1922    <!-- ===== WHATSAPP WIDGET (Global — todos os temas) ===== -->
1923    <!-- Oculto automaticamente se nenhum número estiver configurado -->
1924    <!-- Oculto em mobile via CSS (max-width: 768px) -->
1925    <link rel="stylesheet" href="https://radio93portofeliz.com.br/assets/css/whatsapp-widget.css?v=1779801036">
1926    
1926<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" defer></script>
1926
1927    
1927<script src="https://radio93portofeliz.com.br/assets/js/whatsapp-widget.js" defer></script>
1927
1928    
1929<!-- ===== WHATSAPP WIDGET (Global) ===== -->
1930
1931<!-- Botão Flutuante -->
1932<button id="wa-fab"
1933        aria-label="Fale conosco pelo WhatsApp"
1934        title="Fale conosco pelo WhatsApp">
1935    <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
1936        <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
1937    </svg>
1938</button>
1939
1940<!-- Overlay -->
1941<div id="wa-overlay" aria-hidden="true"></div>
1942
1943<!-- Painel lateral (Drawer) -->
1944<aside id="wa-panel" role="dialog" aria-modal="true" aria-label="WhatsApp">
1945
1946    <!-- Cabeçalho -->
1947    <div id="wa-panel-header">
1948        <p class="wa-ph-title">
1949            <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
1950                <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
1951            </svg>
1952            WhatsApp
1953        </p>
1954        <button id="wa-close-btn" aria-label="Fechar">&#x2715;</button>
1955    </div>
1956
1957    <!-- Corpo -->
1958    <div id="wa-panel-body">
1959
1960        <p class="wa-body-title">Escaneie o QR Code e fale conosco pelo WhatsApp</p>
1961
1962        <!-- QR Code gerado via whatsapp-widget.js -->
1963        <div id="wa-qr-wrap" aria-label="QR Code WhatsApp"></div>
1964
1965        <p class="wa-body-hint">
1966            Aponte a câmera do celular para o código acima
1967        </p>
1968
1969    </div>
1970</aside>
1971
1972<!-- Variáveis injetadas para o JS -->
1973<script>
1974var WA_NUMBER  = "5515996090935";
1975var WA_BTN_POS = "bottom-left";
1976</script>
1976
1977
1978    <!-- Cookie Banner Global (Ajuste 3) -->
1979    <!-- ===== COOKIE BANNER — Bottom Sheet Nativo ===== -->
1980<div
1981    id="cookieBanner"
1982    class="cookie-banner"
1983    role="alertdialog"
1984    aria-live="polite"
1985    aria-label="Aviso de privacidade"
1986    aria-describedby="cookieBannerDesc"
1987    style="display:none;"
1988>
1989    <!-- Handle pill (drag indicator nativo) -->
1990    <div class="cookie-banner__handle" aria-hidden="true"></div>
1991
1992    <!-- Cabeçalho estilo app nativo -->
1993    <div class="cookie-banner__header">
1994                <div class="cookie-banner__app-icon" aria-hidden="true">
1995            <img src="https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png" alt="" width="46" height="46" loading="lazy">
1996        </div>
1997                <div class="cookie-banner__app-info">
1998            <p class="cookie-banner__app-name">Sua privacidade</p>
1999            <p class="cookie-banner__app-host">Rádio 93 FM · radio93portofeliz.com.br</p>
2000        </div>
2001    </div>
2002
2003    <!-- Separador -->
2004    <div class="cookie-banner__divider" aria-hidden="true"></div>
2005
2006    <!-- Texto explicativo -->
2007    <p class="cookie-banner__desc" id="cookieBannerDesc">
2008        Usamos cookies para manter o app funcionando e melhorar sua experiência. Nenhum dado é vendido a terceiros.
2009        <a href="https://radio93portofeliz.com.br/?page=politica-de-privacidade" target="_blank" rel="noopener" class="cookie-banner__privacy-link">Política de privacidade</a>
2010    </p>
2011
2012    <!-- Ações empilhadas (estilo nativo) -->
2013    <div class="cookie-banner__actions">
2014        <button
2015            id="cookieBannerAccept"
2016            class="cookie-banner__btn cookie-banner__btn--primary"
2017            type="button"
2018            aria-label="Aceitar todos os cookies"
2019        >
2020            Aceitar todos
2021        </button>
2022        <button
2023            id="cookieBannerDecline"
2024            class="cookie-banner__btn cookie-banner__btn--secondary"
2025            type="button"
2026            aria-label="Aceitar apenas cookies essenciais"
2027        >
2028            Apenas essenciais
2029        </button>
2030    </div>
2031</div>
2032
2033<script>
2034/* ── Cookie Banner JS (IIFE) ── */
2035(function () {
2036    'use strict';
2037
2038    var STORAGE_KEY = 'cookie_consent';
2039    var banner      = document.getElementById('cookieBanner');
2040    if (!banner) return;
2041
2042    /* Se o usuário já escolheu, não mostrar novamente */
2043    try {
2044        if (localStorage.getItem(STORAGE_KEY)) return;
2045    } catch (e) {
2046        return;
2047    }
2048
2049    /* ── Animação de entrada (slide-up) ── */
2050    function showBanner() {
2051        banner.style.display = 'flex';
2052        void banner.offsetHeight;
2053        banner.classList.add('cookie-banner--visible');
2054    }
2055
2056    function hideBanner(callback) {
2057        banner.classList.remove('cookie-banner--visible');
2058        banner.classList.add('cookie-banner--hiding');
2059        setTimeout(function () {
2060            banner.style.display = 'none';
2061            banner.classList.remove('cookie-banner--hiding');
2062            if (typeof callback === 'function') callback();
2063        }, 400);
2064    }
2065
2066    function saveChoice(choice) {
2067        try { localStorage.setItem(STORAGE_KEY, choice); } catch (e) {}
2068    }
2069
2070    /* ── Mostrar após 1.2s ── */
2071    setTimeout(showBanner, 1200);
2072
2073    /* ── Botão Aceitar todos ── */
2074    var acceptBtn = document.getElementById('cookieBannerAccept');
2075    if (acceptBtn) {
2076        acceptBtn.addEventListener('click', function () {
2077            saveChoice('accepted');
2078            hideBanner(function () {
2079                document.dispatchEvent(new CustomEvent('cookie:accepted'));
2080            });
2081        });
2082    }
2083
2084    /* ── Botão Apenas essenciais ── */
2085    var declineBtn = document.getElementById('cookieBannerDecline');
2086    if (declineBtn) {
2087        declineBtn.addEventListener('click', function () {
2088            saveChoice('essential');
2089            hideBanner(function () {
2090                document.dispatchEvent(new CustomEvent('cookie:declined'));
2091            });
2092        });
2093    }
2094
2095    /* ── Fechar com Escape ── */
2096    document.addEventListener('keydown', function (e) {
2097        if (e.key === 'Escape' && banner.classList.contains('cookie-banner--visible')) {
2098            saveChoice('essential');
2099            hideBanner();
2100        }
2101    });
2102})();
2103</script>
2103
2104
2105    <!-- Smart App Install Banner (global — CTA de loja, prompt PWA ou instruções iOS).
2106         Dentro do app Rádio 3.0 não faz sentido convidar a instalar o app. -->
2107            
2108    <!-- Popup de Abertura (global — campanha cadastrada em /?page=banners).
2109         Sem campanha ativa não emite CSS, JS nem markup. -->
2110    
2111    <!-- Menu Mobile Global (Apenas Mobile via CSS). Também traz o estilo de mini
2112         player e o player expandido — nada disso existe dentro do app Rádio 3.0. -->
2113        
2114<nav class="mobile-bottom-nav">
2115  <!-- Item 1: fixo (Página Inicial) -->
2116  <a class="nav-item" href="https://radio93portofeliz.com.br">
2117    <div class="nav-icon-box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9.5L12 3l9 6.5V20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.5z"/><path d="M9 21V12h6v9"/></svg></div>
2118  </a>
2119
2120  <!-- Itens 2-5: dinâmicos -->
2121              <a class="nav-item" href="/promocoes">
2122        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 12 20 22 4 22 4 12"/><rect x="2" y="7" width="20" height="5"/><line x1="12" y1="22" x2="12" y2="7"/><path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"/><path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"/></svg>        <span>Promoções</span>
2123      </a>
2124            <a class="nav-item" href="https://www.youtube.com/radio93portofeliz">
2125        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>        <span>Estúdio</span>
2126      </a>
2127            <a class="nav-item" href="/programacao">
2128        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>        <span>Programação</span>
2129      </a>
2130            <a class="nav-item" href="/contato">
2131        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>        <span>Contato</span>
2132      </a>
2133        </nav>
2134
2135<style>
2136.mobile-bottom-nav {
2137  display: none;
2138}
2139@media (max-width: 1024px), (pointer: coarse) and (max-width: 1366px) {
2140  .mobile-bottom-nav {
2141    position: fixed;
2142    bottom: 0; left: 0; right: 0;
2143    background: var(--bg-card, #ffffff);
2144    border-top: 1px solid var(--border-color, #e0e0e0);
2145    display: flex;
2146    justify-content: space-around;
2147    align-items: flex-start;
2148    padding: 12px 0 calc(12px + env(safe-area-inset-bottom));
2149    z-index: 9999;
2150  }
2151}
2152
2153html[data-theme="dark"] .mobile-bottom-nav,
2154body.dark-mode .mobile-bottom-nav {
2155    background: var(--bg-card, #121214);
2156    border-top: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
2157}
2158
2159.mobile-bottom-nav .nav-item {
2160  all: unset;
2161  display: flex !important;
2162  flex-direction: column !important;
2163  align-items: center !important;
2164  gap: 5px !important;
2165  cursor: pointer !important;
2166  padding: 4px 10px !important;
2167  text-decoration: none !important;
2168  box-sizing: border-box !important;
2169}
2170
2171.mobile-bottom-nav .nav-item svg { stroke: #9e9e9e; width: 24px; height: 24px; transition: all 0.2s; }
2172.mobile-bottom-nav .nav-item span { font-size: 11px; color: #9e9e9e; font-weight: 500; transition: all 0.2s; }
2173
2174/* Hover — feedback visual ao toque */
2175.mobile-bottom-nav .nav-item:hover svg,
2176.mobile-bottom-nav .nav-item:focus svg { stroke: var(--primary-color, #1B1464); opacity: 0.7; }
2177.mobile-bottom-nav .nav-item:hover span,
2178.mobile-bottom-nav .nav-item:focus span { color: var(--primary-color, #1B1464); opacity: 0.7; }
2179
2180/* Ativo — página atual */
2181.mobile-bottom-nav .nav-item.active svg { stroke: var(--primary-color, #1B1464); opacity: 1; }
2182.mobile-bottom-nav .nav-item.active span { color: var(--primary-color, #1B1464); font-weight: 500; opacity: 1; }
2183
2184.mobile-bottom-nav .nav-icon-box {
2185  width: 48px; height: 48px;
2186  border-radius: 14px;
2187  background: var(--primary-color, #1B1464);
2188  display: flex; align-items: center; justify-content: center;
2189  margin-top: -6px; /* Para alinhar opticamente a caixa maior com os outros ícones */
2190  transition: all 0.2s;
2191}
2192
2193/* Ícone box do item 1 quando ativo */
2194.mobile-bottom-nav .nav-item.active .nav-icon-box { background: var(--primary-color, #1B1464); }
2195
2196/* Ícone box do item 1 quando inativo */
2197.mobile-bottom-nav .nav-item:not(.active) .nav-icon-box { background: var(--border-color, #e0e0e0); }
2198.mobile-bottom-nav .nav-item:not(.active) .nav-icon-box svg { stroke: #9e9e9e !important; opacity: 1; }
2199
2200/* Home icon should be white inside the active solid box */
2201.mobile-bottom-nav .nav-item.active .nav-icon-box svg { stroke: #ffffff !important; }
2202
2203/* Prevent overrides from global player svg rule modifying this one if any */
2204
2205/* Para não sobrepor o footer em sites mobile, adicione padding bottom global body via classe */
2206@media (max-width: 1024px), (pointer: coarse) and (max-width: 1366px) {
2207    body {
2208        padding-bottom: calc(148px + env(safe-area-inset-bottom)) !important;
2209    }
2210    
2211    /* =========================================
2212       REESTILIZAÇÃO GLOBAL DO PLAYER MOBILE
2213       ESTILO DEEZER (PÍLULA FLUTUANTE LIMPA)
2214       ========================================= */
2215    #player, .radio-player, .neon-player, .d5-player, .app-player, .player-container, .global-player {
2216        position: fixed !important;
2217        bottom: calc(85px + env(safe-area-inset-bottom)) !important;
2218        left: 12px !important;
2219        right: 12px !important;
2220        width: auto !important;
2221        height: 64px !important;
2222        min-height: 64px !important;
2223        max-height: 64px !important;
2224        box-sizing: border-box !important;
2225        align-items: center !important;
2226        overflow: hidden !important; /* garante que nada vaze para fora */
2227        border-radius: 12px !important;
2228        background: var(--player-bg, #181818) !important;
2229        box-shadow: 0 8px 25px rgba(0,0,0,0.4) !important;
2230        border: 1px solid var(--border-color, rgba(255,255,255,0.06)) !important;
2231        padding: 0 12px !important;
2232        z-index: 9998 !important;
2233    }
2234
2235    /* Container global - forçar flex row */
2236    #player > div,
2237    .neon-player__inner, 
2238    .d5-player-inner {
2239        display: flex !important;
2240        flex-direction: row !important;
2241        align-items: center !important;
2242        justify-content: flex-start !important;
2243        padding: 0 !important;
2244        height: 100% !important;
2245        gap: 10px !important;
2246        width: 100% !important;
2247        border: none !important;
2248    }
2249
2250    /* Ocultar elementos desnecessários (Artwork e Status Badge agora são visíveis) */
2251    .neon-player__video-btn-wrap,
2252    .wave-player__video-btn-wrap,
2253    .pulse-player__video-btn-wrap,
2254    .glow-player__video-btn-wrap,
2255    .d5-volume-ctrl,
2256    .zmp-volume-slider,
2257    #volumeBtn,
2258    #liveBtn,
2259    #zmpLiveBtn,
2260    #videoBtn,
2261    .d5-play-ring,
2262    #visualizer,
2263    .neon-player__watch-btn,
2264    .wave-player__watch-btn,
2265    .pulse-player__watch-btn,
2266    .glow-player__watch-btn {
2267        display: none !important;
2268    }
2269
2270    /* ==== Ajuste: Alinhamento exato da Capa e Botão Play (Tamanho fixo 48x48) ====
2271       Cada tema pode redefinir o tamanho da capa com especificidade maior
2272       (ex.: WAVE usa 52px no brutalism_override.css). O que importa aqui é que
2273       o slot NUNCA saia do fluxo e não mude de tamanho entre estados. */
2274    .neon-player__artwork,
2275    .d5-player-artwork,
2276    .player-artwork,
2277    #playerArtwork,
2278    .wave-player__artwork,
2279    .pulse-player__artwork,
2280    .glow-player__artwork,
2281    .play-btn,
2282    .neon-player__play-btn,
2283    .d5-play-btn,
2284    #playBtn,
2285    .global-play-btn,
2286    .wave-player__play-btn {
2287        width: 48px !important;
2288        height: 48px !important;
2289        min-width: 48px !important;
2290        max-width: 48px !important;
2291        flex-shrink: 0 !important;
2292        align-self: center !important;
2293        display: flex !important;
2294        align-items: center !important;
2295        justify-content: center !important;
2296        box-sizing: border-box !important;
2297        margin: 0 !important;
2298        padding: 0 !important;
2299    }
2300
2301    .neon-player__artwork,
2302    .d5-player-artwork,
2303    .player-artwork,
2304    #playerArtwork,
2305    .wave-player__artwork,
2306    .pulse-player__artwork,
2307    .glow-player__artwork {
2308        border-radius: 6px !important; /* Capa quadrada com cantos suaves no mobile */
2309        overflow: hidden !important;
2310        display: block !important;
2311    }
2312
2313    .neon-player__artwork img,
2314    .d5-player-artwork img,
2315    .player-artwork img,
2316    #playerArtwork img,
2317    .wave-player__artwork img,
2318    .pulse-player__artwork img,
2319    .glow-player__artwork img {
2320        width: 48px !important;
2321        height: 48px !important;
2322        object-fit: cover !important;
2323        display: block !important;
2324    }
2325
2326    /* ════════════════════════════════════════════════════════════════
2327       ESQUELETO ÚNICO DO MINI PLAYER (NEON / WAVE / PULSE / GLOW)
2328       ────────────────────────────────────────────────────────────────
2329       Regra de ouro: o layout NÃO depende do conteúdo. A ordem e a
2330       distribuição são as mesmas com ou sem metadados — só o texto e a
2331       imagem mudam. Ordem fixa:
2332           capa → badge → título → artista → play (colado na direita)
2333       IMPORTANTE: usamos #player como prefixo para ter especificidade
2334       maior que as regras inline no @media do player.php. --- */
2335    #player .neon-player__left-group,
2336    #player .wave-player__left-group,
2337    #player .pulse-player__left-group,
2338    #player .glow-player__left-group {
2339        display: flex !important;
2340        flex-direction: row !important;
2341        align-items: center !important;
2342        justify-content: flex-start !important; /* sobrescreve qualquer space-between herdado */
2343        gap: 10px !important;
2344        flex: 1 1 0 !important;
2345        width: 100% !important;
2346        min-width: 0 !important;
2347        overflow: hidden !important;
2348    }
2349
2350    /* Bloco capa + texto: ocupa TODA a sobra, sempre. É esse flex:1 1 0 que
2351       impede o botão play de escorregar para o meio quando o texto é curto. */
2352    #player .neon-player__info,
2353    #player .wave-player__info,
2354    #player .pulse-player__info,
2355    #player .glow-player__info {
2356        display: flex !important;
2357        flex-direction: row !important;
2358        align-items: center !important;
2359        justify-content: flex-start !important;
2360        flex: 1 1 0 !important;
2361        min-width: 0 !important;
2362        gap: 10px !important;
2363        order: 1 !important; /* capa + texto primeiro */
2364        overflow: hidden !important;
2365    }
2366
2367    /* Coluna de texto: min-width:0 é obrigatório para o ellipsis funcionar
2368       dentro de um flex item (sem ele o texto empurra o container). */
2369    #player .wave-player__meta,
2370    #player .neon-player__meta,
2371    #player .pulse-player__meta,
2372    #player .glow-player__meta,
2373    #player .d5-player-meta,
2374    #player .player-meta,
2375    #player .nmp-meta-wrap {
2376        display: flex !important;
2377        flex-direction: column !important;
2378        justify-content: center !important;
2379        gap: 0 !important;
2380        line-height: 1 !important;
2381        overflow: hidden !important;
2382        min-width: 0 !important;
2383        flex: 1 1 0 !important;
2384    }
2385
2386    /* Play ancorado na borda direita em TODOS os estados.
2387       margin-left:auto + order:2 = sempre o último, sempre colado à direita.
2388       (o margin-left:20px do player.php perde: 0,2,0 vs 0,1,0) */
2389    #player .zmp-player-controls-wrapper {
2390        margin: 0 0 0 auto !important;
2391        padding: 0 !important;
2392        order: 2 !important;
2393        flex: 0 0 auto !important;
2394        gap: 0 !important;
2395        width: auto !important;
2396    }
2397
2398    /* ---- LÓGICA DEMO 05 ---- */
2399    .d5-player-inner > div:nth-child(2) {
2400        order: -1 !important; /* Bloco do Play vai para a esquerda */
2401        margin: 0 !important;
2402        margin-right: -4px !important;
2403        flex: 0 0 auto !important;
2404        width: auto !important;
2405    }
2406    .d5-player-left {
2407        order: 2 !important; /* Bloco de Info vai para a direita do Play */
2408        flex: 1 !important;
2409        width: auto !important;
2410        gap: 0 !important;
2411    }
2412    .d5-player-inner > div:nth-child(3) {
2413        display: none !important; /* Esconde botões extras estendidos */
2414    }
2415    .d5-player-meta {
2416        justify-content: center !important;
2417        min-height: 0 !important;
2418        gap: 0 !important;
2419    }
2420
2421    /* ---- TIPOGRAFIA COMUM (TITULO / ARTISTA) ----
2422       As três linhas têm ALTURA RESERVADA (badge 10px, título 16px,
2423       artista 12px). Linha vazia continua ocupando o mesmo espaço, então
2424       trocar de estado não causa reflow nem salto de altura. */
2425    .player-status-badge,
2426    .neon-player__status,
2427    .wave-player__status,
2428    .pulse-player__status,
2429    .glow-player__status,
2430    .live-badge,
2431    .wave-player__live-text {
2432        display: block !important;
2433        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
2434        font-size: 10px !important;
2435        font-weight: 700 !important;
2436        text-transform: uppercase !important;
2437        letter-spacing: 0.08em !important;
2438        margin-bottom: 3px !important;
2439        line-height: 1 !important;
2440        height: 10px !important;
2441        flex: 0 0 10px !important;
2442        color: var(--player-text-color, #ffffff) !important;
2443    }
2444
2445    .neon-player__title,
2446    .d5-player-title,
2447    .wave-player__title,
2448    .pulse-player__title,
2449    .glow-player__title,
2450    .player-title {
2451        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
2452        font-size: 13px !important;
2453        font-weight: 800 !important;
2454        color: var(--player-text-color, #ffffff) !important;
2455        text-transform: uppercase !important;
2456        width: 100% !important;
2457        text-shadow: none !important;
2458        -webkit-text-stroke: 0 !important;
2459        line-height: 16px !important;
2460        height: 16px !important;
2461        flex: 0 0 16px !important;
2462        display: block; /* sem !important: o JS do marquee precisa poder ocultar */
2463        padding: 0 !important;
2464        margin: 0 0 2px 0 !important;
2465        white-space: nowrap !important;
2466        overflow: hidden !important;
2467        text-overflow: ellipsis !important;
2468    }
2469
2470    /* Oculta os elementos originais quando o marquee está ativo — usa !important
2471       para sobrescrever regras de tema (ex: wave-player__title tem display:block !important) */
2472    #player .nmp-src-hidden {
2473        display: none !important;
2474    }
2475
2476    .neon-player__artist,
2477    .d5-player-artist,
2478    .wave-player__artist,
2479    .pulse-player__artist,
2480    .glow-player__artist,
2481    .player-artist {
2482        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
2483        font-size: 11px !important;
2484        font-weight: 400 !important;
2485        color: var(--player-text-color, #ffffff) !important;
2486        opacity: 0.8 !important;
2487        text-transform: uppercase !important;
2488        white-space: nowrap !important;
2489        overflow: hidden !important;
2490        text-overflow: ellipsis !important;
2491        width: 100% !important;
2492        line-height: 12px !important;
2493        height: 12px !important;
2494        flex: 0 0 12px !important;
2495        display: block;
2496        padding: 0 !important;
2497        margin: 0 !important;
2498    }
2499
2500    /* ---- AJUSTE 3: ANIMAÇÃO MARQUEE ---- */
2501    --marquee-duration: 10s;
2502
2503    /* Wrapper de texto do mini player mobile.
2504       Nenhum tema gera .nmp-meta-wrap hoje; mantido para compatibilidade.
2505       order:1 para acompanhar o esqueleto (capa+texto antes do play). */
2506    .nmp-meta-wrap {
2507        flex: 1 1 0 !important;
2508        min-width: 0 !important;
2509        overflow: hidden !important;
2510        display: flex !important;
2511        flex-direction: column !important;
2512        justify-content: center !important;
2513        gap: 0 !important;
2514        line-height: 1 !important;
2515        order: 1 !important;
2516    }
2517
2518    /* O antigo bloco .nmp-fallback (logo + nome do site) foi removido: ele
2519       trocava a subárvore inteira do mini player quando não havia metadados,
2520       com markup, tamanhos e ordem próprios — a causa do layout embaralhado.
2521       Agora o esqueleto é sempre o mesmo e só o texto muda. */
2522
2523    /* Linha de texto com suporte a marquee.
2524       O <span> só vira inline-block quando o marquee está ativo. Enquanto for
2525       inline, o text-overflow do pai consegue aplicar o ellipsis — um filho
2526       inline-block seria cortado a seco, sem reticências. */
2527    .mini-player-text {
2528        white-space: nowrap !important;
2529        overflow: hidden !important;
2530        text-overflow: ellipsis !important;
2531        display: block !important;
2532        width: 100% !important;
2533        position: relative !important;
2534    }
2535    .mini-player-text span {
2536        display: inline;
2537        white-space: nowrap !important; /* Impede que o texto duplicado (marquee) quebre linha */
2538        padding-right: 0;
2539    }
2540    /* Estado rolando: o span passa a conter DUAS cópias idênticas (.nmp-run),
2541       cada uma com o intervalo já embutido no próprio padding-right. Assim
2542       metade da largura total é exatamente uma cópia + intervalo, e o
2543       translateX(-50%) emenda o fim no começo sem salto. Se o intervalo
2544       ficasse solto entre as cópias (ou no padding do span), 50% cairia no
2545       meio do vão e a volta daria um pulo visível. */
2546    .mini-player-text span.scrolling {
2547        display: inline-block;
2548        padding-right: 0;
2549        will-change: transform;
2550        animation: nmp-marquee-scroll 12s linear infinite; /* duração real vem do JS */
2551    }
2552    .mini-player-text span.scrolling .nmp-run {
2553        display: inline-block;
2554        white-space: nowrap;
2555        padding-right: 48px; /* intervalo entre o fim de uma cópia e o início da outra */
2556        font-style: normal;
2557    }
2558    @keyframes nmp-marquee-scroll {
2559        0%   { transform: translateX(0); }
2560        100% { transform: translateX(-50%); }
2561    }
2562    /* Respeita quem pediu menos animação no sistema: em vez de rolar, volta ao
2563       texto estático com reticências — a segunda cópia sai de cena para não
2564       aparecer texto repetido cortado no meio. */
2565    @media (prefers-reduced-motion: reduce) {
2566        .mini-player-text span.scrolling {
2567            display: inline;
2568            animation: none !important;
2569            transform: none !important;
2570        }
2571        .mini-player-text span.scrolling .nmp-run {
2572            padding-right: 0 !important;
2573        }
2574        .mini-player-text span.scrolling .nmp-run + .nmp-run {
2575            display: none !important;
2576        }
2577        #player.nmp-one-line #nmpDisplayTitle {
2578            text-overflow: ellipsis !important;
2579        }
2580    }
2581
2582    /* ════════════════════════════════════════════════════════
2583       LAYOUT GLOBAL — Mini Player Mobile (TODOS OS TEMAS)
2584       Abordagem: seletores de CLASSE e nth-child apenas.
2585       SEM :has() — compatível com qualquer browser.
2586       ════════════════════════════════════════════════════════ */
2587
2588    /* --- Container direto do #player: força flex-start em TUDO --- */
2589    #player > div,
2590    #player > .neon-player__inner,
2591    #player > .wave-player__inner,
2592    #player > .pulse-player__inner,
2593    #player > .glow-player__inner,
2594    #player > .d5-player-inner {
2595        flex-wrap: nowrap !important;
2596        gap: 10px !important;
2597        padding: 0 !important;
2598        overflow: hidden !important;
2599        justify-content: flex-start !important;
2600        align-items: center !important;
2601    }
2602
2603    /* Nota: as regras de #player .neon-player__left-group, .neon-player__info e
2604       .zmp-player-controls-wrapper com prefixo #player estão na seção LÓGICA FLEX acima. */
2605
2606    /* ════════════════════════════════════════════════════════
2607       DEMO 01 — Inner: .max-w-screen-2xl
2608       Estrutura: [1:Left=info] [2:Center=play] [3:Right=extras]
2609       ════════════════════════════════════════════════════════ */
2610    /* Centro (playBtn) → vai para a ESQUERDA */
2611    #player > div.max-w-screen-2xl > div:nth-child(2) {
2612        order: 1 !important;
2613        flex: 0 0 auto !important;
2614        width: auto !important;
2615        margin: 0 !important;
2616        gap: 0 !important;
2617        align-items: center !important;
2618    }
2619    /* Esquerda (artwork + info) → expande para preencher */
2620    #player > div.max-w-screen-2xl > div:nth-child(1) {
2621        order: 2 !important;
2622        flex: 1 1 0 !important;
2623        min-width: 0 !important;
2624        overflow: hidden !important;
2625        width: auto !important;
2626    }
2627    /* Direita (extras/volume) → oculto */
2628    #player > div.max-w-screen-2xl > div:nth-child(3) {
2629        display: none !important;
2630    }
2631    /* Container de texto dentro do bloco de info do Demo01 */
2632    #player > div.max-w-screen-2xl > div:nth-child(1) > div:last-child {
2633        flex: 1 1 0 !important;
2634        min-width: 0 !important;
2635        overflow: hidden !important;
2636    }
2637
2638    /* --- Containers de meta-info internos --- */
2639    .neon-player__info,
2640    .wave-player__info,
2641    .pulse-player__info,
2642    .glow-player__info,
2643    .d5-player-meta {
2644        flex: 1 1 0 !important;
2645        min-width: 0 !important;
2646        overflow: hidden !important;
2647    }
2648
2649    /* --- Título e Artista: sempre truncados com ellipsis ---
2650       IMPORTANTE: NÃO adicionar display:block !important aqui —
2651       o JS do marquee precisa conseguir ocultar esses elementos
2652       via element.style.display = 'none' para mostrar os
2653       substitutos #nmpDisplayTitle / #nmpDisplayArtist no lugar. --- */
2654    #player #playerTitle,
2655    #player #playerArtist,
2656    #player .neon-player__title,
2657    #player .wave-player__title,
2658    #player .pulse-player__title,
2659    #player .glow-player__title,
2660    #player .d5-player-title,
2661    #player .player-title,
2662    #player .neon-player__artist,
2663    #player .wave-player__artist,
2664    #player .pulse-player__artist,
2665    #player .glow-player__artist,
2666    #player .d5-player-artist,
2667    #player .player-artist {
2668        max-width: 100% !important;
2669        overflow: hidden !important;
2670        text-overflow: ellipsis !important;
2671        white-space: nowrap !important;
2672        /* display intencionalmente ausente — controlado pelo JS do marquee */
2673    }
2674
2675    /* Garante que playerTitle/Artist aparecem (desfaz 'hidden' do Tailwind/temas)
2676       SEM !important para não bloquear o display:none que o marquee-JS coloca */
2677    #player #playerTitle { display: block; }
2678    #player #playerArtist { display: block; }
2679
2680    /* Elementos de display do marquee (substitutos).
2681       Tipografia e altura reservada IDÊNTICAS às do título/artista originais —
2682       o fallback é troca de string, nunca troca de estilo. */
2683    #nmpDisplayTitle {
2684        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
2685        font-size: 13px !important;
2686        font-weight: 800 !important;
2687        color: var(--player-text-color, #ffffff) !important;
2688        text-transform: uppercase !important;
2689        letter-spacing: 0.02em !important;
2690        line-height: 16px !important;
2691        height: 16px !important;
2692        flex: 0 0 16px !important;
2693        margin: 0 0 2px 0 !important;
2694    }
2695    #nmpDisplayArtist {
2696        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
2697        font-size: 11px !important;
2698        font-weight: 400 !important;
2699        color: var(--player-text-color, #ffffff) !important;
2700        opacity: 0.8 !important;
2701        text-transform: uppercase !important;
2702        line-height: 12px !important;
2703        height: 12px !important;
2704        flex: 0 0 12px !important;
2705    }
2706
2707    /* ═══════════════════════════════════════════════════════════════════
2708       ESTADO COM METADADOS — LINHA ÚNICA (classe .nmp-one-line)
2709       ───────────────────────────────────────────────────────────────────
2710       Com faixa tocando, música e artista deixam de ocupar duas linhas
2711       estreitas e passam a formar UMA linha só:  MÚSICA • ARTISTA.
2712       A coluna de texto do mini player tem ~150px; dividir isso em duas
2713       linhas de 13px/11px espremia os dois textos e cortava os dois.
2714       Em uma linha só, o texto ganha o dobro de largura útil e o que não
2715       couber rola em marquee contínuo (nunca reticências, nunca fonte menor).
2716
2717       O slot do artista continua existindo no DOM — apenas fica vazio e sai
2718       do fluxo — para o esqueleto (capa → badge → texto → play) não mudar.
2719       ═══════════════════════════════════════════════════════════════════ */
2720
2721    /* Linha vazia (o slot do artista) sai do fluxo: nada de 12px em branco. */
2722    #player.nmp-one-line .nmp-empty {
2723        display: none !important;
2724    }
2725
2726    /* Bloco de texto opticamente centrado com a capa de 48px. */
2727    #player.nmp-one-line .neon-player__meta,
2728    #player.nmp-one-line .wave-player__meta,
2729    #player.nmp-one-line .pulse-player__meta,
2730    #player.nmp-one-line .glow-player__meta,
2731    #player.nmp-one-line .d5-player-meta,
2732    #player.nmp-one-line .player-meta,
2733    #player.nmp-one-line .nmp-meta-wrap {
2734        justify-content: center !important;
2735        gap: 0 !important;
2736    }
2737
2738    /* A linha única. Mesmo corpo de fonte do título antigo (13px) — o gan
2738ho de
2739       leitura vem da largura, não de encolher a tipografia.
2740       text-overflow: clip (e não ellipsis) porque o overflow aqui é tratado
2741       pelo marquee; reticências só voltam em prefers-reduced-motion. */
2742    #player.nmp-one-line #nmpDisplayTitle {
2743        font-size: 13px !important;
2744        font-weight: 700 !important;
2745        letter-spacing: 0.01em !important;
2746        line-height: 18px !important;
2747        height: 18px !important;
2748        flex: 0 0 18px !important;
2749        margin: 0 !important;
2750        overflow: hidden !important;
2751        text-overflow: clip !important;
2752    }
2753
2754    /* Nome da música em destaque; separador e artista recuam em peso/opacidade
2755       para a linha ter hierarquia mesmo sendo uma só. */
2756    #player.nmp-one-line #nmpDisplayTitle .nmp-track {
2757        font-weight: 800 !important;
2758    }
2759    #player.nmp-one-line #nmpDisplayTitle .nmp-sep {
2760        font-weight: 400 !important;
2761        opacity: 0.55 !important;
2762        padding: 0 2px !important;
2763    }
2764    #player.nmp-one-line #nmpDisplayTitle .nmp-artist {
2765        font-weight: 400 !important;
2766        opacity: 0.8 !important;
2767    }
2768
2769    /* ═══════════════════════════════════════════════
2770       BADGE "● AO VIVO" — Mini Player Mobile Integrado
2771       ═══════════════════════════════════════════════ */
2772    .wave-player__meta [data-player-status],
2773    .neon-player__meta [data-player-status],
2774    .pulse-player__meta [data-player-status],
2775    .glow-player__meta [data-player-status],
2776    .d5-player-meta [data-player-status],
2777    .player-meta [data-player-status],
2778    .nmp-meta-wrap [data-player-status] {
2779        display: block !important;
2780        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
2781        font-size: 10px !important;
2782        font-weight: 700 !important;
2783        text-transform: uppercase !important;
2784        letter-spacing: 0.08em !important;
2785        white-space: nowrap !important;
2786        overflow: hidden !important;
2787        text-overflow: ellipsis !important;
2788        line-height: 1 !important;
2789        margin-bottom: 2px !important;
2790    }
2791
2792    /* ═══════════════════════════════════════════════════════════
2793       STATUS DO MINI PLAYER — apenas "Ao Vivo", sem avisos offline
2794       O estado de conexão é responsabilidade exclusiva da pílula
2795       flutuante (connection-status.php). Aqui sempre mostramos "Ao Vivo".
2796       Especificidade #player + attr > classe + attr — sobrescreve o bloco acima.
2797       ═══════════════════════════════════════════════════════════ */
2798
2799    /* Zera o texto dinâmico injetado pelo player.js */
2800    #player [data-player-status] {
2801        font-size: 0 !important;
2802        letter-spacing: 0 !important;
2803        color: transparent !important;
2804        margin-bottom: 3px !important;
2805    }
2806
2807    /* Injeta "● Ao Vivo" fixo via pseudo-elemento */
2808    #player [data-player-status]::before {
2809        content: "● Ao Vivo";
2810        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
2811        font-size: 9px !important;
2812        font-weight: 500 !important;
2813        color: var(--player-text-color, #ffffff) !important;
2814        opacity: 0.7 !important;
2815        text-transform: none !important;
2816        letter-spacing: normal !important;
2817        line-height: 1 !important;
2818    }
2819
2820    /* Quando em modo podcast/música, oculta "Ao Vivo" também */
2821    #player [data-player-status][data-is-stream="false"]::before {
2822        content: "" !important;
2823    }
2824
2825    /* ═══════════════════════════════════════════════════════════════════
2826       ESTADO FALLBACK — streaming SEM metadados (classe .nmp-no-track)
2827       ───────────────────────────────────────────────────────────────────
2828       Estado próprio, com design fechado: não existe faixa para anunciar,
2829       então a hierarquia passa a ser
2830           [selo AO VIVO] → [nome da emissora] → [praça que está tocando]
2831       em um bloco de texto ÚNICO e compacto (entrelinhas apertadas), em vez
2832       de linhas soltas com altura reservada em branco.
2833
2834       O esqueleto NÃO muda (capa → texto → play, mesma ordem, mesmos slots) —
2835       apenas as alturas reservadas das linhas vazias são liberadas e a
2836       tipografia é redistribuída. Ver o histórico em assets/css/style.css:
2837       o fallback antigo tirava a capa do fluxo e sangrava o texto.
2838       ═══════════════════════════════════════════════════════════════════ */
2839
2840    /* Linha sem conteúdo sai do fluxo — nada de 12/16px vazios entre os textos.
2841       Vale SÓ no fallback; com metadados o esqueleto de altura fixa é mantido. */
2842    #player.nmp-no-track .nmp-empty {
2843        display: none !important;
2844    }
2845
2846    /* Coluna de texto: bloco coeso, opticamente centrado com a capa de 48px. */
2847    #player.nmp-no-track .neon-player__meta,
2848    #player.nmp-no-track .wave-player__meta,
2849    #player.nmp-no-track .pulse-player__meta,
2850    #player.nmp-no-track .glow-player__meta,
2851    #player.nmp-no-track .d5-player-meta,
2852    #player.nmp-no-track .player-meta,
2853    #player.nmp-no-track .nmp-meta-wrap {
2854        justify-content: center !important;
2855        gap: 0 !important;
2856    }
2857
2858    /* Selo "AO VIVO" — chip translúcido que ancora o topo do bloco.
2859       align-self: flex-start é obrigatório: sem ele o chip esticaria por toda a
2860       largura da coluna (align-items: stretch do flex) e o fundo viraria faixa.
2861       Cor derivada de --player-text-color → acompanha o painel de cores. */
2862    #player.nmp-no-track [data-player-status] {
2863        display: inline-flex !important;
2864        align-items: center !important;
2865        gap: 5px !important;
2866        align-self: flex-start !important;
2867        width: auto !important;
2868        max-width: 100% !important;
2869        height: auto !important;
2870        flex: 0 0 auto !important;
2871        padding: 3px 8px 3px 7px !important;
2872        margin: 0 0 5px 0 !important;
2873        border-radius: 999px !important;
2874        background: rgba(255, 255, 255, 0.16) !important;
2875        background: color-mix(in srgb, var(--player-text-color, #ffffff) 18%, transparent) !important;
2876        color: var(--player-text-color, #ffffff) !important;
2877        font-size: 0 !important; /* silencia o texto dinâmico do player.js */
2878        line-height: 1 !important;
2879        overflow: visible !important;
2880    }
2881
2882    #player.nmp-no-track [data-player-status]::before {
2883        content: "AO VIVO" !important;
2884        font-size: 9px !important;
2885        font-weight: 700 !important;
2886        letter-spacing: 0.1em !important;
2887        line-height: 1 !important;
2888        color: inherit !important;
2889        opacity: 1 !important;
2890        text-transform: uppercase !important;
2891    }
2892
2893    /* Ponto do "ao vivo" com pulso — order:-1 coloca o pseudo depois no código
2894       à ESQUERDA do texto, sem precisar de elemento extra no DOM. */
2895    #player.nmp-no-track [data-player-status]::after {
2896        content: "" !important;
2897        order: -1 !important;
2898        width: 5px !important;
2899        height: 5px !important;
2900        flex: 0 0 5px !important;
2901        border-radius: 50% !important;
2902        background: currentColor !important;
2903        animation: nmp-live-pulse 1.9s ease-in-out infinite !important;
2904    }
2905
2906    /* Podcast/música sob demanda não é "ao vivo" → chip inteiro sai. */
2907    #player.nmp-no-track [data-player-status][data-is-stream="false"] {
2908        display: none !important;
2909    }
2910
2911    @keyframes nmp-live-pulse {
2912        0%, 100% { opacity: 1; transform: scale(1); }
2913        50%      { opacity: 0.45; transform: scale(0.8); }
2914    }
2915
2916    /* Nome da emissora — linha dominante do estado. Caixa natural (não força
2917       maiúsculas): respeita como o nome foi cadastrado no painel. */
2918    #player.nmp-no-track #nmpDisplayTitle {
2919        font-size: 14px !important;
2920        font-weight: 700 !important;
2921        letter-spacing: 0 !important;
2922        text-transform: none !important;
2923        line-height: 17px !important;
2924        height: 17px !important;
2925        flex: 0 0 17px !important;
2926        margin: 0 !important;
2927        opacity: 1 !important;
2928    }
2929
2930    /* Praça em transmissão ("Você está ouvindo X") — linha de apoio, colada ao
2931       nome da emissora. Reaproveita o slot do artista, então herda o marquee
2932       quando o nome da praça não cabe. */
2933    #player.nmp-no-track #nmpDisplayArtist {
2934        font-size: 11px !important;
2935        font-weight: 500 !important;
2936        letter-spacing: 0 !important;
2937        text-transform: none !important;
2938        line-height: 13px !important;
2939        height: 13px !important;
2940        flex: 0 0 13px !important;
2941        margin: 1px 0 0 0 !important;
2942        opacity: 0.72 !important;
2943    }
2944
2945    /* O elemento do seletor de praças vira redundante no fallback: seu texto já
2946       está na linha de apoio acima (com marquee). Só aqui — no desktop e no
2947       estado com metadados ele continua intacto. */
2948    #player.nmp-no-track #agNowPlaying {
2949        display: none !important;
2950    }
2951
2952    /* Instalação sem nome de emissora configurado: a praça assume o papel de
2953       linha principal em vez de sobrar uma linha pequena e solta. */
2954    #player.nmp-no-track.nmp-no-name #nmpDisplayArtist {
2955        font-size: 13px !important;
2956        font-weight: 600 !important;
2957        line-height: 16px !important;
2958        height: 16px !important;
2959        flex: 0 0 16px !important;
2960        margin: 0 !important;
2961        opacity: 0.92 !important;
2962    }
2963
2964    /* LÓGICA DO BOTÃO VOLTAR AO VIVO NO MOBILE (Quando toca Podcast) */
2965    #player.is-playing-ext #zmpLiveBtn {
2966        display: flex !important;
2967        position: absolute !important;
2968        right: 12px !important;
2969        top: 50% !important;
2970        transform: translateY(-50%) !important;
2971        width: 40px !important;
2972        height: 40px !important;
2973        background: var(--color-primary) !important;
2974        border: 2px solid var(--border-color, transparent) !important;
2975        border-radius: 0 !important; /* Estilo Brutalista Quadrado */
2976        color: var(--player-text-color, #ffffff) !important;
2977        align-items: center !important;
2978        justify-content: center !important;
2979        z-index: 99 !important;
2980        cursor: pointer !important;
2981        box-sizing: border-box !important;
2982        padding: 0 !important;
2983    }
2984    #player.is-playing-ext #zmpLiveBtn svg {
2985        width: 18px !important;
2986        height: 18px !important;
2987        margin: 0 !important;
2988        display: block !important;
2989    }
2990    #player.is-playing-ext .neon-player__info,
2991    #player.is-playing-ext .wave-player__info,
2992    #player.is-playing-ext .pulse-player__info,
2993    #player.is-playing-ext .glow-player__info {
2994        padding-right: 48px !important; /* Evitar que o texto fique debaixo do botão */
2995    }
2996
2997    /* ---- ESTILO DO BOTÃO DE PLAY ---- */
2998    #playBtn, .zmp-btn, .d5-play-btn, .neon-player__play-btn {
2999        background: transparent !important;
3000        border: none !important;
3001        box-shadow: none !important;
3002        width: auto !important;
3003        height: auto !important;
3004        padding: 0 !important;
3005        display: flex !important;
3006        align-items: center !important;
3007        justify-content: center !important;
3008        border-radius: 0 !important;
3009        transform: none !important; 
3010        pointer-events: auto !important;
3011        cursor: pointer !important;
3012        z-index: 10 !important;
3013    }
3014    
3015    #playBtn svg, .zmp-svg, .d5-play-btn i, .neon-player__play-btn svg {
3016        width: 28px !important;
3017        height: 28px !important;
3018        fill: var(--player-text-color, #ffffff) !important;
3019        color: var(--player-text-color, #ffffff) !important;
3020        filter: none !important;
3021    }
3022    
3023    /* Outros widgets fixos de rodapé (WhatsApp / Video aberto) sobem para escapar da pílula */
3024    .vmp-widget, #vmpWidget, #waWidget, .wa-widget {
3025        bottom: calc(145px + env(safe-area-inset-bottom)) !important;
3026        z-index: 9998 !important;
3027    }
3028
3029    /* Garantir que o player__inner não clippe os elementos filhos.
3030       O recorte final é do #player (overflow:hidden na pílula). */
3031    .neon-player__inner,
3032    .wave-player__inner,
3033    .pulse-player__inner,
3034    .glow-player__inner {
3035        overflow: visible !important;
3036    }
3037}
3038</style>
3039
3040
3041<script src="https://cdnjs.cloudflare.com/ajax/libs/color-thief/2.3.0/color-thief.umd.js"></script>
vendor: 1 bytes, line 3041
3041
3042<script>
3043document.addEventListener('DOMContentLoaded', function () {
3044  const items = document.querySelectorAll('.mobile-bottom-nav .nav-item');
3045  const currentPath = window.location.pathname;
3046
3047  items.forEach(item => {
3048    const href = item.getAttribute('href');
3049    if (!href) return;
3050    
3051    try {
3052      // Parse full URLs perfectly by using the current origin as base
3053      const urlObj = new URL(href, window.location.origin);
3054      const itemPath = urlObj.pathname;
3055      
3056      // Match if exact path, or if current starts with itemPath (except for root '/')
3057      if (itemPath === currentPath || (currentPath.startsWith(itemPath) && itemPath !== '/')) {
3058        item.classList.add('active');
3059      }
3060    } catch (e) {
3061      console.warn("Invalid Mobile Nav URL: ", href);
3062    }
3063  });
3064
3065  // Special case: Ensure Home acts correctly when at exact root
3066  const homeItems = document.querySelectorAll('.mobile-bottom-nav .nav-item');
3067  homeItems.forEach(item => {
3068      const href = item.getAttribute('href');
3069      if (href) {
3070          try {
3071              const urlObj = new URL(href, window.location.origin);
3072              if (urlObj.pathname === '/' && currentPath === '/') {
3073                  item.classList.add('active');
3074              } else if (urlObj.pathname === '/') {
3075                  item.classList.remove('active');
3076              }
3077          } catch(e){}
3078      }
3079  });
3080
3081  // =========================================
3082  // LOGIC FOR EXPANDED PLAYER
3083  // =========================================
3084  const miniPlayers = document.querySelectorAll('#player, .radio-player, .neon-player, .d5-player, .app-player, .player-container, .global-player');
3085  const epCard = document.getElementById('expandedPlayerCard');
3086  const epHandle = document.getElementById('epHandle');
3087  const epPlayBtn = document.getElementById('epPlayBtn');
3088
3089  // Synchronizing the UI
3090  let observerActivated = false;
3091  
3092  // Waveform logic
3093  const epWaveform = document.getElementById('epWaveform');
3094  const waveCtx = epWaveform ? epWaveform.getContext('2d') : null;
3095  let waveAnimId;
3096  let waveBars = [];
3097  const NUM_BARS = 18;
3098  const BAR_WIDTH = 3;
3099  const BAR_GAP = 3;
3100  let isPlayingAudio = false;
3101
3102  if (waveCtx) {
3103      for(let i=0; i<NUM_BARS; i++) {
3104          waveBars.push({ height: 2, phase: Math.random() * Math.PI * 2, speed: 0.05 + Math.random() * 0.06 });
3105      }
3106  }
3107
3108  function renderWaveform() {
3109      if(!waveCtx) return;
3110      waveCtx.clearRect(0, 0, epWaveform.width, epWaveform.height);
3111      const isCardOpen = epCard && epCard.classList.contains('active');
3112      let allSettled = true;
3113      const centerY = epWaveform.height; // Bars from bottom up
3114
3115      for(let i=0; i<NUM_BARS; i++) {
3116          const bar = waveBars[i];
3117          let targetHeight = 2; // Idle state
3118          
3119          if(isPlayingAudio && isCardOpen) {
3120              bar.phase += bar.speed;
3121              targetHeight = 4 + Math.abs(Math.sin(bar.phase)) * (epWaveform.height - 6);
3122              allSettled = false;
3123          }
3124          
3125          bar.height += (targetHeight - bar.height) * 0.2;
3126          if(Math.abs(bar.height - targetHeight) > 0.5) {
3127              allSettled = false;
3128          }
3129
3130          const isLight = document.body.classList.contains('theme-light');
3131          waveCtx.fillStyle = isLight ? 'rgba(26,23,48,0.7)' : 'rgba(255,255,255,0.7)';
3132          const startX = (epWaveform.width / 2) - ((NUM_BARS * (BAR_WIDTH + BAR_GAP)) / 2) + i * (BAR_WIDTH + BAR_GAP);
3133          waveCtx.fillRect(startX, Math.max(0, centerY - bar.height), BAR_WIDTH, bar.height);
3134      }
3135
3136      if(!allSettled || (isPlayingAudio && isCardOpen)) {
3137          if (waveAnimId) cancelAnimationFrame(waveAnimId);
3138          waveAnimId = requestAnimationFrame(renderWaveform);
3139      }
3140  }
3141
3142  // Background ColorLogic
3143  const epBgLayer1 = document.getElementById('epBgLayer1');
3144  const epBgLayer2 = document.getElementById('epBgLayer2');
3145  let currentBgLayer = 1;
3146  let lastArtworkSrc = '';
3147  let colorThief = null;
3148  try { colorThief = new ColorThief(); } catch(e) {}
3149
3150  function applyColors(imageUrl) {
3151      if (!colorThief || !imageUrl || imageUrl === lastArtworkSrc) return;
3152      lastArtworkSrc = imageUrl;
3153      
3154      const img = new Image();
3155      img.crossOrigin = 'Anonymous';
3156      img.src = imageUrl;
3157      img.onload = () => {
3158          let palette = null;
3159          try { palette = colorThief.getPalette(img, 3); } catch(e) { return; }
3160          if(palette && palette.length >= 2) {
3161              const c1 = `rgb(${palette[0][0]}, ${palette[0][1]}, ${palette[0][2]})`;
3162              const c2 = `rgb(${palette[1][0]}, ${palette[1][1]}, ${palette[1][2]})`;
3163              const gradient = `linear-gradient(135deg, ${c1} 0%, ${c2} 100%)`;
3164              
3165              if(currentBgLayer === 1 && epBgLayer2) {
3166                  epBgLayer2.style.backgroundImage = gradient;
3167                  epBgLayer2.style.opacity = '1';
3168                  if(epBgLayer1) epBgLayer1.style.opacity = '0';
3169                  currentBgLayer = 2;
3170              } else if (currentBgLayer === 2 && epBgLayer1) {
3171                  epBgLayer1.style.backgroundImage = gradient;
3172                  epBgLayer1.style.opacity = '1';
3173                  if(epBgLayer2) epBgLayer2.style.opacity = '0';
3174                  currentBgLayer = 1;
3175              }
3176          }
3177      };
3178  }
3179
3180  window.syncAllPlayerUI = function() {
3181      const epTitle = document.getElementById('epTitle');
3182      const epArtist = document.getElementById('epArtist');
3183      const epArtworkImg = document.getElementById('epArtworkImg');
3184      const epPlayIcon = document.getElementById('epPlayIcon');
3185      
3186      const pTitle = document.getElementById('playerTitle') || document.querySelector('.player-title');
3187      const pArtist = document.getElementById('playerArtist') || document.querySelector('.player-artist');
3188      const pArtwork = document.getElementById('playerArtwork') || document.querySelector('.player-artwork');
3189      const pPlayIcon = document.getElementById('playIcon') || document.querySelector('.play-icon, #playBtn svg, .global-play-btn svg');
3190      
3191      const doSync = () => {
3192          if(pTitle && epTitle) epTitle.textContent = pTitle.textContent;
3193          if(pArtist && epArtist) epArtist.textContent = pArtist.textContent;
3194          
3195          if(pArtwork && epArtworkImg) {
3196              const img = pArtwork.querySelector('img');
3197              if(img && img.src) {
3198                  epArtworkImg.removeAttribute('crossorigin');
3199                  epArtworkImg.src = img.src;
3200                  applyColors(img.src);
3201              } else if (pArtwork.tagName === 'IMG') {
3202                  epArtworkImg.removeAttribute('crossorigin');
3203                  epArtworkImg.src = pArtwork.src;
3204                  applyColors(pArtwork.src);
3205              }
3206          }
3207          
3208          let wasPlaying = isPlayingAudio;
3209          if(pPlayIcon && epPlayIcon) {
3210              const htmlStr = pPlayIcon.tagName.toLowerCase() === 'svg' ? pPlayIcon.outerHTML : pPlayIcon.innerHTML;
3211              // Remove inner IDs to avoid DOM conflicts
3212              const cleanHtmlStr = htmlStr.replace(/id="[^"]*"/g, '');
3213              epPlayIcon.innerHTML = cleanHtmlStr;
3214              
3215              if (htmlStr.includes('M7 ') || htmlStr.includes('M7,')) {
3216                  isPlayingAudio = true;
3217              } else if (htmlStr.includes('M6 3.5') || htmlStr.includes('M6,')) {
3218                  isPlayingAudio = false;
3219              } else {
3220                  isPlayingAudio = window.radioPlayer && window.radioPlayer.state === 'transmitting';
3221              }
3222          }
3223          if (wasPlaying !== isPlayingAudio) {
3224              renderWaveform();
3225          }
3226      };
3227      
3228      doSync();
3229      
3230      if(!observerActivated) {
3231          observerActivated = true;
3232          const observer = new MutationObserver(() => doSync());
3233          if(pTitle) observer.observe(pTitle, { childList: true, characterData: true, subtree: true });
3234          if(pArtist) observer.observe(pArtist, { childList: true, characterData: true, subtree: true });
3235          if(pArtwork) observer.observe(pArtwork, { childList: true, attributes: true, subtree: true, attributeFilter: ['src'] });
3236          if(pPlayIcon) observer.observe(pPlayIcon, { childList: true, subtree: true, attributes: true });
3237      }
3238  };
3239
3240  // Listen to global changes from RadioPlayer
3241  document.addEventListener('radioPlayerStateChange', window.syncAllPlayerUI);
3242
3243  // Bind Open/Toggle actions explicitly
3244  miniPlayers.forEach(p => {
3245      p.addEventListener('click', (e) => {
3246          // Card expandido é exclusivo do mobile (inclui celular em paisagem) — ignorar em desktop/tablet
3247          if (!window.matchMedia('(max-width: 1024px), (pointer: coarse) and (max-width: 1366px)').matches) return;
3248
3249          const isPlayBtn = e.target.closest('#playBtn') || e.target.closest('.d5-play-btn') ||
3250              e.target.closest('.neon-player__play-btn') || e.target.closest('.global-btn') ||
3251              e.target.closest('.global-play') || e.target.closest('#volumeBtn') ||
3252              e.target.closest('#zmpLiveBtn') || e.target.closest('#liveBtn') ||
3253              e.target.closest('.zmp-volume-slider') || e.target.closest('.zmp-player-controls-wrapper') ||
3254              e.target.closest('#btnAssistirAoVivo') || e.target.closest('.neon-player__video-btn-wrap');
3255
3256          if (isPlayBtn) {
3257              e.stopPropagation();
3258              return;
3259          }
3260
3261          if (epCard) {
3262              epCard.style.display = 'flex';
3263              void epCard.offsetWidth; // Force Reflow
3264              epCard.classList.add('active');
3265              document.body.style.overflow = 'hidden'; 
3266              window.syncAllPlayerUI(); 
3267              renderWaveform();
3268          }
3269      });
3270  });
3271
3272  // Expanded Play Btn
3273  if(epPlayBtn) {
3274      epPlayBtn.addEventListener('click', (e) => {
3275          e.stopPropagation();
3276          e.preventDefault();
3277          if (window.radioPlayer && typeof window.radioPlayer.togglePlay === 'function') {
3278              window.radioPlayer.togglePlay();
3279          } else {
3280              // Fallback
3281              const nativePlayBtn = document.getElementById('playBtn') || document.querySelector('.neon-player__play-btn, .d5-play-btn, .global-play');
3282              if(nativePlayBtn) nativePlayBtn.click();
3283          }
3284          setTimeout(window.syncAllPlayerUI, 50);
3285      });
3286  }
3287
3288  // Swipe capabilities
3289  if(epCard && epHandle) {
3290      function closeExpandedPlayer() {
3291          epCard.classList.remove('active');
3292          epCard.style.transform = ''; 
3293          document.body.style.overflow = '';
3294          renderWaveform(); // Jump start fade out
3295          setTimeout(() => {
3296              if(!epCard.classList.contains('active')) {
3297                  epCard.style.display = 'none';
3298              }
3299          }, 450);
3300      }
3301      
3302      let startY = 0;
3303      let currentY = 0;
3304      let isDragging = false;
3305
3306      // Click to close
3307      epHandle.addEventListener('click', (e) => {
3308          e.stopPropagation();
3309          closeExpandedPlayer();
3310      });
3311      epHandle.addEventListener('touchstart', (e) => {
3312          e.stopPropagation(); 
3313      }, {passive: true});
3314      epHandle.addEventListener('touchend', (e) => {
3315          e.stopPropagation();
3316          e.preventDefault();
3317          closeExpandedPlayer();
3318      }, {passive: false});
3319
3320      epCard.addEventListener('touchstart', (e) => {
3321          startY = e.touches[0].clientY;
3322          isDragging = true;
3323          epCard.style.transition = 'none'; 
3324      }, {passive: true});
3325
3326      epCard.addEventListener('touchmove', (e) => {
3327          if(!isDragging) return;
3328          currentY = e.touches[0].clientY;
3329          const diff = currentY - startY;
3330          if(diff > 0) { 
3331              epCard.style.transform = `translateY(${diff}px)`;
3332          }
3333      }, {passive: true});
3334
3335      epCard.addEventListener('touchend', (e) => {
3336          if(!isDragging) return;
3337          isDragging = false;
3338          const diff = currentY - startY;
3339          epCard.style.transition = 'transform 0.4s cubic-bezier(0.32, 0.72, 0, 1)';
3340          
3341          if(diff > 150) { 
3342              closeExpandedPlayer();
3343          } else {
3344              epCard.style.transform = '';
3345          }
3346      });
3347  }
3348
3349
3350  // ======================================================
3351  // Filtro de Metadados + Marquee do mini player mobile
3352  //
3353  // ESQUELETO ÚNICO: a estrutura, a ordem e a distribuição são as mesmas em
3354  // todos os estados. Sem metadados, só o TEXTO muda — nada é removido do DOM
3355  // e nada troca de lugar. Por isso não existe mais bloco de fallback próprio.
3356  // CORREÇÃO: flag de reentrância para evitar loop infinito
3357  // ======================================================
3358  (function initMetadataFilterAndMarquee() {
3359    if (!window.matchMedia('(max-width: 1024px), (pointer: coarse) and (max-width: 1366px)').matches) return; // Apenas mobile (inclui celular em paisagem)
3360
3361    // Palavras que indicam player em modo não-música. 'live' foi removido
3362    // para não bloquear podcasts como "Live Sessions" — 'ao vivo'/'aovivo' bastam.
3363    var BLOCKLIST = ['vinheta','trilha','break','off','locu\u00e7\u00e3o','locutor','intervalo','comercial','patroc\u00ednio','jingle','spot','ao vivo','aovivo'];
3364    var FALLBACK_NAME  = "Rádio 93 FM";
3365    // Deploy em que o include roda fora do escopo de $siteName: cai na constante
3366    // que o head.php do tema publica, em vez de deixar a linha do nome vazia.
3367    if (!FALLBACK_NAME && typeof SITE_NAME !== 'undefined') FALLBACK_NAME = SITE_NAME || '';
3368
3369    // O texto de "carregando" só faz sentido nos primeiros segundos. Passada a
3370    // carência, se ainda não chegou metadado, a linha de título passa a exibir o
3371    // nome da emissora (configuração do tenant) em vez de mentir "carregando".
3372    var LOADING_GRACE_MS = 3000;
3373    var _startedAt = Date.now();
3374
3375    // FLAG: evitar loop infinito (observer -> DOM change -> observer...)
3376    var _busy = false;
3377
3378    function isMusicMetadata(title, artist) {
3379      var str = (title + ' ' + artist).toLowerCase();
3380      return !BLOCKLIST.some(function(w){ return str.indexOf(w) !== -1; });
3381    }
3382
3383    // Título que não representa uma faixa: vazio ou placeholder de carregamento
3384    // já fora da janela de carência.
3385    function isPlaceholderTitle(title) {
3386      if (!title) return true;
3387      return /^carregando/i.test(title) && (Date.now() - _startedAt) > LOADING_GRACE_MS;
3388    }
3389
3390    // Cria elementos de EXIBIÇÃO dedicados para o marquee.
3391    // Os elementos originais (#playerTitle/#playerArtist) NÃO são tocados —
3392    // apenas ocultados. Assim o MutationObserver não entra em loop.
3393    function getOrCreateDisplayEl(sourceEl, displayId) {
3394      var existing = document.getElementById(displayId);
3395      if (existing) return existing;
3396      var d = document.createElement('div');
3397      d.id = displayId;
3398      d.className = 'mini-player-text';
3399      d.appendChild(document.createElement('span'));
3400      sourceEl.parentNode.insertBefore(d, sourceEl.nextSibling);
3401      return d;
3402    }
3403
3404    // Velocidade do marquee em px/s. Duração fixa fazia texto curto se arrastar
3405    // e texto longo passar voando; com velocidade constante a leitura tem sempre
3406    // o mesmo ritmo — só o tempo de volta muda.
3407    var MARQUEE_SPEED  = 45;
3408    var MARQUEE_MIN_S  = 8;
3409    var MARQUEE_MAX_S  = 45;
3410
3411    // Monta uma "passada" do texto. `parts` é uma lista de trechos {text, cls} —
3412    // é o que permite música, separador e artista terem pesos diferentes dentro
3413    // da MESMA linha. wrapAsRun envolve tudo em .nmp-run (usado só no estado
3414    // rolando, onde existem duas cópias iguais).
3415    function buildRun(parts, wrapAsRun) {
3416      var frag = document.createDocumentFragment();
3417      var host = frag;
3418      if (wrapAsRun) {
3419        host = document.createElement('i');
3420        host.className = 'nmp-run';
3421        frag.appendChild(host);
3422      }
3423      for (var i = 0; i < parts.length; i++) {
3424        var p = parts[i];
3425        if (!p.text) continue;
3426        if (p.cls) {
3427          var s = document.createElement('span');
3428          s.className = p.cls;
3429          s.textContent = p.text; // textContent: metadado do streaming não é markup
3430          host.appendChild(s);
3431        } else {
3432          host.appendChild(document.createTextNode(p.text));
3433        }
3434      }
3435      return frag;
3436    }
3437
3438    // Ativa o marquee SÓ quando o texto realmente estoura o container.
3439    // A medição é SÍNCRONA: ler clientWidth já força o cálculo de layout, então
3440    // não dependemos de requestAnimationFrame — que fica suspenso quando a aba
3441    // está em segundo plano. Era essa dependência (mais a medição num único
3442    // frame, quando o elemento ainda podia ter largura 0) que deixava título
3443    // longo cortado sem nunca rolar. O retry usa setTimeout pelo mesmo motivo.
3444    function applyMarqueeOnDisplay(displayEl, parts) {
3445      if (!displayEl) return;
3446      var span = displayEl.querySelector('span');
3447      if (!span) return;
3448
3449      span.classList.remove('scrolling');
3450      span.style.animationDuration = '';
3451      span.textContent = '';
3452      span.appendChild(buildRun(parts, false));
3453
3454      var tries = 0;
3455      function decide() {
3456        // Container ainda sem largura medível (acabou de entrar no DOM, ou a
3457        // pílula ainda não foi posicionada) → tenta de novo em 100ms.
3458        if (displayEl.clientWidth === 0 && tries++ < 20) {
3459          setTimeout(decide, 100);
3460          return;
3461        }
3462        // Medir no CONTAINER, não no <span>: fora do estado de rolagem o span é
3463        // inline (para o ellipsis do pai funcionar) e inline não expõe scrollWidth.
3464        if (displayEl.scrollWidth <= displayEl.clientWidth + 1) return; // cabe: fica parado
3465
3466        // Duas cópias idênticas, cada uma com o intervalo embutido no próprio
3467        // padding — metade da largura total vira exatamente uma cópia, então o
3468        // translateX(-50%) emenda o fim no começo sem salto.
3469        span.textContent = '';
3470        span.appendChild(buildRun(parts, true));
3471        span.appendChild(buildRun(parts, true));
3472        span.classList.add('scrolling');
3473
3474        // A distância percorrida por ciclo é UMA cópia (texto + intervalo) —
3475        // é ela que define a duração, não a largura total do span.
3476        var run = span.querySelector('.nmp-run');
3477        var dist = run ? run.offsetWidth : 0;
3478        if (dist > 0) {
3479          var secs = Math.min(MARQUEE_MAX_S, Math.max(MARQUEE_MIN_S, dist / MARQUEE_SPEED));
3480          span.style.animationDuration = secs.toFixed(1) + 's';
3481        }
3482      }
3483      decide();
3484    }
3485
3486    // Texto da praça em transmissão ("Você está ouvindo X"), injetado pelo
3487    // seletor de praças (Modo Rede). No estado fallback ele passa a ser a linha
3488    // de apoio do bloco de texto — o elemento original é ocultado por CSS para
3489    // não duplicar a informação. Fora do Modo Rede o elemento não existe e o
3490    // fallback fica com duas linhas (selo + nome da emissora).
3491    function getCityLine() {
3492      var el = document.getElementById('agNowPlaying');
3493      if (!el || el.style.display === 'none') return '';
3494      return (el.textContent || '').trim();
3495    }
3496
3497    // Localiza o bloco capa+texto em qualquer um dos quatro temas.
3498    function findInfoEl(playerEl, titleEl) {
3499      var themed = playerEl.querySelector(
3500        '.neon-player__info, .wave-player__info, .pulse-player__info, .glow-player__info'
3501      );
3502      if (themed) return themed;
3503      return (titleEl.parentNode && titleEl.parentNode !== playerEl) ? titleEl.parentNode : null;
3504    }
3505
3506    function applyMetadataFilter() {
3507      if (_busy) return; // Evitar reentrada
3508
3509      var playerEl = document.getElementById('player') ||
3510                     document.querySelector('.neon-player, .radio-player');
3511      if (!playerEl) return;
3512
3513      var titleEl  = document.getElementById('playerTitle');
3514      var artistEl = document.getElementById('playerArtist');
3515      if (!titleEl || !artistEl) return;
3516
3517      var infoEl = findInfoEl(playerEl, titleEl);
3518      if (!infoEl) return;
3519
3520      var title  = (titleEl.textContent || '').trim();
3521      var artist = (artistEl.textContent || '').trim();
3522
3523      // Sem faixa exibível → ESTADO FALLBACK: a linha de título recebe o nome
3524      // da emissora e a de apoio recebe a praça em transmissão. A classe
3525      // .nmp-no-track no player liga o design próprio desse estado (bloco de
3526      // texto compacto + selo "AO VIVO"); o esqueleto continua o mesmo.
3527      var hasTrack   = !isPlaceholderTitle(title) && isMusicMetadata(title, artist);
3528      var showTitle  = hasTrack ? title  : FALLBACK_NAME;
3529      var showArtist = hasTrack ? artist : getCityLine();
3530
3531      // COM FAIXA → LINHA ÚNICA "MÚSICA • ARTISTA".
3532      // Duas linhas de ~150px espremiam os dois textos; em uma linha só o texto
3533      // tem o dobro da largura útil e o excedente rola em marquee. O slot do
3534      // artista continua no DOM, apenas vazio (.nmp-empty) — o esqueleto do
3535      // mini player não muda. Sem faixa, o fallback segue com as duas linhas
3536      // (nome da emissora + praça), cada uma com seu próprio marquee.
3537      var titleParts, artistParts;
3538      if (hasTrack) {
3539        titleParts = [{ text: showTitle, cls: 'nmp-track' }];
3540        if (showArtist) {
3541          titleParts.push({ text: ' • ', cls: 'nmp-sep' });
3542          titleParts.push({ text: showArtist, cls: 'nmp-artist' });
3543        }
3544        artistParts = [];
3545        showArtist  = ''; // o slot do artista fica vazio → sai do fluxo
3546      } else {
3547        titleParts  = [{ text: showTitle }
3547];
3548        artistParts = [{ text: showArtist }];
3549      }
3550
3551      _busy = true;
3552      try {
3553        // Classes de estado no #player — não são observadas pelo
3554        // MutationObserver (que só olha o texto de title/artist), então não há
3555        // risco de reentrância aqui.
3556        playerEl.classList.toggle('nmp-no-track', !hasTrack);
3557        playerEl.classList.toggle('nmp-no-name', !hasTrack && !showTitle);
3558        playerEl.classList.toggle('nmp-one-line', hasTrack);
3559
3560        // O bloco capa+texto NUNCA é ocultado — o esqueleto é sempre o mesmo.
3561        infoEl.style.display = '';
3562
3563        // Exibição dedicada para marquee (não toca nos originais observados)
3564        var displayTitle  = getOrCreateDisplayEl(titleEl, 'nmpDisplayTitle');
3565        var displayArtist = getOrCreateDisplayEl(artistEl, 'nmpDisplayArtist');
3566
3567        applyMarqueeOnDisplay(displayTitle, titleParts);
3568        applyMarqueeOnDisplay(displayArtist, artistParts);
3569
3570        // Linha vazia é retirada do fluxo no fallback e na linha única (regras
3571        // CSS #player.nmp-no-track .nmp-empty / #player.nmp-one-line .nmp-empty)
3572        // — evita o vão vertical em branco entre o selo e o texto.
3573        displayTitle.classList.toggle('nmp-empty', !showTitle);
3574        displayArtist.classList.toggle('nmp-empty', !showArtist);
3575
3576        // Usa classe CSS com !important para sobrescrever regras de tema (ex: wave-player__title)
3577        titleEl.classList.add('nmp-src-hidden');
3578        artistEl.classList.add('nmp-src-hidden');
3579        displayTitle.classList.remove('nmp-src-hidden');
3580        displayArtist.classList.remove('nmp-src-hidden');
3581        displayTitle.style.display = '';
3582        displayArtist.style.display = '';
3583      } finally {
3584        _busy = false;
3585      }
3586    }
3587
3588    // ----------------------------------------------------------------
3589    // Observer nos dois elementos originais.
3590    // CRITICAL: usar characterData:true + subtree:true para detectar
3591    // quando player.js faz `element.textContent = "..."` (muda text node)
3592    // O flag _busy já previne o loop de reintrância.
3593    // ----------------------------------------------------------------
3594    var titleEl  = document.getElementById('playerTitle');
3595    var artistEl = document.getElementById('playerArtist');
3596    var mo = new MutationObserver(function() {
3597      if (_busy) return;
3598      applyMetadataFilter();
3599    });
3600    var obsOpts = { childList: true, characterData: true, subtree: true };
3601    if (titleEl)  mo.observe(titleEl,  obsOpts);
3602    if (artistEl) mo.observe(artistEl, obsOpts);
3603
3604    // Também acionar quando o próprio radioPlayer dispara changestate
3605    setTimeout(applyMetadataFilter, 800);
3606    // Passada extra ao fim da carência: troca "Carregando..." pelo nome da
3607    // emissora caso o streaming não tenha mandado metadado nenhum.
3608    setTimeout(applyMetadataFilter, LOADING_GRACE_MS + 200);
3609    document.addEventListener('radioPlayerStateChange', function() {
3610      if (_busy) return;
3611      // Pequeno delay para o DOM ja ter sido atualizado
3612      setTimeout(applyMetadataFilter, 50);
3613    });
3614
3615    // Rotação de tela / mudança de largura muda a decisão do marquee.
3616    var _rzTimer;
3617    window.addEventListener('resize', function() {
3618      clearTimeout(_rzTimer);
3619      _rzTimer = setTimeout(applyMetadataFilter, 200);
3620    });
3621
3622    // Expor para reavaliação externa (ex.: troca de praça no Modo Rede)
3623    window.checkMiniPlayerOverflow = applyMetadataFilter;
3624  })();
3625
3626});
3627</script>
3627
3628
3629<!-- EXPANDED MOBILE PLAYER CARD -->
3630<div class="expanded-player-card" id="expandedPlayerCard">
3631    <div class="ep-bg-layer" id="epBgLayer1" style="opacity: 1;"></div>
3632    <div class="ep-bg-layer" id="epBgLayer2" style="opacity: 0;"></div>
3633    <div class="ep-bg-overlay"></div>
3634    <button class="ep-handle-btn" id="epHandle" aria-label="Minimize">
3635        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
3636    </button>
3637    <div class="ep-content">
3638        <div class="ep-artwork has-image">
3639            <img src="" alt="Album Art" id="epArtworkImg">
3640        </div>
3641        <div class="ep-metadata">
3642            <h2 class="ep-title" id="epTitle">...</h2>
3643            <p class="ep-artist" id="epArtist">...</p>
3644            <canvas id="epWaveform" class="ep-waveform" width="120" height="30"></canvas>
3645        </div>
3646        <div class="ep-controls">
3647            <button class="ep-play-btn" id="epPlayBtn" aria-label="Play/Pause">
3648                <div class="ep-play-icon" id="epPlayIcon">
3649                    <!-- Ícone injetado dinamicamente -->
3650                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 3.5a1.5 1.5 0 0 1 2.3-.8l11.5 8a1.5 1.5 0 0 1 0 2.6l-11.5 8A1.5 1.5 0 0 1 6 20.5v-17z" fill="currentColor"/></svg>
3651                </div>
3652            </button>
3653        </div>
3654    </div>
3655</div>
3656
3657<style>
3658/* Oculto em Desktop, exibido em Mobile */
3659.expanded-player-card {
3660    display: none;
3661}
3662
3663@media (max-width: 1024px), (pointer: coarse) and (max-width: 1366px) {
3664    .expanded-player-card {
3665        display: flex;
3666        flex-direction: column;
3667        position: fixed;
3668        bottom: calc(85px + env(safe-area-inset-bottom));
3669        left: 12px;
3670        width: calc(100% - 24px);
3671        height: 60px;
3672        z-index: 99999;
3673        background-color: var(--neon-bg-deep, var(--bg-color, #121212));
3674        color: var(--neon-text, var(--text-color, #ffffff));
3675        border-radius: 12px;
3676        opacity: 0;
3677        pointer-events: none;
3678        transition: all 0.42s cubic-bezier(0.32, 0.72, 0, 1);
3679        overflow: hidden;
3680        box-sizing: border-box;
3681    }
3682
3683    .expanded-player-card.active {
3684        bottom: 0;
3685        left: 0;
3686        width: 100%;
3687        height: 100vh;
3688        height: 100dvh;
3689        border-radius: 0;
3690        opacity: 1;
3691        pointer-events: auto;
3692    }
3693    
3694    .ep-bg-layer {
3695        position: absolute;
3696        top: 0; left: 0; right: 0; bottom: 0;
3697        z-index: 0;
3698        transition: opacity 1.5s ease;
3699        background-size: cover;
3700        background-position: center;
3701        background-image: linear-gradient(135deg, var(--neon-bg-deep, var(--bg-color, #121212)) 0%, rgba(0,0,0,0.9) 100%);
3702    }
3703    
3704    body.theme-light .ep-bg-layer {
3705        background-image: linear-gradient(135deg, var(--neon-bg-deep, var(--bg-color, #f0eef5)) 0%, rgba(255,255,255,0.9) 100%);
3706    }
3707    
3708    .ep-bg-overlay {
3709        position: absolute;
3710        top: 0; left: 0; right: 0; bottom: 0;
3711        z-index: 1;
3712        background: linear-gradient(to bottom, rgba(0,0,0,0.4) 0%, var(--neon-bg-deep, var(--bg-color, #121212)) 100%);
3713        pointer-events: none;
3714    }
3715    
3716    body.theme-light .ep-bg-overlay {
3717        background: linear-gradient(to bottom, rgba(255,255,255,0.2) 0%, var(--neon-bg-deep, var(--bg-color, #f0eef5)) 100%);
3718    }
3719    
3720    .ep-handle-btn {
3721        position: absolute;
3722        top: calc(16px + env(safe-area-inset-top));
3723        left: 16px;
3724        width: 44px;
3725        height: 44px;
3726        border-radius: 50%;
3727        background-color: var(--glass-border, rgba(255,255,255,0.1));
3728        border: none;
3729        display: flex;
3730        justify-content: center;
3731        align-items: center;
3732        cursor: pointer;
3733        color: var(--neon-text, var(--text-color, #fff));
3734        transition: transform 0.1s, background-color 0.2s;
3735        z-index: 99;
3736        padding: 0;
3737        pointer-events: auto;
3738    }
3739    
3740    .ep-handle-btn:active {
3741        transform: scale(0.9);
3742        background-color: var(--neon-surface-hover, rgba(255,255,255,0.2));
3743    }
3744    
3745    .ep-content {
3746        flex: 1;
3747        display: flex;
3748        flex-direction: column;
3749        align-items: center;
3750        justify-content: center;
3751        width: 100%;
3752        padding: 0 24px;
3753        box-sizing: border-box;
3754        opacity: 0;
3755        transition: opacity 0.2s ease;
3756        position: relative;
3757        z-index: 2;
3758    }
3759    .expanded-player-card.active .ep-content {
3760        opacity: 1;
3761        transition-delay: 0.15s;
3762    }
3763    
3764    .ep-artwork {
3765        width: 85%;
3766        aspect-ratio: 1 / 1;
3767        border-radius: 16px;
3768        background-color: var(--glass-border, rgba(255,255,255,0.1));
3769        box-shadow: var(--shadow-lg, 0 16px 40px rgba(0,0,0,0.5));
3770        margin-bottom: 6vh;
3771        overflow: hidden;
3772        display: flex;
3773        align-items: center;
3774        justify-content: center;
3775    }
3776    
3777    .ep-artwork img {
3778        width: 100%;
3779        height: 100%;
3780        object-fit: cover;
3781    }
3782    
3783    .ep-metadata {
3784        width: 100%;
3785        text-align: center;
3786        margin-bottom: 6vh;
3787    }
3788    
3789    .ep-title {
3790        font-family: var(--font-primary, sans-serif);
3791        font-size: 24px;
3792        font-weight: 700;
3793        margin: 0 0 8px 0;
3794        white-space: nowrap;
3795        overflow: hidden;
3796        text-overflow: ellipsis;
3797        color: var(--neon-text, var(--primary-color, var(--text-color, #fff)));
3798    }
3799    
3800    .ep-artist {
3801        font-family: var(--font-secondary, var(--font-primary, sans-serif));
3802        font-size: 16px;
3803        font-weight: 400;
3804        margin: 0;
3805        color: var(--neon-text-muted, var(--text-color, #ccc));
3806        opacity: 0.8;
3807        white-space: nowrap;
3808        overflow: hidden;
3809        text-overflow: ellipsis;
3810    }
3811    
3812    .ep-waveform {
3813        display: block;
3814        margin: 16px auto 0 auto;
3815        opacity: 0.8;
3816    }
3817    
3818    .ep-controls {
3819        width: 100%;
3820        display: flex;
3821        justify-content: center;
3822        align-items: center;
3823    }
3824    
3825    .ep-play-btn {
3826        width: 72px;
3827        height: 72px;
3828        border-radius: 50%;
3829        background-color: var(--primary-color, #1B1464);
3830        border: none;
3831        display: flex;
3832        justify-content: center;
3833        align-items: center;
3834        cursor: pointer;
3835        padding: 0;
3836        box-shadow: 0 8px 16px rgba(0,0,0,0.2);
3837        transition: transform 0.2s, background-color 0.2s;
3838        color: #ffffff !important; 
3839    }
3840    
3841    .ep-play-btn:active {
3842        transform: scale(0.95);
3843    }
3844    
3845    .ep-play-icon {
3846        width: 28px;
3847        height: 28px;
3848        display: flex;
3849        justify-content: center;
3850        align-items: center;
3851    }
3852    .ep-play-icon svg {
3853        width: 100%;
3854        height: 100%;
3855        color: #ffffff;
3856        fill: currentColor;
3857    }
3858}
3859</style>
3860    
3861    <!-- Barra de Status de Conexão Global (Apenas Mobile via CSS) -->
3862    
3863<div id="connectionStatusBar" role="alert" aria-live="assertive" aria-atomic="true">
3864    <span id="connectionStatusIcon"></span>
3865    <span id="connectionStatusMsg"></span>
3866</div>
3867
3868<style>
3869/* Oculto em desktop */
3870#connectionStatusBar {
3871    display: none;
3872}
3873
3874@media (max-width: 768px) {
3875    #connectionStatusBar {
3876        position: fixed;
3877        top: calc(env(safe-area-inset-top) + 12px);
3878        left: 16px;
3879        right: 16px;
3880        z-index: 100000;
3881        display: flex;
3882        align-items: center;
3883        gap: 10px;
3884        padding: 12px 20px;
3885        border-radius: 50px;
3886        color: #ffffff;
3887        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
3888        font-size: 14px;
3889        font-weight: 500;
3890        line-height: 1.3;
3891        box-sizing: border-box;
3892        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
3893        /* Estado padrão: acima da viewport com fade */
3894        transform: translateY(-80px);
3895        opacity: 0;
3896        pointer-events: none;
3897        transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1),
3898                    opacity 0.3s ease,
3899                    background-color 0.25s ease;
3900        will-change: transform, opacity;
3901    }
3902
3903    #connectionStatusBar.is-visible {
3904        transform: translateY(0);
3905        opacity: 1;
3906        pointer-events: auto;
3907    }
3908
3909    #connectionStatusBar.state-offline {
3910        background-color: #111111;
3911    }
3912
3913    #connectionStatusBar.state-online {
3914        background-color: #2e7d32;
3915    }
3916
3917    #connectionStatusIcon {
3918        flex-shrink: 0;
3919        width: 20px;
3920        height: 20px;
3921        display: flex;
3922        align-items: center;
3923        justify-content: center;
3924    }
3925
3926    #connectionStatusIcon svg {
3927        width: 20px;
3928        height: 20px;
3929        display: block;
3930    }
3931
3932    #connectionStatusMsg {
3933        flex: 1;
3934        min-width: 0;
3935        white-space: nowrap;
3936        overflow: hidden;
3937        text-overflow: ellipsis;
3938    }
3939}
3940</style>
3941
3942<script>
3943(function () {
3944    /* Ícone Wi-Fi com X — estilo Happn */
3945    var ICON_OFFLINE = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6C4.27 3.47 8.46 2 12 2s7.73 1.47 11 4"/><path d="M5 10a10.16 10.16 0 0 1 7-2.82A10.16 10.16 0 0 1 19 10"/><path d="M8.53 14A5.14 5.14 0 0 1 12 12.85"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="21" y1="15" x2="15" y2="21"/></svg>';
3946    var ICON_ONLINE  = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>';
3947
3948    var bar      = document.getElementById('connectionStatusBar');
3949    var iconEl   = document.getElementById('connectionStatusIcon');
3950    var msgEl    = document.getElementById('connectionStatusMsg');
3951    var dismissTimer = null;
3952
3953    function showBar(type) {
3954        if (!bar) return;
3955
3956        if (dismissTimer) {
3957            clearTimeout(dismissTimer);
3958            dismissTimer = null;
3959        }
3960
3961        // Remove estados anteriores e força reflow para reiniciar a transição
3962        bar.classList.remove('state-offline', 'state-online', 'is-visible');
3963        void bar.offsetWidth;
3964
3965        if (type === 'offline') {
3966            iconEl.innerHTML = ICON_OFFLINE;
3967            msgEl.textContent = 'Sem conexão à internet';
3968            bar.classList.add('state-offline', 'is-visible');
3969
3970            // Vibração háptica — iOS não suporta navigator.vibrate
3971            if (navigator.vibrate) {
3972                navigator.vibrate([200, 100, 200]);
3973            }
3974        } else {
3975            iconEl.innerHTML = ICON_ONLINE;
3976            msgEl.textContent = 'Conexão restabelecida';
3977            bar.classList.add('state-online', 'is-visible');
3978
3979            dismissTimer = setTimeout(function () {
3980                bar.classList.remove('is-visible');
3981                dismissTimer = null;
3982            }, 3000);
3983        }
3984    }
3985
3986    window.addEventListener('offline', function () { showBar('offline'); });
3987    window.addEventListener('online',  function () { showBar('online');  });
3988})();
3989</script>
3989
3990
3991    <!-- Audio Player Script — não carregado dentro do app Rádio 3.0: sem o markup
3992         do player ele quebraria no bindEvents, e lá o site não deve tocar áudio nem
3993         disputar a Media Session com o player nativo. Quem usa window.radioPlayer
3994         já confere se ele existe. -->
3995        
3995<script src="https://radio93portofeliz.com.br/assets/js/player.js?v=1790901861"></script>
3995
3996        
3996<script src="https://radio93portofeliz.com.br/assets/js/app.js?v=1790901861" defer></script>
3996
3997
3998    <!-- PWA Service Worker Registration -->
3999    
3999<script src="https://radio93portofeliz.com.br/sw-init.js" defer></script>
3999
4000
4001    <!-- Analytics Tracker -->
4002    
4002<script src="https://radio93portofeliz.com.br/assets/js/tracker.js" defer></script>
4002
4003
4004    <!-- Top Músicas: título/artista que não cabe no card desliza em vez de cortar -->
4005    
4005<script src="https://radio93portofeliz.com.br/assets/js/chart-marquee.js?v=1790785403" defer></script>
4005
4006
4007    <!-- Banner Tracking (impressões e cliques) -->
4008    <!-- Não redeclarar: o head.php do tema já define `const SITE_URL`. Usar
4009         window.SITE_URL evita "Identifier 'SITE_URL' has already been declared"
4010         (que quebrava o tracking de banner e scripts seguintes). -->
4011    
4011<script>window.SITE_URL = window.SITE_URL || 'https://radio93portofeliz.com.br';</script>
4011
4012        
4012<script>const ACTIVE_CITY_ID = null;</script>
4012
4013    
4013<script src="https://radio93portofeliz.com.br/assets/js/banner-tracking.js?v=1784221298" defer></script>
4013
4014
4015    
4016    <!-- JSON-LD para SEO -->
4017    
4017<script type="application/ld+json">
4018    {
4019      "@context": "https://schema.org",
4020      "@type": "RadioStation",
4021      "name": "Rádio 93 FM",
4022      "url": "https://radio93portofeliz.com.br",
4023      "logo": "https://radio93portofeliz.com.br/uploads/media/6a05dc8e13c73_1778769038.png",
4024      "description": "A primeira em Porto Feliz!"
4025    }
4026    </script>
4026
4027
4028    <!-- Código Personalizado — fim do <body> (raw, escopo do tenant) -->
4029        </body>
4030</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.