/* public/css/app.css — layout, nav, form, button, card, table, modal.
   No literal colour, size or spacing value. Only var(--token). */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--texto-body);
  line-height: var(--lh-body);
  color: var(--content);
  background: var(--surface-ground);
}

h1, h2, h3 { margin: 0; }
h1 { font-size: var(--texto-h1); line-height: var(--lh-h1); letter-spacing: var(--ls-h1); font-weight: 600; }
h2 { font-size: var(--texto-h2); line-height: var(--lh-h2); letter-spacing: var(--ls-h2); font-weight: 600; }
h3 { font-size: var(--texto-h3); line-height: var(--lh-h3); font-weight: 600; }

a { color: var(--primary); }

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Toda cifra alineada en el decimal. No negociable. */
.num, .mono, .kpi__cifra, td.num, th.num, input.num {
  font-variant-numeric: tabular-nums lining-nums;
}
.mono { font-family: var(--font-mono); font-size: var(--texto-sm); }

/* --oro-ink marca oro y sólo oro. */
.oro { color: var(--oro-ink); }

/* Desconocido no es cero. */
.vacio { color: var(--content-disabled); }

/* ---- shell ---- */
.app {
  display: grid;
  grid-template-columns: var(--nav-ancho) 1fr;
  min-height: 100vh;
}

/* Sin sesión el layout no pinta el menú (es cascarón autenticado), así que la
   columna del nav no debe quedar reservada y vacía. La página de error anónima se
   centra como el login: es lo mismo que es, una tarjeta sola. */
.app--sin-nav {
  grid-template-columns: 1fr;
  place-items: center;
}
.app--sin-nav .contenido { max-width: 480px; }

.nav {
  display: flex;
  flex-direction: column;
  gap: var(--e-1);
  padding: var(--e-4) 0;
  background: var(--surface-card);
  border-right: 1px solid var(--border);
  /* La columna se fija a la altura de la ventana y se queda pegada al desplazarse.
     Sin esto la .nav crece hasta la altura del CONTENIDO —2.918px en el dashboard—
     y el `margin-top: auto` del salir lo manda al pie de eso: para cerrar sesión
     había que bajar hasta el final de la pantalla más larga de la aplicación. Con
     esto el pie del menú es el pie de la ventana, que es lo que se quiso decir.
     `overflow-y` porque con los seis submenús abiertos el menú pasa de 100vh y
     entonces se desplaza dentro de sí mismo en vez de recortarse. */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.nav__marca {
  padding: var(--e-2) var(--e-4) var(--e-4);
  font-size: var(--texto-h2);
  font-weight: 650;
  letter-spacing: var(--ls-h2);
  color: var(--content);
}
.nav__enlace {
  padding: var(--e-2) var(--e-4);
  color: var(--content);
  text-decoration: none;
  font-size: var(--texto-sm);
  border-left: 2px solid transparent;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.nav__enlace:hover { background: var(--surface-sunken); }
.nav__enlace[aria-current="page"] {
  background: var(--surface-sunken);
  border-left-color: var(--border-strong);
  font-weight: 600;
}
.nav__enlace--proximamente {
  color: var(--content-disabled);
  cursor: default;
}
.nav__enlace--proximamente::after {
  content: "próximamente";
  display: block;
  font-size: var(--texto-overline);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  color: var(--content-disabled);
}

/* Un padre desplegable es un <button>, así que hay que quitarle la apariencia de
   botón nativo y darle la del resto de los enlaces del menú. */
.nav__enlace--padre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2);
  width: 100%;
  background: none;
  border: 0;
  font: inherit;
  /* `font: inherit` borra el font-size que .nav__enlace fija, y el padre saldría
     más grande que Dashboard, que es un <a> hermano. Se vuelve a poner. */
  font-size: var(--texto-sm);
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* La flecha apunta a la derecha cerrada y hacia abajo abierta. Se rota en vez de
   cambiar el path: un solo SVG, y la transición sale gratis. La duración es la
   misma --dur-fast que ya usan los enlaces del menú: que dos elementos de la
   interfaz se muevan a distinta velocidad se lee como un defecto. */
.nav__flecha {
  flex: none;
  transition: transform var(--dur-fast) var(--ease);
}
.nav__enlace--padre[aria-expanded="true"] .nav__flecha {
  transform: rotate(90deg);
}

/* Quien prefiere menos movimiento no recibe la rotación animada. */
@media (prefers-reduced-motion: reduce) {
  .nav__flecha { transition: none; }
}

/* ---- nav segundo nivel ---- */
.nav__sub {
  display: grid;
  margin: 0 0 var(--e-2) var(--nav-sub-indent);
  border-left: 1px solid var(--border);
}
/* `display: grid` de arriba es regla de autor y el `[hidden] { display: none }` del
   navegador es de la hoja del agente: la de autor gana, así que sin esta línea el
   atributo hidden no oculta nada y los seis submenús salen abiertos siempre. Se vio
   en el navegador, no en la suite: el HTML era correcto y el CSS lo desmentía.
   Va por [hidden] y no por una clase para que el JS siga alternando la propiedad
   estándar, y basta una sola regla porque 0-2-0 le gana también a la de ≤1023px. */
.nav__sub[hidden] { display: none; }
.nav__sub-enlace {
  padding: var(--e-1) var(--e-3);
  font-size: var(--texto-label); line-height: var(--lh-label);
  color: var(--content-secondary); text-decoration: none;
  border-left: 2px solid transparent; margin-left: -1px;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.nav__sub-enlace:hover { color: var(--content); }
.nav__sub-enlace[aria-current="page"] {
  color: var(--content); border-left-color: var(--border-strong); font-weight: 500;
}

/* El salir va al fondo del menú, separado de la navegación: no es un destino más.
   `margin-top: auto` funciona porque .nav es flex column. */
.nav__salir {
  margin-top: auto;
  padding: var(--e-4);
}
.nav__salir__boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.contenido {
  padding: var(--page-pad);
  max-width: var(--page-max);
  width: 100%;
  /* A grid `1fr` track defaults to min-width:auto, so a wide table would push
     the whole page sideways. The table scrolls inside .tabla-envoltura instead;
     the page body never scrolls horizontally (design system). */
  min-width: 0;
  overflow-x: hidden;
}

/* ---- encabezado de página ---- */
.encabezado {
  display: flex;
  gap: var(--e-4);
  align-items: flex-end;
  justify-content: space-between;
  padding-bottom: var(--e-4);
  margin-bottom: var(--section-gap);
  border-bottom: 1px solid var(--border);
}
.encabezado__overline {
  font-size: var(--texto-overline);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--content-secondary);
}

/* ---- tarjetas: hairline, nunca sombra ---- */
.tarjeta {
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radio-card);
  padding: var(--card-pad);
}

/* ---- formularios ---- */
.campo { display: grid; gap: var(--e-1); margin-bottom: var(--field-gap); }
.campo__etiqueta {
  font-size: var(--texto-label);
  line-height: var(--lh-label);
  font-weight: 500;
  color: var(--content-secondary);
}
.campo__control { display: flex; align-items: center; gap: var(--e-2); }

/**
 * Edición en línea de un número dentro de una tabla.
 *
 * El campo tiene que caber el valor ENTERO o la persona edita a ciegas: el salario
 * mensual se guarda como «1400000.0000», que son doce caracteres, y con el ancho
 * anterior —un `max-width:150px` puesto a mano en la plantilla y compartido con el
 * botón «Guardar»— quedaban 77 px para 133 px de texto. Las 36 filas se desbordaban,
 * incluso las cortas.
 *
 * `ch` y no píxeles porque el campo va en tipografía monoespaciada: 14ch son 14
 * dígitos se mida donde se mida, y el valor más largo que existe en la tabla es
 * «1400000.0000» —doce— así que caben con holgura. Más ancho que eso no compra
 * nada y se lo quita a la columna de notas, que sí lo necesita para no partirse en
 * cinco renglones.
 *
 * `flex: none` en los dos niveles porque el campo vive dentro de dos contenedores
 * flex —el formulario de la fila y el propio `.campo__control`— y un ítem flex se
 * encoge por debajo de su `width` salvo que se le prohíba. Sin eso el ancho fijado
 * se queda en una intención: se midió en 98 px de los 133 que pide el valor.
 */
 *
 * `min-width` ADEMÁS de `width`, y no es redundante: `width` fija el tamaño deseado
 * pero NO participa en el ancho mínimo intrínseco que la tabla usa para repartir
 * columnas. Con sólo `width` la celda de acciones se calculaba en 145 px, el input
 * seguía midiendo 212 y se desbordaba de su contenedor hacia la izquierda —el
 * formulario alinea a la derecha— hasta quedar ENCIMA del texto de la nota. Con
 * `min-width` el mínimo intrínseco sube y la tabla le da a la columna lo que pide.
 */
.campo__control--numero { flex: none; }
.campo__control--numero input { width: 14ch; min-width: 14ch; flex: none; }
.campo__sufijo { font-size: var(--texto-label); color: var(--content-secondary); }

input[type="text"], input[type="password"], input[type="date"], input[type="time"],
input[type="month"], input[type="search"], input[type="number"], select, textarea {
  width: 100%;
  padding: var(--e-2) var(--e-3);
  font-family: inherit;
  font-size: var(--texto-body);
  color: var(--content);
  background: var(--surface-card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radio-input);
}
input.num { text-align: right; font-family: var(--font-mono); }
input[type="checkbox"] { width: auto; }
input:disabled, input[readonly] { background: var(--surface-sunken); color: var(--content-secondary); }

/* ---- botones ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e-2);
  height: 36px;
  padding: 0 var(--e-4);
  font-family: inherit;
  font-size: var(--texto-sm);
  font-weight: 500;
  border-radius: var(--radio-input);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn--primario { background: var(--primary); color: var(--primary-contrast); }
.btn--secundario { background: transparent; color: var(--content); border-color: var(--border-strong); }
.btn--secundario:hover { background: var(--surface-sunken); }
.btn--fantasma { background: transparent; color: var(--content-secondary); }
.btn--fantasma:hover { color: var(--content); background: var(--surface-sunken); }
.btn--peligro { background: transparent; color: var(--danger); border-color: var(--danger); }
.btn--peligro:hover { background: var(--danger); color: var(--primary-contrast); }

/* ---- avisos ---- */
.aviso {
  padding: var(--e-3) var(--e-4);
  margin: 0 0 var(--e-4);
  border: 1px solid currentColor;
  border-radius: var(--radio-card);
  font-size: var(--texto-sm);
}
.aviso--ok { color: var(--success); }
.aviso--mal { color: var(--danger); }
.aviso--atencion { color: var(--warn); }
.aviso--info { color: var(--info); }
/* Una lista larga de claves en mono no debe forzar el ancho de la página. */
.aviso { overflow-wrap: anywhere; }

/* ---- login ---- */
.entrar {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: var(--e-4);
}
.entrar__panel { width: 100%; max-width: 360px; }

/* ---- responsive: hasta 360px ---- */
@media (max-width: 1023px) {
  .app { grid-template-columns: 1fr; }
  .nav {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    border-right: 0;
    border-bottom: 1px solid var(--border);
    padding: var(--e-2) var(--e-3);
    /* Aquí el menú es una barra arriba, no una columna: una altura de 100vh se
       comería la pantalla entera antes del contenido. */
    position: static;
    height: auto;
    overflow: visible;
  }
  /* Y en fila no hay pie que empujar: el auto lo mandaría al fondo de su línea. */
  .nav__salir { margin-top: 0; }
  .nav__marca { padding: var(--e-1) var(--e-2); }
  .nav__enlace { border-left: 0; border-bottom: 2px solid transparent; padding: var(--e-2) var(--e-3); }
  .nav__enlace[aria-current="page"] { border-left-color: transparent; border-bottom-color: var(--border-strong); }
  .nav__enlace--proximamente::after { display: none; }
  /* En fila el padre no puede ocupar el ancho completo: la .nav de aquí es un
     flex-wrap row, y con width:100% cada padre se llevaría su propia línea —el
     menú pasaría de una barra a seis. Tampoco separa: la flecha va pegada a su
     etiqueta, no empujada al otro extremo de la pantalla. */
  .nav__enlace--padre { width: auto; justify-content: flex-start; }
  /* Fila que SÍ envuelve. Con grid-auto-flow:column las cuatro entradas de
     Bodega y de Máquinas formaban una sola fila indivisible, y la nav empujaba
     la página 16px a lo ancho en 360px. Las columnas de grid no envuelven. */
  .nav__sub {
    margin-left: 0; border-left: 0;
    display: flex; flex-wrap: wrap; gap: 0 var(--e-1);
    min-width: 0;
  }
}

@media (max-width: 767px) {
  :root { --page-pad: var(--e-4); }
  .encabezado { flex-direction: column; align-items: flex-start; }
}

/* ---- componentes (gate 2) ---- */
.tarjeta__titulo {
  margin: 0 0 var(--e-2);
  font-size: var(--texto-h2); line-height: var(--lh-h2); letter-spacing: var(--ls-h2);
}
.tarjeta__descripcion {
  margin: 0 0 var(--e-4);
  font-size: var(--texto-sm); color: var(--content-secondary);
}

.kpi { display: grid; gap: var(--e-1); align-content: start; }
.kpi__overline {
  font-size: var(--texto-overline); letter-spacing: var(--ls-overline);
  text-transform: uppercase; font-weight: 600; color: var(--content-secondary);
}
.kpi__cifra {
  font-size: var(--texto-display); line-height: var(--lh-display);
  letter-spacing: var(--ls-display); font-weight: 650;
  font-variant-numeric: tabular-nums lining-nums;
}
.kpi__delta { font-size: var(--texto-sm); color: var(--content-secondary); }
.kpi__delta--ok { color: var(--success); }
.kpi__delta--mal { color: var(--danger); }
.kpi__subtitulo { font-size: var(--texto-label); color: var(--content-secondary); }

.rejilla-kpi {
  display: grid; gap: var(--e-4);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  margin-bottom: var(--section-gap);
}

.semaforo {
  display: inline-flex; align-items: center; gap: var(--e-2);
  padding: 2px var(--e-3);
  border: 1px solid currentColor; border-radius: var(--radio-pill);
  font-size: var(--texto-label); white-space: nowrap;
  transition: background var(--dur-fast) var(--ease);
}
.semaforo__punto {
  width: 8px; height: 8px; border-radius: var(--radio-pill); background: currentColor;
  flex: 0 0 auto;
}
.semaforo--ok { color: var(--success); }
.semaforo--atencion { color: var(--warn); }
.semaforo--fuera { color: var(--danger); }
.semaforo--neutral { color: var(--content-secondary); }

/* ---- gráficos (gate 9) ----
   El lienzo tiene alto fijo porque Chart.js con maintainAspectRatio:false crece
   sin límite dentro de un contenedor flexible. El estado vacío ocupa el mismo
   espacio, así que la página no salta cuando JS lo esconde. */
.grafico__caja { position: relative; height: 220px; }
.grafico__lienzo { width: 100%; height: 220px; }
.grafico__vacio {
  position: absolute; inset: 0;
  display: grid; place-items: center; margin: 0; text-align: center;
}
.rejilla-graficos {
  display: grid; gap: var(--e-4);
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  margin-bottom: var(--section-gap);
}

/* ---- barra de horómetro (ProximoMantenimiento) ----
   Verde hasta el 80% del intervalo, ámbar de ahí al 100%, rojo pasado. El color
   lo pone la plantilla desde el semáforo del servicio; aquí sólo va la pista. */
.barra-horometro { display: grid; gap: var(--e-2); }
.barra-horometro__pista {
  height: 4px; border-radius: var(--radio-pill);
  background: var(--surface-sunken); overflow: hidden;
}
.barra-horometro__relleno {
  height: 100%; border-radius: var(--radio-pill);
  transition: width var(--dur-base) var(--ease);
}
.barra-horometro__etiqueta {
  margin: 0; font-size: var(--texto-label); color: var(--content-secondary);
}

/* Una tarjeta por máquina. Diez equipos caben en la rejilla y apilan en 360px. */
.rejilla-maquinas {
  display: grid; gap: var(--e-4);
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.maquina { display: grid; gap: var(--e-3); }
.maquina__encabezado {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--e-3); flex-wrap: wrap;
}
.maquina__consumo {
  margin: 0; font-size: var(--texto-label); color: var(--content-secondary);
}

.estado-vacio {
  display: grid; gap: var(--e-3); justify-items: center;
  padding: var(--e-12) var(--e-4); text-align: center;
}
.estado-vacio__mensaje { margin: 0; color: var(--content-secondary); }

/* ---- tabla ---- */
.tabla-envoltura { overflow-x: auto; }   /* la tabla scrollea, nunca la página */
.tabla { width: 100%; border-collapse: collapse; }
.tabla th, .tabla td {
  padding: var(--table-cell-pad-y) var(--table-cell-pad-x);
  border-bottom: 1px solid var(--border);
}
.tabla thead th {
  background: var(--surface-sunken);
  font-size: var(--texto-overline); letter-spacing: var(--ls-overline);
  text-transform: uppercase; font-weight: 600; text-align: left;
  color: var(--content-secondary); white-space: nowrap;
}
.tabla thead th.num, .tabla td.num { text-align: right; }

/**
 * La columna de acciones se mide por su contenido, no por lo que sobre.
 *
 * `width: 1%` es la forma de decirle a una tabla de ancho automático «dale a esta
 * columna lo MÍNIMO que su contenido permita»: el navegador reparte el resto entre
 * las demás. Sin esto la columna recibía lo que quedaba —217 px— para un formulario
 * de edición que pide 310, y el sobrante se derramaba FUERA de la celda, encima de
 * la nota de la fila. No era un solape de columnas: era el campo saliéndose de la
 * suya, que es lo que se veía en Parámetros.
 *
 * `nowrap` para que los botones de la fila no se partan en dos renglones. Si la suma
 * no cabe, la tabla crece y scrollea dentro de .tabla-envoltura, que es justo lo que
 * esa envoltura existe para hacer; la página nunca scrollea a lo ancho.
 */
.tabla th.acciones, .tabla td.acciones { width: 1%; white-space: nowrap; }
/* Columna declarada `nowrap`: se lee en un renglón. La tabla puede crecer porque
   .tabla-envoltura scrollea; la página nunca lo hace. */
.tabla td.nw { white-space: nowrap; }
.tabla tbody tr:hover { background: var(--surface-sunken); }
.tabla__orden { color: inherit; text-decoration: none; display: inline-flex; gap: var(--e-1); }
.tabla__caret { opacity: 0.4; }
.tabla thead th[aria-sort] .tabla__caret { opacity: 1; }

.filtros {
  display: flex; flex-wrap: wrap; gap: var(--e-3); align-items: flex-end;
  padding-bottom: var(--e-4); margin-bottom: var(--e-3);
  border-bottom: 1px solid var(--border);
}
.filtros__grupo { display: grid; gap: var(--e-1); }
.filtros__acciones { margin-left: auto; display: flex; gap: var(--e-2); align-items: flex-end; }

.paginacion {
  display: flex; gap: var(--e-3); align-items: center; justify-content: space-between;
  padding-top: var(--e-3); font-size: var(--texto-sm); color: var(--content-secondary);
}

/* ---- modal ---- */
.modal { border: 0; padding: 0; background: transparent; max-width: 560px; width: 100%; }
.modal::backdrop { background: rgb(23 23 23 / 0.45); }
.modal__panel {
  background: var(--surface-raised);
  border: 1px solid var(--border); border-radius: var(--radio-card);
  box-shadow: var(--shadow-modal);
  padding: var(--card-pad);
}
.modal__pie {
  display: flex; gap: var(--e-2); justify-content: flex-end;
  padding-top: var(--e-4); border-top: 1px solid var(--border); margin-top: var(--e-4);
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: var(--dur-fast) !important; animation: none !important; }
}
.campo__ayuda {
  margin: var(--e-1) 0 0;
  font-size: var(--texto-label);
  color: var(--content-secondary);
}
