/* Laboratorio de Codrex (labs.codrex.ai): puerta de acceso, buscador, filtros y tarjetas.

   Este archivo se basta solo. Los otros tableros de Codrex (client.codrex.ai y
   lon.codrex.ai) también llevan su hoja única, y el laboratorio de Land of Nomads
   cuelga del sistema de diseño de ese cliente. Aquí no hay sistema que heredar, así que
   los colores de la marca se declaran arriba y de ahí sale todo lo demás.

   La marca es la de Codrex, tomada tal cual del tablero de clientes: base oscura
   #14131c, texto #ecebf5 y el acento iridiscente en degradado. Solo hay piel oscura,
   a propósito: la marca de Codrex está definida sobre base oscura y no existe versión
   clara aprobada. Inventarla sería inventarse el brand, así que el laboratorio no lleva
   conmutador de tema.

   Reglas de la casa respetadas a propósito:
   - Cero bordes gruesos decorativos. Las tarjetas se separan por fondo y sombra.
   - Cero efectos al pasar el ratón por encima de las tarjetas.
   - La imagen ocupa el ANCHO COMPLETO de su tarjeta aunque se recorte.
   - El blob aparece UNA vez por página, como marca de cabecera. Nunca de relleno. */

* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  --bg:    #14131c;
  --txt:   #ecebf5;
  --dim:   #a7a4bd;
  --faint: #918eaa;
  --iris:  #9c9cfc;
  --grad:  linear-gradient(100deg, #ccccfc, #b4ccfc, #e4ccfc);
  /* Tinta oscura para el texto que va ENCIMA del degradado. Sobre el pastel, el texto
     claro desaparece; este da 12:1 de contraste. */
  --sobre-grad: #16151f;

  --card:  rgba(204, 204, 252, .045);
  --card2: rgba(204, 204, 252, .075);
  --line:  rgba(204, 204, 252, .10);
  /* La cabecera, el buscador y los filtros se quedan pegados y tienen que tapar lo que
     pasa por debajo. Es el fondo de la página con algo de transparencia, no un gris. */
  --velo:  rgba(20, 19, 28, .94);

  --r:      16px;
  --r-md:   10px;
  --r-pill: 999px;
  --sh:     0 6px 22px rgba(0, 0, 0, .34);

  --fuente: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;

  /* Margen lateral de TODO el laboratorio: cabecera, cuerpo y pie salen de aquí, así
     que nunca se descuadran entre sí.

     Va con `clamp`, que toma tres valores: un mínimo, uno que se adapta y un máximo. El
     del medio no es un porcentaje del ancho, porque un 14 % dejaría 55 px de margen en
     un móvil y ahogaría la tarjeta. Es la mitad de lo que sobra a partir de 1.000 px:
     hasta ese ancho el margen se queda en 16 px, y de ahí para arriba crece hasta los
     200 px. */
  --hub-pad: clamp(16px, (100vw - 1000px) / 4.6, 200px);
}

/* Todo desplazamiento que dispare la interfaz (el enlace de saltar al listado, los
   anclajes) va suave, nunca de un salto. Aquí no hay ninguna animación de scroll por
   código que se pueda autocancelar con esto. */
html { scroll-behavior: smooth; }

body {
  background: var(--bg);
  background-image: radial-gradient(720px 420px at 50% -80px, rgba(156, 156, 252, .14), transparent 70%);
  background-repeat: no-repeat;
  color: var(--txt);
  min-height: 100vh;
  font-family: var(--fuente);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }
img { max-width: 100%; }

/* Enlace para saltar el cabecero con el teclado. Solo se ve cuando recibe el foco. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--grad); color: var(--sobre-grad);
  font: 600 13px/1 var(--fuente);
  padding: 12px 16px; border-radius: 0 0 var(--r-md) 0;
  text-decoration: none;
}
.skip-link:focus { left: 0; }

/* ---- Cabecera ----
   Se queda pegada arriba durante todo el recorrido. El lateral sale de `--hub-pad`, el
   mismo que el cuerpo y el pie, para que las tres cosas estén siempre alineadas. */
.hub-top {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: 14px;
  padding: clamp(10px, 1.2vw, 16px) var(--hub-pad);
  background: var(--velo);
  backdrop-filter: blur(12px);
  box-shadow: 0 1px 0 var(--line);
}
/* El blob, en su único sitio de esta página. */
.hub-marca__blob { height: 30px; width: auto; display: block; }
.hub-top__sep { width: 1px; height: 20px; background: var(--line); flex: 0 0 auto; }
.hub-top__t { font: 600 14.5px/1 var(--fuente); color: var(--txt); }
.hub-top__acts { margin-left: auto; display: flex; gap: 8px; align-items: center; }

/* ---- Botón ---- */
.hub-b {
  display: inline-flex; align-items: center; gap: 8px;
  border: 0; cursor: pointer; text-decoration: none;
  font: 600 13px/1 var(--fuente);
  padding: 10px 14px; border-radius: var(--r-pill);
  background: var(--card2); color: var(--txt);
}
.hub-b:hover { background: var(--line); }
.hub-b:focus-visible { outline: 2px solid var(--iris); outline-offset: 2px; }
.hub-b svg { width: 16px; height: 16px; flex: 0 0 auto; }
.hub-b--acc { background: var(--grad); color: var(--sobre-grad); }

/* ---- Portada ---- */
.hub {
  max-width: 3000px;
  margin-inline: auto;
  padding: clamp(24px, 3.5vw, 50px) var(--hub-pad);
}
.hub-hero h1 {
  font: 700 clamp(28px, 4.2vw, 44px)/1.1 var(--fuente);
  letter-spacing: -0.02em;
  margin: 0 0 12px; max-width: 22ch;
}
/* El acento iridiscente entra por el texto, que es donde la marca lo usa. Se pinta el
   degradado y se recorta con las letras. */
.hub-hero h1 span {
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hub-hero__lead {
  font: 400 15.5px/1.7 var(--fuente); color: var(--dim);
  max-width: 68ch; margin: 0 0 26px;
}

/* ---- Buscador ----
   Cuelga de <main> y NO de la portada, a propósito. Un elemento pegajoso solo se queda
   pegado mientras su contenedor sigue en pantalla: dentro de la portada, que es corta,
   se iría con ella al bajar. */
.hub-tools {
  position: sticky; top: var(--hub-top-h, 58px); z-index: 32;
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  padding: 10px 0;
  margin-bottom: 10px;
  background: var(--velo);
  backdrop-filter: blur(12px);
}
.hub-q {
  flex: 1 1 340px; min-width: 0;
  font: 400 15px/1.4 var(--fuente); color: var(--txt);
  background: var(--card2); border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: 13px 20px;
}
.hub-q::placeholder { color: var(--faint); }
.hub-q:focus-visible { outline: 2px solid var(--iris); outline-offset: 2px; }
.hub-n { font: 600 12.5px/1 var(--fuente); color: var(--faint); flex: 0 0 auto; }

/* ---- Ejemplos de pregunta ----
   Están para enseñar, de un vistazo, que aquí se PREGUNTA. Un buscador vacío con un
   texto de ayuda genérico no le cuenta a nadie que puede escribir una frase entera. */
.hub-ejs { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; margin-bottom: 6px; }
.hub-ejs__t { font: 500 12.5px/1 var(--fuente); color: var(--faint); }
.hub-ej {
  border: 0; cursor: pointer;
  font: 500 12.5px/1 var(--fuente); color: var(--dim);
  background: var(--card2); border-radius: var(--r-pill); padding: 8px 13px;
}
.hub-ej:hover { background: var(--line); color: var(--txt); }
.hub-ej:focus-visible { outline: 2px solid var(--iris); outline-offset: 2px; }

/* ---- Respuesta directa ----
   Cuando una pieza gana con claridad, se dice cuál es y por qué, en vez de dejar que el
   usuario lo deduzca del orden de la rejilla. */
.hub-resp {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  background: var(--card2); border-radius: var(--r);
  padding: 15px 18px; margin: 12px 0 4px;
}
/* `display:flex` gana al atributo `hidden` del navegador, así que sin esta línea el
   cartel de respuesta se queda como una barra vacía cuando no hay nada que responder.
   Pasa con cualquier componente al que se le dé un display propio. */
.hub-resp[hidden] { display: none; }
.hub-resp__t { font: 600 15px/1.4 var(--fuente); color: var(--txt); margin: 0; }
.hub-resp__m { font: 400 13px/1.5 var(--fuente); color: var(--dim); margin: 3px 0 0; }
.hub-resp__ir {
  margin-left: auto; flex: 0 0 auto; text-decoration: none;
  font: 600 13px/1 var(--fuente);
  padding: 11px 16px; border-radius: var(--r-pill);
  background: var(--grad); color: var(--sobre-grad);
}
.hub-resp__ir:focus-visible { outline: 2px solid var(--iris); outline-offset: 2px; }

/* ---- Filtros por categoría: una sola fila, y lo que no cabe se recorre ---- */
.hub-filtros {
  /* Se pega DEBAJO del buscador, que también es pegajoso. El alto del buscador lo mide
     `hub.js` y lo deja en `--hub-tools-h`, porque cambia al pasar a móvil (el conteo
     baja de línea). Los 68 px son solo el valor de reserva hasta que mide. */
  position: sticky; top: calc(var(--hub-top-h, 58px) + var(--hub-tools-h, 68px)); z-index: 30;
  display: flex; gap: 8px;
  padding: 10px 0;
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
  background: var(--velo);
  backdrop-filter: blur(10px);
}
.hub-filtros::-webkit-scrollbar { display: none; }
.chip-f {
  flex: 0 0 auto; border: 0; cursor: pointer;
  font: 600 12.5px/1 var(--fuente); letter-spacing: .03em;
  padding: 11px 16px; border-radius: var(--r-pill);
  background: var(--card2); color: var(--txt);
}
.chip-f:hover { background: var(--line); }
.chip-f:focus-visible { outline: 2px solid var(--iris); outline-offset: 2px; }
.chip-f.on { background: var(--grad); color: var(--sobre-grad); }
.chip-f b { font-weight: 700; opacity: .72; margin-left: 3px; }

/* ---- Grupos y rejilla ----
   Cuatro columnas en una pantalla de portátil normal (1440 px de ancho) y cinco en un
   monitor grande. El mínimo de 300 px es lo que hace que a 1440 entren cuatro y no
   cinco apretadas. */
.hub-grupo { padding-top: 26px; }
/* El título de la categoría va en capitalización normal, como está escrito en el
   catálogo. Nada de versalitas por CSS: las etiquetas del sistema se leen todas igual,
   y aquí manda la forma que ya usa el tablero de clientes de Codrex. */
.hub-grupo__t {
  font: 650 19px/1.3 var(--fuente); letter-spacing: -0.01em;
  color: var(--txt);
  margin: 0 0 14px;
}
.hub-grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }

/* ---- Tarjeta de pieza ----
   Sin borde y sin efecto al pasar por encima: se distingue por superficie y sombra
   estática, que es la norma de la casa. */
.w {
  display: flex; flex-direction: column;
  background: var(--card); border-radius: var(--r);
  box-shadow: var(--sh); overflow: hidden;
}

/* La imagen ocupa el ancho completo de la tarjeta, recortada a 16:9. Nunca en columna
   estrecha con franjas de fondo a los lados.

   OJO con `min-height: 0`, que parece de adorno y NO lo es. La tarjeta es un contenedor
   flexible en columna, y ahí la altura mínima de cada hijo es, por defecto, la de su
   contenido. Como la foto declara sus medidas en los atributos, la caja crecería hasta
   esa altura y se comería la proporción. Con `min-height: 0` la caja obedece la
   proporción y la foto se recorta dentro. */
.w__img {
  position: relative;
  aspect-ratio: 16 / 9; width: 100%;
  min-height: 0; flex: 0 0 auto; overflow: hidden;
  background: var(--card2);
}
.w__img img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* La fecha va DESTACADA sobre la imagen, abajo a la izquierda, no escondida en letra
   gris al pie. Sobre el fondo de la página al 78 %, que es oscuro de verdad, así que la
   letra clara pasa contraste caiga sobre la foto que caiga. */
.w__fecha {
  position: absolute; left: 12px; bottom: 12px;
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 12px/1 var(--fuente); letter-spacing: .01em;
  padding: 8px 12px; border-radius: var(--r-pill);
  background: rgba(20, 19, 28, .78);
  color: var(--txt);
  backdrop-filter: blur(6px);
}
/* El icono va a 12 px, la misma altura que la letra. A 13 px la caja de la fecha
   quedaba un píxel más alta que la del autor y la pareja se veía desalineada. */
.w__fecha svg { width: 12px; height: 12px; flex: 0 0 auto; opacity: .85; }

/* Quien subió la pieza, en la esquina OPUESTA a la fecha: fecha abajo a la izquierda,
   autor abajo a la derecha, los dos a 12 px del borde y con el mismo alto y redondeo
   para que la pareja quede equilibrada. Va con el acento de la marca para que se lea
   como autoría y no como una etiqueta más. */
.w__autor {
  position: absolute; right: 12px; bottom: 12px;
  display: inline-flex; align-items: center;
  font: 600 12px/1 var(--fuente); letter-spacing: .01em;
  padding: 8px 12px; border-radius: var(--r-pill);
  background: var(--grad); color: var(--sobre-grad);
}

/* Distintivo de acceso, arriba a la derecha. En este laboratorio NO debería aparecer
   nunca: la regla de la casa dice que aquí no se enlaza nada externo, y solo se pinta
   cuando la ruta apunta a otro servidor. Se queda como red de seguridad para que, si
   alguna vez se cuela una ruta de fuera, se vea marcada en vez de pasar desapercibida. */
.w__acc {
  position: absolute; right: 12px; top: 12px;
  font: 600 11.5px/1 var(--fuente); letter-spacing: .01em;
  padding: 7px 11px; border-radius: var(--r-pill);
  backdrop-filter: blur(6px);
}
.w__acc--clave-propia { background: var(--grad); color: var(--sobre-grad); }
.w__acc--abierta { background: rgba(236, 235, 245, .92); color: var(--sobre-grad); }

.w__cuerpo { display: flex; flex-direction: column; gap: 7px; padding: 13px 15px; flex: 1 1 auto; }
.w__t { font: 600 16px/1.25 var(--fuente); margin: 0; }
.w__d { font: 400 13.5px/1.45 var(--fuente); color: var(--dim); margin: 0; }
.w__q { font: 400 12px/1.45 var(--fuente); color: var(--faint); margin: 0; }
/* Etiquetas. Texto a plena intensidad sobre un escalón neutro más claro que la tarjeta:
   se leen de un vistazo y siguen sin gritar más que el título. Sin tinte de color a
   propósito, para que el acento iridiscente siga significando algo. */
.w__tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 1px; }
.w__tag {
  font: 600 11.5px/1 var(--fuente); letter-spacing: .01em;
  color: var(--txt); background: var(--line);
  border-radius: var(--r-pill); padding: 6px 10px;
}
.w__foot { margin-top: auto; padding-top: 10px; }
.w__foot .hub-b { width: 100%; justify-content: center; }

.hub-vacio {
  font: 400 15px/1.6 var(--fuente); color: var(--dim);
  padding: 36px 0;
}

/* ---- Laboratorio sin piezas ----
   El día que no hay nada publicado, el tablero no puede verse roto ni en blanco: se
   pinta este panel, con la marca puesta, explicando que todavía no hay nada. */
.hub-nada {
  background: var(--card); border-radius: var(--r); box-shadow: var(--sh);
  padding: clamp(28px, 5vw, 44px);
  display: grid; gap: 12px; justify-items: start;
  margin-top: 10px;
}
.hub-nada h2 { font: 700 clamp(21px, 2.6vw, 27px)/1.25 var(--fuente); letter-spacing: -0.01em; margin: 0; }
.hub-nada p { font: 400 15px/1.65 var(--fuente); color: var(--dim); max-width: 62ch; margin: 0; }
/* Las categorías ya montadas se enseñan como pistas de dónde va a caer cada pieza. */
.hub-nada__cats { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 4px; }
.hub-nada__cat {
  font: 600 12.5px/1 var(--fuente);
  color: var(--txt); background: var(--line);
  border-radius: var(--r-pill); padding: 9px 14px;
}

/* ---- Pie ---- */
.hub-pie {
  margin-top: 56px;
  padding: 26px var(--hub-pad) 40px;
  display: grid; gap: 8px; justify-items: start;
  border-top: 1px solid var(--line);
}
.hub-pie__marca { font: 650 14px/1 var(--fuente); }
.hub-pie__marca b {
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-weight: 700;
}
.hub-pie p { font: 400 12.5px/1.6 var(--fuente); color: var(--faint); margin: 0; }

/* ---- Formulario de acceso ---- */
.acceso {
  min-height: 100vh; display: grid; place-items: center;
  padding: 48px 24px;
}
.acceso__caja {
  width: min(400px, 100%);
  background: var(--card); border-radius: var(--r);
  box-shadow: var(--sh); padding: 34px 30px 30px;
  display: grid; gap: 16px; text-align: center;
}
/* El blob, en su único sitio de esta página. */
.acceso__blob {
  width: 92px; height: auto;
  margin: 0 auto 4px; display: block;
  filter: drop-shadow(0 14px 32px rgba(156, 156, 252, .35));
}
.acceso__caja h1 { font: 650 21px/1.3 var(--fuente); letter-spacing: -0.01em; margin: 0; }
.acceso__caja h1 span {
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.acceso__caja > p { font: 400 14px/1.55 var(--fuente); color: var(--dim); margin: 0 0 4px; }
.acceso__campo { display: grid; gap: 6px; text-align: left; }
.acceso__campo label { font: 500 12.5px/1 var(--fuente); color: var(--dim); }
.acceso__caja input {
  width: 100%; font: 400 15px/1.4 var(--fuente); color: var(--txt);
  background: var(--card2); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 11px 13px;
}
.acceso__caja input:focus-visible { outline: 2px solid var(--iris); outline-offset: 1px; }
.acceso__caja button {
  border: 0; cursor: pointer; width: 100%;
  font: 600 15px/1 var(--fuente);
  background: var(--grad); color: var(--sobre-grad);
  border-radius: var(--r-md); padding: 13px 16px; margin-top: 4px;
}
.acceso__caja button:focus-visible { outline: 2px solid var(--iris); outline-offset: 2px; }
.acceso__mal {
  font: 500 13.5px/1.5 var(--fuente); color: #ffb4c8;
  background: rgba(255, 180, 200, .09);
  border-radius: 9px; padding: 9px 11px; margin: 0;
}

@media (max-width: 620px) {
  /* El rótulo se quita por falta de sitio, y con él su separador: una rayita suelta al
     lado del blob no separa nada. */
  .hub-top__t,
  .hub-top__sep { display: none; }
  .hub-grid { grid-template-columns: 1fr; }
  .hub-resp__ir { margin-left: 0; width: 100%; text-align: center; justify-content: center; }
}

/* ---------- ordenar por fecha ---------- */
/* Misma píldora que los filtros y mismo acento para lo elegido: se lee como familia suya.
   Lo que cambia es que NO se queda pegado arriba: dos barras pegajosas seguidas se comen
   media pantalla en un móvil. */
.hub-orden{display:flex;align-items:center;gap:7px;flex-wrap:wrap;padding:var(--sp-9,14px) 0 var(--sp-6,8px)}
.hub-orden__t{font-size:var(--fs-sm,.86rem);color:var(--text-dim,#777);margin-right:var(--sp-4,6px)}
.chip-o{flex:0 0 auto;padding:var(--sp-6,8px) var(--sp-12,16px);border-radius:var(--r-pill,999px);
  border:var(--hair-soft,1px solid rgba(0,0,0,.12));background:var(--surface,#fff);
  font-size:var(--fs-sm,.86rem);font-weight:var(--w-medio,500);color:var(--text-dim,#777);
  cursor:pointer;font-family:inherit;white-space:nowrap}
.chip-o:hover{color:var(--text,#111);background:var(--surface-alt,#f4f4f4)}
.chip-o.on{background:var(--lime,var(--accent,#C1FE1A));color:var(--on-accent,#1A1A1A);
  border-color:var(--lime,var(--accent,#C1FE1A));font-weight:var(--w-fuerte,600)}
.chip-o:focus-visible{outline:2px solid var(--lime,var(--accent,#C1FE1A));outline-offset:2px}
