/* Sistema visual del panel — dirección 2a «Mezcla» del handoff aprobado.
   Cabecera maciza + retícula de cifras + botones de decisión a todo el ancho,
   y fichas con borde, aire y etiquetas con palabra para el resto.

   Reescrito entero: la dirección anterior —fondo del documento como barra de
   progreso del plazo (`--progress`, `@property`, `data-state`)— se retira. El
   plazo se dice ahora con una cuenta atrás explícita dentro de la ficha de la
   solicitud, que es más claro y es lo que el responsable ha aprobado.

   Cuatro reglas que gobiernan todo lo de abajo:
     · el color NUNCA es el único indicador de estado — siempre hay palabra, y
       además tratamiento (borde izquierdo de la fila, borde de la etiqueta);
     · --mono es SOLO para datos de máquina: horas, coordenadas, cifras,
       identificadores, dominios. Todo lo humano va en --sans;
     · nada tocable por debajo de 44 px, y las filas de lista, 60;
     · cero emoji y ningún recurso remoto: ni CDN, ni fuentes web, ni imágenes.

   Medidas en px y no en rem a propósito: esto es una PWA de pantalla de inicio
   diseñada a 390 px de ancho, y el handoff da valores exactos —13, 14.5, 16.5,
   25— que redondear a rem convertiría en «casi». */

:root {
  color-scheme: light dark;

  --sans: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  --bg: #eceff2;
  --surface: #ffffff;
  --band: #f2f5f8;
  --bar: #16212a;
  --barInk: #f0f4f7;
  --ink: #121820;
  --ink2: #5b6572;
  --ink3: #88909d;
  --line: #dfe4ea;
  --accent: #0f6a58;
  --ok: #0f6a58;
  --bad: #ab332b;
  --warn: #845411;
  --shadow: 0 2px 10px rgba(18, 24, 32, .07);
}

/* Los dos temas, y el atributo GANA EN LOS DOS SENTIDOS. Son dos reglas y no
   una porque cada una responde a una pregunta distinta:

     :root[data-theme="dark"]                  elegir oscuro sobre un sistema
                                               claro tiene que oscurecer;
     :root:not([data-theme="light"]) dentro
     de @media (prefers-color-scheme: dark)    seguir al sistema, salvo que se
                                               haya elegido claro a mano.

   Sin el `:not(...)` de la segunda, poner `light` sobre un sistema oscuro no
   aclararía nada: la consulta de medios volvería a pintar los tokens oscuros.
   Y sin la primera, elegir oscuro sobre un sistema claro no haría nada. */
:root[data-theme="dark"] {
  --bg: #0d1013;
  --surface: #171c21;
  --band: #1d2429;
  --bar: #0f6a58;
  --barInk: #031310;
  --ink: #e8edf1;
  --ink2: #95a0aa;
  --ink3: #717c86;
  --line: #262e35;
  --accent: #47cfb0;
  --ok: #47cfb0;
  --bad: #ef9089;
  --warn: #dfae5f;
  --shadow: 0 2px 12px rgba(0, 0, 0, .45);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d1013;
    --surface: #171c21;
    --band: #1d2429;
    --bar: #0f6a58;
    --barInk: #031310;
    --ink: #e8edf1;
    --ink2: #95a0aa;
    --ink3: #717c86;
    --line: #262e35;
    --accent: #47cfb0;
    --ok: #47cfb0;
    --bad: #ef9089;
    --warn: #dfae5f;
    --shadow: 0 2px 12px rgba(0, 0, 0, .45);
  }
}

* { box-sizing: border-box; }

/* `hidden` tiene que ganarle a cualquier `display` de aquí abajo: lo llevan las
   piezas que solo existen con JavaScript —hoy, el conmutador de tema—, y sin
   esta regla un `display: flex` cualquiera las enseñaría igual. */
[hidden] { display: none !important; }

/* --- El armazón: barra maciza, área con scroll, nav abajo -----------------
   El documento entero es la altura de la ventana y quien tiene scroll es
   `main`, no el body. Es lo que mantiene la barra de arriba y la de pestañas
   siempre a la vista con el pulgar en cualquier sitio, que es la forma de esta
   aplicación: una mano, de pie, mirando una decisión. */

html { height: 100%; }

body {
  margin: 0;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--ink);
  font: 14.5px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* Diseñado a 390 px; escala hasta ~430 sin cambiar la estructura. A partir de
   ahí, columna centrada: estirar las fichas a mil píxeles de ancho rompería
   todas las proporciones del handoff sin ganar nada. */
@media (min-width: 700px) {
  body {
    max-width: 430px;
    margin-inline: auto;
    border-inline: 1px solid var(--line);
  }
}

/* La barra es una columna: arriba la fila de título + dato, y debajo lo que
   algunas pantallas metan dentro (en Config, el buscador y los chips). Con una
   sola fila el `gap` no se aplica, así que las demás pantallas no cambian. */
.barra {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 15px 18px;
  padding-top: calc(15px + env(safe-area-inset-top));
  background: var(--bar);
  color: var(--barInk);
}
.barra .titulos {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.barra h1 {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -.01em;
}
.barra .sub { margin: 2px 0 0; font-size: 12px; opacity: .75; }
/* El dato de la derecha es siempre de máquina: la hora, un recuento, una
   franja. De ahí el monoespaciado. */
.barra .dato {
  margin: 0;
  flex: none;
  font-family: var(--mono);
  font-size: 13px;
  text-align: right;
}
/* Salvo cuando no lo es. «cerrado», en el login, es una palabra del panel y no
   una medición: en monoespaciado se lee como si fuera un valor devuelto por
   algo, que es justo la distinción que --mono existe para marcar. */
.barra .dato[data-prosa] { font-family: var(--sans); }

main {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* Nota de implementación del handoff: las fichas son hijas de un contenedor
   flex con scroll, así que sin `flex: none` se comprimen y recortan lo que
   llevan dentro en cuanto la pantalla se queda corta. */
main > * { flex: none; }

/* Las páginas sin barra de pestañas —login y confirmación— no tienen quién les
   respete el recorte de abajo, así que se lo respeta el scroller. */
body:not([data-pestanas]) main {
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
}

/* La barra de pestañas: abajo del todo, en la zona del pulgar, cuatro celdas
   de 60 px. Rótulos escritos y ningún icono. La activa va rellena con el color
   de las barras macizas — el mismo recurso, no un color inventado para ella.
   El recorte del sistema lo absorbe el relleno de abajo, y por eso el fondo del
   contenedor es el de las celdas inactivas y no el de los separadores. */
.pestanas {
  flex: none;
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.pestanas a {
  flex: 1;
  min-height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  font-size: 12.5px;
  color: var(--ink2);
}
.pestanas a + a { border-left: 1px solid var(--line); }
.pestanas a[aria-current="page"] {
  background: var(--bar);
  color: var(--barInk);
  font-weight: 700;
}

/* --- Piezas del sistema ---------------------------------------------------
   Una sección se abre SIEMPRE con una barra rotulada o con una ficha de borde.
   Separar solo con aire era uno de los antipatrones descartados: sin línea de
   corte, seis bloques seguidos se leen como una lista larga. */

.rotulo {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 36px;
  padding: 9px 14px;
  border-radius: 11px;
  background: var(--bar);
  color: var(--barInk);
  font-size: 12.5px;
  font-weight: 700;
}
.rotulo > span:first-child { margin-right: auto; }
.rotulo .cuenta {
  font-family: var(--mono);
  font-weight: 400;
  opacity: .85;
}
/* Lo que hay que mirar dentro de una sección cerrada, dicho en el rótulo:
   «1 nueva», «2 temporales». Con palabra Y tratamiento, como todo estado aquí:
   el color solo acompaña. No abre nada — se ve DÓNDE mirar sin desplegar. */
.rotulo .aviso {
  padding: 1px 7px;
  border: 1px solid currentColor;
  border-radius: 8px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--warn);
}

.ficha {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow);
  overflow: hidden;
}

/* Una sección es su barra rotulada más lo que cuelga de ella. */
.seccion {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Una sola regla para la sección sin nada que hacer. La atenuación sale del
   DATO —lo pinta el servidor— y no de una clase que alguien pueda poner a mano
   sobre una sección que sí tiene trabajo. */
[data-vacia="true"] { opacity: .55; }

/* Texto corrido dentro de una ficha. `text-wrap: pretty` para que no quede una
   palabra sola en la última línea. */
.gloss {
  margin: 0;
  padding: 12px 14px;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--ink2);
  text-wrap: pretty;
}
.mono { font-family: var(--mono); }
.empty { color: var(--ink2); }
/* El error dice qué pasó y no se disculpa; el color lo confirma sin adornos, y
   nunca solo el color: la frase ya es la señal. */
.alert { color: var(--bad); }

/* --- Los modos de la política ---------------------------------------------
   Una franja maciza sobre la retícula. Lleva el peso de un rótulo y no el de
   una nota al pie a propósito: un modo cambia lo que significan las cuatro
   cifras de debajo, y el fallo que arregla es que se olvide que está puesto.
   El color acompaña; la frase es la señal, como en todas partes.

   En Config lleva además LA SALIDA, con su nombre escrito. Un modo del que
   solo se sale de rebote —guardando otra cosa— es un modo que se apaga sin
   querer, y este panel tiene dos que cambian lo que hace el iPhone entero. */
.modo {
  margin: 0 0 12px;
  padding: 11px 14px;
  border-left: 3px solid var(--warn);
  border-radius: 0 11px 11px 0;
  background: var(--band);
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.4;
  text-wrap: pretty;
}
main > .modo { margin-bottom: 0; }
.modo p { margin: 0; }
.modo .alert { margin-top: 8px; font-weight: 400; }
.modo form { margin-top: 11px; }
.modo button {
  min-height: 44px;
  border-color: var(--warn);
  color: var(--warn);
  background: transparent;
  font-size: 14.5px;
  font-weight: 700;
}

/* --- Retícula 2 × 2 de cifras --------------------------------------------
   Los separadores son el hueco de la reja sobre el fondo `--line`, no bordes:
   con bordes, la línea del medio saldría doble. */
.cifras {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
}
.cifras > div { background: var(--surface); padding: 13px 14px; }
.cifras dt { font-size: 12px; color: var(--ink2); }
.cifras dd { margin: 0; }
.cifras .cifra {
  margin-top: 3px;
  font-family: var(--mono);
  font-size: 25px;
  line-height: 1.2;
}
/* Cuando el valor es una palabra y no una cifra, deja de ser monoespaciado:
   «Activado» lo escribe el panel para una persona, no lo midió una máquina. */
.cifras .palabra {
  margin-top: 7px;
  font-size: 18px;
  font-weight: 700;
  line-height: 1.2;
}
.cifras .palabra[data-tono="ok"] { color: var(--ok); }
.cifras .palabra[data-tono="aviso"] { color: var(--warn); }
.cifras .pie { font-size: 12px; color: var(--ink2); }

/* --- La ficha de la solicitud --------------------------------------------- */

.solicitud { border-left: 4px solid var(--warn); }
.solicitud .cabeza {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 60px;
  padding: 14px 14px 12px;
}
.solicitud .cabeza > div:first-child { flex: 1; min-width: 0; }
.solicitud .titulo { margin: 0; font-size: 16.5px; font-weight: 600; }
.solicitud .pide { margin: 2px 0 0; font-size: 12.5px; color: var(--ink2); }
.solicitud .plazo { flex: none; text-align: right; }
.solicitud .countdown {
  margin: 0;
  font-family: var(--mono);
  font-size: 15px;
  color: var(--warn);
}
.solicitud .para { margin: 0; font-size: 11px; color: var(--ink2); }
/* La cita del motivo, en su propio bloque: son palabras de la otra persona y
   no del panel, y el subfondo es lo que lo dice. */
.solicitud .motivo {
  margin: 0 14px 14px;
  padding: 11px 13px;
  border-radius: 11px;
  background: var(--band);
  font-size: 14.5px;
  line-height: 1.5;
  text-wrap: pretty;
}

/* La fila de decisión y el botón partido.

   El desplegable es un <details>, así que abre y cierra sin JavaScript. Eso
   obliga a esta disposición: el navegador ESCONDE todo lo que no sea el
   <summary> mientras está cerrado, así que conceder y denegar no pueden vivir
   dentro del <details> —solo existirían con el desplegable abierto—. La
   solución es que el <details> ocupe la reja entera y que su <summary>, de 62
   px con márgenes automáticos, caiga exactamente en la columna del medio: el
   ancho de las dos columnas de los lados es el mismo, así que centrarlo es lo
   mismo que colocarlo. El panel de duraciones fluye debajo, a todo el ancho.

   Y se hace con el flujo normal en vez de metiendo el <details> en otra reja
   porque el navegador interpone SU PROPIA caja entre el <details> y lo que
   lleva dentro (`::details-content`): con `display: grid` en el <details>, el
   panel deja de ser hijo suyo y no hay `grid-area` que valga.

   Los dos formularios se pintan encima, en las celdas 1 y 3 de la primera
   fila. Se pintan por encima del <details> —y son ellos quienes reciben el
   toque— por `position: relative`, y NO por ir después en el marcado: un
   elemento posicionado se pinta sobre los que no lo están, sea cual sea el
   orden del documento. Antes se apoyaba en el orden, y eso obligaba a
   escribirlos al final, con lo que el foco del teclado recorría la fila al
   revés de como se ve. Con esto, el marcado va en el orden en que se lee
   —`Aprobar` · `otro` · `Denegar`— y el pintado sigue siendo el correcto. */
.decision {
  --partido: 1fr 62px 1fr;
  display: grid;
  grid-template-columns: var(--partido);
  grid-template-rows: 60px auto;
  column-gap: 1px;
  background: var(--line);
  border-top: 1px solid var(--line);
}
.decision > .otro { grid-area: 1 / 1 / 3 / -1; }
.decision > .otro > summary {
  width: 62px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 60px;
  background: var(--accent);
  color: var(--barInk);
  font-size: 9.5px;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}
/* El triángulo por defecto de Safari, fuera: el nuestro se dibuja con bordes,
   como el resto de las formas de la interfaz. */
.decision > .otro > summary::-webkit-details-marker { display: none; }
.decision .caret {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid currentColor;
}
.decision > .otro[open] .caret { transform: rotate(180deg); }
.decision > .conceder { grid-area: 1 / 1 / 2 / 2; display: flex; position: relative; }
.decision > .denegar { grid-area: 1 / 3 / 2 / -1; display: flex; position: relative; }
.decision > .conceder button,
.decision > .denegar button {
  flex: 1;
  min-height: 60px;
  padding: 0 10px;
  border: 0;
  border-radius: 0;
  font: inherit;
  font-size: 15px;
  cursor: pointer;
}
.decision > .conceder button {
  background: var(--accent);
  color: var(--barInk);
  font-weight: 700;
}
.decision > .denegar button { background: var(--surface); color: var(--ink); }

.duraciones {
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.duraciones .cabecera {
  margin: 0;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  background: var(--band);
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink2);
}
.duraciones .cabecera .suave { font-weight: 400; }
.duraciones button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 60px;
  padding: 0 16px;
  border: 0;
  border-bottom: 1px solid var(--line);
  /* El borde izquierdo está SIEMPRE, transparente cuando no toca: si apareciera
     solo en la fila elegida, esa fila se desplazaría tres píxeles respecto a
     las otras cinco. */
  border-left: 3px solid transparent;
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14.5px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}
.duraciones button:last-child { border-bottom: 0; }
/* Lo que ha pedido la otra persona, señalado con fondo Y con borde: el color
   por sí solo no distingue nada para quien no lo vea. */
.duraciones button[data-pedida] {
  background: var(--band);
  border-left-color: var(--accent);
}
.duraciones .nota {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  color: var(--ink2);
}
/* «Hasta las…»: la fila del selector de hora, con la misma altura y el mismo
   relleno que los botones para que la lista no dé un salto. El campo es un
   <input type="time"> a secas —en el móvil abre el selector nativo del
   sistema— y va con su etiqueta, no con un placeholder. */
.duraciones .hora {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 60px;
  padding: 0 16px;
  border-bottom: 1px solid var(--line);
  border-left: 3px solid transparent;
  font-size: 14.5px;
  font-weight: 500;
}
.duraciones .hora input {
  min-height: 44px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 16.5px;
}

/* --- El registro del día --------------------------------------------------
   Hora a la izquierda en columna fija de 40 px, para que las seis horas caigan
   en la misma vertical y la lista se lea de un barrido. */
.registro {
  margin: 0;
  padding: 0;
  list-style: none;
}
.registro li {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 60px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
}
.registro li:last-child { border-bottom: 0; }
.registro time {
  flex: none;
  width: 40px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink3);
}
.registro .que { display: block; font-size: 14.5px; line-height: 1.4; }
.registro .quien {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--ink3);
}

/* Una fila de ajuste: título y descripción a la izquierda, valor a la derecha.
   El valor va en pastilla monoespaciada porque es lo que está puesto ahora
   mismo, un dato y no una frase. */
.ajuste {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 60px;
  padding: 9px 14px;
}
.ajuste .titulo { margin: 0; font-size: 15.5px; font-weight: 500; }
.ajuste .detalle { margin: 2px 0 0; font-size: 12.5px; color: var(--ink2); }
.ajuste button {
  width: auto;
  min-height: 44px;
  padding: 0 12px;
  border-radius: 8px;
  background: var(--band);
  font-family: var(--mono);
  font-size: 12.5px;
}

/* De quién es la sesión, y la salida al lado. No es una acción principal —no lo
   es nunca— así que no se le da una fila entera ni sitio en la barra. */
.sesion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 60px;
  padding: 9px 14px;
}
.sesion p { margin: 0; font-size: 12.5px; color: var(--ink2); }
.sesion button { width: auto; min-height: 44px; padding: 0 14px; }

/* --- Controles ------------------------------------------------------------ */

button, input, textarea, select {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0 14px;
  min-height: 60px;
  width: 100%;
}
button { cursor: pointer; }
button[data-weight="primary"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--barInk);
  font-weight: 700;
}
button[data-weight="quiet"] { background: var(--surface); color: var(--ink2); }

.stack { display: flex; flex-direction: column; gap: 12px; padding: 14px; }
label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12.5px;
  color: var(--ink2);
}
label input, label textarea {
  color: var(--ink);
  background: var(--band);
  padding: 15px 14px;
}
textarea { min-height: 8rem; resize: vertical; line-height: 1.6; }

/* La casilla: fila de 60 px con el cuadro a la izquierda, como en el handoff. */
.choice {
  flex-direction: row;
  align-items: center;
  gap: 12px;
  min-height: 60px;
  font-size: 14.5px;
  color: var(--ink);
}
.choice input {
  flex: none;
  width: 23px;
  height: 23px;
  min-height: 0;
  padding: 0;
  border-radius: 6px;
  accent-color: var(--accent);
}

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

/* --- Pantallas todavía sin convertir --------------------------------------
   Config, Ubicación, Ajustes y las dos pantallas sueltas (login y confirmar)
   son de las tareas siguientes. Aquí se les da el lenguaje nuevo —tokens,
   tipografía, radios y tamaños táctiles— sin rehacerles el marcado, para que
   ninguna quede en el sistema viejo mientras tanto. */

.state {
  margin: 0;
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.25;
}
/* Lo que se está a punto de hacer, con el mismo peso que el nombre de la
   acción: es la mitad de la frase que la titular tiene que leer antes de
   teclear el código, y en gris pequeño no se lee. El borde izquierdo es el
   mismo que marca la ficha de la solicitud, para que se reconozca de dónde
   viene. Va apuntada al formulario de la confirmación —y no suelta— porque
   `detalle` es una palabra que otra pantalla puede querer usar. */
.state + .detalle {
  margin: 10px 0 0;
  padding: 10px 14px;
  border-left: 3px solid var(--accent);
  background: var(--band);
  border-radius: 0 11px 11px 0;
  font-size: 16.5px;
  font-weight: 600;
  line-height: 1.35;
}

.readout {
  margin: 0;
  display: flex;
  flex-direction: column;
}
.readout div {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 60px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--line);
}
.readout div:last-child { border-bottom: 0; }
.readout dt { font-size: 14.5px; }
.readout dd { margin: 0; font-family: var(--mono); font-size: 14.5px; }

/* --- Config: qué se puede usar --------------------------------------------

   Lo que se lee en voz alta y no se ve. Lo llevan los controles cuya etiqueta
   visible es el ESTADO —la fila de una app dice «Bloqueada», el chip de una
   web dice el dominio, el interruptor no dice nada—: sin esto, un lector de
   pantalla anunciaría un botón sin decir qué hace al pulsarlo. No es
   `display:none`, que lo sacaría también del árbol de accesibilidad. */
.hace {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* El buscador, dentro de la barra maciza. Llega `hidden` del servidor y lo
   enseña panel.js: sin JavaScript no hay quien filtre por texto y un campo
   pintado que no hace nada es peor que no tenerlo. */
.buscador {
  flex-direction: row;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 13px;
  border-radius: 12px;
  background: rgba(255, 255, 255, .13);
  color: var(--barInk);
}
.buscador .lupa {
  flex: none;
  width: 11px;
  height: 11px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  opacity: .7;
}
.buscador .etiqueta { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.buscador input {
  min-height: 44px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--barInk);
  font-size: 14.5px;
}
.buscador input::placeholder { color: currentColor; opacity: .72; }

/* Los chips de estado. Son radios de verdad —no enlaces— porque filtrar no
   navega a ninguna parte y, sobre todo, NO CAMBIA EL ESTADO DE NADA: no hay
   formulario, no se envía nada y ninguna fila se toca por mirarla de otra
   manera. El filtrado lo hace el CSS de más abajo con `:has()`. */
.filtros { display: flex; gap: 7px; overflow-x: auto; }
.filtro-radio { position: absolute; opacity: 0; pointer-events: none; }
.filtros label {
  flex: none;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  padding: 0 13px;
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: 20px;
  color: var(--barInk);
  font-size: 12.5px;
  white-space: nowrap;
  cursor: pointer;
}
.filtros .filtro-radio:checked + label {
  background: var(--barInk);
  border-color: var(--barInk);
  color: var(--bar);
  font-weight: 700;
}
.filtros .filtro-radio:focus-visible + label { outline: 2px solid var(--barInk); }

/* Filtrar es esconder, y se esconde también el grupo entero que se queda sin
   filas: una barra rotulada con una ficha vacía debajo promete algo que no
   está. Una regla por chip; la de «Todas» no hace falta porque no esconde
   nada. Cuando la Task 39 traiga «programada», su chip ya tiene la suya. */
body:has(#filtro-permitida:checked) .fila:not([data-estado="permitida"]),
body:has(#filtro-bloqueada:checked) .fila:not([data-estado="bloqueada"]),
body:has(#filtro-temporal:checked) .fila:not([data-estado="temporal"]),
body:has(#filtro-programada:checked) .fila:not([data-estado="programada"]) {
  display: none;
}
body:has(#filtro-permitida:checked) .apps .grupo:not(:has([data-estado="permitida"])),
body:has(#filtro-bloqueada:checked) .apps .grupo:not(:has([data-estado="bloqueada"])),
body:has(#filtro-temporal:checked) .apps .grupo:not(:has([data-estado="temporal"])),
body:has(#filtro-programada:checked) .apps .grupo:not(:has([data-estado="programada"])) {
  display: none;
}

/* Y un chip activo tiene que ENSEÑAR lo que dice que cuenta: con las
   categorías colapsadas, «Bloqueadas 2» sobre doscientas filas cerradas no
   enseñaría ninguna, que es un control que no hace nada. Sin JavaScript la
   única llave es `::details-content`, la caja que el navegador interpone
   dentro del <details> —la misma que rompió la retícula de Estado—: aquí se
   usa a favor, para levantar el `content-visibility: hidden` que le pone el
   navegador cuando está cerrado.

   Donde no exista el pseudoelemento, la regla entera se ignora y el filtro
   sigue funcionando con las secciones que estén abiertas: degrada a menos, no
   a roto. El chevron se apaga mientras dura, para que no diga «cerrada» algo
   que se está viendo. */
body:has(.filtros .filtro-radio:checked:not(#filtro-todas)) .apps .grupo::details-content {
  content-visibility: visible;
}
body:has(.filtros .filtro-radio:checked:not(#filtro-todas)) .apps .grupo > summary::after {
  opacity: 0;
}
/* Y el buscador, cuando hay JavaScript: lo que no casa se marca desde
   panel.js con un atributo y el CSS lo esconde. */
.fila[data-fuera] { display: none; }

/* La fila de una app: tile 44, nombre y subtexto, etiqueta de estado. La fila
   ENTERA es el botón —tocarla es lo que la bloquea o la permite— y por eso el
   <form> no pinta nada: solo lleva el CSRF y el bundle. */
.filas .fila { display: block; border-bottom: 1px solid var(--line); }
.filas .fila:last-child { border-bottom: 0; }
.filas .fila button {
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  min-height: 60px;
  padding: 9px 14px;
  border: 0;
  /* El borde izquierdo está SIEMPRE, transparente cuando no toca: si apareciera
     solo en las bloqueadas, esas filas se desplazarían tres píxeles. */
  border-left: 3px solid transparent;
  border-radius: 0;
  background: var(--surface);
  text-align: left;
}
.filas .fila button[data-tono="malo"] { border-left-color: var(--bad); }
.filas .fila button[data-tono="aviso"] { border-left-color: var(--warn); }

/* El icono. En el handoff es un monograma de dos letras sobre un tono derivado
   del nombre —«en producción se sustituyen por los iconos reales de las apps»,
   que MDM no nos da—, y apagado cuando la app está bloqueada: es la cuarta
   señal del estado, después de la palabra, la forma y el borde. */
.fila .tile {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: hsl(var(--matiz, 210) 34% 40%);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .01em;
}
.fila button[data-tono="malo"] .tile {
  background: var(--band);
  border: 1px solid var(--line);
  color: var(--ink3);
}
.fila .texto { flex: 1; min-width: 0; }
.fila .nombre {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 15.5px;
  font-weight: 500;
  color: var(--ink);
}
.fila button[data-tono="malo"] .nombre { color: var(--ink2); }
.fila .sub {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.35;
  color: var(--ink2);
}
/* «Nueva» es ámbar y va arriba del todo (lo ordena el servidor): el estado por
   defecto de una app es mostrarse, así que instalar algo la habilita sola y
   esto es el contrapeso. Es ORTOGONAL al estado, no un cuarto estado. */
.fila .nueva {
  flex: none;
  padding: 2px 7px;
  border: 1px solid var(--warn);
  border-radius: 8px;
  color: var(--warn);
  font-size: 11.5px;
  font-weight: 600;
}

/* La etiqueta de estado: palabra SIEMPRE, y además forma y color. La forma es
   lo que la hace legible para quien no distinga los colores. */
.fila .etiqueta {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
}
.fila button[data-tono="ok"] .etiqueta { color: var(--ok); }
.fila button[data-tono="malo"] .etiqueta {
  background: var(--band);
  border-color: var(--bad);
  color: var(--bad);
}
.fila button[data-tono="aviso"] .etiqueta {
  background: var(--band);
  border-color: var(--warn);
  color: var(--warn);
}
.fila button[data-tono="neutro"] .etiqueta {
  background: var(--band);
  color: var(--ink2);
}
.fila .punto { flex: none; width: 8px; height: 8px; background: currentColor; }
.fila button[data-forma="punto"] .punto { border-radius: 50%; }
.fila button[data-forma="rombo"] .punto { transform: rotate(45deg); }
/* «Programada» (Task 39): una app con horario propio. Media luna de reloj
   dibujada con bordes, como el resto de las formas de la interfaz. */
.fila button[data-forma="reloj"] .punto {
  background: transparent;
  border: 2px solid currentColor;
  border-radius: 50%;
  border-top-color: transparent;
}

/* Con el bloqueo global puesto las filas se atenúan, pero NO se esconden: se
   sigue pudiendo preparar la política que quedará al levantarlo. */
[data-atenuada="true"] .filas { opacity: .5; }

/* --- Las categorías, colapsables ------------------------------------------

   Son más de doscientas apps. Cada categoría es un <details> de verdad, así
   que abre y cierra sin una línea de JavaScript, y quién arranca abierta lo
   decide el servidor (ver portada._datos_config): con envío por fila cada
   toque recarga, y un estado que viviera en el navegador se perdería entero
   en cada cambio.

   AVISO, que ya costó una pantalla: en Chrome `::details-content` se interpone
   entre el <details> y su contenido, y `grid-area` NO lo atraviesa. Por eso
   aquí no hay ninguna reja: el <summary> y la ficha van en flujo normal. */
.apps .grupo { margin: 0; }
.apps .grupo > summary {
  cursor: pointer;
  list-style: none;
  /* El área táctil de 44 px la da el relleno del rótulo más este mínimo. */
  min-height: 44px;
}
.apps .grupo > summary::-webkit-details-marker { display: none; }
/* El chevron, dibujado con bordes y no con un carácter: una flecha de texto
   cambia de forma con la fuente y se lee en voz alta. Gira al abrir, y la
   palabra la dice el propio <summary>, así que el estado no depende del giro. */
.apps .grupo > summary::after {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin-left: 2px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  opacity: .8;
}
.apps .grupo[open] > summary::after {
  transform: rotate(-135deg) translate(-2px, -2px);
}
/* El nombre empuja al resto al extremo. Va en `.rotulo` y no en Config
   porque el rótulo puede llevar ahora tres cosas —nombre, avisos y cuenta— y
   `space-between` solo reparte bien dos. */
.apps .grupo[open] > summary { border-radius: 11px 11px 0 0; }
.apps .grupo[open] > .filas { border-radius: 0 0 14px 14px; }
/* La categoría que trae algo que mirar —una app nueva, o una concesión viva—
   se distingue del resto aunque esté abierta: es por lo que se abrió sola. */
.apps .grupo[data-atencion="true"] > summary { box-shadow: inset 3px 0 0 var(--warn); }

/* Las webs, como etiquetas monoespaciadas con su aspa. El borde izquierdo rojo
   dice «bloqueada» sin depender del color, igual que en las filas. */
.etiquetas {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 13px;
  overflow: visible;
}
.etiquetas .web { display: flex; }
.etiquetas .web button {
  display: flex;
  align-items: center;
  gap: 7px;
  width: auto;
  min-height: 44px;
  padding: 0 11px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--bad);
  border-radius: 9px;
  background: var(--band);
  font-family: var(--mono);
  font-size: 12.5px;
}
/* El aspa se dibuja con dos trazos, no con un carácter: una «×» de texto se lee
   en voz alta como «por» y cambia de forma con la fuente. */
.etiquetas .aspa {
  flex: none;
  position: relative;
  width: 11px;
  height: 11px;
  color: var(--ink3);
}
.etiquetas .aspa::before, .etiquetas .aspa::after {
  content: "";
  position: absolute;
  top: 5px;
  left: 0;
  width: 11px;
  height: 1.5px;
  background: currentColor;
}
.etiquetas .aspa::before { transform: rotate(45deg); }
.etiquetas .aspa::after { transform: rotate(-45deg); }

.etiquetas .anadir { flex: 1 1 100%; }
.etiquetas .anadir > summary {
  display: flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  min-height: 44px;
  padding: 0 13px;
  border: 1px dashed var(--ink3);
  border-radius: 9px;
  color: var(--ink2);
  font-size: 12.5px;
  cursor: pointer;
  list-style: none;
}
.etiquetas .anadir > summary::-webkit-details-marker { display: none; }
.etiquetas .anadir[open] > summary { border-style: solid; }
.etiquetas .anadir > form { padding: 12px 0 0; }
.etiquetas .anadir input { font-family: var(--mono); }

/* La fila de un ajuste con su control a la derecha: el filtro de adulto. */
.fila-ajuste {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 60px;
  padding: 13px 14px;
}
.fila-ajuste .texto { flex: 1; min-width: 0; }
.fila-ajuste .titulo {
  margin: 0;
  font-size: 15.5px;
  font-weight: 500;
  text-wrap: balance;
}
.fila-ajuste .apunte { margin: 3px 0 0; font-size: 12.5px; color: var(--ink2); }
/* Lo que pasa AHORA cuando no es lo que dice el control. En ámbar y con todas
   las letras: el control gobierna la intención, esta línea el efecto. */
.fila-ajuste .efecto {
  margin: 6px 0 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--warn);
  text-wrap: pretty;
}
.fila-ajuste .mando {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* La palabra va DELANTE del interruptor y no depende de él: el estado nunca se
   indica solo por la posición de un pomo. */
.fila-ajuste .palabra { font-size: 12.5px; font-weight: 700; }
.fila-ajuste .palabra[data-tono="ok"] { color: var(--ok); }
.fila-ajuste .palabra[data-tono="aviso"] { color: var(--ink2); }

/* El interruptor: un botón de envío vestido de interruptor. Sin JavaScript
   sigue siendo un formulario que se manda. El área tocable es de 44 px aunque
   la pista mida 31, con relleno transparente. */
.interruptor {
  position: relative;
  width: 52px;
  min-height: 31px;
  height: 31px;
  padding: 3px;
  border: 0;
  border-radius: 10px;
  background: var(--line);
}
.interruptor[aria-pressed="true"] { background: var(--accent); }
.interruptor .pomo {
  display: block;
  width: 25px;
  height: 25px;
  border-radius: 7px;
  background: var(--surface);
  box-shadow: var(--shadow);
  transition: transform 150ms ease-out;
}
.interruptor[aria-pressed="true"] .pomo { transform: translateX(21px); }
.interruptor::after {
  content: "";
  position: absolute;
  inset: -7px -2px;
}

/* La ficha de la acción amplia. Borde rojo, cabecera rellena y el botón de
   CONTORNO: distinto sin dar miedo. Un botón relleno de rojo a todo el ancho
   se pulsa por reflejo. */
.grave {
  border: 1px solid var(--bad);
  border-radius: 14px;
  overflow: hidden;
}
.grave .cabecera {
  margin: 0;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 14px;
  background: var(--bad);
  color: var(--surface);
  font-size: 12.5px;
  font-weight: 700;
}
.grave .cabecera .suave { font-weight: 400; opacity: .9; }
.grave .cuerpo { padding: 14px; background: var(--band); }
.grave .titulo { margin: 0; font-size: 15.5px; font-weight: 600; }
.grave .apunte {
  margin: 7px 0 13px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink2);
  text-wrap: pretty;
}
.grave button {
  border: 1px solid var(--bad);
  border-radius: 12px;
  background: transparent;
  color: var(--bad);
  font-size: 15px;
  font-weight: 700;
}

.limites { padding: 0 0 4px; }
.limites .gloss { padding-top: 8px; }

/* Lo grave se distingue por el color, por el borde y por la palabra —«Terminar
   el acuerdo»—, nunca por un icono ni por una caja de alerta. El botón va de
   contorno y no relleno de rojo: distinto sin dar miedo. */
.avanzados .rotulo { background: var(--bad); }
.avanzados button[data-weight="primary"] {
  background: transparent;
  border: 1px solid var(--bad);
  color: var(--bad);
}

/* La tira de acciones de la pantalla de confirmación, que no tiene barra de
   pestañas debajo: se apoya en el borde inferior y respeta el recorte. */
.thumb {
  flex: none;
  display: flex;
  gap: 1px;
  background: var(--line);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.thumb form { flex: 1; display: flex; }
.thumb form button, .thumb > button { flex: 1; border-radius: 0; border: 0; }

/* Ubicación: --mono solo para lo que midió una máquina —la hora, la coordenada
   y el margen—, y una sola superficie para la lista entera en vez de cincuenta
   fichas, que dirían «tócame» sin llevar a ningún sitio. */
.ubicacion .ultima {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px;
  border-radius: 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.ubicacion .coord { font-family: var(--mono); }
.ubicacion .ultima > .coord { font-size: 21px; letter-spacing: -.01em; }
.ubicacion .cuando { font-size: 12.5px; color: var(--ink2); }
.ubicacion .antes { display: flex; flex-direction: column; gap: 8px; }
.ubicacion .historial {
  margin: 0;
  padding: 0;
  list-style: none;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.ubicacion .historial li {
  display: flex;
  align-items: center;
  gap: 13px;
  flex-wrap: wrap;
  min-height: 60px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--line);
}
.ubicacion .historial li:last-child { border-bottom: 0; }
.ubicacion .historial time {
  flex: none;
  width: 40px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink3);
}
.ubicacion .historial .donde {
  flex: 1;
  text-align: right;
  font-family: var(--mono);
  font-size: 12.5px;
}
/* El día abre el grupo: es el encabezado de la medición que lo estrena, no un
   dato más de esa medición. */
.ubicacion .historial .dia {
  flex-basis: 100%;
  font-size: 12px;
  color: var(--ink2);
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
