AvanzaCast/docs/broadcast_panel_ux.md

58 KiB

INFORME TÉCNICO COMPLEMENTARIO (Análisis de UX del Video)

I. Estructura y Componentes Lógicos (Actualización):

Se confirman los componentes previamente definidos (PageContainer, Sidebar, Header, HeroSection, TransmissionsTable) y se añaden los siguientes componentes lógicos clave basados en la interactividad observada:

  • Modal.tsx (Componente Reutilizable): Un componente genérico para la superposición de modales.
    • Jerarquía DOM: Un div principal para la capa de superposición (overlay), y un div anidado para el contenido del modal (modal-content). Un botón de cierre (x) y un h2 para el título.
    • Medidas Clave:
      • Overlay: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 2000; display: flex; justify-content: center; align-items: center;.
      • Modal Content: background-color: var(--surface-color); border-radius: 8px; box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.15); max-width: 500px; width: 90%; max-height: 90vh; overflow-y: auto; position: relative; padding: 24px;.
      • Botón de cierre (x): position: absolute; top: 16px; right: 16px;.
  • DropdownMenu.tsx (Componente Reutilizable): Para el menú "Mi cuenta".
    • Jerarquía DOM: Un div principal que se posiciona absolutamente. ul con li para los elementos del menú.
    • Medidas Clave:
      • Contenedor: position: absolute; top: 100%; right: 0; background-color: var(--surface-color); border-radius: 8px; box-shadow: 0px 4px 12px var(--shadow-light); min-width: 250px; z-index: 1500;.
      • Items: padding: 10px 16px; font-size: 14px;.
  • ToggleSwitch.tsx (Componente Reutilizable): Para las opciones de encendido/apagado.
    • Jerarquía DOM: Un label que contiene un input[type="checkbox"] oculto y un span para el "slider" visual.
    • Medidas Clave: width: 40px; height: 22px; border-radius: 11px; para el slider. circle o handle de 18px de diámetro.
  • TabbedContent.tsx (Componente Lógico para Pestañas): Se observa en "Configuración del equipo" y dentro del modal "Agregar destino".
    • Jerarquía DOM: Contenedor de button para las pestañas y un div para el contenido activo.
  • FormInput.tsx, FormDropdown.tsx, FormCheckbox.tsx (Componentes de Formulario): Se utilizarán para estandarizar los campos de entrada en los modales y páginas de configuración.

II. Métricas Pixel-Perfect (Actualización):

  • Tipografía Adicional:
    • Títulos de Modal (h2): font-size: 20px; font-weight: 600;.
    • Texto dentro de Modales/Formularios:
      • Labels: font-size: 14px; font-weight: 500;.
      • Input Text: font-size: 14px; font-weight: 400;.
    • Elementos de Dropdown: font-size: 14px; font-weight: 400;.
  • Paleta de Colores Adicional:
    • Overlay de Modal: rgba(0, 0, 0, 0.5) - Negro semitransparente.
    • Fondo de Toggle (off): var(--border-light) (gris claro).
    • Fondo de Toggle (on): var(--primary-blue).
  • Espaciado y Bordeado Adicional:
    • Padding de Modal: 24px alrededor del contenido.
    • Input Fields: padding: 10px 12px; border-radius: 6px; border: 1px solid var(--border-light);.
    • Toggle Switch: Dimensiones ya mencionadas. El "handle" del toggle tiene border-radius: 50%; background-color: white; box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1);.
    • Dropdown Separadores: border-bottom: 1px solid var(--border-light); para separar grupos de elementos en el menú "Mi cuenta".
    • Tooltip: background-color: var(--text-primary); color: var(--surface-color); padding: 6px 10px; border-radius: 4px; font-size: 12px;.

III. Funcionalidad y Scripts Internos (Actualización y Expansión):

Se confirma y expande la funcionalidad descrita previamente, añadiendo los siguientes puntos clave:

  1. Manejo Centralizado de Modales:

    • Se implementará un estado global (posiblemente a través de useState en PageContainer o un contexto simple si se requiere compartir estado entre más componentes) para controlar la visibilidad y el tipo de modal a mostrar.
    • Cada modal tendrá su propia lógica interna para manejar formularios, opciones y acciones, pero su apertura y cierre serán gestionados por el componente padre o el contexto.
    • Se observó que los modales pueden tener contenido complejo como tabs (Agregar destino modal) y videos (¡Obtén StreamYard On-Air!).
    • Cierre de Modal: Los modales se cierran al hacer clic en el botón 'x' o al presionar la tecla Escape.
  2. Manejo de Dropdown "Mi cuenta":

    • El componente Header gestionará el estado de visibilidad del dropdown "Mi cuenta" utilizando useState.
    • Un useEffect con un event listener para click en document se usará para cerrar el dropdown cuando se haga clic fuera de él.
    • Los elementos del dropdown son enlaces de navegación que pueden cambiar la ruta o abrir sub-secciones dentro de la aplicación.
  3. Implementación de Toggle Switches:

    • Los componentes ToggleSwitch serán reutilizables y gestionarán su propio estado checked (useState).
    • Se pasarán props onChange para que los componentes padres puedan reaccionar a los cambios de estado (ej. "Eliminar grabaciones automáticas", "Grabaciones locales").
  4. Navegación en el Sidebar y Pestañas de Contenido:

    • La barra lateral sigue un patrón de "enlaces activos", que se actualizará al hacer clic en ellos, reflejando el cambio de ruta en la URL o el contenido cargado en el área principal.
    • En secciones como "Configuración del equipo" y el modal "Agregar destino", se utilizará useState para gestionar la pestaña activa, cambiando el contenido renderizado (renderizado condicional).
  5. Formularios Interactivos:

    • Modales como "Crear transmisión en vivo" incluyen campos de texto, radio buttons y dropdowns. Estos necesitarán gestión de estado (useState) para sus valores y validación básica (inline validation, if complex).
    • El campo de destino en "Crear transmisión en vivo" muestra avatares de plataformas y un botón de "añadir", que abre otro modal (Agregar destino). Esto implica una interacción entre modales o un flujo de apertura en cadena.
    • Tooltips al Hover: El video muestra un tooltip al pasar el cursor sobre los avatares de destino, indicando el nombre de la plataforma.
  6. Redirecciones Externas:

    • El enlace "Estado del sistema" en el sidebar y el icono de notificación en el header (Mi cuenta) redirigen a URLs externas, lo que se implementará con target="_blank" rel="noopener noreferrer" para abrir en una nueva pestaña.
  7. Estado de Carga (Loading States):

    • Aunque breve, se observa un spinner de carga al navegar a la "Biblioteca", sugiriendo la necesidad de implementar estados de carga para mejorar la experiencia del usuario durante la recuperación de datos.
  8. Gestión de Rutas/Vistas:

    • Dada la navegación entre "Inicio", "Biblioteca", "Destinos", "Miembros", "Configuración del equipo", "Referidos" y "Estado del sistema", la aplicación requerirá un sistema de enrutamiento (ej. React Router DOM) para manejar las diferentes vistas y mantener el estado de la URL. Esto no se implementará directamente en los componentes dados, pero es una consideración crucial para la aplicación completa.

GENERACIÓN DE CÓDIGO (Componentes Adicionales y Modificaciones)

Para integrar la nueva funcionalidad, se necesitarán algunos ajustes y componentes adicionales.

1. src/components/PageContainer.tsx (Actualización - Añadir control de Modal)

import React, { useState } from 'react';
import styles from './PageContainer.module.css';
import Sidebar from './Sidebar/Sidebar';
import Header from './Header/Header';
import HeroSection from './HeroSection/HeroSection';
import TransmissionsTable from './TransmissionsTable/TransmissionsTable';
import Modal from '../components/Modal/Modal'; // Nuevo componente Modal
import CreateRecordingModal from '../components/Modals/CreateRecordingModal'; // Modal específico
import StreamYardOnAirModal from '../components/Modals/StreamYardOnAirModal'; // Modal específico
import CreateLiveStreamModal from '../components/Modals/CreateLiveStreamModal'; // Modal específico

// Definir tipos para el estado del modal
type ModalType = 'createRecording' | 'streamYardOnAir' | 'createLiveStream' | null;

const PageContainer: React.FC = () => {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');
  const [activeModal, setActiveModal] = useState<ModalType>(null);
  const [activeSidebarLink, setActiveSidebarLink] = useState<string>('Inicio'); // Para simular navegación

  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  const openModal = (modalType: ModalType) => {
    setActiveModal(modalType);
  };

  const closeModal = () => {
    setActiveModal(null);
  };

  const handleSidebarLinkClick = (linkId: string) => {
    setActiveSidebarLink(linkId);
    // En una aplicación real, esto manejaría el enrutamiento (ej. history.push('/${linkId.toLowerCase()}'))
    // Para esta simulación, solo cambiaremos el contenido si es "Inicio"
    if (linkId !== 'Inicio') {
      console.log(`Navegando a: ${linkId}`);
      // Simular carga de contenido diferente aquí si es necesario para otros links
    }
  };


  return (
    <div className={`${styles.pageContainer} ${theme === 'dark' ? styles.themeDark : styles.themeLight}`}>
      <Sidebar activeLink={activeSidebarLink} onLinkClick={handleSidebarLinkClick} />

      <div className={styles.mainContent}>
        <Header currentTheme={theme} onToggleTheme={toggleTheme} />

        <div className={styles.contentWrapper}>
          {/* Renderizado condicional del contenido principal basado en el link activo */}
          {activeSidebarLink === 'Inicio' && (
            <>
              {/* Se pasa la función openModal a HeroSection */}
              <HeroSection onOpenModal={openModal} />
              <TransmissionsTable />
            </>
          )}

          {activeSidebarLink === 'Configuracion' && (
            <div>
              <h2 className={styles.tempSectionTitle}>Configuración del equipo</h2>
              {/* Aquí iría el componente de configuración con tabs General/Facturación */}
            </div>
          )}

          {activeSidebarLink === 'Referidos' && (
            <div>
              <h2 className={styles.tempSectionTitle}>Referidos</h2>
              {/* Aquí iría el componente de referidos */}
            </div>
          )}

          {activeSidebarLink === 'Biblioteca' && (
            <div>
              <h2 className={styles.tempSectionTitle}>Biblioteca</h2>
              <p className={styles.noContentMessage}>Actualmente no tienes grabaciones.</p>
              <p className={styles.noContentMessage}>Tus grabaciones aparecerán aquí</p>
            </div>
          )}

          {activeSidebarLink === 'Destinos' && (
            <div>
              <h2 className={styles.tempSectionTitle}>Destinos</h2>
              {/* Aquí iría el componente de destinos, con botón para abrir modal "Agregar destino" */}
            </div>
          )}

          {activeSidebarLink === 'Miembros' && (
            <div>
              <h2 className={styles.tempSectionTitle}>Miembros</h2>
              {/* Aquí iría el componente de miembros */}
            </div>
          )}

          {/* Otros contenidos para el sidebar */}
        </div>

        <button className={styles.helpButton}>
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-help-circle">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
            <line x1="12" y1="17" x2="12.01" y2="17"></line>
          </svg>
          Ayuda
        </button>
      </div>

      {/* Renderizado de modales condicional */}
      {activeModal === 'createRecording' && (
        <Modal title="Crea una grabación" onClose={closeModal}>
          <CreateRecordingModal />
        </Modal>
      )}
      {activeModal === 'streamYardOnAir' && (
        <Modal title="¡Obtén StreamYard On-Air!" onClose={closeModal}>
          <StreamYardOnAirModal />
        </Modal>
      )}
      {activeModal === 'createLiveStream' && (
        <Modal title="Crear transmisión en vivo" onClose={closeModal}>
          <CreateLiveStreamModal />
        </Modal>
      )}
    </div>
  );
};

export default PageContainer;

2. src/components/PageContainer.module.css (Actualización - Añadir estilos genéricos)

/* ... (estilos existentes) ... */

.tempSectionTitle {
  font-size: 22px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 24px;
}

.noContentMessage {
  font-size: 16px;
  color: var(--text-secondary);
  text-align: center;
  margin-top: 50px;
}

/* Media Queries para responsividad */
@media (max-width: 768px) {
  /* ... (estilos existentes) ... */
}

3. src/components/Sidebar/Sidebar.tsx (Actualización - Añadir onLinkClick prop)

import React from 'react';
import styles from './Sidebar.module.css';

interface SidebarProps {
  activeLink: string;
  onLinkClick: (linkId: string) => void;
}

const Sidebar: React.FC<SidebarProps> = ({ activeLink, onLinkClick }) => {
  const navItems = [
    { id: 'Inicio', label: 'Inicio', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg> },
    { id: 'Biblioteca', label: 'Biblioteca', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-video"><path d="M23 7l-7 5V2l7 5z"></path><path d="M22 17H7a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h15v16z"></path></svg> },
    { id: 'Destinos', label: 'Destinos', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-share-2"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg> },
    { id: 'Miembros', label: 'Miembros', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> },
  ];

  const secondaryItems = [
    { id: 'Referidos', label: 'Referidos', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-gift"><polyline points="20 12 20 22 4 22 4 12"></polyline><rect x="2" y="7" width="20" height="5"></rect><line x1="12" y1="22" x2="12" y2="7"></line><path d="M12 7H7.5a2.5 2 0 0 1 0-5C11 2 12 7 12 7z"></path><path d="M12 7h4.5a2.5 2 0 0 0 0-5C13 2 12 7 12 7z"></path></svg> },
    { id: 'Configuracion', label: 'Configuración del equipo', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-settings"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0-.33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg> },
    { id: 'Estado del sistema', label: 'Estado del sistema', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-bar-chart-2"><line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line></svg> },
  ];

  return (
    <aside className={styles.sidebar}>
      <div className={styles.logoSection}>
        <img src="https://static.streamyard.com/images/logo-icon.svg" alt="StreamYard Logo" className={styles.logoIcon} />
        <span className={styles.logoText}>StreamYard</span>
      </div>

      <nav className={styles.navMenu}>
        <ul className={styles.navList}>
          {navItems.map((item) => (
            <li key={item.id} className={`${styles.navItem} ${activeLink === item.id ? styles.activeLink : ''}`}>
              <a
                href="#"
                onClick={(e) => {
                  e.preventDefault();
                  onLinkClick(item.id);
                }}
                className={styles.navLink}
              >
                {item.icon}
                <span>{item.label}</span>
              </a>
            </li>
          ))}
        </ul>

        <div className={styles.secondaryNavGroup}>
          <ul className={styles.navList}>
            {secondaryItems.map((item) => (
              <li key={item.id} className={`${styles.navItem} ${activeLink === item.id ? styles.activeLink : ''}`}>
                {/* Condicional para enlaces externos */}
                {item.id === 'Estado del sistema' ? (
                  <a
                    href="https://status.streamyard.com/"
                    target="_blank"
                    rel="noopener noreferrer"
                    className={styles.navLink}
                  >
                    {item.icon}
                    <span>{item.label}</span>
                  </a>
                ) : (
                  <a
                    href="#"
                    onClick={(e) => {
                      e.preventDefault();
                      onLinkClick(item.id);
                    }}
                    className={styles.navLink}
                  >
                    {item.icon}
                    <span>{item.label}</span>
                  </a>
                )}
              </li>
            ))}
          </ul>
        </div>
      </nav>

      <div className={styles.storageInfo}>
        <h4 className={styles.storageTitle}>Almacenamiento <span className={styles.infoIcon}>?</span></h4>
        <div className={styles.progressBarContainer}>
          <div className={styles.progressBarFill} style={{ width: '0%' }}></div>
        </div>
        <p className={styles.storageUsage}>0 de 5 horas</p>
        <a href="#" className={styles.addMoreLink}>Agregar más</a>
      </div>
    </aside>
  );
};

export default Sidebar;

4. src/components/Header/Header.tsx (Actualización - Añadir DropdownMenu y Tooltip)

import React, { useState, useRef, useEffect } from 'react';
import styles from './Header.module.css';
import DropdownMenu from './DropdownMenu'; // Nuevo componente DropdownMenu

interface HeaderProps {
  currentTheme: 'light' | 'dark';
  onToggleTheme: () => void;
}

const Header: React.FC<HeaderProps> = ({ currentTheme, onToggleTheme }) => {
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
  const dropdownRef = useRef<HTMLDivElement>(null);
  const userMenuRef = useRef<HTMLDivElement>(null);

  // Cierra el dropdown si se hace clic fuera
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        dropdownRef.current &&
        !dropdownRef.current.contains(event.target as Node) &&
        userMenuRef.current &&
        !userMenuRef.current.contains(event.target as Node)
      ) {
        setIsDropdownOpen(false);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, []);

  const handleUserMenuClick = () => {
    setIsDropdownOpen((prev) => !prev);
  };

  const handleNotificationClick = () => {
    window.open('https://new.streamyard.com/', '_blank', 'noopener noreferrer');
  };

  return (
    <header className={styles.header}>
      <div className={styles.headerActions}>
        <button className={styles.planButton}>Mejora tu plan</button>

        <div className={styles.themeToggleGroup}>
          <button
            className={`${styles.themeToggleButton} ${currentTheme === 'light' ? styles.active : ''}`}
            onClick={onToggleTheme}
            aria-label="Toggle light theme"
            data-tooltip={currentTheme === 'light' ? 'Modo claro' : 'Modo automático'} // Tooltip dinámico
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-sun">
              <circle cx="12" cy="12" r="5"></circle>
              <line x1="12" y1="1" x2="12" y2="3"></line>
              <line x1="12" y1="21" x2="12" y2="23"></line>
              <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
              <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
              <line x1="1" y1="12" x2="3" y2="12"></line>
              <line x1="21" y1="12" x2="23" y2="12"></line>
              <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
              <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
            </svg>
            <span className={styles.tooltip}>Modo claro</span> {/* Tooltip */}
          </button>
          <button
            className={`${styles.themeToggleButton} ${currentTheme === 'dark' ? styles.active : ''}`}
            onClick={onToggleTheme}
            aria-label="Toggle dark theme"
            data-tooltip={currentTheme === 'dark' ? 'Modo oscuro' : 'Modo automático'} // Tooltip dinámico
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-moon">
              <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
            </svg>
            <span className={styles.tooltip}>Modo oscuro</span> {/* Tooltip */}
          </button>
        </div>

        <button className={styles.notificationButton} onClick={handleNotificationClick}>
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-bell">
            <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
            <path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
          </svg>
          <span className={styles.notificationDot}></span>
        </button>

        <div className={styles.userMenuWrapper}>
          <div className={styles.userMenu} onClick={handleUserMenuClick} ref={userMenuRef}>
            <span>Mi cuenta</span>
            <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-chevron-down">
              <polyline points="6 9 12 15 18 9"></polyline>
            </svg>
          </div>
          {isDropdownOpen && <DropdownMenu ref={dropdownRef} />}
        </div>
      </div>
    </header>
  );
};

export default Header;

5. src/components/Header/Header.module.css (Actualización - Añadir estilos para Dropdown y Tooltip)

/* ... (estilos existentes) ... */

.themeToggleButton {
  /* ... (estilos existentes) ... */
  position: relative; /* Para el tooltip */
}

.themeToggleButton .tooltip {
  visibility: hidden;
  background-color: var(--text-primary);
  color: var(--surface-color);
  text-align: center;
  border-radius: 4px;
  padding: 6px 10px;
  position: absolute;
  z-index: 1;
  bottom: -35px; /* Posicionar debajo del botón */
  left: 50%;
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 0.3s;
  font-size: 12px;
  white-space: nowrap; /* Evita que el texto se rompa */
}

.themeToggleButton:hover .tooltip {
  visibility: visible;
  opacity: 1;
}

.userMenuWrapper {
  position: relative; /* Contenedor para posicionar el dropdown */
}

.userMenu {
  /* ... (estilos existentes) ... */
}

/* Media Queries para responsividad */
@media (max-width: 768px) {
  /* ... (estilos existentes) ... */
  .themeToggleGroup {
    display: none; /* Ocultar el selector de tema en móviles */
  }

  /* Ajustes para el tooltip en móviles si se decide mostrar */
  .themeToggleButton .tooltip {
    display: none; /* Ocultar tooltips en móviles por simplicidad */
  }
}

6. src/components/Header/DropdownMenu.tsx (Nuevo componente DropdownMenu)

import React, { forwardRef } from 'react';
import styles from './DropdownMenu.module.css';

const DropdownMenu = forwardRef<HTMLDivElement>((props, ref) => {
  return (
    <div className={styles.dropdownMenu} ref={ref}>
      <ul className={styles.menuList}>
        <li className={styles.menuItemHeader}>nexttv.stream@gmail.com</li>
        <li className={styles.menuItem}><a href="#">Referidos</a></li>
        <li className={styles.menuItem}><a href="#">Configuración del equipo</a></li>
        <li className={styles.menuItem}><a href="#">Facturación</a></li>
        <li className={styles.menuItem}><a href="#">Configuración de la cuenta</a></li>
        <li className={styles.menuItemSeparator}></li> {/* Separador visual */}
        <li className={styles.menuItem}><a href="#">Contacto</a></li>
        <li className={styles.menuItem}><a href="#">Mis tickets de soporte</a></li>
        <li className={styles.menuItem}><a href="#">Centro de ayuda</a></li>
        <li className={styles.menuItem}><a href="#">Recursos para socios</a></li>
        <li className={styles.menuItemSeparator}></li> {/* Separador visual */}
        <li className={styles.menuItem}><a href="#">Términos de servicio</a></li>
        <li className={styles.menuItem}><a href="#">Política de privacidad</a></li>
        <li className={styles.menuItemSeparator}></li> {/* Separador visual */}
        <li className={styles.menuItem}><a href="#">Cerrar sesión</a></li>
        <li className={styles.menuItemButton}>
            <button className={styles.helpButtonDropdown}>
                <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-help-circle">
                    <circle cx="12" cy="12" r="10"></circle>
                    <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
                    <line x1="12" y1="17" x2="12.01" y2="17"></line>
                </svg>
                Ayuda
            </button>
        </li>
      </ul>
    </div>
  );
});

export default DropdownMenu;

7. src/components/Header/DropdownMenu.module.css (Nuevo CSS para DropdownMenu)

.dropdownMenu {
  position: absolute;
  top: 100%; /* Posiciona debajo del elemento padre */
  right: 0;
  background-color: var(--surface-color);
  border-radius: 8px;
  box-shadow: 0px 4px 12px var(--shadow-light); /* Sombra para el dropdown */
  min-width: 250px;
  z-index: 1500;
  padding: 8px 0; /* Padding interno */
  margin-top: 10px; /* Separación del botón "Mi cuenta" */
}

.menuList {
  list-style: none;
  padding: 0;
  margin: 0;
}

.menuItemHeader {
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: 8px; /* Espacio antes del primer separador */
}

.menuItem a {
  display: block;
  padding: 10px 16px;
  font-size: 14px;
  color: var(--text-primary);
  text-decoration: none;
  transition: background-color 0.15s ease-in-out;
}

.menuItem a:hover {
  background-color: var(--active-bg-light); /* Fondo azul claro al hover */
}

.menuItemSeparator {
  height: 1px;
  background-color: var(--border-light);
  margin: 8px 0;
}

.menuItemButton {
    padding: 8px 16px;
    text-align: center;
}

.helpButtonDropdown {
    background-color: var(--primary-blue);
    color: #ffffff;
    padding: 10px 16px;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
    width: 100%; /* Ocupa todo el ancho */
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.helpButtonDropdown:hover {
    background-color: #146bdc; /* Azul un poco más oscuro al hacer hover */
}

8. src/components/HeroSection/HeroSection.tsx (Actualización - Añadir onOpenModal prop)

import React from 'react';
import styles from './HeroSection.module.css';
import { ModalType } from '../../components/PageContainer'; // Importar ModalType

interface CardProps {
  icon: React.ReactNode;
  title: string;
  onClick: () => void; // Añadir prop onClick
}

const CreateCard: React.FC<CardProps> = ({ icon, title, onClick }) => {
  return (
    <div className={styles.createCard} onClick={onClick}> {/* Añadir onClick */}
      <div className={styles.cardIcon}>{icon}</div>
      <p className={styles.cardTitle}>{title}</p>
    </div>
  );
};

interface HeroSectionProps {
  onOpenModal: (modalType: ModalType) => void;
}

const HeroSection: React.FC<HeroSectionProps> = ({ onOpenModal }) => {
  return (
    <section className={styles.heroSection}>
      <h2 className={styles.sectionTitle}>Crear</h2>
      <div className={styles.cardGrid}>
        <CreateCard
          icon={<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-video"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg>}
          title="Transmisión en vivo"
          onClick={() => onOpenModal('createLiveStream')} // Abrir modal específico
        />
        <CreateCard
          icon={<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-radio"><circle cx="12" cy="12" r="2"></circle><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path></svg>}
          title="Grabación"
          onClick={() => onOpenModal('createRecording')} // Abrir modal específico
        />
        <CreateCard
          icon={<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-monitor"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>}
          title="Seminario web On-Air"
          onClick={() => onOpenModal('streamYardOnAir')} // Abrir modal específico
        />
      </div>
    </section>
  );
};

export default HeroSection;

9. src/components/Modal/Modal.tsx (Nuevo componente genérico de Modal)

import React, { useEffect } from 'react';
import styles from './Modal.module.css';

interface ModalProps {
  title: string;
  onClose: () => void;
  children: React.ReactNode;
}

const Modal: React.FC<ModalProps> = ({ title, onClose, children }) => {
  // Cierra el modal al presionar la tecla Escape
  useEffect(() => {
    const handleEscape = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        onClose();
      }
    };
    document.addEventListener('keydown', handleEscape);
    return () => {
      document.removeEventListener('keydown', handleEscape);
    };
  }, [onClose]);

  return (
    <div className={styles.modalOverlay} onClick={onClose}>
      <div className={styles.modalContent} onClick={(e) => e.stopPropagation()}> {/* Evita cerrar al hacer clic dentro */}
        <button className={styles.closeButton} onClick={onClose} aria-label="Cerrar">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-x">
            <line x1="18" y1="6" x2="6" y2="18"></line>
            <line x1="6" y1="6" x2="18" y2="18"></line>
          </svg>
        </button>
        <h2 className={styles.modalTitle}>{title}</h2>
        <div className={styles.modalBody}>
          {children}
        </div>
      </div>
    </div>
  );
};

export default Modal;

10. src/components/Modal/Modal.module.css (Nuevo CSS para Modal)

.modalOverlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5); /* Overlay semitransparente */
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 2000; /* Asegura que esté por encima de todo */
  backdrop-filter: blur(2px); /* Efecto de desenfoque */
}

.modalContent {
  background-color: var(--surface-color);
  border-radius: 8px;
  box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.15); /* Sombra más prominente */
  max-width: 500px; /* Ancho máximo */
  width: 90%; /* Ajuste responsivo */
  max-height: 90vh; /* Altura máxima con scroll */
  overflow-y: auto;
  position: relative;
  padding: 24px;
}

.closeButton {
  position: absolute;
  top: 16px;
  right: 16px;
  background-color: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  padding: 0;
  width: 32px;
  height: 32px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  transition: background-color 0.2s ease-in-out;
}

.closeButton:hover {
  background-color: var(--border-light);
}

.modalTitle {
  font-size: 20px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 24px;
  margin-top: 0;
  padding-right: 40px; /* Espacio para el botón de cerrar */
}

.modalBody {
  /* Estilos para el contenido dentro del modal */
  font-size: 14px;
  color: var(--text-primary);
  line-height: 1.6;
}

/* Media Queries para responsividad */
@media (max-width: 768px) {
  .modalContent {
    width: 95%;
    padding: 16px;
  }

  .modalTitle {
    font-size: 18px;
    margin-bottom: 16px;
  }

  .closeButton {
    top: 10px;
    right: 10px;
    width: 28px;
    height: 28px;
  }
}

11. src/components/Forms/ToggleSwitch.tsx (Nuevo componente ToggleSwitch)

import React from 'react';
import styles from './ToggleSwitch.module.css';

interface ToggleSwitchProps {
  id: string;
  label: string;
  checked: boolean;
  onChange: (checked: boolean) => void;
  description?: string;
}

const ToggleSwitch: React.FC<ToggleSwitchProps> = ({ id, label, checked, onChange, description }) => {
  return (
    <div className={styles.toggleWrapper}>
      <label htmlFor={id} className={styles.toggleLabel}>
        {label}
        {description && <span className={styles.toggleDescription}>{description}</span>}
        <input
          type="checkbox"
          id={id}
          checked={checked}
          onChange={(e) => onChange(e.target.checked)}
          className={styles.toggleInput}
        />
        <span className={styles.toggleSlider}></span>
      </label>
    </div>
  );
};

export default ToggleSwitch;

12. src/components/Forms/ToggleSwitch.module.css (Nuevo CSS para ToggleSwitch)

.toggleWrapper {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}

.toggleLabel {
  display: flex;
  align-items: center;
  cursor: pointer;
  font-size: 14px;
  color: var(--text-primary);
  position: relative;
}

.toggleDescription {
  font-size: 12px;
  color: var(--text-secondary);
  margin-left: 8px;
  flex-basis: 100%; /* Permite que la descripción ocupe su propio espacio si es larga */
}

.toggleInput {
  opacity: 0;
  width: 0;
  height: 0;
}

.toggleSlider {
  position: relative;
  display: inline-block;
  width: 40px; /* Ancho del slider */
  height: 22px; /* Altura del slider */
  background-color: var(--border-light); /* Fondo cuando está OFF */
  border-radius: 11px; /* Forma de píldora */
  transition: background-color 0.2s ease;
  margin-left: 12px;
  flex-shrink: 0; /* Evita que el slider se encoja */
}

.toggleSlider:before {
  content: "";
  position: absolute;
  height: 18px; /* Diámetro del "handle" */
  width: 18px; /* Diámetro del "handle" */
  left: 2px;
  bottom: 2px;
  background-color: white;
  border-radius: 50%;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1); /* Sombra del handle */
}

.toggleInput:checked + .toggleSlider {
  background-color: var(--primary-blue); /* Fondo cuando está ON */
}

.toggleInput:checked + .toggleSlider:before {
  transform: translateX(18px); /* Mueve el handle a la derecha */
}

13. src/components/Modals/CreateRecordingModal.tsx (Nuevo componente para modal "Crea una grabación")

import React, { useState } from 'react';
import styles from './ModalContent.module.css'; // Usar un CSS genérico para contenido de modal
import ToggleSwitch from '../Forms/ToggleSwitch';

const CreateRecordingModal: React.FC = () => {
  const [recordingTitle, setRecordingTitle] = useState('');
  const [localRecordings, setLocalRecordings] = useState(false);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    console.log('Crear grabación:', { recordingTitle, localRecordings });
    // Lógica para crear grabación
  };

  return (
    <form onSubmit={handleSubmit}>
      <div className={styles.formGroup}>
        <label htmlFor="recordingTitle" className={styles.formLabel}>Título de la grabación</label>
        <input
          type="text"
          id="recordingTitle"
          className={styles.formInput}
          value={recordingTitle}
          onChange={(e) => setRecordingTitle(e.target.value)}
          placeholder="Mi nueva grabación"
          required
        />
      </div>
      
      <ToggleSwitch
        id="localRecordings"
        label="Grabaciones locales?"
        description="Grabaciones de la mejor calidad con archivos de audio y video individuales para cada participante, necesario para las grabaciones en 4K Ultra HD."
        checked={localRecordings}
        onChange={setLocalRecordings}
      />

      <div className={styles.modalActions}>
        <button type="submit" className={styles.primaryButton}>Crear grabación</button>
      </div>
    </form>
  );
};

export default CreateRecordingModal;

14. src/components/Modals/StreamYardOnAirModal.tsx (Nuevo componente para modal "Obtén StreamYard On-Air!")

import React from 'react';
import styles from './ModalContent.module.css';

const StreamYardOnAirModal: React.FC = () => {
  return (
    <div>
      <p className={styles.modalDescription}>
        Organizo un seminario web, una transmisión en vivo o un evento en StreamYard, o insértalo en tu sitio web. Además, tienes la opción de recopilar direcciones de correo electrónico a través de un formulario de registro.
      </p>
      
      {/* Simulación del video */}
      <div className={styles.videoPlaceholder}>
        <img src="https://static.streamyard.com/images/hero_img.svg" alt="StreamYard On-Air Video" className={styles.videoThumbnail} />
        {/* Aquí iría el reproductor de video real */}
      </div>

      <h3 className={styles.planFeatureTitle}>Advanced</h3>
      <p className={styles.planFeatureDescription}>Para profesionales que quieren llevar su contenido a otro nivel.</p>
      
      <ul className={styles.featureList}>
        <li className={styles.featureItem}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          Webinars en vivo
        </li>
        <li className={styles.featureItem}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          Alta definición completa (1080p)
        </li>
        <li className={styles.featureItem}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          Grabaciones locales en 4K (2160p)
        </li>
        <li className={styles.featureItem}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          Logos, Superposiciones, Clips de video, Fondos
        </li>
        <li className={styles.featureItem}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          Transmisión múltiple - 8 destinos
        </li>
        <li className={styles.featureItem}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          Transmisión y grabación ilimitados
        </li>
        <li className={styles.featureItem}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          y mucho más
        </li>
      </ul>

      <div className={styles.modalActions}>
        <button className={styles.primaryButton}>Continuar</button>
        <a href="#" className={styles.secondaryLink}>Mostrar otros planes</a>
        <p className={styles.commitmentText}>
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={styles.checkIcon}><polyline points="20 6 9 17 4 12"></polyline></svg>
          Sin compromisos y fácil de cancelar.
        </p>
      </div>
    </div>
  );
};

export default StreamYardOnAirModal;

15. src/components/Modals/CreateLiveStreamModal.tsx (Nuevo componente para modal "Crear transmisión en vivo")

import React, { useState } from 'react';
import styles from './ModalContent.module.css'; // Usar CSS genérico para contenido de modal

const CreateLiveStreamModal: React.FC = () => {
  const [source, setSource] = useState<'studio' | 'preRecorded'>('studio');
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');
  const [addReferenceMessage, setAddReferenceMessage] = useState(false);
  const [privacy, setPrivacy] = useState('Publica');
  const [category, setCategory] = useState('');
  const [scheduleLater, setScheduleLater] = useState(false);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    console.log('Crear transmisión en vivo:', {
      source, title, description, addReferenceMessage, privacy, category, scheduleLater
    });
    // Lógica para crear transmisión en vivo
  };

  return (
    <form onSubmit={handleSubmit}>
      <div className={styles.formGroup}>
        <label className={styles.formLabel}>Fuente <span className={styles.infoIcon}>?</span></label>
        <div className={styles.radioGroup}>
          <label className={styles.radioLabel}>
            <input
              type="radio"
              name="source"
              value="studio"
              checked={source === 'studio'}
              onChange={() => setSource('studio')}
              className={styles.radioInput}
            />
            Estudio
          </label>
          <label className={styles.radioLabel}>
            <input
              type="radio"
              name="source"
              value="preRecorded"
              checked={source === 'preRecorded'}
              onChange={() => setSource('preRecorded')}
              className={styles.radioInput}
            />
            Video pregrabado
          </label>
        </div>
      </div>

      <div className={styles.formGroup}>
        <label className={styles.formLabel}>Selecciona los destinos</label>
        <div className={styles.destinationsRow}>
          {/* Avatar de destino simulado */}
          <div className={styles.destinationAvatar} data-tooltip="Da Nation Freedom">
            <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="#E5E7EB" stroke="none" className="avatar"><circle cx="12" cy="12" r="10"></circle><text x="50%" y="50%" dominantBaseline="middle" textAnchor="middle" fill="#6B7280" fontSize="12px">a</text></svg>
            <span className={styles.tooltip}>Da Nation Freedom</span> {/* Tooltip */}
          </div>
          <button type="button" className={styles.addDestinationButton}>
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-plus">
              <line x1="12" y1="5" x2="12" y2="19"></line>
              <line x1="5" y1="12" x2="19" y2="12"></line>
            </svg>
          </button>
          <a href="#" className={styles.secondaryLink}>Omitir por ahora</a>
        </div>
      </div>

      <div className={styles.formGroup}>
        <label htmlFor="streamTitle" className={styles.formLabel}>Título</label>
        <input
          type="text"
          id="streamTitle"
          className={styles.formInput}
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
        <span className={styles.charCount}>0/100</span>
      </div>

      <div className={styles.formGroup}>
        <label htmlFor="streamDescription" className={styles.formLabel}>Descripción</label>
        <textarea
          id="streamDescription"
          className={styles.formTextarea}
          value={description}
          onChange={(e) => setDescription(e.target.value)}
          placeholder="Cuéntanos un poco sobre esta transmisión en vivo"
        ></textarea>
        <span className={styles.charCount}>0/5000</span>
      </div>

      <div className={styles.formGroup}>
        <label className={styles.checkboxLabel}>
          <input
            type="checkbox"
            checked={addReferenceMessage}
            onChange={(e) => setAddReferenceMessage(e.target.checked)}
            className={styles.checkboxInput}
          />
          Agrega el mensaje de referencia a la descripción <span className={styles.infoIcon}>?</span>
          <p className={styles.checkboxDescription}>Gana $25 por cada crédito de referencia exitoso.</p>
        </label>
      </div>

      <div className={styles.formGroup}>
        <label htmlFor="privacy" className={styles.formLabel}>Privacidad</label>
        <select
          id="privacy"
          className={styles.formSelect}
          value={privacy}
          onChange={(e) => setPrivacy(e.target.value)}
        >
          <option value="Publica">Pública</option>
          <option value="Privada">Privada</option>
          <option value="NoListada">No listada</option>
        </select>
      </div>

      <div className={styles.formGroup}>
        <label htmlFor="category" className={styles.formLabel}>Categoría</label>
        <select
          id="category"
          className={styles.formSelect}
          value={category}
          onChange={(e) => setCategory(e.target.value)}
        >
          <option value="">Seleccionar categoría</option>
          {/* Opciones de categoría */}
        </select>
      </div>

      <div className={styles.formGroup}>
        <label className={styles.checkboxLabel}>
          <input
            type="checkbox"
            checked={scheduleLater}
            onChange={(e) => setScheduleLater(e.target.checked)}
            className={styles.checkboxInput}
          />
          Programar para más tarde
        </label>
      </div>

      <div className={styles.modalActions}>
        <button type="submit" className={styles.primaryButton}>Crear transmisión en vivo</button>
        <p className={styles.smallText}>Esta transmisión no se grabará en StreamYard. Para grabar, tendrás que <a href="#" className={styles.secondaryLink}>pasarte a un plan superior</a>.</p>
      </div>
    </form>
  );
};

export default CreateLiveStreamModal;

16. src/components/Modals/ModalContent.module.css (Nuevo CSS genérico para el contenido de los modales de formulario)

.formGroup {
  margin-bottom: 20px;
}

.formLabel {
  display: block;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: 8px;
  display: flex; /* Para el icono de info */
  align-items: center;
  gap: 4px;
}

.formInput,
.formTextarea,
.formSelect {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border-light);
  border-radius: 6px;
  font-size: 14px;
  color: var(--text-primary);
  background-color: var(--surface-color);
  box-sizing: border-box; /* Asegura que padding y borde no aumenten el ancho */
  transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}

.formInput:focus,
.formTextarea:focus,
.formSelect:focus {
  border-color: var(--primary-blue);
  box-shadow: 0 0 0 2px rgba(24, 118, 242, 0.2); /* Sombra de foco azul */
  outline: none;
}

.formTextarea {
  min-height: 80px;
  resize: vertical;
}

.charCount {
  display: block;
  font-size: 12px;
  color: var(--text-secondary);
  text-align: right;
  margin-top: 4px;
}

.radioGroup {
  display: flex;
  gap: 20px;
}

.radioLabel {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
  color: var(--text-primary);
}

.radioInput {
  /* Estilos básicos, se puede personalizar con CSS avanzado para radios custom */
  transform: scale(1.1); /* Ligeramente más grande */
  accent-color: var(--primary-blue); /* Color del radio button */
}

.checkboxLabel {
  display: flex;
  align-items: flex-start; /* Alinea el texto con el checkbox */
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
  color: var(--text-primary);
}

.checkboxInput {
  margin-top: 3px; /* Ajuste visual */
  transform: scale(1.1);
  accent-color: var(--primary-blue);
  flex-shrink: 0; /* Evita que el checkbox se encoja */
}

.checkboxDescription {
  font-size: 12px;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.4;
  flex-grow: 1; /* Permite que la descripción ocupe el espacio restante */
}

.modalActions {
  margin-top: 30px;
  text-align: right;
  border-top: 1px solid var(--border-light);
  padding-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.primaryButton {
  background-color: var(--primary-blue);
  color: #ffffff;
  padding: 12px 20px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: background-color 0.2s ease-in-out;
  width: auto; /* Para que no ocupe todo el ancho */
  align-self: flex-end; /* Alinea a la derecha en columna */
}

.primaryButton:hover {
  background-color: #146bdc;
}

.secondaryLink {
  color: var(--primary-blue);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  align-self: flex-end;
}

.secondaryLink:hover {
  text-decoration: underline;
}

.infoIcon {
  background-color: var(--border-light);
  border-radius: 50%;
  width: 16px;
  height: 16px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 10px;
  color: var(--text-secondary);
  font-weight: 500;
  cursor: help; /* Indica que hay más información */
}

.modalDescription {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-primary);
  margin-bottom: 20px;
}

.videoPlaceholder {
  background-color: #000; /* Fondo negro para simular el video */
  width: 100%;
  padding-bottom: 56.25%; /* Ratio 16:9 */
  position: relative;
  border-radius: 8px;
  overflow: hidden; /* Asegura que la imagen no se salga */
  margin-bottom: 20px;
}

.videoThumbnail {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}


.planFeatureTitle {
  font-size: 18px;
  font-weight: 600;
  color: var(--text-primary);
  margin-top: 24px;
  margin-bottom: 8px;
}

.planFeatureDescription {
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: 20px;
}

.featureList {
  list-style: none;
  padding: 0;
  margin: 0 0 20px 0;
}

.featureItem {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text-primary);
  margin-bottom: 8px;
}

.checkIcon {
  color: #28a745; /* Verde para los checks */
  flex-shrink: 0;
}

.commitmentText {
  font-size: 13px;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  align-self: flex-end; /* Alinea a la derecha en columna */
}

.destinationsRow {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
}

.destinationAvatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background-color: var(--border-light);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  position: relative; /* Para el tooltip */
  cursor: pointer;
}

/* Tooltip para avatares de destino */
.destinationAvatar .tooltip {
  visibility: hidden;
  background-color: var(--text-primary);
  color: var(--surface-color);
  text-align: center;
  border-radius: 4px;
  padding: 6px 10px;
  position: absolute;
  z-index: 10;
  bottom: 120%; /* Posicionar encima del avatar */
  left: 50%;
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 0.3s;
  font-size: 12px;
  white-space: nowrap;
}

.destinationAvatar:hover .tooltip {
  visibility: visible;
  opacity: 1;
}

.addDestinationButton {
  background-color: transparent;
  border: 1px solid var(--border-light);
  border-radius: 50%;
  width: 36px;
  height: 36px;
  display: flex;
  justify-content: center;
  align-items: center;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.2s ease-in-out;
}

.addDestinationButton:hover {
  border-color: var(--primary-blue);
  color: var(--primary-blue);
  background-color: var(--active-bg-light);
}

.smallText {
  font-size: 12px;
  color: var(--text-secondary);
  text-align: right;
}

/* Media Queries para responsividad */
@media (max-width: 768px) {
  .formLabel {
    font-size: 13px;
  }

  .formInput,
  .formTextarea,
  .formSelect,
  .radioLabel,
  .checkboxLabel {
    font-size: 13px;
  }

  .radioGroup {
    flex-direction: column; /* Apilar radios en móviles */
    gap: 10px;
  }

  .charCount, .checkboxDescription, .smallText {
    font-size: 11px;
  }

  .modalActions {
    padding-top: 15px;
    gap: 10px;
  }
  .primaryButton, .secondaryLink, .commitmentText {
    align-self: center; /* Centrar botones y enlaces en columna en móvil */
    width: 100%; /* Ocupar todo el ancho */
    text-align: center;
  }

  .primaryButton {
    padding: 10px 15px;
    font-size: 13px;
  }

  .secondaryLink {
    font-size: 13px;
  }
  
  .featureList {
    font-size: 13px;
  }

  .destinationAvatar {
    width: 28px;
    height: 28px;
  }

  .destinationAvatar .tooltip {
    display: none; /* Ocultar tooltips en móviles */
  }

  .addDestinationButton {
    width: 32px;
    height: 32px;
  }
}