---
name: layout-ctcv3
description: Norma visual y estructural de Control Tower (ctcv3). Úsala antes de crear o modificar cualquier vista Blade o Livewire que se pinte dentro de layouts.app: tokens de color, colores institucionales de la empresa, colores de módulo, plantilla de página, componentes, navegación y modo oscuro.
---

# Layout de Control Tower

> **Diseño de pantallas (indicadores, filtros, tablas con cabecera fija, íconos Heroicons):** skill `ui-ctc` (`.claude/skills/ui-ctc/SKILL.md`), con Pedidos en Curso como referencia. Este documento sigue siendo la norma de tokens, modo oscuro y estructura del layout.

Esta es la referencia para construir cualquier vista dentro del layout principal
(`resources/views/layouts/app.blade.php`). Si una vista nueva sigue estas reglas,
hereda sin trabajo extra el modo oscuro, los colores de la empresa activa y el
color de su módulo.

> **Regla de oro:** ninguna vista escribe un color fijo (`#hex`, `bg-blue-600`,
> `text-gray-800`) para superficies, textos, bordes o acentos. Todo sale de las
> variables CSS de este documento.

---

## 1. Anatomía

```
┌───────────────────── franja de marca 4px (primario → secundario) ─────────────────────┐
│ SIDEBAR (w-64)          │ ENCABEZADO (76px)                                            │
│ ┌─────────────────────┐ │ [☰] [logo | EMPRESA ACTIVA  Nombre  Alt+S ▾]   … 🔔 ☀ │ (RH)▾│
│ │▣ CONTROL TOWER      │ ├──────────────────────────────────────────────────────────────┤
│ │  Asercol S.A.       │ │ BARRA DE MÓDULOS (42px)                                      │
│ └─────────────────────┘ │ ⊞ Dashboard │ ⇩ Importaciones  ⇧ Exportaciones  👥 CIA  ⚙ Adm │
│ ┌ MÓDULO ACTIVO ──────┐ ├──────────────────────────────────────────────────────────────┤
│ │ ⇩ Importaciones     │ │                                                              │
│ └─────────────────────┘ │ CONTENIDO  <main>  ← aquí va {{ $slot }}                      │
│ ▸ Operaciones           │                                                              │
│   │ Control Tower       │                                                              │
│   │ Orden de Compra ◀── │  (enlace actual)                                             │
│ ▸ Configuración         │                                                              │
│ …                       │                                                              │
│ Usuarios y roles        │                                                              │
│ Configuración           │                                                              │
│ [imagotipo Asercol]     │                                                              │
│ [? Centro de ayuda   ›] │                                                              │
└─────────────────────────┴──────────────────────────────────────────────────────────────┘
```

| Zona | Archivo | Notas |
|---|---|---|
| Documento, encabezado, barra de módulos, menú de usuario | `resources/views/layouts/app.blade.php` | No se edita para una vista concreta. |
| Variables de color y tipografía | `resources/views/partials/tema-empresa.blade.php` | Única fuente de tokens. |
| Menú lateral | Dentro de `resources/views/layouts/app.blade.php` (Blade + Alpine, sin componente Livewire) | Lo arma `MenuNavegacion` desde la tabla `menus`. |
| Árbol de navegación | `app/Services/MenuNavegacion.php` | Compartido por sidebar y barra de módulos. |
| Ícono y color de módulos | `config/modulos.php`, `resources/views/components/icono-modulo.blade.php` | |
| Colores de la empresa | `app/Services/TemaEmpresa.php`, `app/Support/PaletaAcento.php` | |
| Modal de colores | `app/Http/Livewire/Layout/ColorEmpresa.php` | Menú de usuario → *Colores de la empresa*. |
| Selector de empresa | `app/Http/Livewire/Layout/SelectorGrupo.php` (fase L4, aún no existe) | Alt + S. Elige compañías **dentro** del grupo empresarial; nunca cambia `session('GrupoEmpresarial')` (ver §7.5). |

Comportamiento fijo del layout:

- **Responsive:** por debajo de `md` (768px) el sidebar es un cajón que se abre con ☰; en
  `md` y más es fijo. La barra de módulos se desplaza horizontalmente si no cabe.
- **`<main>`** ocupa el resto de la pantalla, hace su propio scroll vertical
  (`overflow-y-auto`) y **no tiene padding**: la vista pone el suyo (ver §5).
- **El slot `header` de `<x-app-layout>` no se pinta.** Algunas vistas antiguas lo envían;
  en vistas nuevas el título va dentro del contenido.

---

## 2. Reparto del color

Hay tres familias de color y cada una tiene un papel. No se mezclan los papeles.

| Familia | Papel | Variables | Dónde se usa |
|---|---|---|---|
| **Neutros** | Estructura | `--fondo`, `--superficie*`, `--borde*`, `--texto*` | Fondos, tarjetas, bordes, textos. Es el 90% de la pantalla. |
| **Empresa (institucional)** | Identidad: *de quién* es la pantalla | `--acento-*` (primario), `--secundario-*` | Acción principal, selección, enlace actual, foco, nombres de módulo, franja de marca. |
| **Módulo** | Orientación: *dónde* estás | `--mod`, `--ink`, `--soft`, `--line` | Solo señales pequeñas: el ícono del módulo. |

Reglas:

1. **El primario de la empresa marca lo interactivo y lo seleccionado**: botón principal,
   pestaña o fila seleccionada, enlace actual, anillo de foco, interruptores encendidos.
2. **El secundario es complemento**, nunca la acción principal: degradado de marca, segunda
   serie de una gráfica, insignias o detalles decorativos.
3. **El color de módulo no pinta zonas grandes.** Se limita a íconos. Si la empresa activa
   *Módulos en gris*, todos los `--mod` pasan al gris del Dashboard.
4. **El bloque "Empresa activa" del encabezado es neutro a propósito.** No se le aplican
   colores institucionales.
5. **Los estados (éxito, advertencia, error) no usan ninguna de las tres familias**: tienen
   sus propios colores semánticos (§6.6). Rojo solo para error o destructivo.

---

## 3. Tokens

Se definen en `partials/tema-empresa.blade.php` para `:root` (claro) y `:root.dark`
(oscuro). El modo oscuro es la clase `dark` en `<html>` (se recuerda en `localStorage.dark`).
**Una vista nunca necesita escribir `dark:` para estos tokens**: cambian solos.

### 3.1 Neutros

| Token | Claro | Oscuro | Uso |
|---|---|---|---|
| `--fondo` | `#FFFFFF` | `#0E0E12` | Fondo de `<main>` y de la página. |
| `--superficie` | `#FFFFFF` | `#141419` | Tarjetas, paneles, modales, encabezado. |
| `--superficie-2` | `#F4F4F7` | `#1A1A21` | Cabecera de tabla, pie de modal, zonas secundarias. |
| `--superficie-3` | `#FAFAFA` | `#12121A` | Sidebar, fondos muy sutiles. |
| `--borde` | `#EDEDF1` | `#26262E` | Bordes y separadores normales. |
| `--borde-fuerte` | `#E2E2E8` | `#32323C` | Bordes de campos de formulario y controles. |
| `--texto` | `#16161A` | `#F2F2F5` | Texto principal. |
| `--texto-suave` | `#6E6E7A` | `#A0A0AC` | Texto secundario, descripciones. |
| `--texto-tenue` | `#9A9AA6` | `#7E7E8C` | Etiquetas, metadatos, íconos inactivos. **No llega a contraste AA**: nunca para información que haya que leer para operar. |
| `--sombra-panel` | sombra | sombra | `box-shadow` de menús desplegables y modales. |

### 3.2 Navegación

| Token | Uso |
|---|---|
| `--nav-hover` | Fondo al pasar el mouse por ítems de menú, filas o pestañas. |
| `--nav-actual` | Fondo del ítem actual (blanco en claro). |
| `--nav-actual-borde` | Borde del ítem actual. |

### 3.3 Color de la empresa: primario (`--acento-*`) y secundario (`--secundario-*`)

Cada color de la empresa genera una rampa completa. Los nombres son iguales para los dos,
cambiando el prefijo:

| Token | Uso |
|---|---|
| `--acento` | Relleno fuerte: botón primario, interruptor encendido, subrayado activo, cuadro de iniciales. |
| `--acento-hover` | Relleno al pasar el mouse. |
| `--acento-activo` | Relleno al presionar. |
| `--acento-contraste` | Texto o ícono **encima** de `--acento` (blanco o casi negro, el que contraste más). |
| `--acento-suave` | Fondo teñido: fila o pestaña seleccionada, cabecera del módulo en el sidebar. |
| `--acento-borde` | Borde sobre fondos teñidos o de elementos seleccionados. |
| `--acento-texto` | Texto o ícono en el color de la empresa sobre fondo neutro. **Siempre cumple 4.5:1.** |

`PaletaAcento` calcula la rampa en PHP, ajusta el contraste en claro y en oscuro y la
publica ya resuelta en el primer pintado. Por eso:

- Para **texto** en color de empresa se usa `--acento-texto`, **nunca `--acento`**: el
  primario puro puede ser demasiado claro (por ejemplo, el amarillo de AVGUST).
- Sobre un relleno `--acento` el texto va en `--acento-contraste`, **nunca `text-white`**.

### 3.4 Color de módulo

Solo existen dentro de un elemento con `data-mod="<llave>"` (la llave es una entrada de
`config/modulos.php`: `panel`, `importaciones`, `exportaciones`, `cia`, `administracion`).

| Token | Uso |
|---|---|
| `--mod` | Color del ícono del módulo, cuadro del ícono. |
| `--mod-contraste` | Ícono encima de `--mod`. |
| `--ink`, `--soft`, `--line` | Texto, fondo teñido y borde del módulo. Están disponibles, pero por la regla 2.3 hoy no se usan en zonas grandes. |

```blade
<span data-mod="importaciones" class="flex h-7 w-7 items-center justify-center rounded-md"
      style="background: var(--mod); color: var(--mod-contraste);">
    <x-icono-modulo modulo="importaciones" class="h-[15px] w-[15px]" />
</span>
```

### 3.5 Tipografía

| Token | Fuente | Uso |
|---|---|---|
| `--fuente` | Plus Jakarta Sans | Todo el texto (ya está en `:root`; no hace falta declararla). |
| `--fuente-display` | Archivo | Marca y títulos de presentación en mayúsculas espaciadas. Solo pesos 700 y 800. |
| `--fuente-mono` | monoespaciada del sistema | Códigos, números de documento, JSON. También con la clase `font-mono`. |

---

## 4. Cómo escribir los tokens en Tailwind

Tailwind trabaja de dos maneras en el proyecto (CSS compilado y el CDN JIT en el
navegador), así que ambas formas funcionan:

```blade
{{-- 1. Clase con valor arbitrario: necesaria para hover:, focus:, group-hover: --}}
<a class="text-[var(--texto-suave)] hover:bg-[var(--nav-hover)] hover:text-[var(--texto)]">…</a>

{{-- 2. Atributo style: más legible para estados fijos --}}
<div class="rounded-xl border" style="background: var(--superficie); border-color: var(--borde);">…</div>
```

- Los estados (`hover:`, `focus-visible:`, `active:`, `disabled:`) **solo** se pueden escribir
  con la forma 1.
- Para estilos que cambian con Alpine: `:class` con clases de la forma 1, o `:style` con la
  forma 2.
- **Prohibido** armar clases con datos del servidor (`bg-{{ session('color_tw_p') }}`). El
  JIT no las garantiza y no siguen el color real de la empresa.

---

## 5. Plantilla de página

### 5.1 Vista Blade

```blade
<x-app-layout>
    <div class="mx-auto w-full max-w-7xl px-4 py-5 sm:px-6">

        {{-- Encabezado de página --}}
        <div class="mb-5 flex flex-wrap items-end justify-between gap-3">
            <div class="min-w-0">
                <p class="text-[10.5px] font-extrabold uppercase tracking-[0.12em]"
                   style="color: var(--texto-tenue);">Importaciones · Operaciones</p>
                <h1 class="mt-1 text-xl font-bold" style="color: var(--texto);">Orden de compra</h1>
                <p class="mt-1 text-[13px]" style="color: var(--texto-suave);">
                    Seguimiento de las órdenes abiertas de la empresa activa.
                </p>
            </div>
            <div class="flex flex-shrink-0 items-center gap-2">
                <x-secondary-button>Exportar</x-secondary-button>
                <x-button type="button">Nueva orden</x-button>
            </div>
        </div>

        {{-- Contenido --}}
        <section class="rounded-xl border" style="background: var(--superficie); border-color: var(--borde);">
            …
        </section>
    </div>
</x-app-layout>
```

### 5.2 Componente Livewire de página completa

Mientras existan pantallas Bootstrap, `config/livewire.php` sigue con `layouts.planti` como
layout por defecto. Un componente de página completa nuevo lo pide explícitamente en `render()`:

```php
return view('livewire.area.mi-pantalla')->layout('layouts.app', ['titulo' => 'Mi pantalla']);
```

Los componentes viven en `App\Http\Livewire`. La ruta se declara con la barra inicial, porque
el grupo de `routes/web.php` antepone el namespace de los controladores:
`Route::get('area/mi-pantalla', '\\' . \App\Http\Livewire\Area\MiPantalla::class)`.

La vista empieza directamente con el contenedor de §5.1 (sin `<x-app-layout>`) y debe tener
**un solo elemento raíz**. Ejemplo completo: `App\Http\Livewire\Layout\Muestra` (`/layout/muestra`).

### 5.3 Medidas

| Elemento | Norma |
|---|---|
| Contenedor | `mx-auto w-full max-w-7xl px-4 py-5 sm:px-6`. Pantallas de datos anchas (tablas de muchas columnas) pueden usar `max-w-none`. |
| Separación entre bloques | `space-y-5` o `gap-5`. Dentro de tarjetas, `gap-3` / `gap-4`. |
| Radio | Tarjetas y modales `rounded-xl`; controles, ítems e insignias `rounded-lg` o `rounded-md`. |
| Sombras | Ninguna en tarjetas (se separan con el borde). Solo `--sombra-panel` en lo que flota: menús, modales y popovers. |

### 5.4 Escala tipográfica

| Uso | Clases |
|---|---|
| Título de página | `text-xl font-bold` en `--texto` |
| Título de tarjeta o sección | `text-[15px] font-bold` |
| Texto de interfaz y celdas | `text-[13px]` (`text-sm` en navegación) |
| Descripción | `text-[12px]`–`text-[13px]` en `--texto-suave` |
| Etiqueta o encabezado de grupo | `text-[10px]`–`text-[11px] font-bold uppercase tracking-[0.12em]` en `--texto-tenue` |
| Metadato | `text-[11px]` en `--texto-tenue` |

---

## 6. Componentes

### 6.1 Tarjeta o panel

```blade
<section class="overflow-hidden rounded-xl border" style="background: var(--superficie); border-color: var(--borde);">
    <header class="flex items-center justify-between gap-3 border-b px-4 py-3" style="border-color: var(--borde);">
        <h2 class="text-[15px] font-bold" style="color: var(--texto);">Contenedores en tránsito</h2>
        <span class="text-[11px]" style="color: var(--texto-tenue);">Actualizado hace 5 min</span>
    </header>
    <div class="p-4">…</div>
</section>
```

### 6.2 Indicador (KPI)

```blade
<div class="rounded-xl border p-4" style="background: var(--superficie); border-color: var(--borde);">
    <p class="text-[11px] font-bold uppercase tracking-[0.12em]" style="color: var(--texto-tenue);">Órdenes abiertas</p>
    <p class="mt-2 text-2xl font-bold tabular-nums" style="color: var(--texto);">1.284</p>
    <p class="mt-1 text-[12px]" style="color: var(--texto-suave);">+12 frente a la semana anterior</p>
</div>
```

El número va en `--texto`, no en el color de la empresa: el acento se reserva para lo
interactivo.

### 6.3 Tabla

```blade
<div class="overflow-x-auto rounded-xl border" style="border-color: var(--borde);">
    <table class="min-w-full text-[13px]">
        <thead style="background: var(--superficie-2);">
            <tr>
                <th class="px-3 py-2.5 text-left text-[11px] font-bold uppercase tracking-wider" style="color: var(--texto-tenue);">Orden</th>
                <th class="px-3 py-2.5 text-right text-[11px] font-bold uppercase tracking-wider" style="color: var(--texto-tenue);">Valor</th>
            </tr>
        </thead>
        <tbody style="background: var(--superficie);">
            @foreach ($ordenes as $orden)
                <tr class="border-t border-[var(--borde)] hover:bg-[var(--nav-hover)]">
                    <td class="px-3 py-2.5 font-mono" style="color: var(--texto);">{{ $orden->numero }}</td>
                    <td class="px-3 py-2.5 text-right tabular-nums" style="color: var(--texto);">{{ $orden->valor }}</td>
                </tr>
            @endforeach
        </tbody>
    </table>
</div>
```

- Números alineados a la derecha con `tabular-nums`; códigos y documentos en `font-mono`.
- Fila seleccionada: `background: var(--acento-suave)`.
- Sin filas cebra: el hover y los bordes bastan.
- Lista vacía: un mensaje centrado en `--texto-tenue` dentro del mismo contenedor, nunca una
  tabla sin filas y sin explicación.
- Tablas largas: con el kit ui-ctc (`ctc-tabla-caja` + `ctc-tabla`), cabecera fija y un panel que
  cabe siempre en la pantalla: `ctc.js` ajusta el alto de la tabla al espacio disponible (skill
  ui-ctc, sección "Tablas").

### 6.4 Formularios

Se usan los componentes que ya están migrados a tokens:

| Componente | Uso |
|---|---|
| `<x-label>` | Etiqueta del campo. |
| `<x-input>` | Campo de texto (borde `--borde-fuerte`, foco en `--acento`). |
| `<x-input-error for="campo">` | Mensaje de validación. |
| `<x-checkbox>` | Casilla. |
| `<x-button>` | Acción principal: relleno `--acento`, texto `--acento-contraste`. **Una por formulario o vista.** |
| `<x-secondary-button>` | Acciones secundarias y *Cancelar*. |
| `<x-danger-button>` | Acciones destructivas (eliminar, anular). |

Componentes que **todavía no** usan tokens y no deben usarse en vistas nuevas:
`button-primary`, `button-secondary`, `button-custom`, `input-select`, `input-search`,
`panel-table`, `stat-card*`, `rim-card`. Si una vista los necesita, primero se migran a tokens.

Para un `<select>` o `<textarea>` nativo, se copian las clases de `<x-input>`:

```blade
<select class="form-select rounded-md border-[var(--borde-fuerte)] bg-[var(--superficie)] text-[var(--texto)] shadow-sm focus:border-[var(--acento)] focus:ring-[var(--acento)]">…</select>
```

### 6.5 Modales

- Para diálogos de Livewire: `<x-dialog-modal>` y `<x-confirmation-modal>` (ya usan tokens).
- Para diálogos propios con Alpine se sigue el patrón de `livewire/color-empresa.blade.php`:
  - `x-teleport="body"` y `z-[60]`;
  - fondo `bg-black/40`, panel `rounded-2xl` con `--superficie`, `--borde` y `--sombra-panel`;
  - pie con `--superficie-2`;
  - `role="dialog"`, `aria-modal="true"`, cierre con Escape y con clic en el fondo.
- Capas: sidebar móvil `z-40`, franja de marca `z-[45]`, menús `z-50`, modales `z-[60]`,
  pantalla de carga `z-[80]`.

### 6.6 Estados e insignias

Los estados son semánticos y **no** dependen de la empresa. Se escriben con la paleta de
Tailwind y su variante oscura:

| Estado | Clases |
|---|---|
| Éxito, completado | `bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300` |
| Advertencia, pendiente | `bg-amber-50 text-amber-800 dark:bg-amber-500/10 dark:text-amber-300` |
| Error, vencido | `bg-red-50 text-red-700 dark:bg-red-500/10 dark:text-red-300` |
| Informativo, en curso | `bg-sky-50 text-sky-700 dark:bg-sky-500/10 dark:text-sky-300` |
| Neutro | `style="background: var(--superficie-2); color: var(--texto-suave);"` |

```blade
<span class="inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-[11px] font-semibold bg-amber-50 text-amber-800 dark:bg-amber-500/10 dark:text-amber-300">
    Pendiente
</span>
```

El color nunca va solo: la insignia siempre lleva texto (y si se quiere, un ícono).

### 6.7 Íconos

- SVG en línea, `viewBox="0 0 24 24"`, `fill="none"`, `stroke="currentColor"`,
  `stroke-width="1.6"`–`"1.8"`, `stroke-linecap="round"` y `stroke-linejoin="round"`.
- Tamaños: `h-4 w-4` en controles y navegación, `h-5 w-5` en menús, `h-[18px] w-[18px]` en
  los botones del encabezado.
- Decorativos con `aria-hidden="true"`. Un botón que solo tiene ícono lleva `aria-label`.
- El ícono toma el color del texto (`currentColor`); no se le pone un color fijo.
- Font Awesome sigue cargado por compatibilidad, pero las vistas nuevas usan SVG.

### 6.8 Gráficas (Highcharts)

Los colores se leen de los tokens en el navegador, nunca se escriben fijos:

```js
const css = getComputedStyle(document.documentElement);
const token = (n) => css.getPropertyValue(n).trim();

Highcharts.chart('grafica', {
    colors: [token('--acento'), token('--secundario'), token('--texto-tenue')],
    chart: { backgroundColor: 'transparent', style: { fontFamily: token('--fuente') } },
    xAxis: { lineColor: token('--borde'), labels: { style: { color: token('--texto-suave') } } },
    yAxis: { gridLineColor: token('--borde'), labels: { style: { color: token('--texto-suave') } } },
    legend: { itemStyle: { color: token('--texto-suave') } },
    // …
});
```

- Serie 1: primario de la empresa; serie 2: secundario; el resto en neutros.
- Importaciones contra exportaciones: naranja `#EA580C` y verde `#16A34A`, los colores de
  sus módulos (convención de balanza comercial).
- Al cambiar entre claro y oscuro hay que volver a pintar la gráfica para que tome los nuevos
  valores.

---

## 7. Navegación

### 7.1 Estructura de la tabla `menus`

| Nivel | Qué es | Dónde aparece | Columnas que importan |
|---|---|---|---|
| 1 | Módulo (Importaciones, Exportaciones, CIA, Administracion) | Pestaña en la barra de módulos | `nombre`, `orden` |
| 2 | Grupo (Operaciones, Configuración…) | Desplegable en el sidebar | `nombre`, `depende` → id del nivel 1, `svg` (ícono), `orden` |
| 3 | Enlace a una pantalla | Ítem dentro del grupo | `nombre`, `depende` → id del nivel 2, `ruta`, `target`, `orden` |

Una fila se muestra solo si cumple **todos** estos filtros. Son exactamente los del sidebar
anterior (`Plantilla\Sidebar`): omitir uno expone pantallas de otros clientes, y añadir otro
cambia lo que ve cada cliente.

1. `estado = 1`.
2. Su `id` está en el JSON `compania.aplicaciones` de la **primera compañía activa** del grupo
   (lo que el cliente tiene contratado).
3. `perfiles` incluye el perfil del usuario (`$.perfil`) **o** su id (`$.usuario`), evaluado con
   `JSONARRAY_VERIFY_DATA`. Esta función está definida en la base, no en el repositorio.

Las columnas `regimen` e `id_grupo_empresarial` de `menus` **no** se usan para filtrar.

Los valores van siempre como parámetros enlazados, nunca concatenados en el SQL. Los grupos
sin enlaces y los módulos sin grupos se ocultan.

El menú solo **oculta**: no protege la URL. Mientras no se active el control de acceso por ruta
(ver `MigracionLayout.MD`), una pantalla fuera del menú sigue siendo accesible escribiendo su
dirección.

### 7.2 Agregar una pantalla nueva

1. Crear la ruta en `routes/web.php`.
2. Insertar una fila de **nivel 3** en `menus` con `ruta` igual al path (`/impo/nueva`), con
   o sin `/` inicial, y con los `perfiles` que la pueden ver.
3. Añadir el `id` de esa fila al JSON `compania.aplicaciones` de cada cliente que deba verla.
   Sin este paso la pantalla no aparece.
4. Listo: el sidebar la muestra en su grupo, la marca como actual y selecciona su módulo en
   la barra cuando se está en esa página.

Todo esto se hace con SQL directamente en la base: el proyecto no usa migraciones
(CLAUDE.md, regla 3).

Para que la marca funcione, **el path de la página debe ser exactamente el de `ruta`**. Las
pantallas con parámetros (`/orden/123`) no se marcan solas.

### 7.3 Agregar un módulo nuevo

1. Fila de **nivel 1** en `menus`.
2. Entrada en `config/modulos.php` cuya llave sea el slug del nombre (`"Transporte"` →
   `transporte`), con `icono` (trazos de un SVG 24×24) y la paleta `claro`/`oscuro`
   (`mod`, `ink`, `soft`, `line`). Si no se agrega, el módulo usa el ícono y el gris de `panel`.
3. Elegir un color que no compita con los existentes: naranja = importaciones, verde =
   exportaciones, azul = CIA, gris pizarra = administración, gris neutro = Dashboard.

### 7.4 Estado en el cliente

- `$store.nav.modulo`: slug del módulo que muestra el sidebar.
- `$store.nav.elegir('slug')`: cambia de módulo sin recargar. El último se recuerda en
  `localStorage.modulo_menu`.
- Eventos de ventana: `abrir-selector-empresa` (Alt + S) y `abrir-color-empresa`.
- **`wire:navigate` todavía no se usa.** Mientras haya pantallas Bootstrap, navegar sin recargar
  hacia una de ellas la rompería. Se activa en los enlaces cuando todas las pantallas usen este
  layout (fase L5 de `MigracionLayout.MD`). Los que abren en otra pestaña (`target="_blank"`)
  nunca lo llevan.

### 7.5 Selector de empresa

- **Opciones:** las compañías de `session('companias')`, que el login calcula desde el
  subdominio: las del grupo empresarial y, si el grupo es tercero, las de `companias_tercero`.
- **Selección:** "Todas" (por defecto) o una compañía. Vive en la sesión.
- **Solo aparece** si hay más de una compañía **y** el usuario tiene el permiso
  `empresa-seleccionar`. Ambas condiciones se comprueban también en el servidor al guardar.
- **Qué hace:** *preselecciona* el filtro de compañía de cada módulo al abrirlo. No restringe
  consultas: dentro del módulo el usuario puede cambiar el filtro, y los módulos sin filtro de
  compañía no cambian.
- **Nunca** cambia `session('GrupoEmpresarial')`. El aislamiento entre clientes sigue dependiendo
  solo del grupo.
- Un componente con filtro de compañía usa el trait de preselección y declara el nombre de su
  propiedad y si guarda `compania.id` o `id_websimex`. Al cambiar de empresa, la pantalla abierta
  se recarga.

---

## 8. Colores institucionales de la empresa

- Se guardan en `grupo_empresarial.color_p` (primario), `color_s` (secundario) y
  `modulos_neutros` (sí/no).
- Se cambian desde **menú de usuario → Colores de la empresa**. Solo puede quien tenga el
  permiso `empresa-personalizar` (spatie/laravel-permission, asignado por rol). El permiso se
  crea con SQL en la base; no hay migración.
- `TemaEmpresa` los sirve desde una caché por empresa (`tema-empresa:{id}`, 10 minutos).
  Guardar, o cualquier `save()` del modelo `GrupoEmpresarial`, la borra, y el cambio llega a
  todos los usuarios de esa empresa en su siguiente página.
- En PHP, **siempre** se leen con `app(\App\Services\TemaEmpresa::class)->actual()`.
  **Nunca** con `session('Acento')`, `session('color_tw_p')` ni `session('colorBtnMenu')`:
  son claves heredadas que existen solo por las vistas antiguas.
- En las vistas no hace falta leerlos: se usan los tokens `--acento-*` y `--secundario-*`.

---

## 9. Accesibilidad

- Cada control tiene un nombre accesible (texto visible, `aria-label` o `sr-only`).
- Foco visible: `focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--acento)]`.
  Nunca se quita el foco sin reemplazarlo.
- Enlace o pestaña actual con `aria-current="page"`; desplegables con `aria-expanded`;
  interruptores con `role="switch"`.
- Texto que hay que leer: `--texto` o `--texto-suave`. `--texto-tenue` solo para lo accesorio.
- Las áreas táctiles de navegación miden al menos 32px de alto (`h-8`), 40px en móvil (`h-10`).
- Todo lo que se puede hacer con el mouse se puede hacer con teclado (Escape cierra los
  modales; ↑ ↓ ↵ en el selector de empresa).

---

## 10. Lista de revisión de una vista nueva

- [ ] No hay ningún `#hex` ni clase de color de Tailwind (`bg-gray-*`, `text-blue-*`…) salvo
      los estados semánticos de §6.6.
- [ ] No hay ninguna clase armada con `session(...)`.
- [ ] Se ve bien en claro **y** en oscuro sin ningún `dark:` propio (salvo los estados).
- [ ] Se probó con al menos dos empresas de colores muy distintos (por ejemplo, una con primario
      claro como AVGUST `#FCB404` y otra oscura).
- [ ] Una sola acción principal (`<x-button>`); el resto, `<x-secondary-button>`.
- [ ] El texto en color de empresa usa `--acento-texto`; el texto sobre `--acento` usa
      `--acento-contraste`.
- [ ] Usa el contenedor y el encabezado de página de §5; no depende del slot `header`.
- [ ] Funciona desde 360px de ancho (tablas con `overflow-x-auto`).
- [ ] Si es una pantalla de menú, su fila de nivel 3 en `menus` tiene la `ruta` exacta.
- [ ] Íconos SVG con `currentColor` y `aria-hidden`; botones de solo ícono con `aria-label`.

---

## 11. Pendientes conocidos

- **SQL sin aplicar** (`MigracionLayout.MD` §6): crea la columna `modulos_neutros` y los
  permisos `empresa-personalizar` y `empresa-seleccionar`. Mientras no se aplique, el
  interruptor *Módulos en gris* no aparece en el modal y nadie puede cambiar colores ni usar
  el selector de empresa.
- **Vistas antiguas:** muchas pantallas usan clases con `session('color_tw_p')`, colores
  fijos o los componentes de §6.4 sin migrar. Se migran a esta norma al tocarlas.
- **Fases siguientes de la propuesta de color:** tema global de Highcharts con los colores de
  la empresa, vista previa en vivo en el modal y aviso de colores parecidos, y correos con los
  colores de la empresa.
