# Proyecto: Sitio Web 7THCA

## Contexto

Sitio web para la banda **7THCA**, dúo de música electrónica/rock progresivo integrado por **Claudio González** (sintetizadores & composición) y **Jorge Albornoz** (guitarras & composición).

El diseño replica una maqueta existente con estética **dark/cyberpunk**, acentos en cian `#00e5ff` y naranja `#ff6d00`, tipografías Google Fonts (Orbitron, Inter, Space Mono).

---

## Estado actual

- [x] Archivo `index.html` creado con estructura completa
- [ ] Imágenes: actualmente usan URLs de Unsplash (temporales)
- [ ] Audio: los players tienen UI funcional pero sin archivos MP3 reales
- [ ] Videos: los thumbnails de YouTube son placeholders

---

## Estructura de carpetas objetivo

```
7thca/
├── index.html
├── proyecto.md
├── assets/
│   ├── img/
│   │   ├── hero-bg.jpg
│   │   ├── album-midnight-drive.jpg
│   │   ├── album-life.jpg
│   │   ├── release-just-another-day.jpg
│   │   ├── release-just-another-day-acustico.jpg
│   │   ├── member-claudio.jpg
│   │   ├── member-jorge.jpg
│   │   ├── genesis-estudio.jpg
│   │   └── fotos/
│   │       ├── foto-01.jpg
│   │       ├── foto-02.jpg
│   │       ├── foto-03.jpg
│   │       ├── foto-04.jpg
│   │       ├── foto-05.jpg
│   │       └── foto-06.jpg
│   └── audio/
│       ├── just-another-day.mp3
│       ├── just-another-day-acustico.mp3
│       ├── midnight-drive.mp3
│       ├── permanent-postcard.mp3
│       └── the-ascent.mp3
```

---

## Secciones del HTML

| ID | Sección | Estado |
|----|---------|--------|
| `#inicio` | Hero con fondo ciudad + título 7THCA | ✅ Listo |
| `#ultimos-lanzamientos` | 2 tarjetas: Just Another Day + versión acústica | ✅ Listo |
| `#albumes` | 2 álbumes: The Midnight Drive / Life | ✅ Listo |
| `#nosotros` | Génesis del sonido + 2 miembros | ✅ Listo |
| `#fotos` | Grilla 3×2 + enlace galería | ✅ Listo |
| `#videos` | 2 tarjetas con embed YouTube | ✅ Listo |

---

## Pendientes principales

### 1. Reemplazar imágenes Unsplash por imágenes reales

Buscar en el HTML las etiquetas `<img src="https://images.unsplash.com/...">` y reemplazarlas por rutas locales:

```html
<!-- Antes -->
<img src="https://images.unsplash.com/photo-xxx?w=600&q=80" alt="Claudio González">

<!-- Después -->
<img src="assets/img/member-claudio.jpg" alt="Claudio González">
```

También reemplazar los `background` en CSS del hero:

```css
/* Antes */
background: ... url('https://images.unsplash.com/photo-xxx') ...

/* Después */
background: ... url('assets/img/hero-bg.jpg') ...
```

---

### 2. Conectar players MP3

Cada track tiene un `<div class="track-item">` con un botón `.play-btn`. El plan es:

**a)** Agregar elementos `<audio>` ocultos al final del `<body>`, uno por track:

```html
<!-- Al final del body, antes de </body> -->
<audio id="audio-jad"        src="assets/audio/just-another-day.mp3"         preload="metadata"></audio>
<audio id="audio-jad-ac"     src="assets/audio/just-another-day-acustico.mp3" preload="metadata"></audio>
<audio id="audio-midnight"   src="assets/audio/midnight-drive.mp3"            preload="metadata"></audio>
<audio id="audio-postcard"   src="assets/audio/permanent-postcard.mp3"        preload="metadata"></audio>
<audio id="audio-ascent"     src="assets/audio/the-ascent.mp3"                preload="metadata"></audio>
```

**b)** Agregar el atributo `data-audio` a cada `.track-item` apuntando al ID del audio:

```html
<div class="track-item" data-audio="audio-jad">
  <button class="play-btn">▶</button>
  ...
</div>
```

**c)** Reemplazar el JS actual de play por uno que:
- Conecte el botón con su `<audio>` correspondiente
- Pause el track anterior al reproducir uno nuevo
- Actualice la barra de progreso `.track-progress-bar` en tiempo real
- Muestre la duración real del archivo

```javascript
// Lógica general del audio manager
let currentAudio = null;
let currentBtn   = null;

document.querySelectorAll('.track-item[data-audio]').forEach(item => {
  const btn   = item.querySelector('.play-btn');
  const bar   = item.querySelector('.track-progress-bar');
  const audio = document.getElementById(item.dataset.audio);

  // Mostrar duración real cuando el metadata cargue
  audio.addEventListener('loadedmetadata', () => {
    const dur = item.querySelector('.track-duration');
    if (dur) dur.textContent = formatTime(audio.duration);
  });

  // Actualizar barra de progreso
  audio.addEventListener('timeupdate', () => {
    if (audio.duration) {
      bar.style.width = (audio.currentTime / audio.duration * 100) + '%';
    }
  });

  // Reset al terminar
  audio.addEventListener('ended', () => {
    btn.textContent = '▶';
    bar.style.width = '0%';
    currentAudio = null;
    currentBtn   = null;
  });

  btn.addEventListener('click', e => {
    e.stopPropagation();

    // Pausar el que estaba sonando
    if (currentAudio && currentAudio !== audio) {
      currentAudio.pause();
      currentAudio.currentTime = 0;
      currentBtn.textContent = '▶';
      currentBtn.closest('.track-item')
        .querySelector('.track-progress-bar').style.width = '0%';
    }

    if (audio.paused) {
      audio.play();
      btn.textContent = '⏸';
      currentAudio = audio;
      currentBtn   = btn;
    } else {
      audio.pause();
      btn.textContent = '▶';
      currentAudio = null;
      currentBtn   = null;
    }
  });
});

function formatTime(secs) {
  const m = Math.floor(secs / 60);
  const s = Math.floor(secs % 60).toString().padStart(2, '0');
  return `${m}:${s}`;
}
```

---

### 3. Embeber videos de YouTube

Reemplazar las tarjetas `.video-card` estáticas por iframes reales. Dos opciones:

**Opción A — Click para cargar (recomendado, mejor rendimiento):**

```html
<div class="video-card" data-yt="YOUTUBE_VIDEO_ID">
  <div class="video-thumb">
    <img src="https://img.youtube.com/vi/YOUTUBE_VIDEO_ID/hqdefault.jpg" alt="...">
    <div class="yt-play">▶</div>
  </div>
</div>
```

```javascript
document.querySelectorAll('.video-card[data-yt]').forEach(card => {
  card.addEventListener('click', () => {
    const id = card.dataset.yt;
    const iframe = document.createElement('iframe');
    iframe.src = `https://www.youtube.com/embed/${id}?autoplay=1`;
    iframe.allow = 'autoplay; encrypted-media';
    iframe.allowFullscreen = true;
    iframe.style.cssText = 'width:100%;aspect-ratio:16/9;border:none;';
    card.querySelector('.video-thumb').replaceWith(iframe);
  });
});
```

**Opción B — iframe directo:**

```html
<iframe
  src="https://www.youtube.com/embed/YOUTUBE_VIDEO_ID"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
  allowfullscreen>
</iframe>
```

---

## Variables CSS — Paleta de colores

```css
:root {
  --bg-deep:    #0a0c12;   /* Fondo principal */
  --bg-card:    #111420;   /* Fondo de tarjetas */
  --bg-section: #0e1018;   /* Fondo de secciones alternas */
  --cyan:       #00e5ff;   /* Acento principal */
  --cyan-dim:   #007a8a;   /* Acento suavizado / bordes */
  --orange:     #ff6d00;   /* Acento secundario */
  --orange-dim: #7a3200;   /* Naranja suavizado */
  --text:       #dde4ee;   /* Texto principal */
  --text-muted: #7a8499;   /* Texto secundario */
  --border:     rgba(0,229,255,0.12); /* Bordes sutiles */
}
```

---

## Tipografías (Google Fonts)

| Rol | Familia | Pesos |
|-----|---------|-------|
| Títulos / Logo | Orbitron | 400, 700, 900 |
| Cuerpo / Párrafos | Inter | 300, 400, 500, 600 |
| Etiquetas / Datos / Mono | Space Mono | 400, 700 |

```html
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Inter:wght@300;400;500;600&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
```

---

## Notas

- El HTML es un archivo único autocontenido (HTML + CSS + JS inline), fácil de editar en VSCode sin build tools.
- No usa frameworks externos, solo Google Fonts vía CDN.
- La nav detecta la sección activa automáticamente al hacer scroll.
- Totalmente responsive para móvil (breakpoint en 768px).

---

## Base de datos — Convenciones (`Database/banda.sql`)

### Nomenclatura general
- Tablas y columnas en **PascalCase**.
- Toda tabla tiene una llave primaria técnica `IDENTITY` llamada `Id{NombreTabla}` (ej: `IdCancion`).
- Toda llave foránea se llama igual que la llave primaria a la que apunta (ej: `IdTipoAlbum`).

### Tablas enumeradoras (`Tipo*`)
- Las tablas que actúan como enumeradores se prefijan con `Tipo` (ej: `TipoEstadoCancion`, `TipoCredito`).
- `Tipo` **no significa "tipo" en sentido de negocio**, sino que identifica que la tabla es un enumerador no transaccional.
- Estructura fija: solo `Id{Nombre}` + `Descripcion NVARCHAR(256)`.
- **Excepción:** si `Tipo` tiene significado de negocio real (ej: `TipoFoto` — el tipo fotográfico de una imagen), se conserva el prefijo en los aliases de las vistas.

### Indicadores booleanos
- Se usan columnas `TINYINT` con `DEFAULT 0/1`.
- Los flags llevan prefijo `Ind` (ej: `IndMiembro`, `IndUltimosLanzamientos`).

### Multimedia
- Las rutas físicas de archivos (imagen, audio, video local) se almacenan en columnas `*Ruta` (ej: `AudioRuta`, `PortadaRuta`, `FotoRuta`).
- PHP genera la URL pública a partir de la ruta. No se almacenan URLs.
- Excepción: `YoutubeId` en `Video` guarda solo el ID del video (no la URL completa); PHP construye las URLs de embed y thumbnail.

### Vistas
- **Vistas de entidad** (`VW{Tabla}`): una por cada tabla de negocio. Incluyen todos los campos de la tabla y resuelven las FK:
  - FK → tabla `Tipo*`: JOIN + `Descripcion AS {NombreSinPrefixoTipo}` (ej: `TipoEstadoCancion` → alias `EstadoCancion`). Si `Tipo` tiene significado de negocio real, se conserva el nombre completo como alias (ej: `TipoFoto` → alias `TipoFoto`, `TipoCredito` → alias `TipoCredito`).
  - FK → tabla no-`Tipo`: JOIN trayendo campos descriptivos útiles (Titulo, Nombre, FechaLanzamiento, etc.).
- **Vistas de consulta** (`VW{Proposito}`): para reportes y secciones específicas de la interfaz (ej: `VWUltimosLanzamientos`, `VWAlbumTracklist`).
- Nomenclatura: PascalCase con prefijo `VW` (ej: `VWCancion`, `VWAlbumTracklist`).
- Las vistas no llevan `ORDER BY` (SQL Server no lo permite sin `TOP`/`OFFSET`; el orden se aplica en la consulta).