AvanzaCast/docs/landingtemplate.md

48 KiB

I. Estructura y Componentes Lógicos:

La página web se puede descomponer en los siguientes 5 componentes lógicos modulares de React, envueltos por un PageContainer principal que gestiona el ancho máximo y el centrado:

  1. Header:

    • Descripción: Contiene el logo de la marca, la navegación principal con enlaces y dropdowns, y un botón de "Get started".
    • Jerarquía DOM Clave: <header> que contiene un <div> para el logo y un <nav> para los enlaces de navegación (<ul>, <li> con <a>). Incluye un <a> con estilo de botón para "Get started".
    • Medidas Clave:
      • Altura: Aproximadamente 80px (fijo).
      • Layout: display: flex para alinear el logo y la navegación.
      • Ancho máximo: Contenido limitado por el max-width global del PageContainer.
  2. HeroSection:

    • Descripción: La sección inicial de la página con un título principal impactante, una descripción y un formulario de registro/inicio de sesión.
    • Jerarquía DOM Clave: <section> que contiene dos <div> principales: uno para el contenido de texto (<h1>, <p>) y otro para el formulario de suscripción (<div> con botones, input de email y texto de enlaces). Posiblemente un elemento gráfico de fondo.
    • Medidas Clave:
      • Layout: display: flex para una disposición en dos columnas (texto a la izquierda, formulario a la derecha) en escritorio.
      • Ancho máximo: Contenido limitado por el max-width global del PageContainer.
      • Formulario: Ancho fijo en escritorio, adaptativo en móvil. border-radius: 16px.
  3. FeaturesGrid (Sección de Características Destacadas):

    • Descripción: Presenta las características clave del producto mediante tarjetas interactivas con iconos y títulos.
    • Jerarquía DOM Clave: <section> que contiene un <div> o <ul> para los elementos individuales. Cada elemento (<div> o <li>) contiene un <img> (icono/imagen) y un <h3> (título).
    • Medidas Clave:
      • Layout: display: flex con gap para la separación entre tarjetas. En móviles, implica overflow-x: scroll o una implementación de carrusel con flechas de navegación.
      • Tarjetas: border-radius: 16px, box-shadow sutil. Dimensiones uniformes para cada tarjeta (ej. 200px x 200px).
  4. ContentDetailsSection (Sección de Contenido Detallado):

    • Descripción: Agrupa múltiples bloques de contenido que detallan aspectos específicos del producto, a menudo con un layout de texto e imagen/video lado a lado.
    • Jerarquía DOM Clave: Múltiples <section> anidadas o <div>s, cada una con un div para el texto (H2, P) y un div para el contenido multimedia (imagen/video). El orden texto/imagen varía.
    • Medidas Clave:
      • Layout: display: flex para los bloques de texto y multimedia. gap entre ellos.
      • Ancho máximo: Contenido limitado por el max-width global del PageContainer.
      • Espaciado vertical: padding-block: 80px o 100px para separar cada sub-sección.
  5. CallToActionSection:

    • Descripción: Una sección destacada al final de la página que incita al usuario a comenzar a usar el producto.
    • Jerarquía DOM Clave: <section> con un <h2> para el mensaje principal, un <button> para la acción y un <p> con texto complementario, todo centrado.
    • Medidas Clave:
      • Layout: display: flex, flex-direction: column, align-items: center, justify-content: center.
      • Ancho: 100% del contenedor principal.
      • Color de fondo: Azul primario (#0066FF) con un patrón gráfico sutil.
      • Botón: border-radius: 8px, padding: 16px 32px.

II. Métricas Pixel-Perfect:

A. Tipografía:

  • Familia de Fuentes: sans-serif (posiblemente una fuente personalizada como Inter, o 'system-ui', 'Helvetica Neue', 'Arial'). Para la implementación, se utilizará una fuente genérica sans-serif con Inter como principal si se importara.
  • H1 (The easiest way to live stream and record):
    • Tamaño: 64px
    • Peso: 800 (Extra Bold)
    • Altura de línea: 1.2
    • Color: #1A1A1A
  • H2 (Títulos de Sección, ej. Go live or record podcasts):
    • Tamaño: 44px
    • Peso: 700 (Bold)
    • Altura de línea: 1.3
    • Color: #1A1A1A
  • P (Párrafos de Contenido):
    • Tamaño: 18px
    • Peso: 400 (Regular)
    • Altura de línea: 1.6
    • Color: #333333
  • Texto Pequeño (Footer, captions):
    • Tamaño: 14px
    • Peso: 400
    • Altura de línea: 1.5
    • Color: #666666

B. Paleta de Colores:

  • Fondo Principal (General): #FFFFFF (Blanco puro) o #F9FAFC (Blanco muy ligero).
  • Texto Principal: #1A1A1A (Negro muy oscuro).
  • Texto Secundario/Párrafos: #333333 o #666666.
  • Color de Acento (Azul Primario): #0066FF (Botones, enlaces primarios).
  • Color de Acento (Rosa): #FFDCE6 (Fondos de sección específicos).
  • Color de Acento (Morado): #6432C8 (Fondos de sección específicos).
  • Color de Acento (Amarillo): #FFE664 (Fondos de sección específicos).
  • Bordes/Líneas sutiles: #E0E0E0.
  • Sombras (Box-shadow): 0 4px 12px rgba(0, 0, 0, 0.08) para tarjetas y formularios.
  • Gradientes (ej. Hero Section): linear-gradient(to bottom, #EBF5FF, #FFFFFF).

C. Espaciado y Bordeado:

  • Separación Vertical entre Secciones: padding-top: 100px, padding-bottom: 100px.
  • Separación Interna (Padding):
    • Botones: padding: 16px 32px.
    • Tarjetas: padding: 24px.
    • Contenedores de contenido: padding-inline: 20px (para márgenes laterales en pantallas más pequeñas).
  • Radio de Borde (border-radius):
    • Botones: 8px.
    • Tarjetas (Características, Blog, Testimonios): 16px.
    • Inputs de formulario: 6px.
    • Contenedor del formulario Hero: 16px.

III. Funcionalidad y Scripts Internos:

A. Funcionalidad del Aplicativo Web (Interactividad a Replicar):

  1. Menú de Navegación Colapsable (Móvil):
    • En dispositivos móviles, el menú de navegación principal (Header) se contraerá en un icono de hamburguesa.
    • Al hacer clic en el icono de hamburguesa, el menú se deslizará para mostrar los enlaces de navegación.
    • Al hacer clic nuevamente o fuera del menú, este se ocultará.
  2. Dropdowns de Navegación (Header):
    • Los elementos "Product" y "For Business" en el Header deben mostrar un menú desplegable con sub-enlaces al pasar el ratón por encima (en escritorio).
    • En dispositivos móviles, estos dropdowns deben expandirse/colapsar al hacer clic.
  3. Carrusel de Características (FeaturesGrid):
    • La sección con las tarjetas de características (Recording, Multistream, Guests, etc.) debe ser un carrusel o un contenedor con overflow-x: scroll en pantallas donde las tarjetas no quepan en una sola fila.
    • Debe tener flechas de navegación izquierda/derecha para desplazarse entre las características (o auto-scroll si es overflow).
  4. Carrusel de Testimonios:
    • La sección "60,000,000+ streams..." presentará testimonios en un carrusel.
    • Los usuarios podrán navegar entre los testimonios utilizando flechas de navegación a izquierda y derecha, y/o indicadores de paginación (puntos) en la parte inferior.
  5. Reproducción de Audio Interactivo:
    • En la sección "Studio quality recordings...", hay un elemento visual con un botón de "Play" y un texto "Click to hear the difference".
    • Al hacer clic en este botón, debe simular la reproducción de audio (cambiando el icono a "Pause" y mostrando una barra de progreso visual). Un segundo clic pausaría.

B. Scripts Internos (Lógica JavaScript Clave):

  1. Manejo de Estado del Menú Móvil:
    • Se utilizará el hook useState de React en el componente Header para gestionar un booleano isMobileMenuOpen.
    • Un evento onClick en el icono de hamburguesa alternará este estado.
    • Las clases CSS se aplicarán condicionalmente basándose en isMobileMenuOpen para controlar la visibilidad y animación del menú.
  2. Lógica de Dropdowns:
    • Para los dropdowns de "Product" y "For Business", se usarán hooks useState individuales o un estado combinado para controlar su visibilidad (isProductDropdownOpen, isBusinessDropdownOpen).
    • Eventos onMouseEnter/onMouseLeave (desktop) o onClick (mobile) manejarán el cambio de estado.
  3. Gestión de Carruseles:
    • Se implementará un estado currentIndex o scrollPosition en los componentes FeaturesGrid y en la sección de Testimonios.
    • Las funciones handlePrev y handleNext actualizarán este estado, y las transiciones CSS (transform: translateX) se usarán para animar el movimiento de los elementos.
    • Los puntos de paginación también actualizarán directamente currentIndex.
  4. Control de Audio Simulado:
    • Un hook useState para isPlaying y progress en el componente relevante (dentro de ContentDetailsSection).
    • Una función handlePlayPause alternará el estado isPlaying y simulará la actualización de progress usando setTimeout o setInterval para una animación básica.

Generación de Código

A continuación, se presenta el código de los componentes React/TypeScript y sus archivos CSS Modules, siguiendo el informe técnico.

src/components/PageContainer.tsx

import React, { ReactNode } from 'react';
import styles from './PageContainer.module.css';

interface PageContainerProps {
  children: ReactNode;
}

const PageContainer: React.FC<PageContainerProps> = ({ children }) => {
  return (
    <div className={styles.pageContainer}>
      {children}
    </div>
  );
};

export default PageContainer;

src/components/PageContainer.module.css

/* src/components/PageContainer.module.css */

.pageContainer {
  max-width: 1280px; /* Ancho máximo del contenido */
  margin: 0 auto; /* Centrar el contenedor en la página */
  padding: 0 20px; /* Padding horizontal para evitar que el contenido toque los bordes en pantallas pequeñas */
  overflow-x: hidden; /* Evita el scroll horizontal no deseado */
}

/* Base styles for the page */
:root {
  --primary-blue: #0066FF;
  --light-blue-gradient-start: #EBF5FF;
  --text-dark: #1A1A1A;
  --text-medium: #333333;
  --text-light: #666666;
  --bg-white: #FFFFFF;
  --bg-light-grey: #F9FAFC;
  --accent-pink: #FFDCE6;
  --accent-purple: #6432C8;
  --accent-yellow: #FFE664;
  --border-light: #E0E0E0;
  --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.08);

  --font-size-h1: 64px;
  --font-weight-h1: 800;
  --line-height-h1: 1.2;

  --font-size-h2: 44px;
  --font-weight-h2: 700;
  --line-height-h2: 1.3;

  --font-size-p: 18px;
  --font-weight-p: 400;
  --line-height-p: 1.6;

  --font-size-sm: 14px;
  --font-weight-sm: 400;
  --line-height-sm: 1.5;

  --border-radius-button: 8px;
  --border-radius-card: 16px;
  --border-radius-input: 6px;
}

body {
  font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif; /* Usar Inter si está disponible, sino fallbacks */
  margin: 0;
  padding: 0;
  background-color: var(--bg-white);
  color: var(--text-medium);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1 {
  font-size: var(--font-size-h1);
  font-weight: var(--font-weight-h1);
  line-height: var(--line-height-h1);
  color: var(--text-dark);
  margin-top: 0;
  margin-bottom: 24px;
}

h2 {
  font-size: var(--font-size-h2);
  font-weight: var(--font-weight-h2);
  line-height: var(--line-height-h2);
  color: var(--text-dark);
  margin-top: 0;
  margin-bottom: 20px;
}

p {
  font-size: var(--font-size-p);
  font-weight: var(--font-weight-p);
  line-height: var(--line-height-p);
  color: var(--text-medium);
  margin-bottom: 16px;
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .pageContainer {
    padding: 0 16px;
  }

  h1 {
    font-size: 40px; /* Ajuste para móviles */
    line-height: 1.1;
  }

  h2 {
    font-size: 32px; /* Ajuste para móviles */
    line-height: 1.2;
  }

  p {
    font-size: 16px; /* Ajuste para móviles */
    line-height: 1.5;
  }
}

@media (max-width: 480px) {
  h1 {
    font-size: 32px;
  }

  h2 {
    font-size: 28px;
  }
}

src/components/Header/Header.tsx

import React, { useState } from 'react';
import styles from './Header.module.css';

// Mock icons for demonstration
const MenuIcon = () => <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>;
const CloseIcon = () => <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>;
const ChevronDown = () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>;

const Header: React.FC = () => {
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
  const [isProductDropdownOpen, setIsProductDropdownOpen] = useState(false);
  const [isBusinessDropdownOpen, setIsBusinessDropdownOpen] = useState(false);

  // Función para alternar el menú móvil
  const toggleMobileMenu = () => {
    setIsMobileMenuOpen(!isMobileMenuOpen);
  };

  // Funciones para manejar los dropdowns en desktop (hover)
  const handleProductMouseEnter = () => setIsProductDropdownOpen(true);
  const handleProductMouseLeave = () => setIsProductDropdownOpen(false);
  const handleBusinessMouseEnter = () => setIsBusinessDropdownOpen(true);
  const handleBusinessMouseLeave = () => setIsBusinessDropdownOpen(false);

  // Funciones para manejar los dropdowns en mobile (click)
  const toggleProductDropdown = () => setIsProductDropdownOpen(!isProductDropdownOpen);
  const toggleBusinessDropdown = () => setIsBusinessDropdownOpen(!isBusinessDropdownOpen);

  return (
    <header className={styles.header}>
      <div className={styles.logo}>
        <img src="https://via.placeholder.com/150x40?text=StreamYard+Logo" alt="StreamYard Logo" />
      </div>

      {/* Botón de menú de hamburguesa para móvil */}
      <button className={styles.menuToggle} onClick={toggleMobileMenu} aria-label="Toggle menu">
        {isMobileMenuOpen ? <CloseIcon /> : <MenuIcon />}
      </button>

      {/* Menú de navegación */}
      <nav className={`${styles.nav} ${isMobileMenuOpen ? styles.navOpen : ''}`}>
        <ul className={styles.navList}>
          {/* Dropdown 'Product' */}
          <li
            className={styles.navItemDropdown}
            onMouseEnter={handleProductMouseEnter}
            onMouseLeave={handleProductMouseLeave}
          >
            <a href="#" className={styles.navLink} onClick={toggleProductDropdown}>
              Product <ChevronDown />
            </a>
            {isProductDropdownOpen && (
              <ul className={styles.dropdownMenu}>
                <li><a href="#">Features</a></li>
                <li><a href="#">Pricing</a></li>
                <li><a href="#">Integrations</a></li>
              </ul>
            )}
          </li>

          <li><a href="#" className={styles.navLink}>Contact</a></li>
          <li><a href="#" className={styles.navLink}>Pricing</a></li>
          <li><a href="#" className={styles.navLink}>What's New</a></li>

          {/* Dropdown 'For Business' */}
          <li
            className={styles.navItemDropdown}
            onMouseEnter={handleBusinessMouseEnter}
            onMouseLeave={handleBusinessMouseLeave}
          >
            <a href="#" className={styles.navLink} onClick={toggleBusinessDropdown}>
              For Business <ChevronDown />
            </a>
            {isBusinessDropdownOpen && (
              <ul className={styles.dropdownMenu}>
                <li><a href="#">Enterprise</a></li>
                <li><a href="#">Agencies</a></li>
              </ul>
            )}
          </li>

          <li><a href="#" className={styles.navLink}>Log In</a></li>
          <li>
            <button className={styles.getStartedButton}>Get started</button>
          </li>
        </ul>
      </nav>
    </header>
  );
};

export default Header;

src/components/Header/Header.module.css

/* src/components/Header/Header.module.css */

.header {
  display: flex;
  justify-content: space-between; /* Espacia el logo y la navegación */
  align-items: center;
  padding: 16px 20px; /* Padding interno */
  height: 80px; /* Altura fija */
  background-color: var(--bg-white);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); /* Sombra sutil */
  position: sticky; /* Sticky header */
  top: 0;
  z-index: 1000; /* Asegura que el header esté por encima de otros elementos */
}

.logo img {
  height: 40px; /* Altura del logo */
  width: auto;
}

.nav {
  display: flex; /* Por defecto, flex para escritorio */
}

.navList {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: 30px; /* Espacio entre elementos de navegación */
}

.navLink {
  text-decoration: none;
  color: var(--text-medium);
  font-weight: 500;
  font-size: 16px;
  padding: 8px 0;
  transition: color 0.3s ease;
  display: flex;
  align-items: center;
  gap: 4px; /* Espacio para el icono de chevron */
}

.navLink:hover {
  color: var(--primary-blue); /* Color al pasar el ratón */
}

.getStartedButton {
  background-color: var(--primary-blue);
  color: var(--bg-white);
  border: none;
  border-radius: var(--border-radius-button);
  padding: 12px 24px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.getStartedButton:hover {
  background-color: #0056e6; /* Azul más oscuro al pasar el ratón */
}

/* Estilos para el menú dropdown */
.navItemDropdown {
  position: relative;
}

.dropdownMenu {
  position: absolute;
  top: 100%; /* Debajo del elemento padre */
  left: 0;
  background-color: var(--bg-white);
  box-shadow: var(--shadow-sm);
  border-radius: var(--border-radius-input);
  list-style: none;
  padding: 10px 0;
  margin-top: 5px; /* Pequeño espacio entre el link y el dropdown */
  min-width: 160px;
  display: none; /* Oculto por defecto, se muestra con JS */
  flex-direction: column;
  z-index: 1010;
}

.navItemDropdown:hover .dropdownMenu {
  display: flex; /* Mostrar en hover para desktop */
}

.dropdownMenu li a {
  display: block;
  padding: 8px 15px;
  color: var(--text-medium);
  text-decoration: none;
  font-size: 15px;
}

.dropdownMenu li a:hover {
  background-color: var(--bg-light-grey);
  color: var(--primary-blue);
}

/* Menú de hamburguesa (visible solo en móvil) */
.menuToggle {
  display: none; /* Oculto por defecto */
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}

/* Responsive: Mobile styles */
@media (max-width: 1024px) {
  .navList {
    gap: 20px; /* Reduce el espacio entre elementos */
  }
}

@media (max-width: 768px) {
  .header {
    padding: 16px;
    height: 60px; /* Reduce la altura del header en móvil */
  }

  .nav {
    display: none; /* Oculta la navegación por defecto en móvil */
    flex-direction: column;
    position: absolute;
    top: 60px; /* Debajo del header */
    left: 0;
    width: 100%;
    background-color: var(--bg-white);
    box-shadow: var(--shadow-sm);
    padding: 20px 0;
    z-index: 999;
    /* Animación de deslizamiento */
    transform: translateY(-100%);
    transition: transform 0.3s ease-out;
  }

  .nav.navOpen {
    display: flex; /* Muestra el menú cuando está abierto */
    transform: translateY(0);
  }

  .navList {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .navList li {
    width: 100%;
    padding: 8px 20px; /* Padding para los ítems del menú móvil */
  }

  .navLink {
    width: 100%;
  }

  .navItemDropdown {
    width: 100%;
  }

  .navItemDropdown:hover .dropdownMenu {
    display: none; /* Deshabilita el hover en mobile, se activa con click */
  }

  .navItemDropdown .dropdownMenu {
    position: static; /* Cambia a posición estática para que fluya en el menú móvil */
    box-shadow: none;
    border-radius: 0;
    padding-left: 20px; /* Indentación para sub-items */
    margin-top: 0;
    display: none; /* Oculto por defecto en móvil */
  }

  /* Mostrar dropdowns en móvil al hacer click */
  .navItemDropdown:has(> a.navLink[aria-expanded="true"]) .dropdownMenu {
    display: flex;
  }

  .menuToggle {
    display: block; /* Muestra el botón de hamburguesa */
  }

  .getStartedButton {
    width: calc(100% - 40px); /* Ajuste de ancho para botón en móvil */
    margin: 10px 20px;
    text-align: center;
  }
}

src/components/HeroSection/HeroSection.tsx

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

const HeroSection: React.FC = () => {
  return (
    <section className={styles.heroSection}>
      <div className={styles.heroContent}>
        <h1>The easiest way to live stream and record</h1>
        <p>
          StreamYard is a professional live streaming and recording studio in your
          browser. Record your content, or stream live to Facebook, YouTube, and
          other platforms.
        </p>
      </div>
      <div className={styles.heroFormContainer}>
        <div className={styles.heroForm}>
          <button className={styles.googleButton}>
            <img src="https://upload.wikimedia.org/wikipedia/commons/4/4a/Logo_2013_Google.png" alt="Google Logo" className={styles.googleLogo} />
            Continue with Google
          </button>
          <div className={styles.separator}>Or continue with email</div>
          <input
            type="email"
            placeholder="Enter email address"
            className={styles.emailInput}
          />
          <button className={styles.getStartedButton}>
            Get started - It's free!
          </button>
          <p className={styles.formFooterText}>
            Trusted by 12,000,000+ creators!
            <br />
            By continuing, you accept our <a href="#">User Terms of Service</a> and{' '}
            <a href="#">Plan Usage Policy</a> and acknowledge receipt of our{' '}
            <a href="#">Privacy Policy</a>
          </p>
          <p className={styles.loginText}>Already using StreamYard? <a href="#">Log in.</a></p>
        </div>
        <img
          src="https://via.placeholder.com/400x300?text=Hero+Graphic" // Placeholder image for the graphic
          alt="StreamYard Interface Graphic"
          className={styles.heroGraphic}
        />
      </div>
    </section>
  );
};

export default HeroSection;

src/components/HeroSection/HeroSection.module.css

/* src/components/HeroSection/HeroSection.module.css */

.heroSection {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 100px 0; /* Espaciado vertical */
  background: linear-gradient(to bottom, var(--light-blue-gradient-start), var(--bg-white)); /* Gradiente de fondo */
  gap: 80px; /* Espacio entre el contenido de texto y el formulario */
  flex-wrap: wrap; /* Permite que los elementos se envuelvan en pantallas más pequeñas */
}

.heroContent {
  flex: 1; /* Ocupa el espacio disponible */
  min-width: 300px; /* Ancho mínimo para el texto */
  max-width: 600px; /* Ancho máximo para el texto */
}

.heroContent h1 {
  font-size: var(--font-size-h1);
  font-weight: var(--font-weight-h1);
  line-height: var(--line-height-h1);
  color: var(--text-dark);
}

.heroContent p {
  font-size: var(--font-size-p);
  line-height: var(--line-height-p);
  color: var(--text-medium);
  max-width: 500px; /* Limita el ancho del párrafo */
}

.heroFormContainer {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-end; /* Alinea el formulario y el gráfico a la derecha */
  min-width: 350px;
}

.heroForm {
  background-color: var(--bg-white);
  border-radius: var(--border-radius-card); /* Radio de borde para el formulario */
  box-shadow: var(--shadow-sm); /* Sombra para el formulario */
  padding: 30px;
  display: flex;
  flex-direction: column;
  gap: 15px; /* Espacio entre elementos del formulario */
  width: 380px; /* Ancho fijo para el formulario */
  z-index: 1; /* Asegura que el formulario esté sobre el gráfico */
}

.googleButton {
  background-color: var(--bg-white);
  border: 1px solid var(--border-light);
  border-radius: var(--border-radius-button);
  padding: 12px 20px;
  font-size: 16px;
  font-weight: 500;
  color: var(--text-medium);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.googleButton:hover {
  background-color: var(--bg-light-grey);
}

.googleLogo {
  height: 20px;
  width: 20px;
}

.separator {
  text-align: center;
  font-size: var(--font-size-sm);
  color: var(--text-light);
  position: relative;
  margin: 10px 0;
}

.separator::before,
.separator::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 40%;
  height: 1px;
  background-color: var(--border-light);
}

.separator::before {
  left: 0;
}

.separator::after {
  right: 0;
}

.emailInput {
  border: 1px solid var(--border-light);
  border-radius: var(--border-radius-input);
  padding: 14px 15px;
  font-size: 16px;
  width: calc(100% - 30px); /* Ajusta el ancho por el padding */
}

.emailInput:focus {
  outline: none;
  border-color: var(--primary-blue);
  box-shadow: 0 0 0 2px rgba(0, 102, 255, 0.2);
}

.getStartedButton {
  background-color: var(--primary-blue);
  color: var(--bg-white);
  border: none;
  border-radius: var(--border-radius-button);
  padding: 16px 20px;
  font-size: 18px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.getStartedButton:hover {
  background-color: #0056e6;
}

.formFooterText {
  font-size: 13px;
  color: var(--text-light);
  text-align: center;
  margin-top: 10px;
}

.formFooterText a,
.loginText a {
  color: var(--primary-blue);
  text-decoration: none;
}

.loginText {
  font-size: var(--font-size-sm);
  color: var(--text-medium);
  text-align: center;
  margin-top: 15px;
}

.heroGraphic {
  position: absolute;
  top: -80px; /* Posiciona el gráfico flotando por encima del formulario */
  right: -100px; /* Ajusta la posición del gráfico */
  width: 450px; /* Tamaño del gráfico */
  height: auto;
  opacity: 0.8; /* Transparencia para que parezca de fondo */
  z-index: 0; /* Detrás del formulario */
  filter: blur(1px); /* Ligero desenfoque */
}


/* Responsive adjustments */
@media (max-width: 1024px) {
  .heroSection {
    flex-direction: column; /* Apila el contenido y el formulario */
    padding: 80px 0;
    gap: 50px;
  }

  .heroContent {
    text-align: center;
    max-width: 90%;
    margin: 0 auto;
  }

  .heroContent h1 {
    font-size: 52px;
  }

  .heroContent p {
    font-size: 17px;
  }

  .heroFormContainer {
    align-items: center; /* Centra el formulario */
    min-width: unset;
    width: 100%;
  }

  .heroForm {
    width: 85%; /* El formulario ocupa más ancho en móviles */
    max-width: 450px; /* Limita el ancho del formulario */
  }

  .heroGraphic {
    position: static; /* Quita el posicionamiento absoluto en móvil */
    margin-top: 30px;
    width: 80%;
    max-width: 400px;
    opacity: 1;
    filter: none;
  }
}

@media (max-width: 768px) {
  .heroSection {
    padding: 60px 0;
  }

  .heroContent h1 {
    font-size: 40px;
  }

  .heroForm {
    width: 95%; /* Aún más ancho en pantallas más pequeñas */
    padding: 20px;
  }
}

@media (max-width: 480px) {
  .heroContent h1 {
    font-size: 32px;
  }
  .heroContent p {
    font-size: 16px;
  }
}

src/components/FeaturesGrid/FeaturesGrid.tsx

import React, { useState, useRef } from 'react';
import styles from './FeaturesGrid.module.css';

// Mock data for features
const features = [
  { id: 1, name: 'Recording', icon: 'https://via.placeholder.com/64?text=Rec' },
  { id: 2, name: 'Multistream', icon: 'https://via.placeholder.com/64?text=Multi' },
  { id: 3, name: 'Guests', icon: 'https://via.placeholder.com/64?text=Guests' },
  { id: 4, name: 'Branding', icon: 'https://via.placeholder.com/64?text=Brand' },
  { id: 5, name: 'Podcasts', icon: 'https://via.placeholder.com/64?text=Pod' },
  { id: 6, name: 'Webinars', icon: 'https://via.placeholder.com/64?text=Web' },
  { id: 7, name: 'Analytics', icon: 'https://via.placeholder.com/64?text=Anal' },
];

const FeaturesGrid: React.FC = () => {
  const scrollRef = useRef<HTMLDivElement>(null);

  const scroll = (direction: 'left' | 'right') => {
    if (scrollRef.current) {
      const scrollAmount = 300; // Cantidad de desplazamiento
      if (direction === 'left') {
        scrollRef.current.scrollBy({ left: -scrollAmount, behavior: 'smooth' });
      } else {
        scrollRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' });
      }
    }
  };

  return (
    <section className={styles.featuresSection}>
      <div className={styles.featuresHeader}>
        <h2>Explore powerful features</h2>
        <p>Discover how StreamYard can elevate your live streams and recordings.</p>
      </div>
      <div className={styles.carouselContainer}>
        {/* Flecha de navegación izquierda */}
        <button className={`${styles.navArrow} ${styles.leftArrow}`} onClick={() => scroll('left')} aria-label="Scroll left">
          &#8249; {/* Carácter de flecha izquierda */}
        </button>
        <div className={styles.featuresGrid} ref={scrollRef}>
          {features.map((feature) => (
            <div key={feature.id} className={styles.featureCard}>
              <img src={feature.icon} alt={feature.name} className={styles.featureIcon} />
              <h3>{feature.name}</h3>
            </div>
          ))}
        </div>
        {/* Flecha de navegación derecha */}
        <button className={`${styles.navArrow} ${styles.rightArrow}`} onClick={() => scroll('right')} aria-label="Scroll right">
          &#8250; {/* Carácter de flecha derecha */}
        </button>
      </div>
    </section>
  );
};

export default FeaturesGrid;

src/components/FeaturesGrid/FeaturesGrid.module.css

/* src/components/FeaturesGrid/FeaturesGrid.module.css */

.featuresSection {
  padding: 100px 0; /* Espaciado vertical */
  background-color: var(--bg-light-grey); /* Fondo sutil */
  text-align: center; /* Centra el texto */
}

.featuresHeader {
  margin-bottom: 50px;
}

.featuresHeader h2 {
  margin-bottom: 10px;
}

.featuresHeader p {
  font-size: var(--font-size-p);
  color: var(--text-medium);
}

.carouselContainer {
  position: relative;
  max-width: 1200px; /* Ancho máximo para el carrusel */
  margin: 0 auto;
  display: flex;
  align-items: center;
}

.featuresGrid {
  display: flex;
  overflow-x: auto; /* Permite el desplazamiento horizontal */
  -webkit-overflow-scrolling: touch; /* Suaviza el scroll en iOS */
  scroll-snap-type: x mandatory; /* Ajuste para el scroll-snap */
  gap: 30px; /* Espacio entre las tarjetas */
  padding: 20px; /* Padding para evitar que las tarjetas toquen los bordes */
  margin: 0 -20px; /* Compensa el padding para que el contenido fluya */
  scrollbar-width: none; /* Oculta la barra de desplazamiento en Firefox */
}

/* Oculta la barra de desplazamiento en Chrome/Safari */
.featuresGrid::-webkit-scrollbar {
  display: none;
}

.featureCard {
  flex: 0 0 auto; /* No permite que las tarjetas se encojan */
  width: 220px; /* Ancho fijo de la tarjeta */
  height: 220px; /* Altura fija para que sean cuadradas */
  background-color: var(--bg-white);
  border-radius: var(--border-radius-card);
  box-shadow: var(--shadow-sm);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 20px;
  text-align: center;
  scroll-snap-align: start; /* Alineación para el scroll-snap */
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  cursor: pointer;
}

.featureCard:hover {
  transform: translateY(-5px); /* Pequeño efecto de elevación */
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}

.featureIcon {
  width: 64px; /* Tamaño del icono */
  height: 64px;
  margin-bottom: 15px;
}

.featureCard h3 {
  font-size: 20px;
  font-weight: 600;
  color: var(--text-dark);
  margin: 0;
}

.navArrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background-color: var(--bg-white);
  border: 1px solid var(--border-light);
  border-radius: 50%; /* Botones circulares */
  width: 50px;
  height: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 24px;
  cursor: pointer;
  z-index: 10;
  box-shadow: var(--shadow-sm);
  transition: background-color 0.3s ease, transform 0.3s ease;
}

.navArrow:hover {
  background-color: var(--bg-light-grey);
  transform: translateY(-50%) scale(1.05);
}

.leftArrow {
  left: -25px; /* Posicionar fuera del grid */
}

.rightArrow {
  right: -25px; /* Posicionar fuera del grid */
}

/* Responsive adjustments */
@media (max-width: 1280px) {
  .leftArrow {
    left: 0px; /* Mueve las flechas dentro del padding del contenedor principal */
  }

  .rightArrow {
    right: 0px;
  }
}

@media (max-width: 768px) {
  .featuresSection {
    padding: 60px 0;
  }

  .featuresGrid {
    gap: 20px; /* Reduce el espacio entre tarjetas */
    padding: 15px;
    margin: 0 -15px;
  }

  .featureCard {
    width: 180px; /* Reduce el tamaño de las tarjetas */
    height: 180px;
    padding: 15px;
  }

  .featureIcon {
    width: 50px;
    height: 50px;
  }

  .featureCard h3 {
    font-size: 18px;
  }

  .navArrow {
    width: 40px;
    height: 40px;
    font-size: 20px;
  }
}

@media (max-width: 480px) {
  .featuresHeader h2 {
    font-size: 32px;
  }

  .featuresHeader p {
    font-size: 16px;
  }

  .featuresGrid {
    padding: 10px;
    margin: 0 -10px;
  }

  .featureCard {
    width: 150px; /* Aún más pequeñas */
    height: 150px;
  }

  .navArrow {
    display: none; /* Oculta las flechas si el scroll es el método principal */
  }
}

src/components/ContentDetailsSection/ContentDetailsSection.tsx

import React, { useState } from 'react';
import styles from './ContentDetailsSection.module.css';

// Mock component for Play/Pause icon
const PlayIcon = () => (
  <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <polygon points="5 3 19 12 5 21 5 3"></polygon>
  </svg>
);
const PauseIcon = () => (
  <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <rect x="6" y="4" width="4" height="16"></rect>
    <rect x="14" y="4" width="4" height="16"></rect>
  </svg>
);

const ContentDetailsSection: React.FC = () => {
  const [isPlayingAudio, setIsPlayingAudio] = useState(false);
  const [audioProgress, setAudioProgress] = useState(0);

  // Simula la reproducción de audio
  const handlePlayPauseAudio = () => {
    setIsPlayingAudio(!isPlayingAudio);
    if (!isPlayingAudio) {
      // Simular progreso
      let currentProgress = 0;
      const interval = setInterval(() => {
        currentProgress += 10; // Incrementa el progreso
        if (currentProgress > 100) {
          currentProgress = 0;
          setIsPlayingAudio(false);
          clearInterval(interval);
        }
        setAudioProgress(currentProgress);
      }, 500); // Actualiza cada 0.5 segundos
    } else {
      setAudioProgress(0); // Reinicia si se pausa
    }
  };

  return (
    <div className={styles.contentDetailsSection}>
      {/* Sección 1: Go live or record podcasts with remote guests */}
      <section className={`${styles.contentBlock} ${styles.bgPink}`}>
        <div className={styles.textColumn}>
          <h2>Go live or record podcasts with remote guests</h2>
          <p>
            It's easy for guests to join from their browser or phone in a few
            clicks. No software downloads.
          </p>
          <a href="#" className={styles.learnMoreLink}>Learn more &rarr;</a>
        </div>
        <div className={styles.imageColumn}>
          <img src="https://via.placeholder.com/500x350?text=Remote+Guests" alt="Remote Guests" className={styles.contentImage} />
        </div>
      </section>

      {/* Sección 2: Studio quality recordings on any connection */}
      <section className={`${styles.contentBlock} ${styles.bgPurple} ${styles.reverseLayout}`}>
        <div className={styles.imageColumn}>
          <div className={styles.audioPlayer}>
            <img src="https://via.placeholder.com/500x350?text=Studio+Quality" alt="Studio Quality" className={styles.contentImage} />
            <div className={styles.audioControls}>
              <button onClick={handlePlayPauseAudio} className={styles.playPauseButton}>
                {isPlayingAudio ? <PauseIcon /> : <PlayIcon />}
              </button>
              <div className={styles.progressBarContainer}>
                <div
                  className={styles.progressBar}
                  style={{ width: `${audioProgress}%` }}
                ></div>
              </div>
              <span className={styles.audioLabel}>Click to hear the difference</span>
            </div>
          </div>
        </div>
        <div className={styles.textColumn}>
          <h2>Studio quality recordings on any connection</h2>
          <p>
            Sick of Zoom and Skype ruining your podcast? With local recordings,
            a separate audio and video file is recorded on each user's device.
            Even if someone has weak internet, the recordings won't be blurry or choppy.
          </p>
          <a href="#" className={styles.learnMoreLink}>Discover more &rarr;</a>
        </div>
      </section>

      {/* Sección 3: Multistream to all platforms at once */}
      <section className={`${styles.contentBlock} ${styles.bgWhite}`}>
        <div className={styles.textColumn}>
          <h2>Multistream to all platforms at once</h2>
          <p>
            Stream to Facebook, YouTube, Instagram, LinkedIn, X (Twitter),
            Twitch, and more. Make your audience feel special by featuring
            their comments on screen.
          </p>
          <a href="#" className={styles.learnMoreLink}>See all integrations &rarr;</a>
        </div>
        <div className={styles.imageColumn}>
          <img src="https://via.placeholder.com/500x350?text=Multistream" alt="Multistream" className={styles.contentImage} />
        </div>
      </section>

      {/* Sección 4: Move your webinars to StreamYard On-Air */}
      <section className={`${styles.contentBlock} ${styles.bgBlueish} ${styles.reverseLayout}`}>
        <div className={styles.imageColumn}>
          <img src="https://via.placeholder.com/500x350?text=Webinars" alt="Webinars" className={styles.contentImage} />
        </div>
        <div className={styles.textColumn}>
          <h2>Move your webinars to StreamYard On-Air</h2>
          <p>
            StreamYard On-Air is a live webinar platform. We're redefining
            webinar stability, simplicity, and production quality. You can
            even embed it on your website for a fully white-label experience.
          </p>
          <a href="#" className={styles.learnMoreLink}>See why everyone is switching &rarr;</a>
        </div>
      </section>
    </div>
  );
};

export default ContentDetailsSection;

src/components/ContentDetailsSection/ContentDetailsSection.module.css

/* src/components/ContentDetailsSection/ContentDetailsSection.module.css */

.contentDetailsSection {
  padding: 0; /* Las secciones internas manejan su propio padding */
}

.contentBlock {
  display: flex;
  align-items: center;
  gap: 80px; /* Espacio entre columnas */
  padding: 100px 0; /* Espaciado vertical entre bloques */
  margin-bottom: 0; /* Cada bloque es una sección, no necesita margen inferior extra */
  flex-wrap: wrap; /* Permite envolver columnas en pantallas pequeñas */
}

/* Fondos de sección */
.bgPink {
  background-color: var(--accent-pink);
}

.bgPurple {
  background-color: var(--accent-purple);
  color: var(--bg-white); /* Texto blanco para fondo oscuro */
}
.bgPurple h2, .bgPurple p, .bgPurple .learnMoreLink {
  color: var(--bg-white);
}

.bgYellow {
  background-color: var(--accent-yellow);
}

.bgBlueish {
  background-color: #EBF7FF; /* Un azul muy claro */
}


.textColumn {
  flex: 1; /* Ocupa el espacio disponible */
  min-width: 300px;
  max-width: 600px; /* Limita el ancho del texto */
}

.imageColumn {
  flex: 1; /* Ocupa el espacio disponible */
  min-width: 300px;
  display: flex;
  justify-content: center; /* Centra la imagen dentro de su columna */
  align-items: center;
}

.contentImage {
  max-width: 100%; /* Asegura que la imagen no se desborde */
  height: auto;
  border-radius: var(--border-radius-card);
  box-shadow: var(--shadow-sm);
}

.reverseLayout {
  flex-direction: row-reverse; /* Invierte el orden de las columnas */
}

.learnMoreLink {
  color: var(--primary-blue);
  text-decoration: none;
  font-weight: 600;
  transition: color 0.3s ease;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 15px;
}
.bgPurple .learnMoreLink {
  color: var(--bg-white);
}

.learnMoreLink:hover {
  color: #0047b3; /* Azul más oscuro al pasar el ratón */
}

/* Estilos para el reproductor de audio simulado */
.audioPlayer {
  position: relative;
  width: 100%;
  max-width: 500px; /* Ancho máximo para el reproductor */
  border-radius: var(--border-radius-card);
  box-shadow: var(--shadow-sm);
  overflow: hidden; /* Asegura que la imagen y los controles respeten el borde */
}

.audioPlayer .contentImage {
  border-radius: 0; /* La imagen no necesita borde-radius si el contenedor ya lo tiene */
  display: block; /* Elimina espacios extra debajo de la imagen */
}

.audioControls {
  position: absolute;
  bottom: 20px;
  left: 20px;
  right: 20px;
  background-color: rgba(255, 255, 255, 0.9); /* Fondo semitransparente para los controles */
  border-radius: var(--border-radius-button);
  padding: 10px 15px;
  display: flex;
  align-items: center;
  gap: 15px;
  box-shadow: var(--shadow-sm);
}

.playPauseButton {
  background: none;
  border: none;
  color: var(--primary-blue);
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
}

.playPauseButton svg {
  width: 30px;
  height: 30px;
}

.progressBarContainer {
  flex-grow: 1;
  height: 8px;
  background-color: var(--border-light);
  border-radius: 4px;
  overflow: hidden;
}

.progressBar {
  height: 100%;
  background-color: var(--primary-blue);
  width: 0%; /* Controlado por el estado de React */
  transition: width 0.1s linear; /* Animación suave para el progreso */
}

.audioLabel {
  font-size: var(--font-size-sm);
  color: var(--text-medium);
  white-space: nowrap; /* Evita que el texto se rompa */
}

/* Responsive adjustments */
@media (max-width: 1024px) {
  .contentBlock {
    flex-direction: column; /* Apila las columnas */
    text-align: center;
    padding: 80px 0;
    gap: 40px;
  }

  .reverseLayout {
    flex-direction: column; /* También apila en layout invertido */
  }

  .textColumn, .imageColumn {
    max-width: 90%; /* Ajusta el ancho para móvil */
    margin: 0 auto; /* Centra las columnas */
  }

  .audioControls {
    bottom: 15px;
    left: 15px;
    right: 15px;
    padding: 8px 10px;
    gap: 10px;
  }

  .playPauseButton svg {
    width: 25px;
    height: 25px;
  }

  .audioLabel {
    font-size: 12px;
  }
}

@media (max-width: 768px) {
  .contentBlock {
    padding: 60px 0;
    gap: 30px;
  }

  .textColumn h2 {
    font-size: 32px;
  }

  .textColumn p {
    font-size: 16px;
  }
}

@media (max-width: 480px) {
  .textColumn h2 {
    font-size: 28px;
  }
}

src/components/CallToActionSection/CallToActionSection.tsx

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

const CallToActionSection: React.FC = () => {
  return (
    <section className={styles.ctaSection}>
      <div className={styles.ctaContent}>
        <h2>Get creating.</h2>
        <button className={styles.ctaButton}>Get started - It's free!</button>
        <p>Join millions of StreamYard users and start streaming or podcasting today.</p>
      </div>
    </section>
  );
};

export default CallToActionSection;

src/components/CallToActionSection/CallToActionSection.module.css

/* src/components/CallToActionSection/CallToActionSection.module.css */

.ctaSection {
  background-color: var(--primary-blue); /* Fondo azul */
  /* Imagen de fondo sutil con patrón geométrico */
  background-image: url('data:image/svg+xml;utf8,<svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h100v100H0z" fill="none"/><path d="M0 50h50L25 75zM50 0h50L75 25z" fill="rgba(255,255,255,0.05)"/></svg>');
  background-size: 200px 200px; /* Tamaño del patrón */
  color: var(--bg-white); /* Texto blanco */
  text-align: center;
  padding: 120px 20px; /* Espaciado interno */
  margin-top: 100px; /* Espacio superior para separar de la sección anterior */
}

.ctaContent {
  max-width: 700px; /* Ancho máximo para el contenido del CTA */
  margin: 0 auto;
}

.ctaContent h2 {
  font-size: var(--font-size-h2);
  color: var(--bg-white);
  margin-bottom: 25px;
}

.ctaButton {
  background-color: var(--bg-white); /* Botón blanco sobre fondo azul */
  color: var(--primary-blue);
  border: none;
  border-radius: var(--border-radius-button);
  padding: 18px 36px;
  font-size: 20px;
  font-weight: 700;
  cursor: pointer;
  transition: background-color 0.3s ease, transform 0.3s ease;
  margin-bottom: 20px;
}

.ctaButton:hover {
  background-color: #f0f0f0; /* Ligero cambio de color al pasar el ratón */
  transform: translateY(-2px); /* Pequeño efecto de elevación */
}

.ctaContent p {
  font-size: var(--font-size-p);
  color: rgba(255, 255, 255, 0.8); /* Texto más claro */
  margin: 0;
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .ctaSection {
    padding: 80px 16px;
    margin-top: 80px;
  }

  .ctaContent h2 {
    font-size: 36px;
    margin-bottom: 20px;
  }

  .ctaButton {
    padding: 16px 30px;
    font-size: 18px;
  }

  .ctaContent p {
    font-size: 16px;
  }
}

@media (max-width: 480px) {
  .ctaSection {
    padding: 60px 16px;
    margin-top: 60px;
  }

  .ctaContent h2 {
    font-size: 30px;
  }

  .ctaButton {
    padding: 14px 24px;
    font-size: 16px;
  }
}