/* El panel del middleware.
 *
 * La idea de la que sale todo: esto es un cuadro de conmutación. Entra una
 * petición, se mira el nombre y se enchufa con el sitio que le toca. De ahí la
 * regla que gobierna la tipografía: **la máquina habla en monoespaciada**
 * —dominios, puertos, estados, cifras, registro— y **las personas y Claude
 * hablan en sans**. No es decoración: es la manera de saber de un vistazo si lo
 * que estás leyendo es un dato de la máquina o algo que alguien ha escrito.
 */

@font-face {
  font-family: 'Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/fonts/plex-mono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Plex Mono'; font-style: normal; font-weight: 500; font-display: swap;
  src: url('/fonts/plex-mono-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Plex Mono'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('/fonts/plex-mono-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Instrument'; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('/fonts/instrument.woff2') format('woff2');
}
/* Solo para la terminal: 'Plex Mono' —el monoespaciada de todo lo demás— se
   ve apretada a un tamaño en el que hay que leer columnas de `ls -l` o el
   pie de `htop`. Ubuntu Mono es más ancha a propósito, que es lo que la hace
   cómoda ahí y no en una tabla de datos del panel. */
@font-face {
  font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/fonts/ubuntu-mono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 700; font-display: swap;
  src: url('/fonts/ubuntu-mono-700.woff2') format('woff2');
}

:root {
  color-scheme: light dark;
  --canvas: #f1f3f1; --surface: #ffffff; --sunk: #e9ecea; --raised: #ffffff;
  --ink: #141817; --muted: #666d6b; --faint: #949b98;
  --line: #d9dedb; --line-soft: #e7eae8;
  --live: #1b7a4f; --warn: #9c6410; --down: #a4332b; --link: #275e86;
  --live-soft: #e4efe8; --down-soft: #f6e6e4;
  --mono: 'Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: 'Instrument', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --shadow: 0 1px 2px rgba(20,24,23,.04), 0 10px 28px -18px rgba(20,24,23,.35);
  --r: 10px;
  --tira: 2.25rem;
}
@media (prefers-color-scheme: dark) {
  :root {
    --canvas: #0e100f; --surface: #171a19; --sunk: #121514; --raised: #1c201e;
    --ink: #e6eae7; --muted: #8b928f; --faint: #616a66;
    --line: #262a28; --line-soft: #1f2321;
    --live: #4cb37b; --warn: #c89544; --down: #d9736a; --link: #7fb3d9;
    --live-soft: #14251c; --down-soft: #2a1a18;
    --shadow: 0 1px 2px rgba(0,0,0,.4), 0 10px 28px -18px rgba(0,0,0,.8);
  }
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
/* `dvh` y no solo `100%`: en el navegador de un móvil la barra de direcciones
   aparece y desaparece, y `100%` se queda con la altura de antes. El `100%` es
   el repuesto para quien no entienda `dvh`. */
html, body { height: 100%; height: 100dvh; }
/* El fondo va en `html` y no solo en `body`, y ahí está la tira negra de abajo.
   Instalada como aplicación, la página se queda dentro de la zona segura y las
   franjas que sobran —la del reloj arriba, la del indicador de inicio abajo—
   las pinta el navegador con el color del lienzo, que es el de `html`. Sin
   esto era el negro por defecto, y se veía una tira debajo de la barra de
   escribir. Con `--surface` es exactamente el color de las dos barras que
   tocan esas franjas, la de arriba y la de escribir, así que desaparecen. */
html { background: var(--surface); }
body {
  margin: 0; background: var(--canvas); color: var(--ink);
  font: 400 15px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;              /* el scroll vive dentro, nunca en el body */
}
button, input, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; }
/* Un enlace con pinta de botón es un botón: mismo `inline-flex` que los demás,
   o su texto se queda pegado arriba dentro de una caja de la misma altura y en
   una fila de cinco se ve el escalón. */
a.btn { text-decoration: none; }
:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; border-radius: 4px; }

/* ---------- piezas comunes ---------- */

.mono { font-family: var(--mono); }
.label {
  font: 500 10.5px/1.4 var(--mono); text-transform: uppercase; letter-spacing: .09em;
  color: var(--faint);
}
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* Un botón, una forma. `inline-flex` con el texto centrado y una altura mínima
   en vez de solo relleno vertical: así el que lleva un icono, el que lleva dos
   palabras y el que está dentro de una fila apretada miden lo mismo y se
   alinean por su centro. Antes cada uno acababa de un alto según lo que
   llevara dentro, y en una fila se veía el escalón. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .375rem;
  border: 1px solid var(--line); background: var(--surface); border-radius: 8px;
  padding: .375rem .75rem; min-height: 2rem; font-size: .8125rem; line-height: 1.2;
  color: var(--muted); white-space: nowrap;
  transition: color .12s, border-color .12s, background .12s;
}
.btn:hover { color: var(--ink); border-color: var(--faint); }
.btn.solid { background: var(--ink); color: var(--canvas); border-color: var(--ink); font-weight: 500; }
.btn.solid:hover { opacity: .88; color: var(--canvas); }
.btn.solid:disabled { opacity: .35; cursor: not-allowed; }
.btn.quiet { border-color: transparent; background: none; }
.btn.quiet:hover { background: var(--sunk); }
.btn:disabled { opacity: .75; cursor: default; }
/* Un botón que acaba de fallar lo dice él mismo, donde estabas mirando. */
.btn.bad { color: var(--down); border-color: color-mix(in srgb, var(--down) 40%, var(--line)); }

input[type=password], input[type=text], input[type=search], select, textarea {
  width: 100%; padding: .5rem .6875rem; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  /* 16px clavados: por debajo, el navegador del móvil hace zoom al enfocar y
     deja la página torcida. Ver también la regla de más abajo, que es la que
     de verdad lo garantiza. */
  font-size: 16px;
}
textarea { font-family: var(--sans); resize: none; }

/* ---------- entrar ---------- */

#login { height: 100dvh; display: grid; place-items: center; padding: 1.5rem; }
#login form {
  width: min(21rem, 100%); background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 1.75rem; box-shadow: var(--shadow);
}
#login .mark { display: flex; align-items: center; gap: .5rem; margin-bottom: 1.25rem; }
#login h1 { margin: 0; font: 500 1rem/1 var(--mono); letter-spacing: -.02em; }
#login p { margin: .375rem 0 1.25rem; color: var(--muted); font-size: .875rem; }
#login .btn { width: 100%; margin-top: .75rem; }
.error { color: var(--down); font-size: .8125rem; margin-top: .625rem; min-height: 1.2em; }
/* La salida para quien ha llegado aquí sin saber qué es esto. Discreta: quien
   viene a entrar viene a escribir la contraseña, no a leer. */
#login .que-es {
  display: inline-block; margin-top: .25rem; color: var(--muted);
  font: 400 .8125rem/1.4 var(--sans); text-decoration: none;
}
#login .que-es:hover { color: var(--ink); }

/* ---------- armazón ---------- */

/* `minmax(0, 1fr)` y no `1fr`: la columna implícita de una rejilla se mide por
   el contenido, así que la barra de arriba estiraba el armazón hasta su ancho
   mínimo —326 px en una pantalla de 320— y el menú se quedaba fuera, sin poder
   llegar a él porque aquí no hay desplazamiento horizontal. Con esto la barra
   no puede ser más ancha que la pantalla y son sus piezas las que ceden. */
.shell { height: 100dvh; display: grid;
         grid-template-rows: auto 1fr; grid-template-columns: minmax(0, 1fr); }

.top {
  display: flex; align-items: center; gap: .875rem; flex-wrap: wrap;
  padding: .625rem 1rem; border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.top .name { font: 500 .9375rem/1 var(--mono); letter-spacing: -.02em; }
.top .ver { font: 400 .6875rem/1 var(--mono); color: var(--faint); }
/* Los medidores ceden ancho antes que el menú: `min-width: 0` es lo que deja
   que se encojan en vez de empujar el botón fuera de la pantalla. */
.gauges { display: flex; align-items: center; gap: .875rem; margin-left: auto;
          flex-wrap: wrap; min-width: 0; }
.gauge { display: flex; align-items: center; gap: .375rem; font: 400 .75rem/1 var(--mono); color: var(--muted); }
.gauge b { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }

/* El veredicto: lo primero que se lee al abrir el panel. */
.verdict {
  white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  display: flex; align-items: center; gap: .4375rem;
  font: 500 .8125rem/1 var(--mono); padding: .3125rem .5625rem; border-radius: 999px;
  border: 1px solid var(--line);
}
.verdict.live { color: var(--live); background: var(--live-soft);
                border-color: color-mix(in srgb, var(--live) 25%, var(--line)); }
.verdict.warn { color: var(--warn); background: color-mix(in srgb, var(--warn) 10%, var(--surface));
                border-color: color-mix(in srgb, var(--warn) 30%, var(--line)); }
.verdict.down { color: var(--down); background: var(--down-soft);
                border-color: color-mix(in srgb, var(--down) 30%, var(--line)); }

/* Las tres vistas —el cuadro, un sitio y Cloudflare— viven en la misma celda
   de la rejilla y se turnan. Sin decirlo, la tercera caía en una fila implícita
   de alto cero y se veía la página en blanco. */
.view { min-height: 0; overflow: hidden; grid-row: 2; grid-column: 1; }

/* ---------- el cuadro (rejilla de sitios) ---------- */

.board { height: 100%; overflow-y: auto; padding: 1rem; }
.board-in { max-width: 1500px; margin: 0 auto; }
/* El cuadro deja de ser una lista y pasa a ser familias: cada dominio con lo
 * suyo debajo, y dentro los grupos que el manifiesto quiera. Con veinte
 * portales de Mask entre medias, la rejilla plana ya no decía nada. */
.grid { display: block; }
.grid-in { display: grid; gap: .875rem;
           grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr)); }

.fam { margin-bottom: 1.75rem; }
.fam-head { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .625rem;
            padding-bottom: .375rem; border-bottom: 1px solid var(--line); }
.fam-head .label { color: var(--ink); }
.fam-num { font: 400 .75rem/1 var(--mono); color: var(--faint); }

.sub { margin-top: .875rem; border: 1px solid var(--line); border-radius: var(--r);
       background: color-mix(in srgb, var(--sunk) 55%, transparent); padding: .625rem; }
.sub-head { display: flex; align-items: center; gap: .5rem; width: 100%; background: none;
            border: 0; padding: .125rem .125rem .5rem; cursor: pointer; color: inherit;
            font: inherit; text-align: left; }
.sub-head:hover .label { color: var(--ink); }
.sub-head .caret { flex: none; transform: rotate(-90deg); transition: transform .14s;
                   color: var(--faint); }
.sub-head .caret.open { transform: none; }

.tile {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; box-shadow: var(--shadow); display: flex; flex-direction: column;
  transition: border-color .14s, transform .14s;
}
.tile:hover { border-color: var(--faint); transform: translateY(-1px); }
.tile.down:hover { border-color: color-mix(in srgb, var(--down) 55%, var(--line)); }
.shot { position: relative; aspect-ratio: 16/10; background: var(--sunk); cursor: pointer;
        border-bottom: 1px solid var(--line); overflow: hidden; }
.shot img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.shot .none { position: absolute; inset: 0; display: grid; place-items: center;
              font: 400 .75rem/1.4 var(--mono); color: var(--faint); text-align: center; padding: 1rem; }
/* Va debajo de la imagen: solo se ve si la imagen no llegó y se quitó sola. */
.shot .none.under { z-index: -1; }

/* Volver a capturar, en la propia miniatura. Aparece al acercarse, porque es
   algo que se hace de vez en cuando y no tiene que ocupar sitio siempre. */
.reshoot {
  position: absolute; right: .5rem; bottom: .5rem; opacity: 0;
  border: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(6px); border-radius: 7px; padding: .25rem .5rem;
  font: 400 .6875rem/1 var(--sans); color: var(--muted); transition: opacity .14s;
}
.tile:hover .reshoot, .reshoot:focus-visible { opacity: 1; }
.reshoot { display: inline-flex; align-items: center; gap: .3125rem; }
.reshoot:hover { color: var(--ink); border-color: var(--faint); }
@media (hover: none) { .reshoot { opacity: 1; } }
.shot.off img { filter: grayscale(1) contrast(.85); opacity: .4; }
.shot.off { background: var(--down-soft); }
.shot.off .none { color: var(--down); }
.flags { position: absolute; top: .5rem; left: .5rem; display: flex; gap: .3125rem; flex-wrap: wrap;
         max-width: calc(100% - 1rem); }
.badge {
  font: 500 .625rem/1 var(--mono); text-transform: uppercase; letter-spacing: .08em;
  padding: .25rem .4375rem; border-radius: 5px; background: var(--down); color: #fff;
}
.badge.warm { background: var(--warn); }
.badge.mute { background: color-mix(in srgb, var(--ink) 55%, transparent); }

/* Un sitio en espera no está roto: se ve, atenuado y en ámbar. */
.shot.held img { filter: saturate(.35); opacity: .5; }
.shot.held { background: color-mix(in srgb, var(--warn) 12%, var(--sunk)); }

/* El borde de la tarjeta dice de qué va sin tener que leer nada. */
.tile.down { border-color: color-mix(in srgb, var(--down) 35%, var(--line)); }
.tile.warn { border-color: color-mix(in srgb, var(--warn) 32%, var(--line)); }

.tile-body { padding: .75rem .875rem .875rem; display: flex; flex-direction: column; gap: .5rem; flex: 1; }
.tile-name { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.tile-name .host { font: 500 .875rem/1.3 var(--mono); letter-spacing: -.01em;
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-meta { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap;
             font: 400 .75rem/1.4 var(--mono); color: var(--muted); }
.tile-meta .dim { color: var(--faint); }
.tile-acts { display: flex; align-items: center; flex-wrap: wrap; gap: .375rem; margin-top: auto; }
/* La acción que devuelve el sitio a la vida se distingue de las de mirar. */
.btn.go { color: var(--live); border-color: color-mix(in srgb, var(--live) 35%, var(--line)); }
.btn.go:hover:not(:disabled) { background: var(--live); color: var(--canvas); border-color: var(--live); }
/* Cinco botones en una tarjeta de 300 px: sin apretarlos un poco, «Registro»
   se cae a una segunda fila él solo y la tarjeta queda coja. */
.tile-acts .btn { padding: .25rem .5rem; min-height: 1.75rem; font-size: .71875rem; }
/* De las tres, esta es la que se usa. Se nota sin gritar. */
.tile-acts .talk { color: var(--live); border-color: color-mix(in srgb, var(--live) 30%, var(--line)); }
.tile-acts .talk:hover { color: var(--live); border-color: var(--live); background: var(--live-soft); }

/* El pulso: latencia de las últimas comprobaciones, como la cinta de un
   instrumento. Verde lo que respondió, rojo lo que no. */
.pulse { display: block; width: 100%; height: 18px; background:
         linear-gradient(to top, var(--line-soft) 1px, transparent 1px); }

.dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.dot.live { background: var(--live); box-shadow: 0 0 0 3px var(--live-soft); }
.dot.warn { background: var(--warn); box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 16%, transparent); }
.dot.down { background: var(--down); box-shadow: 0 0 0 3px var(--down-soft); }
.dot.idle { background: var(--faint); }

.hollow { text-align: center; padding: 5rem 1rem; color: var(--muted); }
.hollow p { margin: .25rem 0; }
.hollow code { font-family: var(--mono); background: var(--sunk); padding: .125rem .375rem; border-radius: 5px; }

/* ---------- la vista de un sitio ---------- */

/* minmax(0, 1fr) y no 1fr a secas: una pista de rejilla tiene min-width auto,
   así que crece hasta lo que mida su contenido. Con un comando largo dentro del
   chat, eso es una conversación más ancha que la pantalla y un scroll
   horizontal en todo el panel. Lo mismo vale para .detail y .chat. */
.site { height: 100%; display: grid; grid-template-columns: minmax(0, 1fr); min-height: 0; }

.detail { display: grid; grid-template-rows: auto auto 1fr;
          grid-template-columns: minmax(0, 1fr); min-height: 0; }
/* En la vista de un sitio el alto lo pone .site; cuando el detalle cuelga
   directamente de una vista —Cloudflare— hay que dárselo aquí, o el grid de
   dentro se queda sin sitio y la pantalla sale en blanco. */
.view > .detail { height: 100%; }
.dhead { display: flex; align-items: center; gap: .5rem .75rem;
         padding: .875rem 1.25rem .625rem; flex-wrap: wrap; }
/* Las acciones del sitio van juntas y a la derecha del todo. */
.dacts { display: flex; gap: .375rem; margin-left: auto; }
.dhead .host { font: 500 1.0625rem/1.2 var(--mono); letter-spacing: -.02em;
                overflow-wrap: anywhere; min-width: 0; }
.dhead .state { font: 400 .75rem/1 var(--mono); color: var(--muted); }
.grow { flex: 1; }
/* El hueco de la barra: no empuja nada por sí solo —en ancho el empujón lo dan
   los medidores con su margen automático— y se activa donde hace falta. */
.top .hueco { flex: none; }

.tabs { display: flex; gap: .125rem; padding: 0 1.25rem; border-bottom: 1px solid var(--line); }
.tabs button {
  border: 0; background: none; padding: .5rem .75rem; font: 400 .8125rem/1 var(--sans);
  color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs button:hover { color: var(--ink); }
.tabs button.on { color: var(--ink); border-bottom-color: var(--ink); font-weight: 500; }

.pane { min-height: 0; min-width: 0; overflow: hidden; display: none; }
.pane.on { display: flex; flex-direction: column; }
.scroll { min-height: 0; overflow-y: auto; }

/* ---------- registro ---------- */
.log { flex: 1; min-height: 0; overflow-y: auto; margin: 0; padding: .875rem 1.25rem;
       background: var(--sunk); font: 400 12.5px/1.65 var(--mono); }
.log .ln { display: flex; gap: .75rem; white-space: pre-wrap; word-break: break-word; }
.log .ln time { color: var(--faint); flex: none; }
.log .ln.bad { color: var(--down); }

/* ---------- detalles ---------- */
.facts { padding: 1rem 1.25rem; display: grid; gap: .875rem 1.5rem;
         grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); }
.fact span { display: block; font: 500 10.5px/1.4 var(--mono); text-transform: uppercase;
             letter-spacing: .09em; color: var(--faint); margin-bottom: .1875rem; }
.fact b { font: 500 .875rem/1.4 var(--mono); font-variant-numeric: tabular-nums; word-break: break-word; }
.fact b.prose { font-family: var(--sans); font-weight: 400; }
.rowacts { padding: .75rem 1.25rem; border-top: 1px solid var(--line);
           display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.steps { padding: 0 1.25rem 1rem; font: 400 .75rem/1.6 var(--mono); color: var(--muted); }
.steps div::before { content: "· "; color: var(--faint); }

/* ---------- el selector de sitio ---------- */
/*
 * Uno propio y no un <select>: el nativo no deja pintar el estado de cada sitio,
 * y el estado es justo lo que hace falta ver para decidir a cuál saltar. Ocupa
 * el sitio del título, que es donde ya estabas mirando.
 */
.picker { position: relative; }
.picker-btn {
  display: flex; align-items: center; gap: .5rem; border: 1px solid transparent;
  background: none; border-radius: 8px; padding: .25rem .5rem .25rem .375rem; margin-left: -.375rem;
}
.picker-btn:hover { border-color: var(--line); background: var(--sunk); }
.picker-btn .host { font: 500 1.0625rem/1.2 var(--mono); letter-spacing: -.02em;
                    overflow-wrap: anywhere; }
.picker-btn .caret { color: var(--faint); flex: none; }
.picker[data-open="1"] .picker-btn { border-color: var(--line); background: var(--sunk); }
.picker[data-open="1"] .caret { transform: rotate(180deg); }

.picker-pop {
  position: absolute; top: calc(100% + .375rem); left: 0; z-index: 30;
  width: min(24rem, calc(100vw - 2rem));
  background: var(--surface); border: 1px solid var(--line); border-radius: 11px;
  box-shadow: var(--shadow); overflow: hidden;
}
.picker-pop input {
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  padding: .625rem .75rem; font-size: .875rem; background: none;
}
.picker-pop input:focus { outline: 0; border-bottom-color: var(--faint); }
.picker-list { max-height: min(24rem, 60vh); overflow-y: auto; padding: .25rem; }
.pick {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: .625rem;
  align-items: center; width: 100%; border: 0; background: none; border-radius: 8px;
  padding: .4375rem .5rem; text-align: left;
}
/* Dentro de una fila que se puede deslizar, el botón necesita un fondo
   opaco propio: sin él, el rojo de «Eliminar» se transparentaría por debajo
   incluso cerrado. Va antes que el de abajo a propósito, para que seguir
   encima o eligiéndolo lo pise igual que siempre. */
.pick-row .pick { position: relative; z-index: 1; background: var(--surface); }
.pick:hover, .pick.cursor { background: var(--sunk); }
.pick[aria-selected="true"] { background: var(--live-soft); }
.pick .host { font: 500 .8125rem/1.35 var(--mono);
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick .side { font: 400 .6875rem/1.3 var(--mono); color: var(--faint); white-space: nowrap; }
.pick .side.warm { color: var(--warn); }
.pick .side.bad { color: var(--down); }
.picker-none { padding: .875rem .75rem; font-size: .8125rem; color: var(--muted); }

/* ---------- bloques dentro de detalles ---------- */
.block { padding: 0 1.25rem 1.25rem; }
.block-head { display: flex; align-items: center; gap: .75rem; padding: .875rem 0 .5rem;
              border-top: 1px solid var(--line); }
.block-head select { width: auto; padding: .3125rem .5rem; font-size: .8125rem; }
.note { margin: 0 0 .75rem; font-size: .8125rem; color: var(--muted); }

/* Las dos listas de direcciones, una al lado de la otra mientras quepan: son
   lo mismo leído en dos sentidos y se escriben mirándolas juntas. */
.dos-listas { display: grid; gap: .75rem; margin-bottom: .75rem;
              grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); }
.dos-listas label { display: flex; flex-direction: column; gap: .375rem; min-width: 0;
                    font: 400 .75rem/1.4 var(--sans); color: var(--muted); }
.dos-listas textarea { width: 100%; resize: vertical; font: 400 .8125rem/1.6 var(--mono); }

/* El registro de arreglos: cuándo se activó, por qué y qué hizo. */
.fix { border: 1px solid var(--line); border-radius: 8px; padding: .625rem .75rem;
       margin-bottom: .5rem; background: var(--surface); }
.fix-top { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
           font: 400 .75rem/1.5 var(--mono); color: var(--muted); }
.fix-top .when { color: var(--faint); }
.fix-top .why { color: var(--ink); font-weight: 500; }
.fix-out { font: 500 .6875rem/1 var(--mono); text-transform: uppercase; letter-spacing: .07em;
           padding: .1875rem .375rem; border-radius: 4px; }
.fix-out.ok { background: var(--live-soft); color: var(--live); }
.fix-out.bad { background: var(--down-soft); color: var(--down); }
.fix-out.wait { background: var(--sunk); color: var(--muted); }
.fix-what { margin: .5rem 0 0; font-size: .875rem; line-height: 1.55; overflow-wrap: anywhere; }
.fix-why { margin: .25rem 0 0; font: 400 .75rem/1.5 var(--mono); color: var(--faint);
           overflow-wrap: anywhere; }

/* Retirar un sitio: se separa de lo demás porque de aquí no se vuelve. */
/* La casa: cuando lo que se mira no es un sitio sino el propio middleware. Los
   bloques de la pestaña de detalles hablan todos de un sitio —su encendido, su
   cajón, su cartel, su retirada— y ninguno tiene sentido aquí; se quedan los
   hechos y el relevo. */
.pick-sep { height: 1px; background: var(--line); margin: .25rem .5rem; }
#casa-block { display: none; }
body.casa #pane-info > .scroll > *:not(#d-facts):not(#casa-block) { display: none; }
body.casa #casa-block { display: block; }
/* La casa sí tiene pestaña de acceso —el panel se protege como cualquier
   sitio—, pero su primer bloque es otro: la contraseña del panel no se pone,
   ya está puesta, y se cambia en Ajustes. */
#casa-access-block, .casa-only { display: none; }
body.casa #d-access-block { display: none; }
body.casa #casa-access-block { display: block; }
body.casa .casa-only { display: block; }

/* ---------- los módulos ---------- */
/*
 * Una tarjeta por módulo, con lo que hace, lo que se pierde si no está y sus
 * credenciales dentro. El formulario va plegado: lo normal es entrar aquí a
 * mirar qué hay encendido, no a escribir un token.
 */
.modulos { display: flex; flex-direction: column; gap: .5rem; margin: .5rem 0 .75rem; }
.modulo { border: 1px solid var(--line); border-radius: var(--r); padding: .75rem;
          background: var(--surface); }
.modulo.on { border-color: color-mix(in srgb, var(--live) 40%, var(--line)); }
.mod-top { display: flex; align-items: center; gap: .625rem; margin-bottom: .375rem; }
.mod-quien { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.mod-quien b { font: 550 .875rem/1.3 var(--sans); }
/* La insignia por defecto es roja —está pensada para «caído»— y aquí diría lo
   contrario de lo que pasa. */
.mod-quien .badge:not(.mute) { background: var(--live); }
.modulo .note.inline { margin: 0 0 .25rem; }
.modulo .note.inline.dim { color: var(--faint); }
.mod-falta { color: var(--warn); }
.mod-form { margin-top: .625rem; padding-top: .625rem; border-top: 1px solid var(--line); }
.mod-form .field { margin-bottom: .625rem; }
.modulo .rowacts { padding-left: 0; padding-right: 0; border-top: 0; }

/* Los certificados de la puerta ordinaria: uno por línea, con lo que le queda.
   El color solo cuando hay algo que mirar — en verde todo el rato, nadie lo
   mira; en ámbar a treinta días, se mira. */
.certs { display: flex; flex-direction: column; gap: .25rem; margin: .5rem 0 .25rem; }
.cert { display: flex; align-items: baseline; gap: .5rem;
        font: 400 .75rem/1.6 var(--mono); color: var(--muted); }
.cert .dom { color: var(--ink); }
.cert.tal .queda { color: var(--warn); }
.cert.no .queda { color: var(--down); font-weight: 500; }

/* El informe de una comprobación: una línea por permiso mirado. El punto
   medio es para lo que no se ha podido comprobar sin tocar nada — que no es
   un fallo y tampoco es un sí. */
.mod-informe { margin-top: .5rem; display: flex; flex-direction: column; gap: .25rem; }
.comp { display: flex; gap: .5rem; align-items: flex-start;
        font: 400 .75rem/1.5 var(--sans); color: var(--muted); }
/* En el modal, un `span` suelto hereda decoración de los campos del
   formulario. Aquí no queremos ninguna: es un carácter. */
.comp .marca { font-family: var(--mono); flex: none; width: 1rem; padding: 0;
               background: none; border: 0; border-radius: 0; text-align: center; }
.comp.si .marca { color: var(--live); }
.comp.no .marca { color: var(--down); }
.comp.no b { color: var(--ink); }
.comp.tal .marca { color: var(--faint); }
.note.mal { color: var(--down); }

/* Lo que es de un módulo apagado no se enseña: unos ajustes de algo que no
   está encendido son una promesa que no se puede cumplir. */
body.sin-claude .mod-claude { display: none; }

/* ---------- la pestaña de seguridad ---------- */
/*
 * La casa no la tiene: sus listas no viven en ningún manifiesto y su puerta es
 * otra. La pestaña desaparece entera en vez de quedarse muerta.
 */
body.casa .sitio-only { display: none; }

/* Una casilla con su nombre y su explicación debajo: la explicación es la
   mitad de la decisión —cerrar los buscadores saca la web de Google— y en una
   línea suelta al lado no se lee. */
.marcas { display: flex; flex-direction: column; gap: .375rem; margin: .25rem 0 .5rem; }
.marca { display: flex; gap: .625rem; align-items: flex-start; padding: .5rem .625rem;
         border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--sunk);
         cursor: pointer; }
.marca input { margin: .1875rem 0 0; flex: none; }
.marca .qué { min-width: 0; }
.marca .qué b { display: block; font: 550 .8125rem/1.4 var(--sans); }
.marca .qué span { display: block; font: 400 .75rem/1.5 var(--sans); color: var(--muted); }

/* Los países elegidos. En fichas y no en una caja de texto: son códigos de dos
   letras, y una lista de códigos no se relee — «SE» y «SI» se confunden a la
   primera. Cada uno lleva su nombre entero y su forma de quitarlo. */
.chips { display: flex; flex-wrap: wrap; gap: .375rem; margin: .25rem 0 .5rem; }
.chip { display: inline-flex; align-items: center; gap: .375rem; padding: .25rem .25rem .25rem .5rem;
        border: 1px solid var(--line); border-radius: 999px; background: var(--sunk);
        font: 400 .8125rem/1.4 var(--sans); }
.chip code { font: 500 .75rem/1 var(--mono); color: var(--faint); }
.chip button { border: 0; background: none; color: var(--faint); cursor: pointer;
               font-size: .9375rem; line-height: 1; padding: .125rem .25rem; }
.chip button:hover { color: var(--down); }
.chips .vacio, .fichas .vacio { font: 400 .8125rem/1.6 var(--sans); color: var(--faint); }

/* Las dos formas de leer una lista —«estos no» y «solo estos»— son la misma
   lista con el sentido cambiado, así que van juntas y se elige una. */
.picks.segmodo { margin: 0 0 .75rem; }
.picks.segmodo label { display: flex; align-items: center; gap: .5rem; }

/* Lo que la puerta ha echado: una línea por petición, con lo que decía ser. */
.bloqueo { display: flex; flex-direction: column; gap: .125rem; padding: .5rem .625rem;
           border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--sunk); }
.bloqueo .top { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
                font: 400 .75rem/1.5 var(--mono); color: var(--muted); }
.bloqueo .top .when { color: var(--faint); }
.bloqueo .top .qué { color: var(--ink); font-weight: 500; }
.bloqueo .ua { font: 400 .75rem/1.5 var(--mono); color: var(--faint);
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* La casilla de recordar y la segunda puerta, en la pantalla de acceso. */
.recuerda { display: flex; align-items: center; gap: .5rem; margin-top: -.25rem;
            font: 400 .8125rem/1 var(--sans); color: var(--muted); }
.recuerda input { accent-color: var(--live); width: 1rem; height: 1rem; }

/* Las fichas: un dispositivo, una passkey, un enlace. Todas se leen igual —un
   nombre, una línea de letra pequeña debajo y lo que se puede hacer con ella a
   la derecha— porque las tres responden a la misma pregunta: quién tiene una
   llave de esto. */
.fichas { display: flex; flex-direction: column; gap: .375rem; margin: .5rem 0 .25rem; }
.ficha {
  display: flex; align-items: center; gap: .75rem; min-width: 0;
  padding: .5rem .625rem; border: 1px solid var(--line-soft); border-radius: var(--r);
  background: var(--sunk);
}
.ficha .quien { min-width: 0; flex: 1; }
.ficha .quien b { display: block; font: 500 .8125rem/1.4 var(--mono);
                  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ficha .quien span { display: block; font: 400 .75rem/1.5 var(--mono); color: var(--faint);
                     overflow: hidden; text-overflow: ellipsis; }
.ficha .btn { flex: none; }
/* Las zonas protegidas: cada una es una fichita con sus cuatro cosas. En un
   móvil las cajas se apilan solas porque el envoltorio envuelve. */
.zonas { display: flex; flex-direction: column; gap: .5rem; margin: .5rem 0 .25rem; }
.zona {
  border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--sunk);
  padding: .625rem;
}
.zona .fila { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.zona .fila > input { flex: 1 1 10rem; min-width: 0; }
.zona .listas { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .5rem; }
.zona .listas label { flex: 1 1 12rem; min-width: 0; font: 400 .75rem/1.6 var(--sans);
                      color: var(--muted); }
.zona .listas textarea { width: 100%; }
.zona .pie { display: flex; align-items: center; gap: .5rem; margin-top: .5rem;
             font: 400 .75rem/1.5 var(--mono); color: var(--faint); }
.zona .pie .grow { flex: 1; }

/* Los dominios buscados: una lista de líneas que se leen de un vistazo, con el
   estado a la derecha. */
/* Un bloque que se abre solo si a alguien le interesa. El triángulo del
   navegador se queda: es lo que dice que ahí dentro hay algo. */
.block.plegado { padding-top: .5rem; }
.block.plegado > summary { display: flex; align-items: center; gap: .625rem;
                           cursor: pointer; list-style-position: inside; }
.block.plegado > summary::marker { color: var(--faint); }
.block.plegado[open] > summary { margin-bottom: .5rem; }

.doms { display: flex; flex-direction: column; gap: .25rem; margin-top: .5rem; }
.dom {
  display: flex; align-items: center; gap: .75rem; min-width: 0;
  padding: .5rem .625rem; border: 1px solid var(--line-soft); border-radius: var(--r);
  background: var(--sunk);
}
.dom.libre { border-color: color-mix(in srgb, var(--live) 40%, var(--line)); background: var(--live-soft); }
.dom.urge { border-color: color-mix(in srgb, var(--down) 45%, var(--line)); background: var(--down-soft); }
.dom .nom { flex: 1; min-width: 0; font: 500 .875rem/1.4 var(--mono);
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dom .nom small { display: block; font: 400 .75rem/1.5 var(--mono); color: var(--faint); }
.dom .precio { flex: none; font: 500 .8125rem/1 var(--mono); }
.dom .est { flex: none; font: 400 .6875rem/1 var(--mono); color: var(--muted);
            padding: .3125rem .5rem; border-radius: 999px; background: var(--surface);
            border: 1px solid var(--line-soft); }
.dom .est.si { color: var(--live); border-color: color-mix(in srgb, var(--live) 35%, var(--line)); }
.dom .est.no { color: var(--faint); }
#dom-precios-txt { width: 100%; font: 400 .8125rem/1.6 var(--mono); margin-top: .5rem; }

.ficha.ojo { border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); }
.badge.bad { color: var(--down); border-color: color-mix(in srgb, var(--down) 35%, var(--line)); }
.ficha.yo { border-color: color-mix(in srgb, var(--live) 40%, var(--line)); background: var(--live-soft); }
.ficha input.renombra {
  flex: 1; min-width: 0; font: 500 .8125rem/1.4 var(--mono);
  padding: .25rem .375rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--surface); color: var(--ink);
}
.fichas .vacio { font: 400 .8125rem/1.6 var(--sans); color: var(--faint); }
#d-inv-url { font: 400 .75rem/1.4 var(--mono); }

/* El mapa de puertos. Una lista y no una tabla: en un móvil una tabla de cinco
   columnas se lee con lupa, y aquí lo que se busca es un número y quién lo
   tiene. */
.puertos { display: flex; flex-direction: column; gap: .25rem; margin-top: .5rem; }
.pfila {
  display: flex; align-items: center; gap: .875rem; min-width: 0;
  padding: .5rem .625rem; border: 1px solid var(--line-soft); border-radius: var(--r);
  background: var(--sunk);
}
.pfila.ojo { border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); }
.pnum { flex: none; width: 4.5rem; font: 500 .875rem/1 var(--mono);
        font-variant-numeric: tabular-nums; }
.pquien { min-width: 0; flex: 1; }
.pquien b { display: block; font: 500 .8125rem/1.4 var(--mono);
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pquien > span { display: block; font: 400 .75rem/1.5 var(--mono); color: var(--faint); }
.pojo { display: block; font: 400 .75rem/1.5 var(--sans); color: var(--warn); }
.ptag { flex: none; font: 400 .6875rem/1 var(--mono); color: var(--muted);
        padding: .3125rem .5rem; border-radius: 999px; background: var(--surface);
        border: 1px solid var(--line-soft); }
.ptag.casa { color: var(--live); border-color: color-mix(in srgb, var(--live) 35%, var(--line)); }
.ptag.otro { color: var(--faint); }
label.mini { display: flex; align-items: center; gap: .375rem;
             font: 400 .75rem/1 var(--sans); color: var(--muted); }
label.mini input { width: 6.5rem; }

/* La terminal: xterm.js de verdad, con pestañas —una por sesión de tmux— y
   una tira de teclas, pensada primero para el móvil, donde no hay Ctrl que
   valga. */
.term-page { height: 100%; display: flex; flex-direction: column; }

.term-tabs {
  flex: none; display: flex; align-items: stretch; gap: .25rem;
  padding: .5rem .5rem 0; overflow-x: auto; scrollbar-width: none;
}
.term-tabs::-webkit-scrollbar { display: none; }
.term-tab {
  flex: none; display: flex; align-items: center; gap: .5rem; max-width: 11rem;
  padding: .5rem .5rem .5rem .75rem; border: 1px solid var(--line-soft); border-bottom: none;
  border-radius: .5rem .5rem 0 0; background: var(--sunk); color: var(--muted);
  font: 400 .8125rem/1.2 var(--mono); cursor: pointer;
}
.term-tab.on { background: var(--surface); color: var(--ink); border-color: var(--line); }
.term-tab-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.term-tab-x {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 1.125rem; height: 1.125rem; border-radius: 50%; color: var(--faint);
}
.term-tab-x:hover { background: var(--down-soft); color: var(--down); }
.term-tab-add {
  flex: none; width: 2rem; align-self: center; border: 1px dashed var(--line);
  border-radius: .5rem; background: none; color: var(--faint);
  font: 400 1.125rem/1 var(--sans); cursor: pointer;
}
.term-tab-add:hover { color: var(--ink); border-color: var(--faint); }

.term-screen-wrap {
  flex: 1; min-height: 0; position: relative; background: var(--surface);
  border-top: 1px solid var(--line); padding: .5rem;
}
#term-screen, #term-screen .xterm { height: 100%; width: 100%; }
.term-empty {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: .75rem; text-align: center; padding: 2rem;
}

.term-keys {
  flex: none; display: flex; gap: .375rem; padding: .5rem; overflow-x: auto;
  scrollbar-width: none; border-top: 1px solid var(--line); background: var(--sunk);
  /* Pegada al fondo de lo que el navegador deja visible: es lo que evita que
     el teclado del móvil, al salir, se la lleve fuera de la pantalla. */
  position: sticky; bottom: 0;
}
.term-keys::-webkit-scrollbar { display: none; }
.term-keys button {
  flex: none; padding: .5rem .625rem; border: 1px solid var(--line-soft); border-radius: .5rem;
  background: var(--surface); color: var(--ink); font: 500 .75rem/1 var(--mono);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.term-keys button:active { background: var(--live-soft); }
.term-keys-sep { flex: none; width: 1px; background: var(--line); margin: 0 .25rem; }

/* La tira de «esto está abierto de par en par». Roja, arriba del todo y en
   todas las pantallas: un panel sin contraseña no es un estado que se pueda
   contar en letra pequeña dentro de una pestaña. */
#abierta {
  position: fixed; inset: 0 0 auto 0; z-index: 60; height: var(--tira);
  display: flex; align-items: center; gap: .75rem;
  padding: 0 1rem; background: var(--down); color: #fff;
  font: 500 .8125rem/1 var(--mono);
}
#abierta-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#abierta .btn {
  margin-left: auto; flex: none; color: #fff;
  border-color: rgba(255,255,255,.55); background: rgba(255,255,255,.12);
}
#abierta .btn:hover { background: rgba(255,255,255,.22); }
/* La clase la pone el guion en vez de un `:has`, que es lo mismo con menos
   confianza en el navegador de un teléfono. */
body.abierta .shell { height: calc(100dvh - var(--tira)); margin-top: var(--tira); }

.danger-zone { border-top: 1px solid var(--line); margin-top: .5rem; padding-top: .25rem; }
.danger-zone .rowacts { border-top: 0; padding-left: 0; padding-right: 0; }
.rowacts.flat { padding-top: .375rem; padding-bottom: .375rem; }
.note.inline { margin: 0; font-size: .75rem; }
.danger-zone input { max-width: 22rem; }
.btn.danger { color: var(--down); border-color: color-mix(in srgb, var(--down) 35%, var(--line)); }
.btn.danger:hover:not(:disabled) { background: var(--down); color: #fff; border-color: var(--down); }
.btn.danger:disabled { opacity: .4; }
.danger-zone .check { margin-top: .5rem; }

/* ---------- el espejo de Cloudflare ---------- */
.dns { width: 100%; border-collapse: collapse; font: 400 .8125rem/1.5 var(--mono); }
.dns th { text-align: left; font: 500 10.5px/1.4 var(--mono); text-transform: uppercase;
          letter-spacing: .09em; color: var(--faint); padding: .75rem 1.25rem .375rem;
          border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--surface); }
.dns td { padding: .5rem 1.25rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.dns tr:hover td { background: var(--sunk); }
.dns .kind { color: var(--muted); width: 1%; white-space: nowrap; }
.dns .name { font-weight: 500; overflow-wrap: anywhere; }
.dns .to { color: var(--muted); overflow-wrap: anywhere; max-width: 28rem; }
.dns .flag { width: 1%; white-space: nowrap; }
.dns .edit { width: 1%; white-space: nowrap; text-align: right; }
.pill { font: 500 .625rem/1 var(--mono); text-transform: uppercase; letter-spacing: .06em;
        padding: .1875rem .375rem; border-radius: 4px; border: 1px solid var(--line);
        color: var(--muted); }
.pill.on { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, var(--line));
           background: color-mix(in srgb, var(--warn) 10%, transparent); }
.pill.here { color: var(--live); border-color: color-mix(in srgb, var(--live) 35%, var(--line));
             background: var(--live-soft); }

.tun { border-bottom: 1px solid var(--line); padding: 1rem 1.25rem; }
.tun h3 { margin: 0 0 .25rem; font: 500 .9375rem/1.3 var(--mono); }
.tun .meta { font: 400 .75rem/1.5 var(--mono); color: var(--muted); margin-bottom: .625rem; }
.tun ul { margin: 0; padding: 0; list-style: none; font: 400 .75rem/1.7 var(--mono); }
.tun li { display: flex; gap: .625rem; align-items: baseline; }
.tun li .h { min-width: 16rem; overflow-wrap: anywhere; }
.tun li .s { color: var(--faint); overflow-wrap: anywhere; }
.tun li.here .s { color: var(--live); }
.tun .more { color: var(--faint); }

/* ---------- ajustes ---------- */
.rule { border: 0; border-top: 1px solid var(--line); margin: 1.25rem 0 1rem; }
.sub-head { margin: 0 0 .75rem; font: 500 .8125rem/1 var(--mono); }
.field.row3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .625rem; }
.field.row2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .625rem; }
.field.row3 > div, .field.row2 > div { position: relative; }
.field.row3 label, .field.row2 label { display: block; font: 500 10.5px/1.4 var(--mono); text-transform: uppercase;
                    letter-spacing: .09em; color: var(--faint); margin-bottom: .3125rem; }
.field .unit { position: absolute; right: .625rem; bottom: .625rem; font: 400 .75rem/1 var(--mono);
               color: var(--faint); pointer-events: none; }
#s-brakes { padding-left: 1.75rem; }
#s-brakes.off { opacity: .45; pointer-events: none; }

/* ---------- chat ---------- */

.chat { display: grid; grid-template-rows: auto 1fr auto;
        grid-template-columns: minmax(0, 1fr); min-height: 0; height: 100%; }
.chat-top { display: flex; gap: .5rem; align-items: center;
            padding-top: .5rem; padding-bottom: .5rem;
            border-bottom: 1px solid var(--line); flex-wrap: wrap; min-width: 0; }
/* El botón del desplegable de conversaciones: más bajo que el del sitio, que
   es el título de la pantalla y este no. */
.picker-btn.slim { padding: .3125rem .5rem; max-width: min(26rem, 60vw); }
.picker-btn.slim .ctitle { font: 400 .8125rem/1.3 var(--sans); color: var(--ink);
                           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-btn.slim .ctitle.vacio { color: var(--faint); }
.pick.chat { grid-template-columns: minmax(0, 1fr) auto; }
.pick.chat .host { font: 400 .8125rem/1.4 var(--sans); overflow: hidden;
                   text-overflow: ellipsis; white-space: nowrap; }
.pick.chat[aria-selected="true"] { background: var(--sunk); font-weight: 500; }

/* Borrar una conversación desde la lista: en el móvil deslizando, como en
   iOS; en escritorio con una cruceta que solo aparece al pasar por encima,
   porque ahí no hay gesto que la sustituya. */
.pick-row { position: relative; overflow: hidden; border-radius: 8px; }
.pick-row .pick.chat {
  transition: transform .18s ease; touch-action: pan-y; will-change: transform;
}
.pick-row.tirando .pick.chat { transition: none; }
.pick-row.abierta .pick.chat { transform: translateX(-5.5rem); }
.pick-del {
  position: absolute; inset: 0 0 0 auto; width: 5.5rem; border: 0; border-radius: 0;
  background: var(--down); color: #fff; font: 500 .8125rem var(--sans); cursor: pointer;
}
.pick-x {
  position: absolute; top: 50%; right: .4375rem; transform: translateY(-50%);
  width: 1.375rem; height: 1.375rem; z-index: 2; display: none; align-items: center;
  justify-content: center; border: 0; border-radius: 50%; background: var(--sunk);
  color: var(--muted); font: 400 1.0625rem/1 var(--sans); cursor: pointer;
  opacity: 0; transition: opacity .12s ease;
}
@media (hover: hover) and (pointer: fine) {
  .pick-row:hover .pick-x { display: flex; opacity: 1; }
  .pick-row:hover .pick.chat .side { opacity: 0; }
}
.pick-x:hover { background: var(--down-soft); color: var(--down); }
/* Las dos cosas que se le hacen a la conversación abierta van al pie del
   desplegable: se usan poco y en la barra le robaban el sitio al título. */
.pop-acts { display: flex; gap: .25rem; padding: .25rem; border-top: 1px solid var(--line-soft); }
.pop-acts .btn { flex: 1; justify-content: flex-start; }
.pop-acts #chat-new { color: var(--ink); font-weight: 500; }
.pop-acts #chat-del:hover:not(:disabled) { color: var(--down); }

.chat-cost { font: 400 .75rem/1 var(--mono); color: var(--faint); }
/* El gasto de la cuenta va al lado del de la conversación, y aguanta en el
   móvil aunque aquel se esconda: el coste de un turno se puede mirar luego, y
   quedarse sin ventana a media frase, no. Color solo cuando aprieta —de tres
   cuartos en adelante—; teñirlo siempre sería una luz más que ignorar. */
/* Y quien cede sitio es el nombre de la conversación, que ya tiene puntos
   suspensivos para eso. Sin este `min-width`, el envoltorio del desplegable no
   encoge por debajo de su contenido —da igual lo que diga el botón de dentro— y
   en un móvil el gasto se sale por la derecha: no es que no se pinte, es que
   cae fuera de la pantalla. */
.chat-top .picker { min-width: 0; }
.chat-top .picker-btn.slim { max-width: min(26rem, 100%); min-width: 0; }
.chat-uso { font: 400 .75rem/1 var(--mono); color: var(--muted); white-space: nowrap;
            flex: none; }
/* Cuando los dos números están a la vez, una raya entre ellos: sin ella se leen
   como una sola frase y «64 % 12 turnos» no quiere decir nada. */
.chat-uso + .chat-cost { padding-left: .625rem; border-left: 1px solid var(--line); }
.chat-uso.medio { color: var(--warn); }
.chat-uso.alto { color: var(--down); }

/* Una columna de lectura, no la pantalla entera.
 *
 * En un monitor ancho una respuesta se estiraba hasta los mil cuatrocientos
 * píxeles: líneas de doscientos caracteres, en las que el ojo pierde el
 * renglón al volver. El relleno lateral crece con la ventana hasta dejar el
 * texto en unos setenta caracteres, que es lo que se lee de un tirón. La caja
 * de escribir y la barra de la conversación llevan la misma cuenta, para que
 * las tres cosas caigan en la misma columna. */
.chat-top, .msgs, .composer { --lectura: 58rem;
  padding-left: max(1.25rem, calc((100% - var(--lectura)) / 2));
  padding-right: max(1.25rem, calc((100% - var(--lectura)) / 2)); }
.msgs { min-height: 0; overflow-y: auto; padding-top: 1.25rem; padding-bottom: 1.5rem;
        display: flex; flex-direction: column; gap: .875rem; }
/* Sin `flex: none`, los hijos de una columna flex con overflow se encogen para
   caber y una herramienta plegada acaba midiendo dos píxeles: el texto está,
   pero no se ve. Y sin `max-width` al lado, la misma regla deja que un comando
   largo empuje a lo ancho y aparezca un scroll horizontal en toda la
   conversación. Las dos cosas van juntas. */
.msgs > * { flex: none; max-width: 100%; min-width: 0; }
.msgs { overflow-x: hidden; }
.msg { max-width: 100%; }
/* Un mensaje que no llegó a entrar: se queda a la vista, pero apagado, para que
   se distinga del que sí está en la conversación. */
.msg.user.fallo { opacity: .5; border: 1px dashed var(--down); }
.msg.user {
  align-self: flex-end; max-width: 78%; background: var(--sunk); border: 1px solid var(--line);
  padding: .5rem .75rem; border-radius: 10px 10px 2px 10px; white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.msg.assistant { white-space: pre-wrap; line-height: 1.68; max-width: min(46rem, 100%);
                 overflow-wrap: anywhere; }
.msg.assistant code { font: 400 .875em/1.5 var(--mono); background: var(--sunk);
                      padding: .0625rem .3125rem; border-radius: 4px; }
.msg.assistant pre { background: var(--sunk); border: 1px solid var(--line); border-radius: 8px;
                     padding: .625rem .75rem; overflow-x: auto; max-width: 100%;
                     font: 400 12.5px/1.6 var(--mono); white-space: pre; }
.msg.system { font: 400 .75rem/1.5 var(--mono); color: var(--faint); overflow-wrap: anywhere; }
.msg.system.bad { color: var(--down); }

.tool { border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
        overflow: hidden; max-width: 100%; }
.tool summary { padding: .4375rem .625rem; font: 400 .8125rem/1.4 var(--mono); color: var(--muted);
                cursor: pointer; display: flex; align-items: flex-start; gap: .5rem; list-style: none;
                min-width: 0; overflow-wrap: anywhere; }
.tool summary::before { flex: none; }
.tool summary .what { min-width: 0; }
.tool summary::-webkit-details-marker { display: none; }
.tool summary::before { content: "+"; color: var(--faint); font-weight: 500; width: .75rem; }
.tool[open] summary::before { content: "−"; }
.tool.bad summary { color: var(--down); }
.tool .run { width: 6px; height: 6px; border-radius: 50%; background: var(--warn); flex: none;
             animation: blink 1.2s ease-in-out infinite; }
@keyframes blink { 0%,100% { opacity: .25 } 50% { opacity: 1 } }
.tool pre { margin: 0; padding: .625rem; border-top: 1px solid var(--line); background: var(--sunk);
            font: 400 12px/1.6 var(--mono); white-space: pre-wrap; word-break: break-word;
            max-height: 24rem; overflow: auto; }

.turn { font: 400 .75rem/1.4 var(--mono); color: var(--faint);
        border-top: 1px dashed var(--line); padding-top: .5rem; overflow-wrap: anywhere; }
.working { display: flex; align-items: center; gap: .5rem; font: 400 .8125rem/1 var(--mono); color: var(--muted); }
.working i { width: 6px; height: 6px; border-radius: 50%; background: var(--live);
             animation: blink 1.1s ease-in-out infinite; }

/* Preguntas: en modo -p Claude no tiene AskUserQuestion, así que las escribe en
   un bloque y aquí vuelven a ser botones. Siempre con salida a escribir: una
   lista de opciones nunca agota lo que se puede querer. */
.ask { border: 1px solid var(--line); background: var(--raised); border-radius: 10px;
       padding: .875rem; box-shadow: var(--shadow); max-width: min(46rem, 100%); }
.ask-q { font-weight: 500; margin-bottom: .75rem; }
.ask-opts { display: flex; gap: .5rem; flex-wrap: wrap; }
.ask-opts button { border: 1px solid var(--line); background: var(--surface); border-radius: 8px;
                   padding: .4375rem .75rem; font-size: .875rem; max-width: 100%;
                   text-align: left; overflow-wrap: anywhere; }
.ask-opts button:hover:not(:disabled) { border-color: var(--link); color: var(--link); }
.ask-opts button:disabled { opacity: .4; cursor: default; }
.ask-opts button.picked { opacity: 1; background: var(--ink); color: var(--canvas); border-color: var(--ink); font-weight: 500; }
.ask-opts button.other { border-style: dashed; color: var(--muted); }
/* La recomendada: la misma tarjeta con el borde marcado y la coletilla al lado,
   como la marca «(recomendado)» del Claude de la terminal. Se distingue por el
   borde y por la palabra, no solo por el color. */
.ask-opts button.rec { border-color: color-mix(in srgb, var(--link) 55%, var(--line));
                       box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--link) 22%, transparent); }
.ask-opts button.rec span { margin-left: .375rem; font-size: .75rem; color: var(--link); }
.ask-opts button.rec.picked span { color: var(--canvas); opacity: .8; }
.ask-free { display: flex; gap: .5rem; margin-top: .625rem; }
.ask-free input { flex: 1; }

.composer { border-top: 1px solid var(--line); padding-top: .75rem; padding-bottom: .75rem;
            display: flex; gap: .5rem;
            align-items: flex-end; background: var(--surface); min-width: 0; }
/* El clip: cuadrado y del alto de la caja vacía, para que los tres controles
   de la fila caigan en la misma línea de base. */
.composer .btn.clip { display: grid; place-items: center; width: 2.5rem; height: 2.5rem;
                      padding: 0; color: var(--muted); cursor: pointer; }
.composer .btn.clip:hover { color: var(--ink); }
.composer .btn.clip.yendo { opacity: .55; }
/* Es un `label`, no un botón, así que no tiene `disabled`: cuando no hay con
   quién hablar se apaga a mano. */
.composer .btn.clip[aria-disabled="true"] { opacity: .45; pointer-events: none; }
/* El input de ficheros. Fuera de la pantalla pero no oculto: un
   `input[type=file]` con `display: none` deja de abrir el diálogo en algunos
   móviles, y el fallo es mudo. */
.fuera { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
         overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* Una ficha que todavía va de camino al servidor. Existe para que elegir un
   fichero se note en el acto: si al elegirlo no aparece nada, el problema es
   que el navegador ni siquiera avisó de la elección. */
.attach .chip.yendo { opacity: .7; }
.attach .chip.yendo i { font-variant-numeric: tabular-nums; }

/* La tira de lo que se ha subido y aún no se ha mandado. Va encima de la caja
   de escribir, dentro de la misma columna de lectura que el resto del chat. */
.attach { --lectura: 58rem; display: flex; flex-wrap: wrap; gap: .375rem;
          padding: .625rem max(1.25rem, calc((100% - var(--lectura)) / 2)) 0;
          background: var(--surface); }
.attach .chip { display: inline-flex; align-items: center; gap: .375rem; max-width: 100%;
                background: var(--sunk); border: 1px solid var(--line); border-radius: 999px;
                padding: .1875rem .1875rem .1875rem .625rem; font-size: .8125rem; }
.attach .chip b { font-weight: 500; overflow: hidden; text-overflow: ellipsis;
                  white-space: nowrap; max-width: 14rem; }
.attach .chip i { font-style: normal; color: var(--faint); font-size: .75rem; }
/* Una imagen y un documento se distinguen de un vistazo por el punto de la
   izquierda, que es más barato que una miniatura y no hace saltar la fila. */
.attach .chip.img { padding-left: 1.5rem; position: relative; }
.attach .chip.img::before { content: ""; position: absolute; left: .625rem; width: .5rem;
                            height: .5rem; border-radius: 2px; background: var(--link); }
/* La equis. En un móvil se toca con el dedo, así que mide lo que mide un dedo
   aunque el aspa se vea pequeña. */
.attach .chip button { border: 0; background: none; cursor: pointer; color: var(--faint);
                       font: 400 1.125rem/1 var(--sans); width: 1.75rem; height: 1.75rem;
                       border-radius: 50%; display: grid; place-items: center; flex: none; }
.attach .chip button:hover:not(:disabled) { background: var(--line); color: var(--ink); }
/* Cuánto lleva subido: la propia ficha se va llenando por detrás del texto.
   Una barra de dos píxeles al pie de una píldora la corta el radio y no se ve
   desde medio metro, que es la distancia a la que se mira un móvil; el relleno
   se lee de un vistazo sin ocupar ni una línea más. */
.attach .chip { position: relative; overflow: hidden; }
.attach .chip .barra { position: absolute; inset: 0; }
.attach .chip .barra > span { display: block; height: 100%; width: 0;
                              background: color-mix(in srgb, var(--link) 20%, transparent);
                              transition: width .15s linear; }
.attach .chip > b, .attach .chip > i, .attach .chip > button { position: relative; }

/* Los ficheros que iban con un mensaje ya mandado. No son enlaces: para cuando
   se relee la conversación, los ficheros ya se han borrado. */
.msg-files { display: flex; flex-wrap: wrap; gap: .25rem .5rem; margin-top: .375rem;
             padding-top: .375rem; border-top: 1px solid var(--line); }
.msg-files span { font: 400 .75rem/1.4 var(--mono); color: var(--faint);
                  overflow-wrap: anywhere; }

/* Arrastrando algo por encima. El aviso es el borde entero de la conversación:
   soltar vale en cualquier punto de ella, y un recuadro pequeño diría lo
   contrario. */
.chat.soltando { outline: 2px dashed var(--link); outline-offset: -4px; border-radius: 8px; }
.composer textarea { min-width: 0; flex: 1; }
.composer .btn { flex: none; }
.composer textarea { min-height: 2.5rem; max-height: 12rem; }
/* Cuando no hay nada dicho todavía, lo que hay es un vacío de pantalla entera:
   el mensaje se centra en él en vez de quedarse pegado arriba con dos tercios
   de nada debajo. */
.msgs:has(> .chat-hollow) { justify-content: center; }
.chat-hollow { padding: 1.5rem 1.25rem; text-align: center; color: var(--muted);
               font-size: .875rem; line-height: 1.6; }
.chat-more { align-self: center; }

/* ---------- levantar un sitio ---------- */
.modal { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; padding: 1.25rem;
         background: color-mix(in srgb, var(--canvas) 60%, rgba(0,0,0,.55)); }
.modal form { width: min(29rem, 100%); background: var(--surface); border: 1px solid var(--line);
              border-radius: var(--r); padding: 1.5rem; box-shadow: var(--shadow);
              max-height: 90dvh; overflow-y: auto; }
.modal h2 { margin: 0 0 .25rem; font: 500 1rem/1.3 var(--mono); letter-spacing: -.02em; }
.modal .sub { margin: 0 0 1.25rem; color: var(--muted); font-size: .8125rem; }
.field { margin-bottom: .875rem; }
.field > label { display: block; font: 500 10.5px/1.4 var(--mono); text-transform: uppercase;
                 letter-spacing: .09em; color: var(--faint); margin-bottom: .3125rem; }
.field .hint { font-size: .75rem; color: var(--faint); margin-top: .3125rem; }
.field .hint code { font-family: var(--mono); }
.picks { display: flex; gap: .5rem; }
.picks label { flex: 1; border: 1px solid var(--line); border-radius: 8px; padding: .625rem .75rem;
               cursor: pointer; font-size: .8125rem; }
.picks label.on { border-color: var(--ink); background: var(--sunk); }
.picks input { display: none; }
.picks b { display: block; font-weight: 500; }
.picks small { color: var(--muted); }
/* Un `label.check` es hijo de `.field` y estaba heredando el estilo de las
   etiquetas: una frase entera en versalitas y monoespaciada. Aquí se lee. */
.field > label.check, .check {
  display: flex; gap: .5rem; align-items: flex-start;
  font: 400 .875rem/1.5 var(--sans); color: var(--ink);
  text-transform: none; letter-spacing: normal; margin-bottom: 0;
}
.check b { font-weight: 550; }
/* El interruptor de los detenidos va suelto encima de la rejilla, así que
   necesita el aire que en un formulario le da el campo de alrededor. */
.check.detenidos { align-items: center; width: fit-content; margin-bottom: 1rem;
                   padding: .375rem .625rem .375rem .5rem; border: 1px solid var(--line);
                   border-radius: 7px; background: var(--surface); cursor: pointer;
                   font-size: .8125rem; color: var(--muted); }
.check.detenidos:hover { border-color: var(--faint); color: var(--ink); }
.check.detenidos input { margin-top: 0; }
.check.detenidos .num { font-family: var(--mono); font-weight: 500; color: var(--ink); }
.check input { width: auto; margin-top: .2rem; }
.check code { font-family: var(--mono); }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- estrecho ---------- */
@media (max-width: 820px) {
  /* Y la barra de arriba, mientras estás dentro, se queda con lo justo. */
  body.in-site .gauges { display: none; }
  /* Con los medidores fuera no queda nada que empuje el menú al canto derecho
     —el empujón era suyo, un `margin` automático— y el botón se quedaba a
     media barra. El hueco hace ese trabajo sin depender de quién esté puesto. */
  body.in-site .top .hueco { flex: 1; }
  body.in-site .top { padding: .5rem .75rem; gap: .5rem; }

  .dhead { padding: .625rem 1rem .5rem; gap: .125rem .5rem; }
  /* En estrecho el estado pasa a su propia línea, debajo del nombre, y el
     botón se queda arriba con él. Antes envolvía al revés: el nombre solo en
     la primera fila y el botón solo en la segunda, una franja de 44 px para
     una palabra. Es el orden lo que se cambia, no la caja: así las otras
     cabeceras que comparten `.dhead` —Cloudflare, las tareas— no se enteran. */
  #view-site .dhead .state { order: 3; flex-basis: 100%; }
  .dhead .host { font-size: .9375rem; }
  .msg.user { max-width: 90%; }
  .tabs, .chat-top, .composer, .msgs, .facts, .rowacts, .steps {
    padding-left: 1rem; padding-right: 1rem;
  }
  .chat-top { padding-top: .375rem; padding-bottom: .375rem; flex-wrap: nowrap; }
  /* El nombre de la conversación es lo que hay que poder leer aquí; el coste
     ya va al pie de cada respuesta. */
  .picker-btn.slim { max-width: none; min-width: 0; }
  .chat-cost { display: none; }
  .msgs { padding-top: 1rem; }
  /* La cabecera de cada respuesta lleva la ruta del proyecto entera, y en un
     móvil son tres renglones de ruido antes de cada cosa dicha: se queda en
     una línea con puntos suspensivos, que es lo que hace falta para saber de
     qué modelo y de qué carpeta se está hablando. */
  .msg.system { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .msg.system.bad { white-space: normal; }

}

/* Muy estrecho: el nombre del sitio ya está en la cabecera del detalle, así que
   la marca de arriba sobra y el alto se agradece. */
@media (max-width: 560px) {
  body.in-site .top .name, body.in-site .top .ver { display: none; }
}

/* La cabecera lleva ya bastante, y partirse en dos filas le come alto a todo lo
   demás. Medido: en una sola fila necesita 1431 px, de los que la tira de
   recursos se lleva 280 y las cifras 364.
   Se van por orden de lo que menos falta hace, y lo que se puede consultar
   entrando cae antes que lo que solo se ve aquí. El veredicto no se va nunca:
   es lo primero que se lee al abrir el panel, y la CPU tampoco, que es lo que
   se mira de reojo.
   Están marcadas g1/g2/g3 y no por posición porque algunas solo aparecen a
   veces, y con nth-child se irían las que no toca. */
@media (max-width: 1440px) {
  .gauge.g1 { display: none; }          /* cuánto lleva arriba */
  .v-spark { display: none; }
}
@media (max-width: 1380px) {
  .gauge.g2, .gauge.g3 { display: none; }   /* sitios, tareas, y los avisos sueltos */
}
@media (max-width: 1120px) {
  .vitals .v-part:nth-child(3) { display: none; }   /* el disco cambia una vez al día */
}
@media (max-width: 900px) {
  .vitals .v-part:nth-child(2) { display: none; }   /* y queda la CPU sola */
  .vitals { gap: .375rem; padding: .25rem .5rem; }
}

/* ---- las tareas ------------------------------------------------------ */

.tgroup { padding: 1rem 1.25rem .25rem; }
.tgroup > .label { display: block; margin-bottom: .5rem; color: var(--faint); }

.task { border: 1px solid var(--line); border-radius: var(--r); background: var(--surface);
        margin-bottom: .5rem; overflow: hidden; max-width: 100%; }
.task.open { border-color: var(--faint); }

.task-head { width: 100%; display: grid; gap: .25rem .75rem; align-items: center;
             grid-template-columns: auto minmax(0, 1fr) auto;
             background: none; border: 0; padding: .625rem .75rem; text-align: left;
             cursor: pointer; color: inherit; font: inherit; }
.task-head:hover { background: var(--sunk); }
.task-head .tname { font: 500 .875rem/1.3 var(--mono); letter-spacing: -.01em;
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-head .twhen { font: 400 .75rem/1 var(--mono); color: var(--muted); justify-self: end; }
.task-head .tlast { grid-column: 2 / -1; font: 400 .75rem/1.4 var(--mono); color: var(--faint);
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-head .tlast.bad { color: var(--down); }
.task.off .tname { color: var(--muted); text-decoration: line-through; }

.task-body { border-top: 1px solid var(--line-soft); padding: .875rem .75rem; }
.task-body .note { margin: 0 0 .875rem; font: 400 .8125rem/1.6 var(--sans); color: var(--muted); }
.task-body .facts { padding: 0 0 .875rem; }
.tacts { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.tacts input[type=text] { width: 9rem; font-family: var(--mono); font-size: 16px; }

.runs { margin-top: .875rem; border-top: 1px solid var(--line-soft); padding-top: .625rem; }
.run { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: .5rem;
       align-items: baseline; padding: .25rem 0;
       font: 400 .75rem/1.5 var(--mono); color: var(--muted); }
.run .rwhen { color: var(--ink); }
.run .rhow { justify-self: end; }
.run.bad .rhow { color: var(--down); }
.run.ok .rhow { color: var(--live); }
.run .rtail { grid-column: 1 / -1; color: var(--faint); white-space: pre-wrap;
              word-break: break-word; margin: .125rem 0 .5rem; }

.tlog { margin-top: .875rem; max-height: 16rem; overflow: auto; background: var(--sunk);
        border-radius: 8px; padding: .625rem .75rem; font: 400 .75rem/1.5 var(--mono);
        color: var(--muted); white-space: pre-wrap; word-break: break-word; }

@media (max-width: 640px) {
  .task-head { grid-template-columns: auto minmax(0, 1fr); }
  .task-head .twhen { grid-column: 2; justify-self: start; }
  .task-head .tlast { grid-column: 1 / -1; }
}

/* ---- los procesos propios de un sitio -------------------------------- */

.wrk { border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
       margin-top: .5rem; overflow: hidden; max-width: 100%; }
.wrk-head { display: grid; gap: .25rem .625rem; align-items: center;
            grid-template-columns: auto minmax(0, 1fr) auto; padding: .5rem .625rem; }
.wrk-head .wname { font: 500 .8125rem/1.3 var(--mono); overflow: hidden;
                   text-overflow: ellipsis; white-space: nowrap; }
.wrk-head .wnum { font: 400 .75rem/1 var(--mono); color: var(--muted); justify-self: end; }
.wrk-note { grid-column: 1 / -1; font: 400 .8125rem/1.5 var(--sans); color: var(--muted); }
.wrk-acts { display: flex; flex-wrap: wrap; gap: .375rem; padding: 0 .625rem .625rem; }
.wrk-log { margin: 0 .625rem .625rem; max-height: 14rem; overflow: auto; background: var(--sunk);
           border-radius: 6px; padding: .5rem .625rem; font: 400 .75rem/1.5 var(--mono);
           color: var(--muted); white-space: pre-wrap; word-break: break-word; }

.pick-fam { padding: .5rem .625rem .25rem; font: 500 10.5px/1.4 var(--mono);
            text-transform: uppercase; letter-spacing: .09em; color: var(--faint); }
.pick-fam.sub { padding-left: 1.25rem; text-transform: none; letter-spacing: 0;
                font-size: .75rem; color: var(--muted); }

/* ---------- lo que se cuenta y lo que se pregunta ----------
 *
 * El panel usaba los diálogos del navegador. Funcionan, pero son de otra casa:
 * tipografía del sistema, botones en inglés según el idioma del navegador, y
 * el nombre del host arriba como si fuera una web sospechosa. Lo que se
 * pregunta antes de borrar algo se pregunta aquí dentro, con las mismas letras
 * que el resto; y lo que se cuenta después sale en un aviso que se va solo.
 */
#toasts { position: fixed; z-index: 80; right: 1rem; bottom: 1rem; display: flex;
          flex-direction: column-reverse; gap: .5rem; pointer-events: none;
          max-width: min(26rem, calc(100vw - 2rem)); }
.toast { pointer-events: auto; background: var(--surface); border: 1px solid var(--line);
         border-left: 3px solid var(--live); border-radius: 8px; box-shadow: var(--shadow);
         padding: .625rem .875rem; font-size: .8125rem; line-height: 1.45; color: var(--ink);
         display: flex; gap: .625rem; align-items: flex-start;
         animation: toast-in .18s ease-out; }
.toast.bad { border-left-color: var(--down); }
.toast.warn { border-left-color: var(--warn); }
.toast.going { animation: toast-out .22s ease-in forwards; }
.toast b { font: 500 .8125rem/1.45 var(--mono); }
.toast .x { margin-left: auto; border: 0; background: none; color: var(--faint); cursor: pointer;
            font: 400 1rem/1 var(--mono); padding: 0 0 0 .5rem; }
.toast .x:hover { color: var(--ink); }
@keyframes toast-in { from { opacity: 0; transform: translateY(.5rem); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(.35rem); } }
@media (prefers-reduced-motion: reduce) {
  .toast, .toast.going { animation: none; }
}

#askmodal .sub { white-space: pre-line; }
#askmodal.grave form { border-color: color-mix(in srgb, var(--down) 30%, var(--line)); }

/* Las puertas a las que llama alguien y no existen. */
.knocks .knock { display: flex; align-items: center; gap: .625rem; padding: .3125rem 0;
                 font: 400 .8125rem/1.5 var(--mono); border-top: 1px solid var(--line); }
.knocks .knock:first-of-type { border-top: 0; }
.knocks .knock b { font-weight: 500; }
.knocks .knock .num { color: var(--muted); }
.knocks .knock .side { color: var(--faint); margin-left: auto; }
.knocks .knock .btn { padding: .125rem .5rem; font-size: .75rem; }

/* ---------- el desplegable de la cabecera ---------- */
.menu { position: relative; }

/* El botón de las conversaciones sale siempre que haya alguna, así que su
   aspecto lo decide a quién le toca: apagado si solo están ahí, ámbar y latiendo
   si alguien está hablando, y con fondo si alguna espera una respuesta — que es
   lo único de los tres que pide que hagas algo. */
.live-btn { display: inline-flex; align-items: center; gap: .4375rem;
            color: var(--muted); }
.live-btn:hover { border-color: var(--ink); color: var(--ink); }
.live-btn .latido { width: 6px; height: 6px; border-radius: 50%; background: var(--faint); flex: none; }
.menu.viva .live-btn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, var(--line)); }
.menu.viva .live-btn:hover { border-color: var(--warn); color: var(--warn); }
.menu.viva .live-btn .latido { background: var(--warn); animation: latir 1.4s ease-in-out infinite; }
.menu.espera .live-btn { color: var(--warn); border-color: var(--warn);
                         background: color-mix(in srgb, var(--warn) 10%, transparent); }
.menu.espera .live-btn .latido { background: var(--warn); animation: latir 2.2s ease-in-out infinite; }
@keyframes latir { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .latido { animation: none; } }

.drop { position: absolute; top: calc(100% + .375rem); right: 0; z-index: 70;
        min-width: 20rem; max-width: min(26rem, calc(100vw - 2rem));
        background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
        box-shadow: var(--shadow); padding: .25rem; }
.drop-item { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;
             background: none; border: 0; border-radius: 6px; padding: .5rem .625rem;
             cursor: pointer; color: var(--ink); }
.drop-item:hover { background: var(--sunk); }
.drop-item .latido { width: 6px; height: 6px; border-radius: 50%; background: var(--warn); flex: none;
                  animation: latir 1.4s ease-in-out infinite; }
.drop-item .espiga { width: 6px; height: 6px; border-radius: 50%; background: var(--warn); flex: none;
                     box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent); }
.drop-item .quieto { width: 6px; height: 6px; border-radius: 50%; background: var(--faint);
                     flex: none; opacity: .55; }
.drop-item.espera { background: color-mix(in srgb, var(--warn) 8%, transparent); }
.drop-item.espera .drop-side { color: var(--warn); }
.drop-main { display: flex; flex-direction: column; min-width: 0; }
.drop-main b { font: 500 .8125rem/1.4 var(--mono); }
.drop-main small { font: 400 .75rem/1.4 var(--sans); color: var(--muted);
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drop-side { margin-left: auto; font: 400 .6875rem/1.4 var(--mono); color: var(--faint);
             white-space: nowrap; }

/* ---------- cómo va la máquina ---------- */
.vitals { display: inline-flex; align-items: center; gap: .75rem; cursor: pointer;
          background: none; border: 1px solid var(--line); border-radius: 7px;
          padding: .25rem .625rem; color: var(--muted); }
.vitals:hover { border-color: var(--ink); color: var(--ink); }
.vitals.warn { border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); color: var(--warn); }
.vitals.bad { border-color: var(--down); color: var(--down);
              background: color-mix(in srgb, var(--down) 8%, transparent); }
.v-part { display: inline-flex; align-items: baseline; gap: .3125rem; }
.v-lab { font: 500 9.5px/1 var(--mono); text-transform: uppercase; letter-spacing: .08em;
         color: var(--faint); }
.vitals.bad .v-lab, .vitals.warn .v-lab { color: currentColor; opacity: .7; }
.v-num { font: 500 .8125rem/1 var(--mono); color: currentColor; min-width: 2.4em; text-align: right; }
.v-spark { width: 46px; height: 14px; }
.v-spark path { fill: none; stroke: currentColor; stroke-width: 1.25; opacity: .55;
                stroke-linejoin: round; }

/* Los bloques de esta vista no van dentro del panel lateral de un sitio, que es
   de donde vienen los márgenes de `.block`: aquí sobra la sangría. */
#view-vitals .block { padding: 0 0 1.5rem; }
#view-vitals .board-in { max-width: 1100px; }

.v-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
          gap: .75rem; margin-bottom: 1.5rem; }
.v-card { border: 1px solid var(--line); border-radius: var(--r); padding: .875rem 1rem;
          background: var(--surface); }
.v-card.warn { border-color: color-mix(in srgb, var(--warn) 35%, var(--line)); }
.v-card.bad { border-color: color-mix(in srgb, var(--down) 40%, var(--line)); }
.v-card-lab { font: 500 10px/1.4 var(--mono); text-transform: uppercase; letter-spacing: .09em;
              color: var(--faint); }
.v-card-num { font: 500 1.75rem/1.15 var(--mono); letter-spacing: -.02em; margin: .25rem 0 .125rem; }
.v-card.warn .v-card-num { color: var(--warn); }
.v-card.bad .v-card-num { color: var(--down); }
.v-card-pie { font: 400 .75rem/1.5 var(--sans); color: var(--muted); }

.v-chart { width: 100%; height: 120px; display: block; }
.v-chart path { fill: none; stroke-width: 1.5; stroke-linejoin: round; }
.v-cpu-line { stroke: var(--link); }
.v-mem-line { stroke: var(--muted); opacity: .45; }
.v-umbral { stroke: var(--down); stroke-width: 1; stroke-dasharray: 3 4; opacity: .5; }

.v-proc { display: grid; grid-template-columns: 3.5rem 5rem minmax(0, 1fr) minmax(0, 14rem);
          gap: .625rem; align-items: baseline; padding: .3125rem 0;
          border-top: 1px solid var(--line-soft); font: 400 .8125rem/1.5 var(--mono); }
.v-proc:first-child { border-top: 0; }
.v-proc-cpu { text-align: right; font-weight: 500; }
.v-proc-mem, .v-proc-de { color: var(--muted); }
.v-proc-cmd, .v-proc-de { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v-proc-de { text-align: right; font-size: .75rem; color: var(--faint); }
@media (max-width: 46rem) {
  .v-proc { grid-template-columns: 3.5rem 5rem minmax(0, 1fr); }
  .v-proc-de { grid-column: 3; text-align: left; }
}

.v-ep { border-top: 1px solid var(--line); padding: .5rem 0; }
.v-ep:first-of-type { border-top: 0; }
.v-ep summary { cursor: pointer; display: flex; gap: .75rem; align-items: baseline;
                font: 400 .8125rem/1.6 var(--mono); }
.v-ep summary b { font-weight: 500; }
.v-ep-dur { color: var(--muted); }
.v-ep-pico { margin-left: auto; color: var(--faint); font-size: .75rem; }
.v-culpas { margin: .5rem 0 .25rem; padding-left: 1rem; border-left: 2px solid var(--line); }

/* ---------- filtrar el cuadro ---------- */
.board-tools { display: flex; align-items: center; gap: .75rem; margin-bottom: 1rem;
               flex-wrap: wrap; }
.board-tools #filtro { width: min(22rem, 100%); padding: .375rem .625rem;
                       font: 400 .8125rem/1.4 var(--mono); }
.board-tools #filtro.activo { border-color: var(--ink); }
/* Dentro de la fila de herramientas el interruptor ya no necesita su margen. */
.board-tools .check.detenidos { margin-bottom: 0; }
.board-tools .note.inline { white-space: nowrap; }

/* ---------- el cuadro en tabla ---------- */
#board-flags { display: flex; align-items: center; gap: .75rem; }
#board-vista { margin-left: auto; }
.segmento { display: inline-flex; border: 1px solid var(--line); border-radius: 7px;
            overflow: hidden; }
.segmento button { background: none; border: 0; cursor: pointer; padding: .3125rem .625rem;
                   font: 400 .75rem/1.4 var(--mono); color: var(--muted); }
.segmento button + button { border-left: 1px solid var(--line); }
.segmento button:hover { color: var(--ink); }
.segmento button.on { background: var(--sunk); color: var(--ink); font-weight: 500; }

.oculto { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

.lista { width: 100%; border-collapse: collapse; margin: 0 0 .25rem; }
.lista th { text-align: left; padding: .375rem .625rem; border-bottom: 1px solid var(--line);
            font: 500 9.5px/1.4 var(--mono); text-transform: uppercase; letter-spacing: .09em;
            color: var(--faint); white-space: nowrap; }
.lista td { padding: .4375rem .625rem; border-bottom: 1px solid var(--line-soft);
            font: 400 .8125rem/1.5 var(--mono); vertical-align: middle; }
.lista tbody tr { cursor: pointer; }
.lista tbody tr:hover { background: var(--sunk); }
.lista tbody tr:last-child td { border-bottom: 0; }
.lista .c-dot { width: 1.5rem; padding-right: 0; }
.lista .c-host { max-width: 22rem; }
.lista .c-host .host { font-weight: 500; }
/* `position: static` no sobra: en la tarjeta las marcas van sueltas sobre la
   captura, y sin devolverlas aquí se salían de la fila y se pegaban a la
   esquina de la página — una pastilla de «sin publicar» flotando arriba a la
   izquierda, sin nada que dijera de quién era. */
.lista .c-host .flags { position: static; display: inline-flex; gap: .3125rem;
                        vertical-align: middle; margin-left: .375rem; }
.lista .dim { color: var(--muted); }
.lista .c-num { text-align: right; white-space: nowrap; color: var(--muted);
                font-variant-numeric: tabular-nums; }
.lista .c-pulso { width: 7rem; }
.lista .c-pulso .pulse { width: 100%; height: 14px; display: block; }
.lista .c-acts { width: 1%; padding-left: 1rem; }
.lista .c-acts .tile-acts { margin-top: 0; flex-wrap: nowrap; justify-content: flex-end; }
/* Una fila caída se marca como una tarjeta caída: por el borde, no por el
   fondo, que a lo ancho de la pantalla sería un cartel. */
.lista tbody tr.down td:first-child { box-shadow: inset 2px 0 0 var(--down); }
.lista tbody tr.warn td:first-child { box-shadow: inset 2px 0 0 var(--warn); }

/* La tabla se sale de ancho antes que la rejilla: lo primero que sobra es el
   pulso, luego el puerto y la latencia. El dominio y los botones, nunca. */
@media (max-width: 1100px) { .lista .c-pulso { display: none; } }
@media (max-width: 860px) { .lista .c-num { display: none; } }
@media (max-width: 680px) { .lista td:nth-child(3), .lista th:nth-child(3) { display: none; } }

/* ======================================================================
   EL MÓVIL
   ======================================================================
   Este panel se abre desde el sofá tanto como desde el escritorio, y hasta
   aquí solo se había estrechado: las mismas piezas, más juntas. Estrecharse no
   es lo mismo que pensarse para un móvil — siete botones sueltos en la barra
   se convertían en dos filas que se comían un tercio de la pantalla, la tabla
   se salía por la derecha sin poder arrastrarla, y todo lo que hay que tocar
   medía menos que un dedo.

   Tres frenos, por orden: el tamaño de lo que se toca (que no depende del
   ancho sino del puntero), lo que se recoge en el menú, y lo que cambia de
   forma. */

/* ---------- lo que se toca con el dedo ---------- */
/* 44 px es la medida de Apple y la de Android es 48; por debajo de eso se
   falla el botón y se pulsa el de al lado. No va por ancho de pantalla sino
   por puntero: una tableta grande también se toca. */
/* NADA por debajo de 16 px en un campo, y esta vez de verdad.
 *
 * Safari en el iPhone —y Chrome en Android detrás— hace zoom él solo al
 * enfocar un campo cuya letra mida menos, y de ahí no se vuelve: la página se
 * queda torcida, más ancha que la pantalla, y hay que pellizcar para
 * recolocarla. Ya estaba puesto en la regla general, pero tres sitios la
 * pisaban después con tipografía más pequeña —el filtro del cuadro, el
 * selector de conversación y el de un sitio—, y son justo los tres que más se
 * tocan desde el móvil.
 *
 * `!important` porque esto no es una decisión de estilo que pueda perder una
 * discusión de especificidad con un ajuste tipográfico: es lo que impide que
 * el navegador te mueva la página debajo del dedo. El zoom a mano sigue donde
 * estaba — el `viewport` no lleva `maximum-scale`, que sería quitárselo a
 * quien lo necesita. */
@media (pointer: coarse) {
  input:not([type=checkbox]):not([type=radio]):not([type=button]):not([type=submit]),
  select, textarea { font-size: 16px !important; }
}

@media (pointer: coarse) {
  .btn, .segmento button, .tabs button, .picks label {
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  }
  /* Menos estas dos tarjetas: llevan un título y una explicación debajo, y
     centrar en fila las ponía una al lado de la otra —«Ficheros | Los sirve
     este proceso»— con el título partido en dos renglones. Lo que se agranda
     es la caja, no la forma de lo que hay dentro. */
  .picks label { flex-direction: column; align-items: flex-start;
                 justify-content: center; text-align: left; }
  .tile-acts .btn { padding: .4375rem .75rem; font-size: .78125rem; }
  /* Estos tres no son `.btn` y se quedaban a la mitad: la tira de la máquina,
     volver a capturar y la cabecera de un grupo plegable. Los tres se pulsan. */
  .vitals { min-height: 44px; }
  .sub-head, .fam-head { min-height: 44px; }
  input[type=password], input[type=text], input[type=search], select, textarea { min-height: 44px; }
  /* La casilla del navegador es de trece píxeles y no hay forma de agrandarla
     sin dibujarla entera; lo que se agranda es lo que se pulsa, que es toda la
     etiqueta. */
  .check.detenidos { min-height: 44px; padding: .5rem .75rem .5rem .625rem; }
  .check input { width: 20px; height: 20px; }
  /* Volver a capturar se queda en el icono. En táctil no hay «acercarse», así
     que este botón está puesto siempre; con la palabra entera era un cartel de
     dos centímetros tapando media captura, que es justo lo que se ha venido a
     mirar. Es además la acción menos frecuente de la tarjeta: la principal
     —abrir el sitio— es la miniatura entera. */
  .reshoot { opacity: 1; right: .375rem; top: .375rem; bottom: auto;
             padding: .5rem; min-height: 32px; }
  .reshoot .rlab { position: absolute; width: 1px; height: 1px;
                   overflow: hidden; clip-path: inset(50%); }
  .v-ep summary { min-height: 44px; align-items: center; }
}

/* Sin ratón no hay «acercarse», y el navegador deja el `:hover` pegado después
   de tocar: un botón se quedaba resaltado como si fuera el sitio donde estás.
   Aquí no resalta nada por pasar por encima, porque no se pasa por encima. */
@media (hover: none) {
  .btn:hover, .btn.quiet:hover { color: var(--muted); border-color: var(--line); background: none; }
  .btn.quiet:hover { border-color: transparent; }
  .btn.solid:hover { color: var(--canvas); opacity: 1; }
  .tile:hover { transform: none; border-color: var(--line); }
  .tile.down:hover { border-color: color-mix(in srgb, var(--down) 35%, var(--line)); }
  .lista tbody tr:hover, .dns tr:hover td, .task-head:hover { background: none; }
  .segmento button:hover { color: var(--muted); }
  .segmento button.on { color: var(--ink); }
}

/* ---------- el menú de la barra ---------- */
.menu-caja { position: relative; }
.men-btn { padding: .375rem .5rem; }
.men-btn.on { color: var(--ink); background: var(--sunk); border-color: var(--line); }
.acciones {
  position: absolute; z-index: 60; top: calc(100% + .5rem);
  right: 0; min-width: 14rem; padding: .375rem;
  display: flex; flex-direction: column; align-items: stretch;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 11px; box-shadow: var(--shadow);
}
.men {
  display: flex; flex-direction: column; align-items: flex-start; gap: .0625rem;
  border: 0; background: none; border-radius: 8px; text-align: left;
  padding: .5rem .625rem; font: 400 .875rem/1.3 var(--sans); color: var(--ink);
}
.men { text-decoration: none; }
.men:hover, .men:focus-visible { background: var(--sunk); }
.men-nota { font: 400 .75rem/1.3 var(--sans); color: var(--faint); }
.men-tit { padding: .5rem .625rem .25rem; font: 500 10.5px/1.4 var(--mono);
           text-transform: uppercase; letter-spacing: .09em; color: var(--faint); }
.men-sep { height: 1px; background: var(--line-soft); margin: .375rem .375rem; }
/* Lo que solo sale en un móvil, donde su botón no cabe en la barra. En ancho
   está fuera, y tenerlo también aquí sería preguntarse cuál de los dos hace
   algo. */
.solo-estrecho { display: none; }

/* En estrecho la barra no se parte: cabe porque casi todo está en el menú. Lo
   que queda es lo que se mira sin pulsar nada —la marca, el veredicto, la
   CPU—, y los bordes respetan la muesca del teléfono. */
@media (max-width: 72rem) {
  .top { position: relative; flex-wrap: nowrap; gap: .625rem;
         padding: .5rem max(.75rem, env(safe-area-inset-right))
                  .5rem max(.75rem, env(safe-area-inset-left)); }
  .top .ver { display: none; }
  .gauges { gap: .5rem; flex-wrap: nowrap; overflow: hidden; }
  /* Y que cedan de verdad: en una barra que no envuelve, un hijo solo se
     encoge por debajo de su contenido si se le quita el mínimo. Los medidores
     y el veredicto ceden; el menú no, que es el que tiene que caber. */
  .top > .gauges, .top > .vitals { min-width: 0; }
  .vitals { overflow: hidden; }
  .acciones { right: max(0px, calc(env(safe-area-inset-right) - .75rem)); }
}

/* En el móvil «Levantar un sitio» es una frase larga para una barra de 390 px:
   se queda en el signo, que al lado del menú se lee igual de bien. */
@media (max-width: 40rem) {
  /* En 390 px la marca es lo primero que sobra: el veredicto y la CPU dicen
     más, y el menú tiene que caber entero o no es un menú. */
  .top .name, .top .ver { display: none; }
  /* Sin la marca delante, los medidores dejan de estar empujados a la derecha:
     el veredicto vuelve a ser lo primero que se lee. */
  .gauges { margin-left: 0; margin-right: auto; }
  /* Y «Levantar un sitio» se va entero al menú. Antes se quedaba como un «+»
     al lado de la hamburguesa: dos botones pequeños seguidos, uno de ellos
     para lo que menos se hace desde un teléfono, y era justo el ancho que le
     faltaba a lo que sí se mira. */
  #new-site { display: none; }
  .men.solo-estrecho { display: flex; }
  .men-tit.solo-estrecho, .men-sep.solo-estrecho { display: block; }

  /* Aquí nada se aplasta: o cabe entero o no está.
   *
   * El reparto de más arriba —`min-width: 0` a los medidores y a la máquina,
   * y `overflow: hidden` para que no empujen— resuelve bien los anchos medios,
   * pero en 390 px se llevaba por delante lo único que quedaba en la barra:
   * el veredicto salía cortado a media palabra y sin puntos suspensivos, y la
   * cápsula de la máquina se quedaba con la palabra «CPU» y sin el número, que
   * es exactamente lo contrario de lo que hay que enseñar.
   *
   * De las cuatro cifras de la izquierda solo se queda el veredicto, que es lo
   * que se lee de un vistazo; las demás se consultan entrando. La máquina se
   * queda con su número pelado —la etiqueta ocupa más que el dato y, siendo el
   * único número de la cápsula, no hace falta decir de qué es—. Y si aun así
   * no cabe, quien cede es el veredicto, con puntos suspensivos de verdad. */
  .gauge { display: none; }
  .gauges, .vitals { overflow: visible; }
  .top > .vitals { flex: none; min-width: auto; }
  .vitals .v-lab { display: none; }
  .vitals .v-num { font-size: .8125rem; }
}

/* ---------- una tabla que no cabe se arrastra ---------- */
/* Los registros de DNS son cuatro columnas de datos técnicos: aplastarlos en
   un móvil partía los nombres letra a letra —«dcimo rra.e s»— y dejaba el
   botón de editar fuera de la pantalla, sin forma de llegar a él. Se arrastran
   de lado, que es lo que se hace con una tabla que no cabe. */
/* Y se ve que se arrastra. Una tabla cortada por la derecha sin ninguna señal
   parece una tabla rota; con la sombrita en el canto se entiende que hay más
   ahí. Es el truco de los degradados fijos y móviles: los dos primeros van con
   el contenido y tapan la sombra cuando ya no queda nada que ver, los dos
   últimos se quedan quietos en los cantos. Sin una línea de JavaScript. */
.tabla-ancha {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(to right, var(--surface) 40%, transparent) left / 2.5rem 100% no-repeat local,
    linear-gradient(to left, var(--surface) 40%, transparent) right / 2.5rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.11), transparent)
      left / .875rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.11), transparent)
      right / .875rem 100% no-repeat scroll;
}
@media (max-width: 46.25rem) {
  .dns th, .dns td { padding: .5rem .75rem; white-space: nowrap; }
  .dns .name, .dns .to { overflow-wrap: normal; max-width: none; }
}

/* ---------- el cuadro en un móvil ---------- */
@media (max-width: 40rem) {
  .board { padding: .875rem max(.875rem, env(safe-area-inset-right))
                   2rem max(.875rem, env(safe-area-inset-left)); }

  /* La captura ocupaba 244 px de alto en una pantalla de 664: un sitio por
     pantalla y treinta y cuatro pantallas de scroll. Recortada por arriba, que
     es donde está la cabecera de la web y lo que la hace reconocible. */
  .shot { aspect-ratio: 2/1; }

  /* Y la tabla deja de ser una tabla. Cuatro columnas en 390 px no son cuatro
     columnas: son cuatro columnas cortadas por la derecha, con los botones
     fuera de la pantalla y sin forma de llegar. Cada fila se convierte en un
     bloque —punto y dominio arriba, cómo está debajo, los botones en su línea—
     que es lo mismo que enseña la tarjeta cuando le quitas la foto. */
  .lista, .lista tbody, .lista tr, .lista td { display: block; }
  .lista thead { display: none; }
  .lista tbody tr {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    align-items: center; gap: .125rem .5rem;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--surface); padding: .625rem .75rem; margin-bottom: .5rem;
  }
  .lista tbody tr:last-child { margin-bottom: 0; }
  .lista td { border: 0; padding: 0; }
  .lista .c-dot { width: auto; grid-column: 1; line-height: 0; }
  .lista .c-dot .dot { display: inline-block; }
  .lista .c-host { grid-column: 2; max-width: none; }
  .lista .c-host .host { font-size: .8125rem; overflow-wrap: anywhere; }
  /* «Cómo está» pasa a la segunda línea, alineado bajo el dominio. */
  .lista td:nth-child(4) { grid-column: 2; color: var(--muted); font-size: .75rem; }
  .lista .c-acts { grid-column: 1 / -1; width: auto; padding: .5rem 0 0; }
  .lista .c-acts .tile-acts { flex-wrap: wrap; justify-content: flex-start; }
  /* El borde de color pasa a la caja entera: `td:first-child` ya no es una
     columna a la izquierda de nada. */
  .lista tbody tr.down td:first-child,
  .lista tbody tr.warn td:first-child { box-shadow: none; }
  .lista tbody tr.down { border-color: color-mix(in srgb, var(--down) 45%, var(--line)); }
  .lista tbody tr.warn { border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); }

  /* Cinco botones de 44 px no caben en 390: se partían en dos filas y dejaban
     «Registro» solo abajo, como si se hubiera caído. Los cuatro que cambian
     algo se reparten el ancho en una fila; al registro se llega tocando el
     nombre del sitio, que es lo que abre su ficha. */
  .tile-acts, .lista .c-acts .tile-acts { flex-wrap: nowrap; gap: .3125rem; }
  .tile-acts > .btn { flex: 1 1 0; min-width: 0; padding: .4375rem .25rem;
                      font-size: .71875rem; text-align: center; }
  .tile-acts > [data-tab="log"] { display: none; }

  /* La fila de herramientas: el filtro entero arriba, y debajo el interruptor
     y el conmutador repartiéndose el ancho. */
  .board-tools { gap: .5rem; }
  .board-tools #filtro { width: 100%; }
  #board-flags { flex: 1 1 auto; min-width: 0; }
  #board-vista { flex: none; }

  /* Los cuatro números de la máquina, de dos en dos: apilados eran cuatro
     pantallazos de tarjeta para leer cuatro cifras. */
  #v-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .v-card { padding: .75rem .875rem; }
  .v-card-num { font-size: 1.5rem; }

  /* Tres campos de dos dígitos no caben en tres columnas: las etiquetas se
     partían en tres líneas cada una y no se sabía cuál iba con cuál. */
  .field.row3, .field.row2 { grid-template-columns: 1fr; }

  /* Los diálogos, pegados a los bordes que hay y no a los que había. */
  .modal { padding: max(.75rem, env(safe-area-inset-top)) max(.75rem, env(safe-area-inset-right))
                   max(.75rem, env(safe-area-inset-bottom)) max(.75rem, env(safe-area-inset-left)); }
  .modal form { padding: 1.125rem; max-height: calc(100dvh - 1.5rem); }
  .rowacts, .steps, .facts { padding-left: 1rem; padding-right: 1rem; }
}

/* En el más estrecho de todos, el nombre del programa cede su sitio: el
   veredicto y la CPU dicen más. */
@media (max-width: 22.5rem) {
  .top .name { display: none; }
}

/* ---------- los desplegables en un móvil ----------
 *
 * En ancho, un desplegable sale pegado a su botón: es donde estabas mirando y
 * es lo que dice de dónde ha salido. En un móvil eso lo deja descentrado
 * siempre —el botón empieza a medio dedo del canto, el desplegable mide casi
 * la pantalla entera, y queda con cuatro píxeles a un lado y veinticinco al
 * otro—, y a veces colgando de un botón que ni está en el centro.
 *
 * Aquí el punto de referencia deja de ser el botón y pasa a ser la barra que lo
 * contiene: eso es lo que hace el `static` del medio, que empuja el anclaje al
 * ancestro posicionado siguiente. Con `left` y `right` iguales queda centrado
 * de verdad, no hay ancho que se pueda salir de la pantalla, y cae debajo de la
 * barra entera en vez de encima de ella. Nada de `100vw`, que cuenta la barra
 * de desplazamiento y saca la página unos píxeles a la derecha.
 *
 * Va al final del fichero a propósito: el bloque de `72rem` de más arriba
 * también le pone un `right` al menú de la barra, y a igual especificidad manda
 * el último que se lea. */
@media (max-width: 820px) {
  .top, .dhead, .chat-top { position: relative; }
  .picker, .menu, .menu-caja { position: static; }
  .picker-pop, .drop, .acciones {
    left: max(.75rem, env(safe-area-inset-left));
    right: max(.75rem, env(safe-area-inset-right));
    width: auto; min-width: 0; max-width: none;
  }
}
