# JAILGAMES — Plan Técnico Completo

---

## 1. Arquitectura General

### Decisión: Eleventy (11ty) como generador de sitio estático

Se descartan tres alternativas y se elige una:

| Opción | Ventajas | Inconvenientes |
|---|---|---|
| HTML/CSS/JS puro | Sin dependencias | Hay que duplicar HTML por cada juego; cualquier cambio en el layout obliga a tocar todos los archivos |
| Astro / Vite | Moderno, componentes | Pesado para <10 juegos; requiere Node + build complejo |
| Microframework (Flask, Express) | Dinámico | Necesita servidor con runtime activo; innecesario para contenido estático en ferias |
| **Eleventy (11ty)** | **Plantillas Nunjucks, lee JSON/YAML nativamente, genera HTML puro, cero JS en el navegador** | **Requiere Node para el build (no en producción)** |

**Justificación concreta:**

- **Mantenimiento sencillo**: añadir un juego = crear un archivo YAML + copiar imágenes. Nada más.
- **Cero dependencias en producción**: el resultado es HTML/CSS/JS estático servido por Nginx.
- **Funciona sin internet**: perfecto para ferias presenciales. Se sirve desde un portátil con Docker.
- **Nginx en lugar de Apache**: más ligero, configuración mínima para servir estáticos. Si se prefiere Apache, el cambio es trivial (se indica en la sección Docker).

---

## 2. Estructura de Carpetas

```
jailgames/
├── src/                          # ← Fuentes (nunca se sirven directamente)
│   ├── _data/
│   │   └── site.json             # Datos globales (nombre del sitio, redes, etc.)
│   ├── _includes/
│   │   ├── base.njk              # Layout base HTML (head, header, footer)
│   │   ├── game-card.njk         # Componente: tarjeta de juego para la home
│   │   └── game-page.njk        # Layout: ficha individual de juego
│   ├── games/                    # Un archivo YAML por juego
│   │   ├── cosmic-escape.yaml
│   │   ├── pixel-dungeon.yaml
│   │   └── ...
│   ├── css/
│   │   └── style.css             # Estilos globales
│   ├── js/
│   │   └── main.js               # JS mínimo (lightbox, filtros, etc.)
│   ├── index.njk                 # Página principal (home)
│   └── game.njk                  # Plantilla que genera cada /game/slug/
│
├── static/                       # ← Archivos copiados tal cual al build
│   ├── logo/
│   │   └── jailgames-logo.png    # Logo general del sitio
│   ├── games/                    # Carpeta por juego (nombre = slug)
│   │   ├── cosmic-escape/
│   │   │   ├── logo.png
│   │   │   ├── screenshot-1.png
│   │   │   ├── screenshot-2.png
│   │   │   └── screenshot-3.png
│   │   └── pixel-dungeon/
│   │       ├── logo.png
│   │       └── ...
│   └── favicon.ico
│
├── downloads/                    # ← Binarios descargables
│   ├── cosmic-escape/
│   │   ├── cosmic-escape-v1.2-windows.zip
│   │   ├── cosmic-escape-v1.2-linux.tar.gz
│   │   └── cosmic-escape-v1.2-mac.dmg
│   └── pixel-dungeon/
│       └── ...
│
├── _site/                        # ← Salida del build (se sirve con Nginx)
│
├── .eleventy.js                  # Configuración de Eleventy
├── package.json
├── Dockerfile
├── docker-compose.yml
├── nginx.conf                    # Configuración de Nginx
└── README.md
```

### Resumen de ubicaciones

| Contenido | Ruta |
|---|---|
| Datos de cada juego | `src/games/<slug>.yaml` |
| Imágenes de cada juego | `static/games/<slug>/` |
| Binarios descargables | `downloads/<slug>/` |
| Plantillas HTML | `src/_includes/` |
| Página principal | `src/index.njk` |
| Logo del sitio | `static/logo/jailgames-logo.png` |
| CSS | `src/css/style.css` |
| Salida final | `_site/` |

---

## 3. Formato de Datos por Juego

Cada juego se define en un archivo YAML independiente dentro de `src/games/`. El nombre del archivo es el **slug** (identificador URL).

### Ejemplo: `src/games/cosmic-escape.yaml`

```yaml
name: "Cosmic Escape"
slug: "cosmic-escape"
tagline: "Escapa de la estación antes de que colapse"
description: |
  Un juego de plataformas 2D donde controlas a un astronauta atrapado en una
  estación espacial que se desmorona. Recoge oxígeno, esquiva meteoritos y
  encuentra la cápsula de escape antes de que se agote el tiempo.

  Desarrollado en Godot Engine. Incluye 12 niveles y modo contrarreloj.

version: "1.2.0"
release_date: "2025-11-15"
tags:
  - plataformas
  - 2D
  - singleplayer

logo: "/games/cosmic-escape/logo.png"

screenshots:
  - "/games/cosmic-escape/screenshot-1.png"
  - "/games/cosmic-escape/screenshot-2.png"
  - "/games/cosmic-escape/screenshot-3.png"

downloads:
  - platform: "Windows"
    file: "/downloads/cosmic-escape/cosmic-escape-v1.2-windows.zip"
    size: "85 MB"
  - platform: "Linux"
    file: "/downloads/cosmic-escape/cosmic-escape-v1.2-linux.tar.gz"
    size: "78 MB"
  - platform: "macOS"
    file: "/downloads/cosmic-escape/cosmic-escape-v1.2-mac.dmg"
    size: "90 MB"

# Campos opcionales
engine: "Godot 4.2"
players: "1 jugador"
genre: "Plataformas / Acción"
repo: "https://gitea.local/jailgames/cosmic-escape"  # Solo referencia interna
```

### Campos obligatorios vs opcionales

| Campo | Obligatorio | Descripción |
|---|---|---|
| `name` | Sí | Nombre visible del juego |
| `slug` | Sí | Identificador para URLs y carpetas |
| `description` | Sí | Texto descriptivo (soporta párrafos) |
| `version` | Sí | Versión del juego |
| `release_date` | Sí | Fecha de publicación (YYYY-MM-DD) |
| `downloads` | Sí | Al menos una entrada con platform/file/size |
| `screenshots` | Sí | Al menos una captura |
| `logo` | No | Si no existe, se usa una imagen placeholder |
| `tagline` | No | Frase corta para la tarjeta de la home |
| `tags` | No | Etiquetas para filtrar |
| `engine` | No | Motor de desarrollo |
| `players` | No | Número de jugadores |
| `genre` | No | Género del juego |
| `repo` | No | Enlace al repositorio (solo referencia) |

---

## 4. Generación de Páginas

### 4.1 Configuración de Eleventy (`.eleventy.js`)

```javascript
const yaml = require("js-yaml");
const fs = require("fs");
const path = require("path");

module.exports = function (eleventyConfig) {
  // Parsear archivos YAML
  eleventyConfig.addDataExtension("yaml,yml", (contents) =>
    yaml.load(contents)
  );

  // Copiar archivos estáticos al build
  eleventyConfig.addPassthroughCopy("static");
  eleventyConfig.addPassthroughCopy("downloads");
  eleventyConfig.addPassthroughCopy("src/css");
  eleventyConfig.addPassthroughCopy("src/js");

  // Colección: todos los juegos ordenados por fecha
  eleventyConfig.addCollection("games", function () {
    const gamesDir = path.join(__dirname, "src", "games");
    const files = fs.readdirSync(gamesDir).filter((f) => f.endsWith(".yaml"));
    return files
      .map((f) => {
        const data = yaml.load(
          fs.readFileSync(path.join(gamesDir, f), "utf-8")
        );
        return data;
      })
      .sort((a, b) => new Date(b.release_date) - new Date(a.release_date));
  });

  // Filtro para formatear fechas
  eleventyConfig.addFilter("dateFormat", function (dateStr) {
    const d = new Date(dateStr);
    return d.toLocaleDateString("es-ES", {
      year: "numeric",
      month: "long",
      day: "numeric",
    });
  });

  // Filtro Markdown para descripciones
  const markdownIt = require("markdown-it");
  const md = markdownIt({ html: true });
  eleventyConfig.addFilter("markdown", (content) => md.render(content || ""));

  return {
    dir: {
      input: "src",
      includes: "_includes",
      data: "_data",
      output: "_site",
    },
    templateFormats: ["njk", "md"],
    htmlTemplateEngine: "njk",
  };
};
```

### 4.2 Página Principal (`src/index.njk`)

La home itera sobre la colección `games` y renderiza una tarjeta por cada juego.

```html
---
layout: base.njk
title: "JAILGAMES — Nuestros Juegos"
---

<section class="hero">
  <img src="/logo/jailgames-logo.png" alt="JAILGAMES" class="site-logo" />
  <p class="hero-subtitle">Juegos caseros, hechos con cariño</p>
</section>

<section class="games-grid">
  {% for game in collections.games %}
    {% include "game-card.njk" %}
  {% endfor %}
</section>
```

### 4.3 Tarjeta de Juego (`src/_includes/game-card.njk`)

```html
<a href="/game/{{ game.slug }}/" class="game-card">
  <div class="game-card__image">
    {% if game.logo %}
      <img src="{{ game.logo }}" alt="{{ game.name }} logo" loading="lazy" />
    {% else %}
      <div class="game-card__placeholder">🎮</div>
    {% endif %}
  </div>
  <div class="game-card__info">
    <h2 class="game-card__title">{{ game.name }}</h2>
    {% if game.tagline %}
      <p class="game-card__tagline">{{ game.tagline }}</p>
    {% endif %}
    <div class="game-card__meta">
      <span class="game-card__version">v{{ game.version }}</span>
      {% if game.tags %}
        {% for tag in game.tags %}
          <span class="game-card__tag">{{ tag }}</span>
        {% endfor %}
      {% endif %}
    </div>
  </div>
</a>
```

### 4.4 Generación de Fichas Individuales (`src/game.njk`)

Eleventy genera una página por cada archivo YAML mediante **paginación**:

```html
---
pagination:
  data: collections.games
  size: 1
  alias: game
permalink: "/game/{{ game.slug }}/"
layout: game-page.njk
eleventyComputed:
  title: "{{ game.name }} — JAILGAMES"
---
```

### 4.5 Layout de Ficha (`src/_includes/game-page.njk`)

```html
{% extends "base.njk" %}

{% block content %}
<article class="game-detail">

  <header class="game-detail__header">
    {% if game.logo %}
      <img src="{{ game.logo }}" alt="{{ game.name }}" class="game-detail__logo" />
    {% endif %}
    <div>
      <h1>{{ game.name }}</h1>
      {% if game.tagline %}<p class="game-detail__tagline">{{ game.tagline }}</p>{% endif %}
      <div class="game-detail__meta">
        <span>v{{ game.version }}</span>
        <span>{{ game.release_date | dateFormat }}</span>
        {% if game.genre %}<span>{{ game.genre }}</span>{% endif %}
        {% if game.players %}<span>{{ game.players }}</span>{% endif %}
        {% if game.engine %}<span>{{ game.engine }}</span>{% endif %}
      </div>
    </div>
  </header>

  <section class="game-detail__screenshots">
    {% for img in game.screenshots %}
      <img src="{{ img }}" alt="Captura de {{ game.name }}" loading="lazy"
           class="game-detail__screenshot" />
    {% endfor %}
  </section>

  <section class="game-detail__description">
    {{ game.description | markdown | safe }}
  </section>

  <section class="game-detail__downloads">
    <h2>Descargar</h2>
    <div class="download-buttons">
      {% for dl in game.downloads %}
        <a href="{{ dl.file }}" class="download-btn" download>
          <span class="download-btn__platform">{{ dl.platform }}</span>
          <span class="download-btn__size">{{ dl.size }}</span>
        </a>
      {% endfor %}
    </div>
  </section>

  {% if game.tags %}
  <section class="game-detail__tags">
    {% for tag in game.tags %}
      <span class="tag">{{ tag }}</span>
    {% endfor %}
  </section>
  {% endif %}

  <a href="/" class="back-link">← Volver al catálogo</a>

</article>
{% endblock %}
```

### 4.6 Descargas Locales

Las descargas se enlazan directamente a archivos en `/downloads/<slug>/`. El atributo `download` en el `<a>` fuerza la descarga en lugar de abrir el archivo en el navegador. Nginx sirve estos archivos como estáticos. No hay enlaces a Gitea ni servicios externos.

---

## 5. Diseño y Estilo

### 5.1 Framework CSS: PicoCSS

Se recomienda **PicoCSS** (`@picocss/pico`) por:

- Estilo limpio sin necesidad de clases (aplica estilos directamente a elementos HTML semánticos).
- Muy ligero (~10 KB).
- Soporte nativo de tema oscuro/claro.
- Responsive por defecto.
- Sin dependencias de build (se puede usar como archivo CSS estático).

Si se prefiere más control visual, la alternativa es **Tailwind CSS** (requiere build step).

### 5.2 Esquema de Layout

```
┌──────────────────────────────────────────────┐
│              HEADER                          │
│   [Logo JAILGAMES]     Juegos caseros...     │
├──────────────────────────────────────────────┤
│                                              │
│   ┌──────┐  ┌──────┐  ┌──────┐              │
│   │ Logo │  │ Logo │  │ Logo │              │
│   │      │  │      │  │      │              │
│   │ Name │  │ Name │  │ Name │   ← Grid    │
│   │ Tag  │  │ Tag  │  │ Tag  │     3 cols   │
│   └──────┘  └──────┘  └──────┘              │
│                                              │
│   ┌──────┐  ┌──────┐                        │
│   │ ...  │  │ ...  │                        │
│   └──────┘  └──────┘                        │
│                                              │
├──────────────────────────────────────────────┤
│              FOOTER                          │
│   JAILGAMES · Hecho con ♥                   │
└──────────────────────────────────────────────┘
```

**Ficha individual:**

```
┌──────────────────────────────────────────────┐
│   ← Volver     HEADER                       │
├──────────────────────────────────────────────┤
│                                              │
│   [Logo]  Nombre del Juego                   │
│           v1.2.0 · 15 nov 2025 · Godot       │
│                                              │
│   ┌──────────────────────────────────────┐   │
│   │      Galería de capturas             │   │
│   │  [img1]    [img2]    [img3]          │   │
│   └──────────────────────────────────────┘   │
│                                              │
│   Descripción del juego en prosa...          │
│   Párrafo 1...                               │
│   Párrafo 2...                               │
│                                              │
│   ┌──────────┐ ┌──────────┐ ┌──────────┐    │
│   │ Windows  │ │  Linux   │ │  macOS   │    │
│   │  85 MB   │ │  78 MB   │ │  90 MB   │    │
│   └──────────┘ └──────────┘ └──────────┘    │
│                                              │
│   #plataformas  #2D  #singleplayer           │
│                                              │
├──────────────────────────────────────────────┤
│              FOOTER                          │
└──────────────────────────────────────────────┘
```

### 5.3 Diseño Responsive

- **Desktop (>1024px)**: Grid de 3 columnas para tarjetas.
- **Tablet (768–1024px)**: Grid de 2 columnas.
- **Móvil (<768px)**: 1 columna, capturas en scroll horizontal.

### 5.4 Paleta de Colores Sugerida

```css
:root {
  --color-bg: #0f0f0f;
  --color-surface: #1a1a2e;
  --color-primary: #e94560;
  --color-secondary: #16213e;
  --color-text: #eaeaea;
  --color-text-muted: #a0a0a0;
  --color-accent: #0f3460;
  --color-success: #4ecca3;
  --font-main: "Inter", "Segoe UI", sans-serif;
  --font-heading: "Space Grotesk", "Inter", sans-serif;
}
```

Tema oscuro por defecto (estética gaming), con contraste suficiente para legibilidad.

---

## 6. Pipeline de Actualización

### Añadir un juego nuevo

```
Paso 1  Crear el archivo YAML
        → src/games/<slug>.yaml
        (copiar de un juego existente y modificar campos)

Paso 2  Crear carpeta de imágenes
        → static/games/<slug>/
        Copiar: logo.png, screenshot-1.png, screenshot-2.png, ...

Paso 3  Crear carpeta de descargas
        → downloads/<slug>/
        Copiar binarios: <slug>-v<version>-<platform>.<ext>

Paso 4  Verificar que las rutas en el YAML coinciden con los archivos copiados.

Paso 5  Regenerar el sitio:
        $ npm run build
        (Esto ejecuta: npx @11ty/eleventy)

Paso 6  Verificar localmente:
        $ npm run serve
        (Esto ejecuta: npx @11ty/eleventy --serve)
        Abrir http://localhost:8080 y comprobar.

Paso 7  Reconstruir el contenedor Docker:
        $ docker compose up --build -d
```

### Modificar un juego existente

```
Paso 1  Editar src/games/<slug>.yaml
Paso 2  Si hay imágenes nuevas, copiarlas a static/games/<slug>/
Paso 3  Si hay binarios nuevos, copiarlos a downloads/<slug>/
Paso 4  Regenerar: npm run build
Paso 5  Reconstruir Docker: docker compose up --build -d
```

### Diagrama del flujo

```
[YAML nuevo] + [Imágenes] + [Binarios]
        │
        ▼
   npm run build      ← Eleventy lee YAML, genera HTML
        │
        ▼
    _site/             ← HTML/CSS/JS estático listo
        │
        ▼
   docker compose up   ← Nginx sirve _site/ + downloads/
        │
        ▼
   http://localhost     ← Web lista para la feria
```

---

## 7. Plan de Docker

### 7.1 Dockerfile

```dockerfile
# ---------- ETAPA 1: Build ----------
FROM node:20-alpine AS builder

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

COPY src/ src/
COPY static/ static/
COPY .eleventy.js ./

RUN npx @11ty/eleventy

# ---------- ETAPA 2: Servir ----------
FROM nginx:alpine

# Configuración personalizada de Nginx
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Copiar el sitio generado
COPY --from=builder /app/_site /usr/share/nginx/html

# Copiar descargas (no pasan por Eleventy)
COPY downloads/ /usr/share/nginx/html/downloads/

EXPOSE 80
```

### 7.2 docker-compose.yml

```yaml
version: "3.8"

services:
  jailgames:
    build: .
    container_name: jailgames-web
    ports:
      - "80:80"
    volumes:
      # Volumen para descargas: permite actualizar binarios
      # sin reconstruir la imagen completa
      - ./downloads:/usr/share/nginx/html/downloads:ro
      # Volumen para imágenes de juegos (opcional, misma idea)
      - ./static/games:/usr/share/nginx/html/games:ro
    restart: unless-stopped
```

### 7.3 nginx.conf

```nginx
server {
    listen 80;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;

    # Compresión para HTML/CSS/JS
    gzip on;
    gzip_types text/html text/css application/javascript application/json image/svg+xml;
    gzip_min_length 256;

    # Cacheo agresivo para assets estáticos (feria local)
    location ~* \.(png|jpg|jpeg|gif|svg|ico|webp)$ {
        expires 7d;
        add_header Cache-Control "public, immutable";
    }

    # Descargas: forzar descarga en lugar de abrir en navegador
    location /downloads/ {
        add_header Content-Disposition "attachment";
        sendfile on;
        tcp_nopush on;
    }

    # SPA fallback (por si se accede directamente a /game/slug/)
    location / {
        try_files $uri $uri/ $uri.html =404;
    }

    # Página 404 personalizada
    error_page 404 /404.html;
}
```

### 7.4 Uso con Apache (alternativa)

Si se prefiere Apache, reemplazar la etapa 2 del Dockerfile:

```dockerfile
FROM httpd:alpine
COPY --from=builder /app/_site /usr/local/apache2/htdocs/
COPY downloads/ /usr/local/apache2/htdocs/downloads/
COPY apache.conf /usr/local/apache2/conf/httpd.conf
EXPOSE 80
```

### 7.5 Despliegue en Ferias sin Internet

```
1. En la máquina de desarrollo (con internet):
   $ docker compose build
   $ docker save jailgames-web:latest | gzip > jailgames-image.tar.gz

2. Copiar jailgames-image.tar.gz al portátil de la feria (USB).

3. En el portátil de la feria (sin internet):
   $ gunzip -c jailgames-image.tar.gz | docker load
   $ docker run -d -p 80:80 \
       -v /ruta/local/downloads:/usr/share/nginx/html/downloads:ro \
       --name jailgames jailgames-web:latest

4. Abrir http://localhost en cualquier navegador.
   (O configurar un punto de acceso WiFi para que otros dispositivos
    se conecten a la IP local del portátil)
```

---

## 8. Lista de Tareas para Claude Code

Copiar esta lista tal cual y dársela a Claude Code como instrucciones paso a paso.

---

### BLOQUE A — Inicialización del Proyecto

```
TAREA A1: Crear la estructura de carpetas del proyecto "jailgames"

Crear los siguientes directorios:
  - src/_data/
  - src/_includes/
  - src/games/
  - src/css/
  - src/js/
  - static/logo/
  - static/games/
  - downloads/

TAREA A2: Inicializar package.json

Ejecutar: npm init -y
Instalar dependencias:
  npm install --save-dev @11ty/eleventy js-yaml markdown-it

Añadir scripts a package.json:
  "build": "npx @11ty/eleventy",
  "serve": "npx @11ty/eleventy --serve --port=8080",
  "clean": "rm -rf _site"
```

### BLOQUE B — Configuración de Eleventy

```
TAREA B1: Crear .eleventy.js en la raíz del proyecto

Contenido: Configuración de Eleventy que:
  - Parsea archivos YAML con js-yaml
  - Copia "static" y "downloads" como passthrough
  - Copia "src/css" y "src/js" como passthrough
  - Crea una colección "games" leyendo todos los .yaml de src/games/
  - Ordena los juegos por release_date (más recientes primero)
  - Añade filtro "dateFormat" que formatea fechas en español
  - Añade filtro "markdown" que convierte texto a HTML con markdown-it
  - Input: src/, Output: _site/, Includes: _includes/, Data: _data/

TAREA B2: Crear src/_data/site.json

Contenido:
{
  "name": "JAILGAMES",
  "tagline": "Juegos caseros, hechos con cariño",
  "url": "http://localhost",
  "lang": "es"
}
```

### BLOQUE C — Plantillas HTML (Nunjucks)

```
TAREA C1: Crear src/_includes/base.njk

Layout base HTML5 que incluya:
  - DOCTYPE html, lang="es"
  - Meta charset, viewport
  - Title dinámico: {{ title }} o fallback a site.name
  - Enlace a /css/style.css
  - Header con logo de JAILGAMES (enlazado a /)
  - Bloque {% block content %}{% endblock %}
  - Footer con "JAILGAMES · Hecho con ♥"
  - Enlace a /js/main.js antes de cerrar body

TAREA C2: Crear src/_includes/game-card.njk

Componente tarjeta de juego:
  - Enlace <a> a /game/{{ game.slug }}/
  - Imagen del logo del juego (o placeholder si no hay logo)
  - Nombre del juego <h2>
  - Tagline si existe
  - Versión y tags en un div de metadatos

TAREA C3: Crear src/_includes/game-page.njk

Layout para ficha individual. Extiende base.njk. Incluye:
  - Header con logo del juego + nombre + tagline + metadatos
    (versión, fecha formateada, género, players, engine)
  - Sección de galería de capturas (iterar screenshots)
  - Sección de descripción (usar filtro markdown)
  - Sección de descargas: un botón por cada entrada en downloads
    (con atributo download, mostrar plataforma y tamaño)
  - Sección de tags
  - Enlace "← Volver al catálogo" a /

TAREA C4: Crear src/index.njk

Página principal:
  - Layout: base.njk
  - Sección hero con logo y subtítulo
  - Sección games-grid que itera collections.games
    e incluye game-card.njk para cada juego

TAREA C5: Crear src/game.njk

Plantilla de paginación que genera /game/<slug>/ para cada juego:
  - Front matter con pagination (data: collections.games, size: 1, alias: game)
  - Permalink: /game/{{ game.slug }}/
  - Layout: game-page.njk
  - eleventyComputed title
```

### BLOQUE D — Estilos CSS

```
TAREA D1: Crear src/css/style.css

Archivo CSS completo que incluya:
  - Variables CSS (colores tema oscuro gaming, fuentes)
  - Reset mínimo (box-sizing, margin 0)
  - Body: fondo oscuro, color claro, fuente sans-serif
  - .site-header: flex, centrado, con logo a la izquierda
  - .site-logo: max-height 60px
  - .hero: centrado, padding generoso
  - .hero-subtitle: color muted
  - .games-grid: CSS Grid, 3 columnas en desktop, 2 en tablet, 1 en móvil
  - .game-card: bloque con fondo surface, borde redondeado, hover con sombra,
    transición suave. Sin text-decoration.
  - .game-card__image img: object-fit contain, altura fija
  - .game-card__title: color blanco
  - .game-card__tagline: color muted, font-size pequeño
  - .game-card__tag: badge pequeño con color accent
  - .game-detail: max-width 900px, centrado, padding
  - .game-detail__header: flex, logo a la izquierda, info a la derecha
  - .game-detail__screenshots: grid o flex con scroll horizontal en móvil
  - .game-detail__screenshot: borde redondeado, max-width 100%
  - .download-buttons: flex, wrap
  - .download-btn: botón grande con color primary, hover,
    icono de plataforma si es posible
  - .tag: badge igual que game-card__tag
  - .back-link: estilo enlace simple
  - .site-footer: centrado, padding, color muted
  - Media queries para responsive (768px, 1024px)
```

### BLOQUE E — JavaScript Mínimo

```
TAREA E1: Crear src/js/main.js

Funcionalidad mínima:
  - Lightbox simple para capturas: al hacer clic en una screenshot,
    mostrarla en overlay a pantalla completa. Clic en overlay = cerrar.
  - (Opcional) Filtrado de juegos por tags en la home.
  - No usar frameworks, vanilla JS puro.
```

### BLOQUE F — Datos de Ejemplo

```
TAREA F1: Crear 2 archivos YAML de ejemplo en src/games/

Archivo 1: src/games/cosmic-escape.yaml
  - Rellenar con datos ficticios pero realistas
  - Incluir al menos 3 screenshots, 2 plataformas de descarga
  - Incluir todos los campos opcionales como ejemplo

Archivo 2: src/games/pixel-dungeon.yaml
  - Rellenar con datos ficticios diferentes
  - Incluir solo campos obligatorios (ejemplo minimalista)

TAREA F2: Crear imágenes placeholder

  - static/logo/jailgames-logo.png → placeholder SVG o imagen genérica
  - static/games/cosmic-escape/logo.png → placeholder
  - static/games/cosmic-escape/screenshot-1.png → placeholder 800x450
  - static/games/cosmic-escape/screenshot-2.png → placeholder 800x450
  - static/games/pixel-dungeon/logo.png → placeholder

  (Pueden ser SVGs generados con texto identificativo)

TAREA F3: Crear archivos de descarga de ejemplo

  - downloads/cosmic-escape/README.txt
    (archivo de texto que diga "Aquí irá el binario de Cosmic Escape")
  - downloads/pixel-dungeon/README.txt
    (archivo de texto que diga "Aquí irá el binario de Pixel Dungeon")
```

### BLOQUE G — Página 404

```
TAREA G1: Crear src/404.njk

Página de error 404:
  - Layout: base.njk
  - Permalink: /404.html
  - Mensaje amigable: "Página no encontrada"
  - Enlace de vuelta a la home
```

### BLOQUE H — Docker

```
TAREA H1: Crear Dockerfile en la raíz

Multi-stage build:
  - Etapa 1 (builder): node:20-alpine, npm ci, npx @11ty/eleventy
  - Etapa 2 (serve): nginx:alpine, copiar _site y downloads, copiar nginx.conf

TAREA H2: Crear nginx.conf en la raíz

Configuración Nginx:
  - Puerto 80, root /usr/share/nginx/html
  - Gzip activado para text/html, text/css, application/javascript
  - Cache de imágenes 7 días
  - Descargas con Content-Disposition: attachment
  - try_files para rutas limpias
  - error_page 404 → /404.html

TAREA H3: Crear docker-compose.yml en la raíz

Servicio jailgames:
  - Build desde el directorio actual
  - Puerto 80:80
  - Volumen downloads como read-only
  - restart: unless-stopped
```

### BLOQUE I — Documentación

```
TAREA I1: Crear README.md en la raíz

Incluir:
  - Nombre del proyecto y descripción breve
  - Requisitos previos (Node.js 18+, Docker)
  - Cómo instalar dependencias (npm ci)
  - Cómo ejecutar en desarrollo (npm run serve)
  - Cómo hacer build (npm run build)
  - Cómo añadir un juego nuevo (los 7 pasos del pipeline)
  - Cómo desplegar con Docker (docker compose up --build)
  - Cómo desplegar sin internet (docker save/load)
  - Estructura de carpetas resumida
```

### BLOQUE J — Verificación Final

```
TAREA J1: Ejecutar npm run build y verificar que _site/ se genera correctamente.

TAREA J2: Ejecutar npm run serve y verificar en http://localhost:8080:
  - La home muestra las tarjetas de los 2 juegos de ejemplo
  - Hacer clic en una tarjeta lleva a la ficha individual
  - La ficha muestra logo, capturas, descripción y botones de descarga
  - Los botones de descarga funcionan (descargan el archivo)
  - El enlace "Volver" funciona
  - La página 404 funciona al visitar una ruta inexistente
  - El diseño es responsive (probar en móvil con DevTools)

TAREA J3: Ejecutar docker compose up --build y verificar en http://localhost:80
  que todo funciona igual que en J2.
```

---

## Resumen Ejecutivo

| Aspecto | Decisión |
|---|---|
| Generador | Eleventy (11ty) |
| Formato de datos | YAML (1 archivo por juego) |
| Plantillas | Nunjucks |
| CSS | CSS custom con tema oscuro (opcionalmente PicoCSS) |
| Servidor | Nginx en Docker |
| Build | Multi-stage Docker (Node → Nginx) |
| Tiempo para añadir un juego | ~5 minutos (crear YAML + copiar archivos + rebuild) |
| Funciona sin internet | Sí (docker save/load) |
