/* ===========================================================================
   El panel de administración.
   ===========================================================================

   Los tokens son los de `apps/website/src/styles.css`, copiados y no importados:
   el panel se sirve desde otro origen y su CSP es `default-src 'self'`, así que
   no puede traerse una hoja de la web aunque quisiera. Copiados a mano, además,
   es un archivo que se lee entero sin salir de él.

   Lo que NO se copia es el trabajo de la portada. Esto es una herramienta
   interna: la misma paleta, la misma tipografía, las mismas aristas, y ni un
   gradiente de más. La prueba de que está bien no es que impresione, es que
   nadie que venga de hakunaaudio.com note que ha cambiado de empresa.

   Sin build. Es CSS plano con variables, porque un paso de compilación para
   trescientas líneas convierte un despliegue —que es copiar tres archivos— en
   un pipeline que puede fallar.
   =========================================================================== */

/* Sólo dos pesos, los dos que se usan. El subset latino, como en la web: el
   peso entero de Noto Sans trae devanagari y cirílico para un panel en
   castellano. */
@font-face {
  font-family: 'Noto Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/noto-sans-latin-400-normal.woff2') format('woff2');
}
@font-face {
  font-family: 'Noto Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/noto-sans-latin-600-normal.woff2') format('woff2');
}

:root {
  --void: #050607;
  --bg: #080a0c;
  --surface: #0d1013;
  --raised: #11151a;
  --titanium: #171b20;

  /* Blanco a baja opacidad, nunca gris opaco. Un borde opaco pesa lo mismo
     sobre el negro que sobre el grafito, así que o se ve tosco en uno o
     desaparece en el otro. Esto se apoya en lo que tiene debajo. */
  --line: rgb(255 255 255 / 0.09);
  --line-soft: rgb(255 255 255 / 0.05);
  --line-bright: rgb(255 255 255 / 0.16);

  --ink: #f3f4f4;
  --muted: #a7adb3;
  --dim: #848b93;
  /* Medido contra la superficie más clara en la que se pinta, no contra el
     fondo base: 4.57:1 en el peor caso. */
  --faint: #7d848c;

  /* El reflejo frío de la casa. Foco y selección, nada más — en cuanto rellena
     algo deja de leerse como luz. */
  --ice: #a9c2d6;

  --ok: #6ea86e;
  --warn: #e8b923;
  --error: #d0554a;

  --radius-sm: 4px;
  --radius-md: 7px;
  --radius-lg: 10px;

  --shadow-panel: 0 3px 14px rgb(0 0 0 / 0.46);
  --shadow-lift: 0 18px 50px rgb(0 0 0 / 0.55);

  /* Ninguna curva pasa de 1 en su recorrido: nada sobrepasa su destino y
     vuelve. Un mecanismo de precisión no rebota. */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur: 180ms;

  --mono: ui-monospace, 'Cascadia Mono', Consolas, monospace;
}

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

html {
  background: var(--bg);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: 'Noto Sans', ui-sans-serif, system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3 {
  font-weight: 600;
  letter-spacing: -0.022em;
  margin: 0;
}

:focus-visible {
  outline: 2px solid var(--ice);
  outline-offset: 3px;
  border-radius: 2px;
}

* {
  scrollbar-color: var(--line-bright) var(--bg);
  scrollbar-width: thin;
}

::selection {
  background: var(--ice);
  color: var(--void);
}

[hidden] {
  display: none !important;
}

/* ---------------------------------------------------------------------------
   Entrada
   --------------------------------------------------------------------------- */

.gate {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  /* Un halo muy tenue detrás de la tarjeta. Suficiente para que la tarjeta se
     lea como una pieza apoyada sobre algo, y no como un recorte. */
  background:
    radial-gradient(60rem 40rem at 50% -10%, rgb(169 194 214 / 0.05), transparent 60%),
    var(--void);
}

.gate-card {
  width: 100%;
  max-width: 23rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  padding: 2rem 1.75rem 1.75rem;
}

.gate-mark {
  width: 34px;
  height: 34px;
  display: block;
  margin-bottom: 1.25rem;
  opacity: 0.92;
}

.gate-card h1 {
  font-size: 1.0625rem;
}

.gate-sub {
  color: var(--faint);
  font-size: 0.8125rem;
  margin: 0.3rem 0 1.5rem;
}

/* ---------------------------------------------------------------------------
   Campos
   --------------------------------------------------------------------------- */

.field {
  margin-bottom: 0.875rem;
}

.field label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 0.4rem;
}

input[type='text'],
input[type='password'],
input[type='number'],
select {
  width: 100%;
  background: var(--void);
  border: 1px solid var(--line-bright);
  border-radius: var(--radius-md);
  color: var(--ink);
  font-family: inherit;
  font-size: 0.9375rem;
  padding: 0.625rem 0.75rem;
  transition:
    border-color var(--dur) var(--ease-out),
    background-color var(--dur) var(--ease-out);
}

input:hover,
select:hover {
  border-color: #3a4149;
}

input:focus,
select:focus {
  border-color: var(--ice);
  outline: none;
  background: #04070a;
}

/* El código son seis dígitos y se leen de un vistazo. Monoespaciado y
   espaciado: teclear seis cifras sin poder contarlas es cómo se falla dos
   veces seguidas y se gasta la mitad del límite de intentos. */
#code {
  font-family: var(--mono);
  letter-spacing: 0.42em;
  text-align: center;
  font-size: 1.125rem;
  padding-left: 0.42em;
}

/* ---------------------------------------------------------------------------
   Botones
   --------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  border-radius: var(--radius-md);
  padding: 0.7rem 1.25rem;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.01em;
  cursor: pointer;
  border: 1px solid transparent;
  transition:
    background-color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out),
    transform 120ms var(--ease-out);
}

.btn:active {
  transform: translateY(1px);
}

/* Plata sobre negro. El color de la casa es el material, no un tono. */
.btn-primary {
  background-image: linear-gradient(180deg, #f7f8f9, #d9dde1);
  color: #0a0b0c;
  border-color: #ffffff26;
  box-shadow: 0 1px 0 rgb(255 255 255 / 0.25) inset;
  width: 100%;
}

.btn-primary:hover:not(:disabled) {
  background-image: linear-gradient(180deg, #ffffff, #e6eaee);
}

.btn-ghost {
  background: var(--raised);
  border-color: var(--line-bright);
  color: var(--muted);
}

.btn-ghost:hover {
  color: var(--ink);
  border-color: #3a4149;
  background: var(--titanium);
}

/* Deshabilitado se ve deshabilitado. Un botón normal que no responde es peor
   que uno apagado: la primera reacción es volver a pulsarlo. */
.btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

.btn-sm {
  padding: 0.45rem 0.8rem;
  font-size: 0.8125rem;
}

/* ---------------------------------------------------------------------------
   Avisos
   --------------------------------------------------------------------------- */

.notice {
  border-radius: var(--radius-md);
  padding: 0.625rem 0.75rem;
  font-size: 0.8125rem;
  margin-bottom: 0.875rem;
  border: 1px solid;
}

.notice-error {
  background: rgb(208 85 74 / 0.09);
  border-color: rgb(208 85 74 / 0.32);
  color: #e8968d;
}

.notice-info {
  background: rgb(169 194 214 / 0.07);
  border-color: rgb(169 194 214 / 0.22);
  color: var(--ice);
}

/* ---------------------------------------------------------------------------
   Armazón
   --------------------------------------------------------------------------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0 1.5rem;
  height: 3.5rem;
  background: rgb(8 10 12 / 0.86);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
}

.brand img {
  width: 22px;
  height: 22px;
  opacity: 0.92;
}

.brand span {
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--muted);
}

/* El aviso de que esto no es la web pública. Pequeño, permanente, y en la
   esquina donde un producto de verdad pone su nombre. */
.brand em {
  font-style: normal;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  border: 1px solid var(--line-bright);
  border-radius: var(--radius-sm);
  padding: 0.1rem 0.35rem;
}

.nav {
  display: flex;
  gap: 0.25rem;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.nav::-webkit-scrollbar {
  display: none;
}

.nav a {
  position: relative;
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius-sm);
  color: var(--dim);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  transition: color 160ms var(--ease-out);
}

.nav a:hover {
  color: var(--ink);
}

.nav a[aria-current='page'] {
  color: var(--ink);
  background: var(--raised);
}

.who {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-shrink: 0;
  font-size: 0.8125rem;
  color: var(--faint);
}

main {
  max-width: 68rem;
  margin: 0 auto;
  padding: 2rem 1.5rem 5rem;
}

.view h2 {
  font-size: 1.375rem;
  margin-bottom: 0.35rem;
}

.view > p {
  color: var(--faint);
  font-size: 0.875rem;
  margin: 0 0 1.75rem;
}

/* ---------------------------------------------------------------------------
   Cifras
   --------------------------------------------------------------------------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.75rem;
}

.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
  box-shadow: var(--shadow-panel);
}

.stat dt {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.5rem;
}

.stat dd {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: -0.03em;
  /* Tabular: cuatro cifras en fila que no se muevan de sitio al actualizarse. */
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.stat small {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.75rem;
  color: var(--faint);
}

/* Una cifra que no es cero y significa "esto no está medido" no puede pintarse
   igual que las demás. */
.stat.is-warn {
  border-color: rgb(232 185 35 / 0.34);
}

.stat.is-warn dd {
  color: var(--warn);
}

/* ---------------------------------------------------------------------------
   Secciones aún por construir
   --------------------------------------------------------------------------- */

/* Un hueco que dice qué falta y en qué tarjeta vive. Una sección vacía que no
   se explica se lee como algo roto, y la reacción es recargar. */
.pending {
  background: var(--surface);
  border: 1px dashed var(--line-bright);
  border-radius: var(--radius-lg);
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--faint);
}

.pending strong {
  display: block;
  color: var(--muted);
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.pending code {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--dim);
}

.muted {
  color: var(--faint);
  font-size: 0.8125rem;
}

/* ---------------------------------------------------------------------------
   El nombre de la cuenta, arriba a la derecha
   --------------------------------------------------------------------------- */

/* Un botón y no un enlace con aspecto de texto: lleva a otra vista, así que
   tiene que parecer pulsable antes de que nadie pase el ratón por encima. */
.who-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  padding: 0.3rem 0.5rem;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--muted);
  cursor: pointer;
  transition:
    color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    background-color var(--dur) var(--ease-out);
}

.who-link:hover,
.who-link[aria-current='page'] {
  color: var(--ink);
  background: var(--raised);
  border-color: var(--line);
}

/* Las iniciales. Un avatar sin foto que sigue siendo reconocible de un vistazo,
   y que no necesita que nadie suba nada. */
.avatar {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--titanium);
  border: 1px solid var(--line-bright);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
}

/* ---------------------------------------------------------------------------
   Etiquetas de estado
   --------------------------------------------------------------------------- */

.tag {
  display: inline-block;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-bright);
  padding: 0.1rem 0.4rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}

.tag-ok {
  border-color: rgb(110 168 110 / 0.4);
  color: var(--ok);
}

.tag-warn {
  border-color: rgb(232 185 35 / 0.4);
  color: var(--warn);
}

.tag-error {
  border-color: rgb(208 85 74 / 0.4);
  color: #e8968d;
}

/* ---------------------------------------------------------------------------
   Paneles y formularios dentro de una vista
   --------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  box-shadow: var(--shadow-panel);
  margin-bottom: 1rem;
}

.card h3 {
  font-size: 1rem;
  margin-bottom: 0.3rem;
}

.card > p {
  color: var(--faint);
  font-size: 0.8125rem;
  margin: 0 0 1.25rem;
}

.card-narrow {
  max-width: 27rem;
}

.row {
  display: flex;
  gap: 0.6rem;
  align-items: flex-end;
  flex-wrap: wrap;
}

.row .field {
  margin-bottom: 0;
  flex: 1;
  min-width: 9rem;
}

.actions {
  display: flex;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

/* Un botón dentro de `.actions` se ajusta a su texto. `.btn-primary` es ancho
   completo porque en la pantalla de entrada es el único y debe serlo; aquí hay
   dos y competirían por la fila. */
.actions .btn {
  width: auto;
}

/* Estas cuatro existen porque la CSP prohíbe `style=""` en el marcado, y esa
   prohibición es lo que permite escribir `style-src 'self'` sin `unsafe-inline`.
   La primera versión llevaba estilos en línea y el navegador los bloqueó — el
   servidor de desarrollo manda la misma cabecera que Caddy justamente para que
   eso saliera aquí y no la primera vez que se abrió el panel en producción. */
.hint {
  margin: 0.4rem 0 0;
}

/* Con `.row .field` delante: dos clases ganan a una, asi que `.field-wide` sola
   perdia y el campo de busqueda salia del mismo ancho que los desplegables. */
.row .field-wide {
  flex: 2;
}

.keys-count {
  margin-top: 0.75rem;
}

.stacked {
  margin-top: 1rem;
}

/* Las cabeceras de columnas numéricas se alinean con sus celdas. */
th.num {
  text-align: right;
}

/* ---------------------------------------------------------------------------
   Tablas
   --------------------------------------------------------------------------- */

/* El contenedor desplaza, no la página. Una tabla ancha que empuja el body de
   lado se lee como un fallo de maquetación. */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
}

thead th {
  position: sticky;
  top: 3.5rem;
  background: var(--raised);
  text-align: left;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

tbody td {
  padding: 0.7rem 0.85rem;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr:hover {
  background: rgb(255 255 255 / 0.018);
}

/* Los importes se comparan en columna, así que alineados a la derecha y con
   cifras de ancho fijo. */
td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

td.key {
  font-family: var(--mono);
  font-size: 0.75rem;
  white-space: nowrap;
  color: var(--ink);
}

.empty {
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--faint);
  font-size: 0.875rem;
}

/* ---------------------------------------------------------------------------
   Detalle de una clave
   ---------------------------------------------------------------------------

   Se despliega debajo de su propia fila en vez de abrir otra pantalla. La
   pregunta que trae aquí a alguien —"¿por qué dice que no quedan asientos?"— se
   contesta mirando la fila y lo que hay debajo a la vez, y perder de vista la
   lista al abrirlo obliga a volver a buscar para mirar la siguiente.
   --------------------------------------------------------------------------- */

/* La fila abierta se marca, para que al mirar el detalle esté claro de cuál es. */
tbody tr.is-open > td {
  background: var(--raised);
  border-bottom-color: transparent;
}

tr.detail > td {
  background: var(--raised);
  padding: 0 0.85rem 1.1rem;
}

.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.9rem 1.5rem;
  padding: 0.9rem 0 1.1rem;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 1rem;
}

.detail-grid div {
  min-width: 0;
}

.detail-grid dt {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.25rem;
}

.detail-grid dd {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.machines {
  display: grid;
  gap: 0.5rem;
}

.machine {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  background: var(--void);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0.6rem 0.75rem;
}

/* El identificador es un hash y ocupa más de lo que aporta. Se recorta con
   `text-overflow` y no cortando la cadena: lo que se copia sigue siendo entero. */
.machine-id {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--muted);
  flex: 1;
  min-width: 8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.machine-when {
  font-size: 0.75rem;
  color: var(--faint);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Copiar
   --------------------------------------------------------------------------- */

/* Una clave de licencia se transcribe a mano fatal — es justo el caso que el
   alfabeto sin vocales ni 0/O intenta mitigar. Copiarla no debería requerir
   seleccionarla con el ratón sin pasarse. */
.copy {
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--faint);
  font: inherit;
  font-size: 0.6875rem;
  padding: 0.1rem 0.35rem;
  margin-left: 0.4rem;
  cursor: pointer;
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}

tr:hover .copy,
.copy:focus-visible {
  border-color: var(--line-bright);
  color: var(--muted);
}

.copy.is-done {
  color: var(--ok);
  border-color: rgb(110 168 110 / 0.4);
}

/* ---------------------------------------------------------------------------
   Credenciales de un solo uso
   ---------------------------------------------------------------------------

   Lo que sale al crear una cuenta. Se ve distinto del resto a propósito: es lo
   único en todo el panel que no se puede volver a consultar.
   --------------------------------------------------------------------------- */

.secret {
  background: var(--void);
  border: 1px solid rgb(232 185 35 / 0.3);
  border-radius: var(--radius-md);
  padding: 1rem 1.1rem;
  margin-top: 1rem;
}

.secret dl {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.45rem 1rem;
  align-items: baseline;
}

.secret dt {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
}

.secret dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink);
  word-break: break-all;
}

.secret .muted {
  margin-top: 0.9rem;
  display: block;
}

/* ---------------------------------------------------------------------------
   Pantallas estrechas
   ---------------------------------------------------------------------------

   La barra superior tiene tres bloques y sólo el del medio encoge. Con la marca
   escrita a la izquierda y el nombre de la cuenta a la derecha, en 375 px la
   suma pasaba del ancho de la pantalla y empujaba la página de lado — que es un
   fallo que en el móvil se nota antes que ninguna otra cosa.

   Se recortan los dos extremos, no el del medio: el isotipo sigue diciendo de
   quién es esto, la chapa "Panel" sigue diciendo que no es la web pública, y las
   iniciales siguen siendo un botón que lleva al perfil. Lo que desaparece es
   texto que ya está implícito.
   --------------------------------------------------------------------------- */

@media (max-width: 620px) {
  .topbar {
    gap: 0.75rem;
    padding: 0 0.85rem;
  }

  /* "Hakuna Audio" al lado de su propio isotipo. */
  .brand span {
    display: none;
  }

  /* El nombre, que las iniciales ya resumen. */
  .who-name {
    display: none;
  }

  .who {
    gap: 0.5rem;
  }

  main {
    padding: 1.5rem 0.85rem 4rem;
  }
}

/* ---------------------------------------------------------------------------
   Movimiento
   --------------------------------------------------------------------------- */

.fade-in {
  animation: fade-in 240ms var(--ease-out) both;
}

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
