/* ---- Base y tokens de diseño ---- */

:root {
  color-scheme: dark;

  --fondo: #0f1115;
  --superficie: #171a21;
  --superficie-2: #1e222b;
  --borde: #2a2f3a;
  --borde-suave: #212630;

  --texto: #e6e8ec;
  --texto-suave: #9aa2b1;
  --texto-tenue: #6b7386;

  --acento: #5b8cff;
  --acento-hover: #7aa2ff;

  /* El Verde+ va en violeta, no en verde: es la distinción más difícil de leer
     de toda la escala, y separarla de tono resuelve también el daltonismo
     rojo-verde, donde Verde+ y Verde caían casi encima. */
  --verde-plus: #a78bfa;
  --verde: #4ade80;
  --naranja: #fb923c;
  --rojo: #f87171;

  --radio: 10px;
  --radio-sm: 6px;
  --sombra: 0 2px 12px rgba(0, 0, 0, .35);

  --fuente: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono: ui-monospace, 'Cascadia Code', Consolas, monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--fuente);
  background: var(--fondo);
  color: var(--texto);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0 0 .5rem; line-height: 1.25; font-weight: 600; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.2rem; }

a { color: var(--acento); text-decoration: none; }
a:hover { color: var(--acento-hover); text-decoration: underline; }

/* ---- Formularios ---- */

label {
  display: block;
  font-size: .875rem;
  color: var(--texto-suave);
  margin-bottom: .35rem;
}

input, select, textarea, button {
  font-family: inherit;
  font-size: 1rem;
}

input[type="text"],
input[type="password"],
input[type="number"],
input[type="date"],
select,
textarea {
  width: 100%;
  padding: .6rem .75rem;
  background: var(--superficie-2);
  border: 1px solid var(--borde);
  border-radius: var(--radio-sm);
  color: var(--texto);
  transition: border-color .15s, box-shadow .15s;
}

/*
 * El calendario y las flechitas que dibuja el navegador vienen en su tema claro
 * y sobre esta superficie quedan ilegibles. `color-scheme` le avisa que la
 * interfaz es oscura y los adapta, incluido el desplegable del datepicker.
 */
input[type="date"] { color-scheme: dark; }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--acento);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--acento) 25%, transparent);
}

textarea { resize: vertical; min-height: 8rem; font-family: var(--mono); font-size: .875rem; }

button, .boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .6rem 1.1rem;
  background: var(--acento);
  color: #fff;
  border: 1px solid transparent;
  border-radius: var(--radio-sm);
  cursor: pointer;
  font-weight: 500;
  transition: background .15s, opacity .15s;
}

button:hover:not(:disabled), .boton:hover { background: var(--acento-hover); text-decoration: none; }
button:disabled { opacity: .5; cursor: not-allowed; }

button.secundario {
  background: var(--superficie-2);
  border-color: var(--borde);
  color: var(--texto);
}
button.secundario:hover:not(:disabled) { background: var(--borde); }

/* ---- Bloques ---- */

.campo { margin-bottom: 1rem; }

.tarjeta {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  padding: 1.5rem;
  box-shadow: var(--sombra);
}

.mensaje {
  padding: .7rem .9rem;
  border-radius: var(--radio-sm);
  font-size: .9rem;
  margin-bottom: 1rem;
}

.mensaje.error {
  background: rgba(248, 113, 113, .12);
  border: 1px solid rgba(248, 113, 113, .35);
  color: #fca5a5;
}

.mensaje.exito {
  background: rgba(74, 222, 128, .12);
  border: 1px solid rgba(74, 222, 128, .35);
  color: #86efac;
}

.mensaje.info {
  background: rgba(91, 140, 255, .12);
  border: 1px solid rgba(91, 140, 255, .35);
  color: #a5c0ff;
}

.oculto { display: none !important; }

.ayuda {
  font-size: .8rem;
  color: var(--texto-tenue);
  margin-top: .35rem;
}

/* ---- Pantallas centradas (login, cambio de contraseña) ---- */

.pantalla-centrada {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
}

.pantalla-centrada .tarjeta {
  width: 100%;
  max-width: 24rem;
}

.marca {
  text-align: center;
  margin-bottom: 1.5rem;
}

.marca h1 { font-size: 1.35rem; margin-bottom: .25rem; }
.marca p { margin: 0; color: var(--texto-tenue); font-size: .875rem; }

/* ---- Navegación ---- */

:root { --sidebar-w: 250px; }

/* Panel lateral fijo a la izquierda. El body reserva su ancho con padding. */
.con-sidebar { padding-left: var(--sidebar-w); }

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--sidebar-w);
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--superficie);
  border-right: 1px solid var(--borde);
  z-index: 20;
}

.sidebar-cabecera {
  padding: 1rem 1.1rem;
  border-bottom: 1px solid var(--borde);
}
.sidebar-marca { font-weight: 700; font-size: 1.05rem; color: var(--texto); }
.sidebar-marca:hover { text-decoration: none; color: var(--acento); }
.sidebar-marca.activo { color: var(--acento); }

.sidebar-cuerpo { flex: 1; overflow-y: auto; padding: .85rem .7rem; }
.sidebar-titulo {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--texto-tenue);
  padding: 0 .4rem .5rem;
}

.sidebar-servers { display: flex; flex-direction: column; gap: .25rem; }

.side-grupo { border-radius: var(--radio-sm); overflow: hidden; }

.side-cabecera {
  display: flex;
  align-items: center;
  gap: .55rem;
  width: 100%;
  padding: .5rem .55rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radio-sm);
  color: var(--texto);
  font-size: .92rem;
  font-weight: 500;
  cursor: pointer;
  text-align: left;
}
.side-cabecera:hover { background: var(--superficie-2); }
.side-grupo.abierto > .side-cabecera {
  background: var(--superficie-2);
  border-color: color-mix(in srgb, var(--acento) 35%, var(--borde));
}
.side-logo { width: 22px; height: 22px; border-radius: 5px; object-fit: cover; flex-shrink: 0; }
.side-nombre { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-flecha { color: var(--texto-tenue); font-size: .8rem; transition: transform .18s; }
.side-grupo.abierto .side-flecha { transform: rotate(180deg); color: var(--acento); }

.side-secciones {
  position: relative;
  max-height: 0;
  overflow: hidden;
  transition: max-height .22s ease;
}
/* Ocho secciones: el alto tiene que dar para todas o el acordeón las recorta. */
.side-grupo.abierto .side-secciones { max-height: 32rem; }

/*
 * Guía vertical continua que cuelga de la network.
 *
 * Reemplaza al chevron que antes llevaba cada ítem: con el ícono adelante había
 * dos glifos por fila peleando por el mismo lugar, y ninguno terminaba de leerse.
 * Una sola línea dice lo mismo —"esto cuelga de esta network"— sin repetirlo
 * ocho veces, y de paso da el riel donde marcar cuál está activa.
 */
.side-secciones::before {
  content: '';
  position: absolute;
  left: 1rem;
  top: .3rem;
  bottom: .3rem;
  width: 1px;
  background: var(--borde);
}

.side-seccion {
  position: relative;
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: .05rem .15rem .05rem .5rem;
  padding: .45rem .65rem .45rem 1.15rem;
  border-radius: var(--radio-sm);
  color: var(--texto-suave);
  font-size: .875rem;
  transition: background .15s, color .15s;
}

/*
 * La activa se marca pintando un tramo de la guía, no con una barra suelta a la
 * derecha: ahí quedaba desalineada del ícono, del texto y de la línea, y a ocho
 * ítems se leía como una columna de rayitas sin sentido.
 */
.side-seccion::before {
  content: '';
  position: absolute;
  left: .5rem;
  top: 50%;
  width: 2px;
  height: 1.15rem;
  border-radius: 1px;
  background: var(--acento);
  transform: translate(-.5px, -50%) scaleY(0);
  transition: transform .15s;
}

.side-seccion:hover {
  background: var(--superficie-2);
  color: var(--texto);
  text-decoration: none;
}

.side-seccion.activo {
  color: var(--acento);
  font-weight: 600;
  background: color-mix(in srgb, var(--acento) 12%, transparent);
}
.side-seccion.activo::before { transform: translate(-.5px, -50%) scaleY(1); }

/*
 * El ícono acompaña al texto, no compite con él: más tenue que la etiqueta y a
 * color pleno solo en la activa. Hereda `currentColor`, así que el estado se
 * pinta con la opacidad y nada más.
 */
.side-icono { width: 16px; height: 16px; flex-shrink: 0; opacity: .55; }
.side-seccion:hover .side-icono { opacity: .85; }
.side-seccion.activo .side-icono { opacity: 1; }

/*
 * Los dos historiales cuelgan de su reporte, no son hermanos suyos: van un
 * escalón más adentro y en un cuerpo más chico, así "Semanal → su historial" se
 * lee de una en vez de como cuatro entradas sueltas del mismo nivel.
 */
.side-seccion.sub {
  margin-left: 1.5rem;
  padding-left: .8rem;
  font-size: .82rem;
}
.side-seccion.sub .side-icono { width: 14px; height: 14px; }
.side-seccion.sub::before { left: .15rem; height: .95rem; }

.sidebar-pie {
  padding: .7rem .9rem 1rem;
  border-top: 1px solid var(--borde);
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.side-admin {
  padding: .35rem .5rem;
  border-radius: var(--radio-sm);
  color: var(--texto-suave);
  font-size: .88rem;
}
.side-admin:hover { background: var(--superficie-2); color: var(--texto); text-decoration: none; }
.side-admin.activo { color: var(--acento); font-weight: 500; }
.sidebar-usuario {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-top: .3rem;
}
.nav-usuario { color: var(--texto-tenue); font-size: .85rem; }
.nav-salir { padding: .3rem .7rem; font-size: .85rem; }

/* En pantallas angostas el panel pasa a ser una barra estática arriba. */
@media (max-width: 860px) {
  .con-sidebar { padding-left: 0; }
  .sidebar {
    position: static;
    width: 100%;
    height: auto;
    border-right: none;
    border-bottom: 1px solid var(--borde);
  }
  .sidebar-cuerpo { max-height: 60vh; }
}

/* ---- Layout ---- */

.contenido { max-width: 82rem; margin: 0 auto; padding: 1.5rem 1.25rem 4rem; }

.encabezado-pagina {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.encabezado-pagina p { margin: .15rem 0 0; color: var(--texto-suave); font-size: .9rem; }

.acciones { display: flex; gap: .5rem; flex-wrap: wrap; }

/* ---- Tablas ---- */

.tabla-contenedor { overflow-x: auto; border-radius: var(--radio); border: 1px solid var(--borde); }

table { width: 100%; border-collapse: collapse; font-size: .9rem; background: var(--superficie); }

th {
  text-align: left;
  padding: .65rem .75rem;
  background: var(--superficie-2);
  color: var(--texto-suave);
  font-weight: 500;
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  white-space: nowrap;
  border-bottom: 1px solid var(--borde);
}

td { padding: .6rem .75rem; border-bottom: 1px solid var(--borde-suave); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--superficie-2); }

.num { text-align: right; font-variant-numeric: tabular-nums; }

/*
 * Tabla de staff (preview del semanal e historial): mismas columnas y mismo
 * alineado. Se centran los conteos y todo lo marcado como `centro`; de Staff y
 * Extras se centra solo el título, porque el contenido va a la izquierda.
 */
.tabla-staff .num,
.tabla-staff .centro { text-align: center; }

.tabla-staff th:first-child { text-align: center; }

/* Los puntos son el número que decide el corazón: se leen antes que el resto. */
.tabla-staff .puntos { font-weight: 600; color: var(--texto); font-variant-numeric: tabular-nums; }

/*
 * Columnas quietas.
 *
 * Con el layout automático el ancho sale del contenido, así que una fila con
 * chip de ascenso en Extras ensancha esa columna y corre todas las demás: pasar
 * de una semanal a otra hace saltar la tabla entera. Con `fixed` los anchos los
 * manda el <colgroup> y el contenido se acomoda adentro.
 *
 * El min-width es lo que evita que el navegador reparta a lo ancho de la
 * ventana: por debajo de eso scrollea el contenedor, que ya es `overflow-x:auto`.
 */
.tabla-fija { table-layout: fixed; min-width: 59rem; }

/*
 * El userID de Discord es largo y de una sola pieza: se corta con puntos
 * suspensivos como el nick de arriba. Partirlo en varias líneas engordaba la
 * fila y no se leía mejor.
 */
.tabla-fija .staff-userid { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/*
 * Conteos editables: parecen texto hasta que se los toca, para que la tabla se
 * siga leyendo como una tabla y no como un formulario.
 */
.num-edit {
  width: 5rem;
  padding: .3rem .4rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radio-sm);
}

.num-edit:hover { border-color: var(--borde); background: var(--superficie-2); }

.num-edit:focus {
  border-color: var(--acento);
  background: var(--superficie-2);
  outline: none;
}

/* Se puede editar igual, pero este conteo no suma a la sanción de ese rango. */
.num-edit.no-suma { color: var(--texto-tenue); }

/* Un número corregido a mano queda marcado: el reporte ya no es el del JSON. */
.num-edit.editado {
  color: var(--acento-hover);
  font-weight: 600;
  border-bottom-color: var(--acento);
}

/* Las flechitas del spinner roban ancho y no aportan nada acá. */
.num-edit::-webkit-outer-spin-button,
.num-edit::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.num-edit { -moz-appearance: textfield; appearance: textfield; }
.tenue { color: var(--texto-tenue); }
.mono { font-family: var(--mono); font-size: .85em; }

/* ---- Etiquetas de categoría ---- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .15rem .5rem;
  border-radius: 999px;
  font-size: .75rem;
  font-weight: 500;
  white-space: nowrap;
}

.chip.VERDE_PLUS { background: rgba(167, 139, 250, .18); color: var(--verde-plus); }
.chip.VERDE { background: rgba(74, 222, 128, .13); color: var(--verde); }
.chip.NARANJA { background: rgba(251, 146, 60, .15); color: var(--naranja); }
.chip.ROJO { background: rgba(248, 113, 113, .15); color: var(--rojo); }
.chip.neutro { background: var(--superficie-2); color: var(--texto-suave); }
.chip.aviso { background: rgba(251, 146, 60, .15); color: var(--naranja); }
.chip.strike { background: rgba(248, 113, 113, .18); color: var(--rojo); }
.chip.ascenso { background: color-mix(in srgb, var(--acento) 18%, transparent); color: var(--acento); }
.chip.INACTIVIDAD { background: var(--superficie-2); color: var(--texto-suave); }
.chip.EXPULSION { background: rgba(248, 113, 113, .18); color: var(--rojo); }

/*
 * Ascenso ya aplicado: lleva el color del rango de destino, en el mismo par
 * fondo/texto que el tag de rango, así que el fondo se pinta inline y acá solo
 * queda el tamaño del ícono.
 */
/* Tamaño base; dentro de los botones de Semanal manda `.marca-opcion svg`. */
.icono-marca { width: 16px; height: 16px; flex-shrink: 0; display: block; }

.chip-ascenso { font-weight: 600; }
.chip-ascenso .icono-marca { width: 13px; height: 13px; }

/*
 * Marcas ya aplicadas en una tabla de solo lectura (Historial): el mismo ícono
 * que en Semanal, sin fondo ni texto — el nombre va en el `title`. Los colores
 * replican los del botón prendido.
 */
.extras-fila { display: flex; align-items: center; justify-content: center; gap: .3rem; flex-wrap: wrap; }

/* La calavera ocupa el lugar del corazón, así que va del tamaño del corazón. */
.marca-corazon { color: var(--texto); }
.marca-corazon .icono-marca { width: 22px; height: 22px; }

/* `vertical-align: middle` por lo mismo que el corazón: sin él se apoya en la
   baseline del texto y queda un par de píxeles por encima del centro de la fila. */
.marca-fija { display: inline-flex; align-items: center; vertical-align: middle; line-height: 0; }
.marca-fija .icono-marca { width: 18px; height: 18px; }
.marca-fija.INACTIVIDAD { color: var(--texto); }
.marca-fija.EXPULSION,
.marca-fija.STRIKE { color: var(--rojo); }
.marca-fija.AVISO { color: var(--naranja); opacity: .85; }

/* Ver más abajo: los iconos de la columna Extras (.selector-marcas). */

/* ---- Avisos flotantes ---- */

.avisos {
  position: fixed;
  bottom: 1.25rem;
  right: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  z-index: 100;
}

/*
 * Acotado a su contenedor a propósito. Suelto, este `.aviso` también agarraba a
 * los `<span class="chip aviso">` de las tablas —el chip de "Avisado"— y les
 * metía el padding y el borde del globo flotante, así que se veían el doble de
 * grandes que el resto de los chips.
 */
.avisos .aviso {
  padding: .65rem 1rem;
  border-radius: var(--radio-sm);
  font-size: .875rem;
  box-shadow: var(--sombra);
  transition: opacity .3s;
  background: var(--superficie-2);
  border: 1px solid var(--borde);
}

.avisos .aviso.exito { border-color: rgba(74, 222, 128, .4); color: #86efac; }
.avisos .aviso.error { border-color: rgba(248, 113, 113, .4); color: #fca5a5; }

/* ---- Utilidades ---- */

.vacio { padding: 2.5rem 1.5rem; text-align: center; color: var(--texto-tenue); }
.cargando { padding: 2rem; text-align: center; color: var(--texto-tenue); }

.grilla { display: grid; gap: 1rem; }
.grilla.cols-2 { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); }
.grilla.cols-3 { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }

.fila { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; }
.entre { justify-content: space-between; }

input[type="checkbox"] { width: auto; accent-color: var(--acento); }

/* ---- Lista del historial ---- */

.item-semanal {
  display: block;
  width: 100%;
  text-align: left;
  padding: .75rem .9rem;
  margin-bottom: .5rem;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-sm);
  color: var(--texto);
  font-weight: 400;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}

.item-semanal:hover { background: var(--superficie-2); border-color: var(--borde); }
.item-semanal.activo { border-color: var(--acento); background: var(--superficie-2); }

/*
 * La columna izquierda acompaña el scroll de la página en vez de irse arriba:
 * el detalle es largo y volver a la lista obligaba a subir todo.
 *
 * El buscador queda fijo arriba y solo la lista scrollea, únicamente cuando el
 * puntero está encima. `overscroll-behavior: contain` corta el encadenamiento:
 * llegar al final de la lista no debe empujar el scroll de la página.
 */
.columna-historial {
  position: sticky;
  top: 1rem;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 2rem);
}

.lista-semanales {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Aire para que el borde del ítem activo no quede pegado a la barra. */
  padding-right: .3rem;
}

/*
 * Sacar a alguien de la semanal. Apagada hasta el hover: es destructiva y no
 * tiene que competir por atención con los corazones y las marcas, que son lo
 * que uno viene a mirar.
 */
.quitar-staff {
  padding: .3rem;
  background: transparent;
  border: none;
  border-radius: var(--radio-sm);
  color: var(--rojo);
  opacity: .65;
  cursor: pointer;
  line-height: 0;
  transition: opacity .12s, background .12s, transform .12s;
}
.quitar-staff svg { width: 17px; height: 17px; display: block; }
.quitar-staff:hover {
  background: rgba(248, 113, 113, .16);
  opacity: 1;
  transform: translateY(-1px);
}

/*
 * La celda se ajusta a los cuatro botones y nada más. Antes reservaba 11rem para
 * que entrara el chip de ascenso en una línea, pero ese ancho de más se repartía
 * a los costados y dejaba un hueco enorme contra el tacho. El chip envuelve en
 * dos líneas sin problema, que es más barato que ese hueco en todas las filas.
 */
.celda-acciones { width: 1%; white-space: nowrap; }

/* La fila de íconos pasa a ser un bloque para que el chip de ascenso caiga
   debajo y no se meta en la columna del tacho. */
.celda-acciones .selector-marcas { display: flex; justify-content: center; }
.celda-acciones .chip,
.celda-acciones .tenue { white-space: normal; max-width: 9rem; }

/*
 * Ver el historial. En azul y no en el naranja de la interfaz: no cambia nada,
 * solo muestra, y conviene que se lea distinto de los botones que sí deciden.
 */
.info-staff {
  padding: .3rem;
  background: transparent;
  border: none;
  border-radius: var(--radio-sm);
  color: #5b8cff;
  opacity: .7;
  cursor: pointer;
  line-height: 0;
  transition: opacity .12s, background .12s, transform .12s;
}
.info-staff svg { width: 17px; height: 17px; display: block; }
.info-staff:hover {
  background: rgba(91, 140, 255, .16);
  opacity: 1;
  transform: translateY(-1px);
}

/* El título de esa columna es el mismo tacho, en gris de encabezado. */
.col-tacho svg {
  width: 15px;
  height: 15px;
  display: inline-block;
  vertical-align: middle;
  color: var(--texto-tenue);
}

/*
 * Barra de la semana: número, rango, carga de staff y guardar. Acompaña el
 * scroll porque la tabla de revisión es larga y todo eso se necesita mientras se
 * la revisa; tener que volver arriba para guardar era el peor tramo del flujo.
 */
.barra-semana {
  position: sticky;
  top: 0;
  z-index: 30;
  /* La tarjeta es translúcida en el borde: sin fondo sólido, la tabla se ve
     pasar por debajo del texto. */
  background: var(--superficie);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .35);
}
/*
 * Dos grupos —los datos de la semana y los botones— que comparten la línea y
 * bajan juntos si no entran. Los campos pueden encogerse; los botones no, para
 * que nunca queden cortados a la mitad.
 */
.barra-semana-fila {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .75rem 1rem;
}

.barra-semana-datos {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: .6rem;
}

.barra-semana .acciones { flex-shrink: 0; }

.campo-barra { min-width: 0; }
.campo-barra input { min-width: 0; }
/* Lo que pide un selector de fecha para mostrar dd/mm/aaaa y su ícono. */
.campo-barra input[type="date"] { width: 9.5rem; }

.barra-semana label {
  font-size: .78rem;
  display: block;
  margin-bottom: .2rem;
}

.barra-semana-pie {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .25rem .9rem;
  margin-top: .6rem;
  font-size: .8rem;
}

/* El campo de fecha: se ve como un input, pero abre el calendario de rango. */
.campo-fecha {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  padding: .6rem .75rem;
  background: var(--superficie-2);
  border: 1px solid var(--borde);
  border-radius: var(--radio-sm);
  color: var(--texto);
  font-size: 1rem;
  font-weight: 400;
  white-space: nowrap;
  cursor: pointer;
}
.campo-fecha:hover { background: var(--superficie-2); border-color: var(--acento); }
.campo-fecha svg { width: 16px; height: 16px; color: var(--texto-tenue); flex-shrink: 0; }
.campo-fecha:hover svg { color: var(--acento); }

/* ---- Calendario de rango ---- */

.cal-capa { position: fixed; inset: 0; z-index: 70; }

.calendario {
  position: fixed;
  width: 17.5rem;
  padding: .75rem;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: 0 14px 40px rgba(0, 0, 0, .55);
}

.cal-cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .5rem;
  font-size: .9rem;
  text-transform: capitalize;
}

.cal-nav {
  padding: .1rem .5rem;
  background: none;
  border: none;
  color: var(--texto-suave);
  font-size: 1.2rem;
  cursor: pointer;
}
.cal-nav:hover { background: var(--superficie-2); color: var(--texto); }

.cal-grilla { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }

.cal-dias {
  margin-bottom: .25rem;
  font-size: .7rem;
  color: var(--texto-tenue);
  text-align: center;
}

.cal-dia {
  padding: .35rem 0;
  background: none;
  border: none;
  border-radius: var(--radio-sm);
  color: var(--texto-suave);
  font-size: .82rem;
  cursor: pointer;
}
.cal-dia:hover { background: var(--superficie-2); color: var(--texto); }

/* El domingo se destaca porque es donde arranca y termina la semana. */
.cal-domingo { color: var(--texto); font-weight: 600; }

.cal-dentro { background: color-mix(in srgb, var(--acento) 16%, transparent); color: var(--texto); }
.cal-extremo {
  background: var(--acento) !important;
  color: #1a1206 !important;
  font-weight: 700;
}
.cal-hoy { outline: 1px solid var(--borde); }

.cal-pie {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-top: .6rem;
  padding-top: .6rem;
  border-top: 1px solid var(--borde);
  font-size: .78rem;
}
.cal-aplicar { padding: .3rem .8rem; font-size: .82rem; }

/* El período de la mensual son cuatro semanas: el texto es más largo que el de
   una sola, y el botón necesita lugar para "28 Jun - 19 Jul". */
.campo-periodo { min-width: 11.5rem; }

/* Botón que se lee como un enlace: es una opción secundaria, no una acción. */
.enlace {
  padding: 0;
  background: none;
  border: none;
  color: var(--texto-tenue);
  font-size: .8rem;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.enlace:hover { background: none; color: var(--acento); }

/* La semana no va de domingo a domingo: se avisa sin impedirlo. */
.aviso-rango { color: var(--naranja) !important; }

/*
 * Fila de chips del resumen. No usa `.fila` porque ahí los chips son items de
 * un flex y al envolver se estiran; acá van como texto en línea, que es lo que
 * son, y cada uno conserva su tamaño.
 */
.resumen-chips { margin-bottom: 1rem; line-height: 2; }
.resumen-chips .chip { margin-right: .3rem; }

/* ---- Tooltip propio ---- */

/*
 * Va en `position: fixed` sobre el body porque la tabla vive en un contenedor
 * con scroll horizontal, que recortaría cualquier globo posicionado adentro.
 * `pointer-events: none` evita que se robe el hover del botón que describe.
 */
.tooltip {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 80;
  max-width: 19rem;
  padding: .45rem .65rem;
  background: var(--superficie-2);
  border: 1px solid var(--borde);
  border-radius: var(--radio-sm);
  color: var(--texto);
  font-size: .8rem;
  line-height: 1.4;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .5);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .12s;
}
.tooltip.visible { opacity: 1; visibility: visible; }

/* ---- Modal ---- */

/*
 * Confirmación propia en vez del `confirm()` del navegador: el del navegador no
 * se puede redactar bien ni se parece al panel, y para algo destructivo conviene
 * que se lea con calma. Cancelar es siempre lo que pasa por defecto.
 */
.modal-fondo {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: rgba(8, 10, 14, .68);
}

/*
 * Los anchos alternativos van DESPUÉS de `.modal` a propósito: tienen la misma
 * especificidad, así que si se declararan antes, el max-width de acá las
 * pisaría y el modal ancho nunca se ensancharía.
 */
.modal {
  width: 100%;
  max-width: 27rem;
  padding: 1.35rem 1.45rem 1.45rem;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: 0 18px 48px rgba(0, 0, 0, .5);
}

/* El modal del JSON, con su textarea. */
.modal-ancho { max-width: 44rem; }
.modal-ancho textarea { min-height: 15rem; }

/* El historial lleva una tabla de 7 columnas y necesita más aire. */
.modal-tabla { max-width: 56rem; }

/* Botón de acción destructiva: el rojo es la señal de que no es reversible. */
.peligro {
  background: var(--rojo);
  color: #1a0d0d;
  font-weight: 600;
}
.peligro:hover { background: #fca5a5; }

/* ---- Estado en el listado de staff ---- */

/*
 * Activo y ex-staff con colores propios. Antes los dos eran el chip neutro, así
 * que había que leer la palabra para distinguirlos; el color lo resuelve de un
 * vistazo, que es para lo que sirve una columna de estado.
 */
.chip.estado-activo { background: rgba(74, 222, 128, .14); color: var(--verde); }
.chip.estado-baja { background: var(--superficie-2); color: var(--texto-tenue); }

/* El número de semanal lleva a esa semanal; el ícono avisa que es clickeable. */
.enlace-semanal {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  color: inherit;
}
.enlace-semanal svg { width: 12px; height: 12px; color: var(--texto-tenue); flex-shrink: 0; }
.enlace-semanal:hover { color: var(--acento); text-decoration: none; }
.enlace-semanal:hover svg { color: var(--acento); }

/*
 * La carrera de ascensos: un paso por línea, en orden. Compacta a propósito —
 * son varias filas dentro de una celda y no tienen que estirar la tabla.
 */
.escalera { display: inline-flex; flex-direction: column; gap: .25rem; }

.ascenso-paso {
  display: flex;
  align-items: center;
  gap: .4rem;
  white-space: nowrap;
}
.ascenso-paso .tenue { font-size: .72rem; }

/* Ícono en el encabezado, en lugar de un título que no entraría. */
.th-icono { display: inline-flex; vertical-align: middle; line-height: 0; }
.th-icono svg { width: 16px; height: 16px; color: var(--texto-tenue); }

/*
 * Acciones de fila. Todas apagadas hasta el hover, y ahí cada una toma el color
 * de lo que hace: azul mirar, naranja editar, gris salir, rojo borrar. El color
 * al pasar es la última señal antes del clic.
 */
.icono-accion {
  padding: .3rem;
  background: transparent;
  border: none;
  border-radius: var(--radio-sm);
  color: var(--texto-tenue);
  opacity: .7;
  cursor: pointer;
  line-height: 0;
  transition: color .12s, opacity .12s, background .12s, transform .12s;
}
.icono-accion svg { width: 17px; height: 17px; display: block; }
.icono-accion:hover { opacity: 1; transform: translateY(-1px); }

.icono-accion.info { color: #5b8cff; }
.icono-accion.info:hover { background: rgba(91, 140, 255, .16); }
.icono-accion:not(.info):not(.peligro):not(.alta):hover { background: var(--superficie-2); color: var(--acento); }
.icono-accion.alta { color: var(--verde); }
.icono-accion.alta:hover { background: rgba(74, 222, 128, .14); }
.icono-accion.baja:hover { background: var(--superficie-2); color: var(--texto); }
.icono-accion.peligro { color: var(--rojo); }
.icono-accion.peligro:hover { background: rgba(248, 113, 113, .16); }

/* ---- Editar una semanal ya publicada ---- */

/*
 * Banda de "estás editando, no creando". El panel es el mismo que el de generar
 * una semanal nueva, así que sin este aviso es fácil creerse en el flujo normal.
 */
.aviso-edicion {
  border-color: color-mix(in srgb, var(--acento) 55%, var(--borde));
  background: color-mix(in srgb, var(--acento) 10%, transparent);
  color: var(--texto);
}

/* Bloques del detalle de la cascada, dentro de su modal. */
.cascada-bloque { margin-bottom: .85rem; font-size: .9rem; }
.cascada-bloque:last-child { margin-bottom: 0; }
.cascada-bloque ul { margin: .3rem 0 0; padding-left: 1.15rem; color: var(--texto-suave); }
.cascada-bloque li { margin: .15rem 0; }

/* ---- Tarjetas de network en el dashboard ---- */

.tarjeta-network {
  display: block;
  padding: 1.15rem;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-top: 3px solid var(--color-network, var(--acento));
  border-radius: var(--radio);
  color: var(--texto);
  transition: transform .15s, border-color .15s;
}

.tarjeta-network:hover { transform: translateY(-2px); text-decoration: none; color: var(--texto); }

.tarjeta-network .cabecera { display: flex; align-items: center; gap: .65rem; margin-bottom: .9rem; }
.tarjeta-network .logo { width: 36px; height: 36px; border-radius: 8px; object-fit: cover; }
.tarjeta-network h3 { margin: 0; font-size: 1rem; }

.metricas { display: flex; gap: 1.25rem; flex-wrap: wrap; }
.metrica .valor { font-size: 1.5rem; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; }
.metrica .etiqueta { font-size: .75rem; color: var(--texto-tenue); text-transform: uppercase; letter-spacing: .03em; }

.variacion { font-size: .8rem; font-weight: 500; }
.variacion.sube { color: var(--verde); }
.variacion.baja { color: var(--rojo); }

/* ---- Placeholder de logo ---- */

.logo-placeholder {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 1.1rem;
  color: #fff;
  background: var(--color-network, var(--acento));
}

/* ---- Barra de proporción de categorías ---- */

.barra-dist {
  display: flex;
  height: 6px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--superficie-2);
  gap: 2px;   /* separa los segmentos: el color no queda pegado */
}

.barra-dist span { display: block; }
.barra-dist.vacia { opacity: .4; }

/* ---- Gráficos ---- */

canvas { max-width: 100%; }

/* ---- Banco de frases ---- */

.fila-frase {
  display: flex;
  gap: .75rem;
  align-items: flex-start;
  padding: .75rem;
  border-bottom: 1px solid var(--borde-suave);
}

.fila-frase:last-child { border-bottom: none; }
.fila-frase.inactiva { opacity: .5; }

.fila-frase .vista-previa {
  margin-top: .3rem;
  font-style: italic;
  color: var(--texto-tenue);
}

code {
  background: var(--superficie-2);
  border: 1px solid var(--borde);
  border-radius: 4px;
  padding: .1em .35em;
  font-family: var(--mono);
  font-size: .85em;
}

/* ---- Identidad de staff (Minecraft + Discord) ---- */

.staff-id { display: flex; flex-direction: column; gap: .15rem; }

.staff-linea {
  display: flex;
  align-items: center;
  gap: .4rem;
  font-size: .85rem;
  line-height: 1.35;
  min-width: 0;
}

.staff-linea > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* userID de Discord, en segundo plano bajo el nick */
.staff-userid {
  margin-left: calc(14px + .4rem);
  font-size: .72rem;
  color: var(--texto-tenue);
  line-height: 1.2;
}

.icono-plataforma {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  display: block;
}

/* ---- Tags de rango ---- */

.tag-rango {
  display: inline-block;
  padding: .15rem .55rem;
  border-radius: 999px;
  font-size: .75rem;
  font-weight: 600;
  white-space: nowrap;
  line-height: 1.4;
}

/* ---- Desglose de sanciones y tickets con íconos ---- */

.desglose-sanciones {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-top: .4rem;
  font-size: .8rem;
}

.desglose-sanciones .sancion {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  color: var(--texto-suave);
  font-variant-numeric: tabular-nums;
  cursor: default;
}

.desglose-sanciones .sancion.cero { color: var(--texto-tenue); opacity: .45; }
.desglose-sanciones .sep { color: var(--borde); }

.icono-sancion { width: 14px; height: 14px; flex-shrink: 0; display: block; }

/* ---- Selector de corazones (imágenes de Minecraft) ---- */

.selector-corazon { display: inline-flex; gap: .3rem; }

.corazon-opcion {
  padding: .2rem;
  background: transparent;
  border: none;
  border-radius: var(--radio-sm);
  cursor: pointer;
  line-height: 0;
  transition: transform .12s, background .12s;
}

.corazon-opcion img {
  width: 26px;
  height: 26px;
  display: block;
  /* El pixel-art no debe verse borroso al escalar. */
  image-rendering: pixelated;
  /* Los inactivos quedan "apagados": sin color y tenues. */
  filter: grayscale(1) opacity(.35);
  transition: filter .12s;
}

.corazon-opcion:hover { background: var(--superficie-2); transform: translateY(-1px); }
.corazon-opcion:hover img { filter: grayscale(.4) opacity(.7); }

/* El activo se "prende": color pleno. */
.corazon-opcion.activo img { filter: none; }

/*
 * Marcas de la columna Extras: calavera (inactividad), cruz (expulsión), mazo
 * (strike) y galones (ascenso). Mismo comportamiento que los corazones: en fila
 * y apagadas hasta que se las prende, pero dibujadas como iconos de trazo, que
 * es lo que se lee bien a 22px.
 *
 * El SVG va inline y hereda `color`, así que el estado se pinta con una sola
 * propiedad en vez de filtros.
 */
.selector-marcas { display: inline-flex; gap: .15rem; margin-bottom: .35rem; }

.marca-opcion {
  padding: .3rem;
  background: transparent;
  border: none;
  border-radius: var(--radio-sm);
  cursor: pointer;
  line-height: 0;
  color: var(--texto-tenue);
  opacity: .55;
  transition: color .12s, opacity .12s, background .12s, transform .12s;
}

.marca-opcion svg { width: 20px; height: 20px; display: block; }

/*
 * Hover: fondo teñido en el color que va a tomar el botón si se lo prende, no un
 * gris genérico. Así el hover adelanta el resultado en vez de solo señalar que
 * hay algo clickeable. Cada uno en SU color y no todos en rojo: el rojo es del
 * tacho, que es el único que borra.
 *
 * `button:hover` pintaría el fondo de acento, así que hay que ganarle.
 */
.marca-opcion:hover:not(:disabled) {
  background: var(--superficie-2);
  color: var(--texto);
  opacity: 1;
  transform: translateY(-1px);
}
.marca-opcion.inactividad:hover:not(:disabled) {
  background: rgba(230, 232, 238, .13);
  color: var(--texto);
}
.marca-opcion.expulsion:hover:not(:disabled),
.marca-opcion.strike:hover:not(:disabled) {
  background: rgba(248, 113, 113, .16);
  color: var(--rojo);
}
.marca-opcion.ascenso:hover:not(:disabled) {
  background: color-mix(in srgb, var(--acento) 16%, transparent);
  color: var(--acento);
}

.marca-opcion:disabled { opacity: .18; cursor: not-allowed; }

/* Prendida: cada una con su color, el mismo de su chip. */
.marca-opcion.activo { opacity: 1; }
.marca-opcion.inactividad.activo { color: var(--texto); }
.marca-opcion.expulsion.activo,
.marca-opcion.strike.activo { color: var(--rojo); }
.marca-opcion.ascenso.activo { color: var(--acento); }

/*
 * Estados que el sistema decidió solo y todavía se pueden confirmar o pisar:
 * el aviso de strike y el ascenso sugerido. Se insinúan sin gritar, para que se
 * distingan de lo que ya está aplicado.
 */
.marca-opcion.strike.avisado { color: var(--naranja); opacity: .85; }
.marca-opcion.ascenso.sugerido { color: var(--acento); opacity: .8; }

/* Corazón de solo lectura: el que le tocó, siempre a color. */
/*
 * `vertical-align: middle` no es decorativo: siendo un inline-block con
 * `line-height: 0`, por defecto se alinea a la baseline y apoya su borde
 * inferior sobre ella, lo que lo deja unos 2px por encima del centro de la fila.
 */
.corazon-fijo { display: inline-block; vertical-align: middle; line-height: 0; padding: .2rem; }
.corazon-fijo img { width: 26px; height: 26px; display: block; image-rendering: pixelated; }

/* ---- Encabezados ordenables ---- */

th.ordenable { cursor: pointer; user-select: none; white-space: nowrap; }
th.ordenable:hover { color: var(--texto); }

th.ordenable::after {
  content: '↕';
  margin-left: .3rem;
  opacity: .35;
  font-size: .85em;
}

th.ordenable.orden-activo { color: var(--acento); }
th.ordenable.orden-activo[data-dir="desc"]::after { content: '↓'; opacity: 1; }
th.ordenable.orden-activo[data-dir="asc"]::after { content: '↑'; opacity: 1; }

/* ---- Medallas de la mensual ---- */

/*
 * El podio no tiene escala de color propia como los corazones: son tres metales,
 * y el oro/plata/bronce ya son universalmente legibles. Se usan como color de
 * acento del chip y del borde de la fila, nunca como único portador del dato —
 * al lado siempre está el número de puesto, que es lo que se lee sin color.
 */
:root {
  --oro: #f3c53f;
  --plata: #d3dae2;
  --bronce: #cd8340;
}

.icono-medalla { width: 20px; height: 20px; flex-shrink: 0; display: block; }

.selector-medallas { display: inline-flex; gap: .2rem; }

.medalla-opcion {
  padding: .2rem;
  background: transparent;
  border: none;
  border-radius: var(--radio-sm);
  cursor: pointer;
  line-height: 0;
  /* Apagadas hasta que se las prende, igual que los corazones. */
  filter: grayscale(1) opacity(.32);
  transition: filter .12s, background .12s, transform .12s;
}

.medalla-opcion .icono-medalla { width: 26px; height: 26px; }

.medalla-opcion:hover { background: var(--superficie-2); transform: translateY(-1px); filter: grayscale(.4) opacity(.75); }
.medalla-opcion.activo { filter: none; }

/* Medalla de solo lectura: la que se llevó, siempre a color. */
.medalla-fija { display: inline-block; vertical-align: middle; line-height: 0; padding: .15rem; }
.medalla-fija .icono-medalla { width: 24px; height: 24px; }

.chip-medalla {
  gap: .35rem;
  font-weight: 600;
  background: var(--superficie-2);
  color: var(--texto);
}
.chip-medalla .icono-medalla { width: 15px; height: 15px; }
.chip-medalla.ORO { background: rgba(243, 197, 63, .14); color: var(--oro); }
.chip-medalla.PLATA { background: rgba(211, 218, 226, .13); color: var(--plata); }
.chip-medalla.BRONCE { background: rgba(205, 131, 64, .15); color: var(--bronce); }

/* La columna de puesto: el número manda, así que va en mono y algo más grande. */
.tabla-staff td.puesto { font-family: var(--mono); font-size: 1rem; color: var(--texto-suave); }
.fila-podio td.puesto { color: var(--texto); font-weight: 700; }

/*
 * Las tres filas del podio se marcan con una banda del color de su metal. Es
 * redundante con la medalla y con el puesto a propósito: la tabla se puede
 * reordenar por cualquier columna, y ahí el podio deja de estar arriba de todo.
 */
.fila-podio td:first-child { box-shadow: inset 3px 0 0 var(--borde); }
.fila-podio.ORO td:first-child { box-shadow: inset 3px 0 0 var(--oro); }
.fila-podio.PLATA td:first-child { box-shadow: inset 3px 0 0 var(--plata); }
.fila-podio.BRONCE td:first-child { box-shadow: inset 3px 0 0 var(--bronce); }

/* ══════════════════════════════════════════════════════════════════════
 * Esqueletos de carga
 *
 * El VPS esta en Dallas: cada ida y vuelta son ~180ms, y armar una pagina
 * encadena varios. En localhost eso era invisible; en produccion se ve como
 * un parpadeo donde la estructura no existe todavia.
 *
 * Regla: si el dato ya se sabe al escribir el HTML, va escrito. Si depende
 * del servidor, va un esqueleto CON LAS MEDIDAS FINALES. Un esqueleto que
 * mide distinto que su reemplazo produce el mismo salto que se queria
 * evitar, solo que con un paso intermedio.
 * ══════════════════════════════════════════════════════════════════════ */

.esq {
  display: block;
  background:
    linear-gradient(90deg,
      var(--superficie-2) 25%,
      color-mix(in srgb, var(--superficie-2) 55%, var(--borde)) 37%,
      var(--superficie-2) 63%);
  background-size: 400% 100%;
  border-radius: var(--radio-sm);
  animation: esq-brillo 1.4s ease-in-out infinite;
}

@keyframes esq-brillo {
  from { background-position: 100% 50%; }
  to   { background-position: 0 50%; }
}

/* Medidas calcadas de .side-logo y de la tipografia de .side-nombre. */
.esq-logo { width: 22px; height: 22px; border-radius: 5px; flex-shrink: 0; }
.esq-nombre { height: .8rem; width: 7.5rem; }
.esq-seccion-texto { height: .78rem; }
.esq-usuario { height: .82rem; width: 5.5rem; }

/* Filas de una lista que todavia no llego. */
.esq-lista { display: flex; flex-direction: column; gap: .5rem; }
.esq-fila { height: 4.6rem; }
.esq-fila-corta { height: 2.6rem; }

/* El esqueleto no es contenido: quien usa lector de pantalla no deberia oirlo. */
[data-esqueleto] { pointer-events: none; }

/* ══════════════════════════════════════════════════════════════════════
 * Transicion entre paginas
 *
 * El panel son paginas separadas, asi que cada click es una carga completa y
 * el navegador pinta blanco en el medio. Esto la funde en su lugar, y al
 * nombrar la barra lateral la mantiene fija en vez de reconstruirla: deja de
 * sentirse como que se reinicia todo.
 *
 * Donde no este soportado se ignora sin romper nada.
 * ══════════════════════════════════════════════════════════════════════ */

@view-transition { navigation: auto; }

.sidebar { view-transition-name: barra-lateral; }

@media (prefers-reduced-motion: reduce) {
  .esq { animation: none; }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* Celdas de esqueleto dentro de una tabla ya dibujada. La fila copia el alto de
   una real (dos lineas de texto en la columna de staff) para que reemplazarla no
   mueva nada. */
.esq-celda { height: .8rem; }
.esq-input { height: 2.1rem; width: 100%; border-radius: var(--radio-sm); }
.esq-fila-tabla td { vertical-align: middle; }
