# Prototipo 01 — Sistema de Gestión de Inventario y Personalización

Primera iteración de diseño completa. **Un prototipo, tres vistas**: cliente, tienda y corporativo. Cuando cambiemos la dirección de diseño, eso será `prototipo-02`.

## Cómo abrirlo

Doble clic en `index.html`. Es la portada; desde ahí se entra a las tres vistas. No requiere servidor, ni instalación, ni base de datos.

## Estructura

```
prototipo-01/
├── index.html            Portada · punto de entrada de la demo
├── captura.html          Cliente · captura por QR (móvil)
├── ordenes.html          Tienda · bandeja de órdenes
├── inventario.html       Corporativo · existencias y mermas
├── materiales.html       Maestro del catálogo de materiales
├── traspasos.html        Traspasos, conteos físicos y alertas
├── reportes.html         Consumo y merma · exportación SAP
├── administracion.html   Usuarios, sucursales y suscripciones
├── configuracion.html    Parámetros del sistema
├── css/
│   ├── tokens.css        Variables de diseño, claro y oscuro. Única fuente
│   ├── base.css          Reset, tipografía, accesibilidad
│   ├── layout.css        Shell, menú lateral, barra superior
│   ├── components.css    Botones, badges, tabla, filtros, panel, menús, formulario
│   ├── captura.css       Solo la vista móvil. Se carga sobre base.css
│   └── portada.css       Solo la portada
├── js/
│   ├── iconos.js         Sprite SVG · una sola copia para las nueve vistas
│   ├── ui.js             Avisos, menús, confirmación, panel lateral, tema
│   ├── shell.js          Menú lateral y barra superior · mapa de navegación
│   ├── tabla.js          Tabla con filtros, orden y paginación
│   ├── jersey.js         Render del estampado sobre la foto del jersey
│   └── <vista>.js        Lógica propia de cada vista
├── data/
│   ├── jerseys.json      Modelos y zonas de estampado
│   ├── catalogo.json     Catálogo de la vista de captura
│   ├── ordenes.json      Órdenes de la bandeja
│   ├── inventario.json   Existencias y alertas
│   └── operacion.json    Materiales, traspasos, conteos, reportes, admin y config
└── assets/
    ├── images/           Fotografías de los seis jerseys
    └── logos/            Logotipo de Innovasport
```

**Cuatro módulos compartidos sostienen las nueve vistas**, y esa es la decisión estructural que más importa:

- `iconos.js` — el sprite estaba duplicado en cada HTML. Con nueve vistas, agregar un icono significaba editar nueve archivos, y bastó una para que se desincronizaran: al unificar se perdieron tres símbolos que solo existían en la copia de inventario.
- `shell.js` — el menú lateral y la barra superior se arman desde un mapa de navegación. Agregar una vista es agregar una entrada; antes eran nueve bloques de HTML que había que mantener a mano.
- `tabla.js` — filtros, orden, paginación, pie y estado vacío. Cinco vistas repiten ese patrón; escribirlo cinco veces garantizaba que se comportaran distinto entre ellas.
- `ui.js` — avisos, menús desplegables, confirmación de acciones irreversibles, panel lateral y cambio de tema.

Los CSS base son uno solo para todas las vistas. Lo específico de una vista vive en su propio archivo y se carga encima.

## Stack

HTML, CSS y JavaScript puros. Sin framework, sin build, sin dependencias. La única petición externa es la tipografía Inter desde Google Fonts.

## Por qué los datos se cargan desde un `.js` y no desde el `.json`

`file://` bloquea `fetch()` de archivos locales por política de CORS, así que un `index.html` abierto con doble clic no puede leer su propio JSON. Como el prototipo es una herramienta de venta y tiene que abrirse sin fricción, cada `*.data.js` envuelve el contenido de su `.json` en una variable global.

Los `.json` siguen siendo la fuente de verdad. Para regenerar un envoltorio tras editarlo:

```bash
cd prototipos/prototipo-01
printf '/* Generado desde data/ordenes.json. No editar a mano. */\nwindow.MOCK_DATA = ' > data/ordenes.data.js
cat data/ordenes.json >> data/ordenes.data.js
printf ';\n' >> data/ordenes.data.js
```

Las variables globales son `MOCK_DATA` (órdenes), `INV` (inventario) y `CATALOGO` (captura).

---

<a id="vista-captura"></a>
## Vista cliente — captura por QR

El comprador escanea el QR de su ticket y captura él mismo su personalización, para eliminar el error ortográfico que hoy comete el colaborador al teclear. Móvil, 390–414px. Uso único en la vida del usuario: sin tutorial, sin login, sin segunda oportunidad.

**La pantalla va en dos bloques, y el orden importa.** Arriba, *Tu personalización*: nombre, número, disposición y color — lo que el cliente quiere escribir, que es lo que mueve la previsualización. Abajo, *Tipo de estampado*: las tres opciones con su precio. El cobro va al final porque es una decisión comercial, no creativa; el cliente primero ve su jersey y después elige cuánto quiere gastar en él.

Eso deja una dependencia que hay que resolver a la vista: elegir **Auténtico** oculta los campos de nombre y número, porque ahí el estampado lo define el jugador. Sin explicarlo, el bloque de arriba se vaciaría sin motivo visible, así que aparece una nota que lo dice. Y la previsualización cambia a mostrar el jugador seleccionado: mostrar el texto que el cliente tecleó antes sería previsualizar algo que no va a recibir, que es justo el error que este módulo existe para evitar.

- **Previsualización sobre la fotografía real del jersey**, no sobre una silueta dibujada. Se usa `assets/images/01.*`, que es la espalda del jersey de la Selección Mexicana. El texto curvo usa `<textPath>` sobre un arco real, no letras rotadas una por una, y se redibuja con cada tecla.
- **Auto-ajuste del nombre.** Con 12 caracteres el nombre se desbordaría de los hombros. En vez de adivinar un tamaño por longitud, se mide el texto ya renderizado con `getComputedTextLength()` y se reduce solo lo necesario: `ALE` conserva 25px, `ALEJANDRA` baja a 21, `CUAUHTEMOCXX` baja a 14.
- **Mayúsculas forzadas al teclear**, conservando acentos y signos (`Á É Í Ó Ú Ñ Ü ¿ ? ¡ ! . - ' &`).
- **Contador de caracteres siempre visible**, en ámbar al llegar al límite.
- **Tres tipos de estampado** con su precio. Auténtico pide jugador, no texto libre, porque su disponibilidad depende del dorsal que surta el proveedor.
- **Bloqueo por falta de material (FA1).** Escribe `QUINONES`: la `Q` no tiene material Oficial en San Pedro, se marca el campo y la confirmación queda deshabilitada.
- **Licencia restringida (RN1).** Club América no autoriza estampado genérico: elegirlo bloquea y lo explica.

<a id="vista-ordenes"></a>
## Vista tienda — bandeja de órdenes

Reemplaza los tickets de papel que hoy se apilan en una mesa, se caen y se pierden entre turnos. Tablet o PC, de 1024px hacia arriba.

- **Filtros combinables** por estado, sucursal, tipo, prioridad, licencia y colaborador, más chips de folio y búsqueda libre.
- **Paginación** con tamaño conmutable y ventana deslizante.
- **Acciones masivas** que sustituyen a la métrica de fila al seleccionar: asignar, marcar urgente, exportar. La casilla maestra marca solo la página visible.
- **Panel de detalle** con previsualización del jersey y **consumo carácter por carácter**, con material y lote resueltos por carácter.
- **Tiempo estimado de la fila**, calculado como órdenes en cola × minutos por pieza ÷ colaboradores en turno.

<a id="vista-dashboard"></a>
## Dashboard

La vista de dirección, primera del menú después de la portada. Responde dos preguntas: **qué tan bien está corriendo el negocio hoy** y **hacia dónde va**.

### Los seis KPIs

No son cifras bonitas: cada uno mide algo que alguien tiene que decidir.

| KPI | Por qué está |
|---|---|
| **Órdenes del mes** | El volumen que sostiene todo lo demás |
| **Cumplimiento de compromiso** | % entregadas en la fecha prometida, contra meta de 95%. Es el SLA que el cliente percibe |
| **Tiempo medio de entrega** | Minutos reales contra los 15 de estándar. Si sube, la fila se alarga y se pierden ventas |
| **Merma sobre consumo** | El número que más discute la operación. Meta 25% — seis aprovechables de ocho por metro |
| **Órdenes bloqueadas** | Sin material en la plaza. Cada una es un cliente que se fue sin personalizar |
| **Valor de inventario** | Capital inmovilizado en material |

Los cuatro con meta llevan **barra contra objetivo**: el número solo no dice si va bien. Y el delta se lee por **flecha, signo y color** —nunca solo color—, con la bondad según la métrica: bajar la merma es bueno, bajar órdenes no.

### Las gráficas

Todas en **SVG puro**, sin librerías, con capa de hover.

- **Órdenes por mes** — área de una serie. Se ve el pico del mundial.
- **Mezcla de estampado** — barras pareadas: volumen contra ingreso sobre los mismos tres tipos.
- **Consumo y merma** — dos series. Comparten gráfica porque **comparten unidad**: ambas en metros.
- **Plazas por volumen** — barras horizontales.
- **Cuatro tendencias semanales** con sparkline.
- **Demanda por día y hora** — mapa de calor más el bloque **qué implica para los turnos**: hora pico del grupo, demanda por plaza en ese pico y colaboradores necesarios, con la carga por día en barras.
- **Desempeño por plaza** — tabla con anillo de cumplimiento y semáforo.

### Decisiones de visualización

**Un solo eje por gráfica.** Órdenes (conteo) y consumo (metros) nunca comparten gráfica: un segundo eje mentiría sobre la relación entre ambas. Van en dos gráficas separadas.

**La paleta de series se validó con un verificador de daltonismo** contra estas superficies —`#FFFFFF` y `#181B1F`—, no contra las de un ejemplo. Los tres tonos pasan separación para deuteranopía, protanopía y tritanopía en ambos modos. El aqua queda en 2.82:1 sobre blanco, por debajo del mínimo de 3:1, así que la regla de relieve obliga a etiquetas directas: el donut las lleva con valor y porcentaje.

**El rojo de marca no se usa para datos.** `#E21736` está reservado a acción; una serie con ese rojo competiría con los botones.

**El mapa de calor usa un solo tono claro→oscuro**, nunca arcoíris: la magnitud es continua y un arcoíris inventa saltos que no existen.

**El mapa de calor no se queda en el «cuándo».** Su subtítulo prometía que de ahí salía cuántos colaboradores poner por turno, y no lo entregaba: dejaba la mitad de la tarjeta vacía. Ahora traduce el mapa en la decisión que insinúa. El pico del grupo son 87 órdenes/hora el domingo a las 18:00; repartido entre las 8 plazas son 11 por plaza, y a 4 piezas por hora y persona eso da **3 colaboradores** — el mismo número que muestra el chip «3 en turno» de la bandeja, lo que valida el cálculo contra el resto del sistema.

Las barras por día llevan **valor directo** porque el rango real va de 274 a 496 y la diferencia de altura sola se lee como «todos parecidos». Y el día más flojo solo se destaca **cuando es único**: lunes y jueves empatan en 274, así que marcar uno de los dos sería arbitrario y se leería como un error.

**Por qué la mezcla de estampado dejó de ser un donut.** La primera versión tenía tres defectos: su total decía 1,051 contra las 970 del KPI de arriba —dos cifras para lo mismo, a dos tarjetas de distancia—; su subtítulo afirmaba que «el oficial deja más margen» sin mostrar ningún margen; y saber que el 58% es genérico no llevaba a ninguna decisión. Además, un donut de tres segmentos son tres números vestidos de gráfica.

Ahora compara **volumen contra ingreso** con barras pareadas. Las dos barras de cada fila se miden como porcentaje de su propio total, así que su diferencia de largo se lee directamente sin un segundo eje, y ahí está el hallazgo: el genérico es el 58.2% de las órdenes pero solo el 42.6% del ingreso, mientras el auténtico es el 9.4% del volumen y el 16.3% del ingreso. Eso sí es una decisión: empujar oficial y auténtico donde la licencia lo permita mueve la caja sin alargar la fila.

<a id="vista-materiales"></a>
## Materiales

El maestro del catálogo: **qué es** cada material y con qué rendimiento se consume. El **cuánto hay** vive en Inventario; separar las dos cosas es lo que permite que un cambio de merma no toque las existencias.

- 22 materiales de los tres tipos: vinil en rollo, carácter oficial y kit auténtico.
- Filtros por tipo, unidad, licencia, proveedor y color.
- El detalle muestra la **regla de merma** del material —6 aprovechables de 8 teóricas por metro— y explica que cambiarla altera el descuento de las órdenes futuras, no las ya impresas.
- La regla de rotación se expresa por lote, no por material.

<a id="vista-traspasos"></a>
## Traspasos y conteos

Tres caras del mismo problema, en pestañas: mover material donde falta, confirmar lo que de verdad hay, y avisar antes de que la tienda se quede en cero.

- **Traspasos** con su flujo real: Solicitado → Autorizado → Enviado → Recibido. Cada transición es un botón que avanza el estado y actualiza la tabla. El detalle separa cantidad solicitada, enviada y recibida, y marca las líneas donde se recibió de menos.
- **Conteos físicos** con el comparativo sistema contra físico línea por línea. Al cerrar un conteo se explicita que cada diferencia genera su movimiento de ajuste en el kardex: el saldo nunca se corrige a mano.
- **Alertas** de los cuatro tipos, ordenadas por severidad, con acción de atender individual o masiva.

<a id="vista-reportes"></a>
## Reportes

- **Consumo y merma** por sucursal y material, con el porcentaje de merma en semáforo contra el estándar. La pregunta que responde es dónde se está desperdiciando más vinil.
- **Exportación SAP**, que es la salida real de la Fase 1: el sistema no se integra con SAP. Cada archivo queda registrado con su **hash**, que es la única prueba de que lo cargado en el módulo MM es lo que el sistema generó. Generar una exportación crea un folio nuevo con su hash.

<a id="vista-admin"></a>
## Administración

- **Usuarios** con su rol, y el detalle explica en una frase qué puede hacer cada rol. Son cuatro: administrador, analista, colaborador y cliente.
- **Sucursales** con su enseña, país, canal y si tienen personalización habilitada. El detalle calcula la espera que aporta cada orden con los minutos por pieza y los colaboradores de esa plaza.
- **Suscripciones**, porque el modelo comercial es **por plaza, no por tenant**. El pie de tabla suma la facturación mensual del filtro activo.

<a id="vista-config"></a>
## Configuración

Los parámetros que cambian el comportamiento del negocio, en seis tarjetas: rendimiento del vinil, cola de trabajo, rotación, captura por QR, exportación y notificaciones.

- La merma contable **se recalcula en vivo** al mover el rendimiento: bajar el estándar de 6 a 5 muestra que la merma sube de 25% a 38%.
- Un estándar mayor que el teórico es **inválido** y bloquea el guardado con el motivo a la vista.
- La barra de guardado cuenta los cambios pendientes comparando contra lo guardado, no contando pulsaciones: volver un valor a su original lo descuenta.
- Al guardar un cambio de rendimiento, la confirmación advierte que altera el descuento de todas las órdenes futuras y que las ya impresas no se recalculan.

<a id="abc"></a>
## El ABC completo

Una auditoría del prototipo mostró tres huecos: **inventario no tenía ninguna operación**, **no existía ninguna baja real** —solo cancelaciones de traspaso y orden— y **cuatro catálogos vivían solo como valores de un filtro**.

### Inventario: entrada, ajuste y baja

Aquí no se "agrega un material": eso es el maestro del catálogo. Aquí se registra **cuánto llega**, se corrige lo que no cuadra y se da de baja lo que ya no sirve.

| Operación | Qué hace |
|---|---|
| **Registrar entrada** | La recepción que hoy no existe: el vinil llega por paquetería y se deja en el área de impresión sin que nadie le dé alta. Pide plaza, material, cantidad, **lote** y ubicación, y adelanta cómo queda la existencia antes de guardar. El lote es obligatorio: sin él no hay trazabilidad de rotación |
| **Ajustar existencia** | Con **motivo obligatorio**. Si no hay diferencia contra el sistema, no deja guardar: no hay nada que ajustar |
| **Dar de baja** | Para material sin rotación o inservible, con confirmación |

Las tres generan movimiento de kardex; ninguna edita el saldo a mano. El saldo siempre debe poder reconstruirse sumando movimientos.

### Catálogos auxiliares

Licencias, temporadas, colores y proveedores tienen ahora su ABC completo, en pestañas dentro de Materiales. Los cuatro comparten el mismo formulario genérico porque su estructura es la misma, y cada uno valida lo suyo: una temporada no admite fin anterior al inicio, un color exige formato `#RRGGBB`, una licencia declara si restringe su catálogo.

**La baja nunca borra: desactiva.** De estos catálogos cuelgan materiales, órdenes y existencias ya registradas; borrarlos rompería el histórico. Un registro inactivo deja de ofrecerse en altas nuevas y se puede reactivar.

### La barra de selección flota

Vivía en el pie de la tabla. Con 56 líneas, marcar una fila de arriba dejaba la acción fuera de pantalla: seleccionabas y no sabías qué seguía. Ahora **flota centrada sobre el viewport** mientras hay selección y desaparece cuando no, así el grid se administra sin bajar el scroll. El pie conserva siempre su métrica.

<a id="vista-inventario"></a>
## Vista corporativo — inventario y mermas

Da visibilidad del vinil que hoy no existe en ningún sistema y que cuesta dos semanas al mes reconstruir entre un Forms, un Excel y llamadas tienda por tienda. Escritorio, modo oscuro por defecto.

- **Cuatro indicadores**, cada uno respondiendo una pregunta concreta. Sin gráficas decorativas.
- **Panel de alertas** ordenado por severidad: agotado, bajo mínimo, sin rotación y diferencia de conteo. "Ver material" filtra la tabla y baja a ella.
- **Tabla con primera columna fija**: al desplazarse a lo ancho, la sucursal no se pierde.
- **Semáforo de nivel** por fila, coloreado según estado.
- **Exportación tabular** para carga manual en el módulo MM de SAP, que es la salida real de la Fase 1.

## Botones de acción

**Todo control visible hace algo.** No queda ninguno que muestre un aviso de «no incluido».

### Altas y ediciones

| Control | Qué hace |
|---|---|
| **Nuevo traspaso** | Modal con origen, destino y líneas. La cantidad **se valida contra las existencias reales del origen**: pedir 260 pz donde hay 210 se rechaza con el número a la vista. El destino excluye la plaza de origen. Crea el traspaso en estado *Solicitado* |
| **Nuevo conteo** | Sucursal, periodo y tipo. Previsualiza las líneas que se abrirán con su cantidad de sistema. El cíclico solo toma material con existencia; el general, todo |
| **Nuevo material** | El bloque de rendimiento **solo aparece para vinil en rollo** — lo precortado viene en piezas. La merma se recalcula en vivo y un código duplicado se rechaza |
| **Editar material** | Mismo formulario precargado, con el código bloqueado: de él cuelgan las existencias y el kardex |
| **Nuevo usuario** | Elegir Analista o Administrador **deshabilita la sucursal**: son roles corporativos. El correo se valida y no se admite duplicado |
| **Editar usuario** | Precargado, con el correo bloqueado por ser su identificador |
| **Nueva sucursal** | Calcula en vivo cuánta espera aporta cada orden con los minutos por pieza y los colaboradores capturados |
| **Editar sucursal** | Precargado, con el código bloqueado |
| **Nueva suscripción** | Muestra cómo cambia la facturación mensual del grupo al agregar esa plaza |

### Acciones y documentos

| Control | Qué hace |
|---|---|
| **Restablecer contraseña** | Confirmación nombrando el correo destino, y avisa que la contraseña actual deja de servir |
| **Acuse de traspaso** | Documento imprimible con logotipo, folio, ruta, material y dos espacios de firma: entrega y recibe |
| **Acuse de conteo** | Documento imprimible con las diferencias encontradas y firmas de quien contó y quien autorizó |
| **Mi perfil** | Panel con el rol, el alcance y una explicación de qué puede hacer ese rol. Incluye el cambio de tema |
| **Cerrar sesión** | Confirmación, y es explícito en que el prototipo no tiene autenticación |
| **Base de conocimiento** | Ocho reglas del negocio con buscador: los tres tipos de estampado, por qué 6 y no 8 por metro, qué significa sin rotación, por qué se bloquea una orden, y por qué el kardex no se corrige a mano |

El **modal** es distinto del panel lateral a propósito: bloquea el fondo, atrapa el foco mientras está abierto y lo devuelve al cerrar. Un alta exige una decisión antes de seguir; una consulta no.

Las validaciones **no dejan guardar y explican por qué** en el pie del modal, en vez de aceptar el dato y fallar después.

| Control | Qué hace |
|---|---|
| Notificaciones | Panel con datos reales: órdenes bloqueadas por material, urgentes en cola y recién confirmadas. En inventario, las alertas críticas y altas. "Marcar como leído" vacía el contador |
| Avatar | Menú de perfil con el usuario y su rol, cambio de tema, y accesos marcados como fuera de alcance |
| Selector de enseña | **Filtra los datos de verdad.** Elegir Innvictus o Marathon acota la tabla a esa cadena y sincroniza el filtro de la barra. Es la operación real de un grupo con cinco enseñas |
| + Nueva orden | Abre la vista de captura en otra pestaña: el alta de una orden empieza con el cliente, no con un formulario del colaborador |
| Papelera por fila | Cancela la orden, con confirmación en línea. La orden pasa a *Cancelada*, se libera su material y la tira de estados se actualiza. Deshabilitada en órdenes ya entregadas o canceladas |
| Menú lateral · vistas no incluidas | Marcadas con un punto y atenuadas; al pulsarlas explican que el prototipo 01 cubre captura, bandeja e inventario |

La confirmación de acciones irreversibles es un popover propio, no un `confirm()` del navegador: ese bloquea la página y rompería la demo.

## Las imágenes del jersey

El estampado se dibuja sobre la fotografía real de la **espalda** de cada jersey, que es la cara que se imprime. Seis modelos: Selección Mexicana, Club América, Cruz Azul, Chivas, Tigres UANL y Toluca.

**Las fotos vienen en formatos y proporciones distintas**, y el modelo lo absorbe: `jerseys.json` guarda el ancho y alto real de cada imagen, y la caja toma esa proporción desde JavaScript. Chivas es 1200×1440 —no cuadrada— y forzar `1:1` le habría deformado el estampado.

| Modelo | Archivo | Dimensiones |
|---|---|---|
| Selección Mexicana | `01.avif` / `01.png` | 766×766 |
| Club América | `america.jpg` | 500×500 |
| Cruz Azul | `cruzazul.avif` / `cruzazul.jpg` | 1200×1200 |
| Chivas | `chivas.webp` | 1200×1440 |
| Tigres UANL | `tigres.jpg` | 2400×2400 |
| Toluca | `toluca.webp` | 1920×1920 |

Los AVIF se sirven con `<picture>` y respaldo: el de México cae a PNG y el de Cruz Azul a un JPEG generado. WebP y JPEG no necesitan respaldo — tienen soporte universal.

**Las zonas de impresión viven en los datos, no en el código.** Cada modelo define dónde va el nombre según la disposición, dónde el número y cuál es el ancho imprimible, en el espacio de píxeles de su propia foto:

```json
"Chivas": {
  "w": 1200, "h": 1440, "tinta": "#10224C",
  "z": { "nomArrY": 520, "arcoArr": "M 430 533 Q 600 476 770 533",
         "numArrY": 742, "nomFs": 60, "nomAncho": 325 }
}
```

Añadir otro modelo es añadir su foto y sus zonas. No se toca JavaScript.

**Son fotos de producto con patrocinadores ya impresos**, así que el área personalizable está realmente acotada: el nombre va bajo el logo superior y el número en la franja limpia del centro. En Chivas, entre OMNILIFE y AKRON queda poco margen; las coordenadas se calibraron para que quepa sin encimarse. El grosor del contorno se escala con el tamaño de la foto para que se vea igual en una imagen de 500 px y en una de 2400.

El render vive en `js/jersey.js`, **compartido por la captura y por el detalle de la bandeja**: el cliente y el colaborador tienen que ver exactamente la misma previsualización.

El jersey de muestra de la captura es el de **Selección Mexicana**. Como esa licencia no está restringida, el estampado Genérico sí es seleccionable y el selector de color del vinil aparece en la demo — con el del América no pasaba.

`02.avif` y `02.png` quedan sin usar: son la otra vista del jersey, que no es la que se estampa.

## El logotipo

`assets/logos/logo-innovasport.svg` es el wordmark oficial: degradados de cian, azul, morado y rojo, proporción 6.7:1. Se usa en el menú lateral oscuro y en la portada.

**No se usa sobre el rojo de marca.** Lo probé sobre blanco, sobre el fondo oscuro del menú y sobre `#E21736`: en el rojo las letras rojas del logo se pierden contra el fondo. Sobre blanco y sobre oscuro se lee perfecto.

## Decisiones de diseño

**Los colores de marca son reales**, extraídos de las variables CSS que publica `innovasport.com`. Ver `docs/marca/identidad-innovasport.md`.

**El rojo de marca no se usa para errores.** `#E21736` está reservado a acción primaria y estado activo; el error usa `#B42318`. Si el botón principal y el estado de error son el mismo rojo, el usuario deja de distinguir "acción" de "problema".

**Ningún componente de interfaz usa un color literal.** Todo pasa por `tokens.css`. Habilitar otra enseña del grupo — Innvictus, Over Time, Rookie Kids, Marathon — es sobrescribir `--brand` y `--sidebar-bg`; el resto del sistema no se toca.

Hay **una excepción deliberada**: el estampado sobre el jersey usa los colores del material elegido, no los tokens de la app. Es contenido, no interfaz. Si cambiara con el modo oscuro, le estaríamos mostrando al cliente una previsualización que no corresponde a lo que va a recibir impreso.

**Metros y piezas nunca se suman ni se muestran sin unidad**, y toda cifra usa `tabular-nums`. Confundirlos es el error que el sistema existe para evitar.

## Defectos encontrados en verificación y corregidos

Se documentan porque los tres son patrones, no accidentes:

- **Nodo opcional ausente que tumba la inicialización.** Al consolidar el sidebar desapareció el `id="navCount"`, y `init()` lanzaba excepción. En inventario abortaba antes de pintar; en órdenes abortaba **después** de pintar la tabla pero antes de registrar los eventos — la página se veía perfecta y no respondía a nada. Es el peor modo de fallo posible: parece correcta. Ahora la escritura en nodos opcionales pasa por una guarda.
- **`<span>` donde hace falta layout de bloque.** `width` y `height` no aplican a un elemento `inline`. Rompió una barra de progreso, que renderizaba a 0×0, y juntó título y detalle de las alertas en una sola línea. Las reglas ahora declaran `display` explícitamente.
- **SVG sin tamaño por omisión.** Un ícono sin regla propia adopta el tamaño intrínseco del viewport, 300×150, y revienta el layout. Resuelto en `base.css`.
- **`<span>` es `inline`, así que `width`, `height` y los márgenes verticales no le aplican** y dos spans hermanos se renderizan pegados en la misma línea. Este defecto reincidió cinco veces: rompió una barra de progreso, juntó el título y el detalle de las alertas, pegó el nombre y la licencia en la bandeja, y pegó el nombre del grupo con la sucursal en el pie del menú. Ahora hay una regla explícita en `components.css` que declara el `display` de todas las clases que apilan texto.
- **Sprite duplicado.** Al unificarlo en `iconos.js` se perdieron tres símbolos que solo existían en la copia de inventario. Se detecta recorriendo cada `<use>` de la página y comprobando que su símbolo exista.
- **Opacidad sobre un contenedor fijo.** Atenuar la barra de guardado con `opacity` dejaba ver el formulario a través de ella. Se atenúa el texto, nunca la barra.
- **Colisión de nombres de token.** Los colores de serie se llamaron `--s1`, `--s2`, `--s3` — y esos nombres **ya eran la escala de espaciado**. Sobrescribirlos convirtió `gap: var(--s3)` en `gap: #1baf7a`, y el prototipo entero perdió su espaciado: iconos pegados al texto en las nueve vistas. Ahora los colores de serie son `--serie-1..3`. Antes de crear un token, busca si el nombre ya existe.

## Pendiente

Logotipos en `assets/brand/`: el sitio de Innovasport y su CDN responden `403` a toda petición que no venga de un navegador con sesión real. Coloca los archivos ahí y el prototipo los tomará sin cambios de código; mientras tanto muestra un wordmark tipográfico.
