/* ==========================================================================
   movil.css · LA HOJA QUE CORRIGE EL MÓVIL

   La cargan LAS DOS cáscaras —`layout.html.twig` (web de venta) y
   `_cascara.html.twig` (aplicación)— y SIEMPRE la última:

       app.css  →  cascara.css  →  movil.css

   Ese orden NO ES NEGOCIABLE, y no es una preferencia de estilo. Dos motivos
   concretos:

   1 · Esta hoja corrige reglas de `app.css` (`.nav` con `order:3;width:100%`,
       apartado 4.1 de la especificación). Una corrección que cargue antes de lo
       que corrige no corrige nada.
   2 · El hueco reservado del aviso de instalar (bloque 3) y el hueco reservado
       de la barra inferior (`cascara.css`) casan LOS DOS a la vez en cuanto la
       tarjeta se muestra dentro de la aplicación. Los dos declaran
       `padding-bottom` sobre el `body` con la misma especificidad, así que gana
       el último por orden de cascada — y tiene que ganar el de aquí, que es el
       único que SUMA las dos alturas. Si esta hoja cargara antes, la tarjeta se
       plantaría encima de la barra y de las últimas filas de `/nuevo`, que es
       exactamente el fallo que `app-ui.css` ya documenta haber sufrido una vez
       con `.barra-acciones`.

   NI UN HEXADECIMAL, SÓLO `var()`. `app.css` está intocable mientras dure este
   trabajo y sus tokens los puede estar moviendo otro agente ahora mismo. Con
   colores propios esta hoja se desincronizaría en silencio; con `var()` el
   fallo se ve de golpe. Es el riesgo 1 del apartado 10.

   Contiene tres bloques y nada más:
     1 · La cabecera de venta en una sola línea (apartado 4.2).
     2 · El menú `<details>` (apartado 4.5).
     3 · El aviso de instalar y su hueco reservado (apartado 3.3).
   ========================================================================== */


/* ==========================================================================
   1 · CABECERA DE VENTA EN MÓVIL · UNA SOLA LÍNEA

   Corrige `app.css:321-325`, donde `.nav` es `order:3;width:100%` y por tanto
   se lleva una fila entera para ella sola, fila que además parte en dos porque
   `.nav` también envuelve. Una fila de marca + dos de navegación = tres líneas,
   con la cuenta abierta y sin ella.

   Se gana por especificidad, no por `!important`: `.site-head .nav` es (0,2,0)
   contra la `.nav` de `app.css`, que es (0,1,0). Cuando el agente de la portada
   suelte `app.css`, este bloque entero se dobla allí (fase 5).
   ========================================================================== */

@media (max-width:819px){

  .site-head .nav{display:none}     /* los mismos enlaces viven en el menú */
  .site-head .menu{display:block}

  /* 1 · Ya no hay nada que envolver…

     `padding-block:6px` NO estaba en el fragmento del apartado 4.2 y se añade
     aquí porque sin él la verificación 11.3 no puede pasar. Medido: la fila más
     alta de esta cabecera es la hamburguesa, 44px de zona táctil; con los
     `padding:12px 0` de `app.css:301` el total sale 44+24+1 = 69px, y 11.3 pide
     ≈56. Con 6px arriba y 6px abajo salen 44+12+1 = 57. Es además lo que hace
     que cambiar de la web de venta a la cáscara de aplicación no dé un salto:
     `.cabecera-app` mide 56 clavados.

     Y `padding-inline:var(--pad)` DEVUELVE el margen lateral que `.wrap` ya
     declara (`app.css:252`) y que el atajo `padding:12px 0` de `:301` había
     puesto a cero de paso, sin quererlo: escribir el atajo para fijar el eje
     vertical arrasa también el horizontal. Medido: con la cabecera a tres
     líneas casi no se notaba, pero en una sola línea el logotipo arranca en el
     píxel 0 mientras todo lo que hay debajo empieza en el 20, y la hamburguesa
     pega su círculo contra el canto de la pantalla. Con esto la cabecera de
     venta queda alineada con su propio contenido y con la cabecera de la
     aplicación, que es lo que hace que pasar de una cáscara a otra no dé un
     salto. Se escriben los dos ejes por separado y no el atajo, para no repetir
     el mismo accidente. */
  .head-in{
    flex-wrap:nowrap; gap:var(--sp-2);   /* 8px, no 14 */
    padding-block:6px;
    /* Los `env()` repiten lo que `.wrap` ya declara en `app.css`. Hay que
       repetirlos porque esta regla vuelve a escribir `padding-inline` entero y
       lo que no se repita se pierde: sin ellos, en horizontal con notch la
       cabecera de venta sería la única fila del sitio metida debajo del
       recorte. En un teléfono sin recorte los dos valen 0. */
    padding-inline:
      calc(var(--pad) + env(safe-area-inset-left))
      calc(var(--pad) + env(safe-area-inset-right));
  }

  /* 2 · …PERO SÍ TIENE QUE PODER ENCOGER, y esto es lo que faltaba en la
     revisión anterior (fallo #9). `app.css:303` declara `.brand{flex:none}` y
     `:1047` `.head-sesion{flex:none}`, o sea `flex-shrink:0`. Con
     `flex-wrap:nowrap` nada puede envolver y con `flex:none` nada puede
     encoger: el contenido se sale por la derecha y, como `.head-in` es
     `justify-content:space-between`, lo que se sale es el ÚLTIMO elemento, que
     es la hamburguesa. O sea la única salida de sesión que tiene la web de
     venta en móvil. Un elemento flex no encoge por debajo de su tamaño mínimo
     de contenido mientras `min-width` valga `auto`; ponerlo a 0 es lo que
     desbloquea el encogimiento. */
  .head-in > .brand,
  .head-in > .head-sesion{min-width:0}
  .head-sesion .btn{min-width:0; white-space:nowrap}
}

/* 3 · Y por debajo de 420px el rótulo largo se cambia por el corto. Dos
   <span> en el marcado y un `display` en CSS: el que está oculto NO entra en el
   árbol de accesibilidad, así que el nombre accesible del botón es siempre el
   que se ve. Cero líneas de JavaScript, y ningún desajuste posible entre lo que
   se lee y lo que se anuncia.

   Las cuentas a 320px, con los valores reales de `app.css` (`body` a 17px):
   marca ≈118 + botón corto ≈80 + hamburguesa 44 + dos huecos de 8 = ≈258px,
   contra 280px útiles (`--pad` clavado en su mínimo de 20px). Cabe con holgura.
   Con el rótulo largo serían ≈334px y desbordaría 54px. */
.btn-rotulo-corto{display:none}
@media (max-width:419px){
  .head-sesion .btn-rotulo-largo{display:none}
  .head-sesion .btn-rotulo-corto{display:inline}
}

@media (min-width:820px){
  /* A partir de aquí la navegación vuelve a ser la fila de `.nav` y el menú
     sobra. OJO: esto esconde SÓLO el menú de la web de venta. El de la cáscara
     de aplicación (`.cabecera-app-in > .menu`) sigue viéndose en escritorio,
     porque ahí guarda los enlaces legales y «Salir», que no están entre los
     cuatro destinos. */
  .site-head .menu{display:none}
}


/* ==========================================================================
   2 · EL MENÚ · un `<details>`, no un botón con JavaScript

   El elemento nativo se abre y se cierra solo. `ui/menu.js` sólo AÑADE Escape,
   clic fuera y la sincronía de `aria-expanded`; si ese módulo no carga, el menú
   sigue funcionando y con él la salida de sesión, que es lo único que este
   proyecto no deja depender de nada (ver el docblock de `LogoutWebAction`).
   ========================================================================== */

.menu{position:relative}

/* 44×44 de zona táctil con un glifo de 24 dentro: el área es lo que se toca,
   el dibujo es lo que se ve. `list-style:none` + el pseudoelemento de WebKit
   quitan el triangulito nativo del `<summary>`, que en las dos plataformas se
   dibuja de una forma distinta y en ninguna se parece a una hamburguesa. */
.menu-btn{
  list-style:none;
  width:44px;height:44px;
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--r-pill);color:var(--ink-soft);cursor:pointer;
  /* Cromo: sin recuadro gris de toque, sin espera de doble toque y sin que se
     pueda seleccionar el glifo de la hamburguesa. */
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
}
.menu-btn::-webkit-details-marker{display:none}
.menu-btn:hover{color:var(--teal-ink);background:var(--tint-teal)}
.menu-btn:active{color:var(--teal-ink);background:var(--tint-teal)}
.menu-btn:focus-visible{outline:3px solid var(--focus);outline-offset:2px}
.menu[open] .menu-btn{color:var(--teal-ink);background:var(--tint-teal)}

/* EL MENÚ SE VA AL BORDE DERECHO EN LA CÁSCARA DE APLICACIÓN.

   `.cabecera-app-in` es una rejilla de tres pistas (`cascara.css`) y por debajo
   de 820px la fila de destinos va `display:none`, o sea que SALE de la rejilla y
   deja libre la pista del medio. Sin esto, en las variantes `marca` y `titulo`
   el menú cae por colocación automática en esa pista flexible y se pinta pegado
   al logotipo o al título en vez de en el margen — se ve a simple vista en
   `/pwa`, `/acuerdos` y `/cuenta`.

   Se le da la TERCERA pista explícitamente (`-2 / -1` es la última, cuente la
   rejilla las pistas que cuente) y no sólo `justify-self:end`: con `justify-self`
   a secas el menú se quedaba en la pista del medio y entre él y el margen
   asomaba el hueco de 8px que la rejilla deja hacia la tercera pista vacía. Con
   la pista explícita el botón acaba clavado en el margen, medido a 320, 360 y
   390.

   Se apunta al `.menu` y no a `:last-child` a propósito: en `/a/{token}` el
   único hijo de esa cabecera es el logotipo, y con `:last-child` el logotipo se
   iría a la derecha.

   Va dentro de la consulta de medios porque a partir de 820px la fila de
   destinos SÍ es una pista ocupada: fijar ahí el menú a la tercera lo pondría
   encima de ella y la colocación automática empujaría los destinos a una
   segunda fila. */
@media (max-width:819px){
  .cabecera-app-in > .menu{grid-column:-2 / -1; justify-self:end}
}

/* z-index 45: por encima de `.site-head` (40, `app.css:294`) y por debajo de
   `.nav-app` (60) y del banner de cookies (70). El panel tapa la cabecera, pero
   nunca la barra de destinos ni una decisión sobre cookies. */
.menu-panel{
  position:absolute;top:calc(100% + var(--sp-2));right:0;z-index:45;
  min-width:15rem;
  background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-2);
  box-shadow:var(--sh-s);
  padding:var(--sp-2);
  animation:menu-entra .16s ease-out;
}
@keyframes menu-entra{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
/* Envuelta, y no por formalismo: un panel que se desliza cada vez que se abre
   el menú es movimiento repetido en el elemento más usado de la cabecera. */
@media (prefers-reduced-motion:reduce){ .menu-panel{animation:none} }

.menu-grupo{list-style:none;margin:0;padding:0}
.menu-grupo + .menu-grupo{margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--line-soft)}

/* El enlace y el botón de salir se dibujan IGUAL a propósito: dentro del panel
   los dos son lo mismo, una entrada de menú, y que «Salir» pareciera un botón
   de acción lo convertiría en lo que más llama la atención del menú. */
.menu-grupo a,
.menu-salir button{
  display:block;width:100%;text-align:left;
  font:inherit;font-size:var(--fs-small);font-weight:var(--fw-bold);
  color:var(--ink-soft);text-decoration:none;
  background:none;border:0;border-radius:var(--r-1);
  padding:.7em .8em;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
  -webkit-user-select:none;
  user-select:none;
}
.menu-grupo a:hover,
.menu-salir button:hover{color:var(--teal-ink);background:var(--tint-teal)}
/* Escrito DESPUÉS del `:hover` y con la misma especificidad: mientras el dedo
   sigue apoyado casan los dos, y el que gana es el último. */
.menu-grupo a:active,
.menu-salir button:active{color:var(--teal-ink);background:var(--tint-teal-2)}
/* Offset NEGATIVO: el panel tiene 8px de padding y un contorno hacia fuera se
   comería el borde de la tarjeta en la primera y la última entrada. */
.menu-grupo a:focus-visible,
.menu-salir button:focus-visible{outline:3px solid var(--focus);outline-offset:-2px}
.menu-salir{margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--line-soft)}


/* ==========================================================================
   3 · AVISO DE INSTALAR

   Tarjeta flotante, anclada al borde inferior, por encima de la barra de la
   aplicación cuando la hay. `--nav-app-hueco` sólo existe en `cascara.css`,
   que sólo cargan las pantallas de la aplicación: en la web de venta el
   respaldo de `var()` deja la tarjeta pegada a la zona segura y ya está.
   ========================================================================== */

.aviso-instalar{
  position:fixed;
  left:var(--sp-4); right:var(--sp-4);
  bottom:calc(var(--nav-app-hueco, env(safe-area-inset-bottom)) + var(--sp-2));
  z-index:55;

  display:grid;
  /* CUATRO columnas: icono · texto · Instalar · ×.
     La `×` tiene la suya y NO va en position:absolute. Ver 3.2. */
  grid-template-columns:auto minmax(0,1fr) auto auto;
  align-items:center;
  gap:var(--sp-3);

  max-width:34rem; margin-inline:auto;
  padding:var(--sp-4);
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r-3);
  box-shadow:var(--sh-s);

  transition:transform .18s ease;

  /* Es cromo, no contenido: ni se selecciona ni parpadea al tocarlo. */
  -webkit-tap-highlight-color:transparent;
  -webkit-user-select:none;
  user-select:none;
}
/* `display:grid` gana a la regla de agente de usuario `[hidden]{display:none}`,
   así que hay que devolverlo a mano o la tarjeta se ve antes de que `pwa.js`
   decida si debe verse. */
.aviso-instalar[hidden]{display:none}

/* En iOS no hay botón que pulsar —la instalación es un gesto manual y no existe
   forma de lanzarla por código—, así que esa columna no llega a tener contenido.
   Sin esta línea la pista seguiría estando y su `gap` empujaría la `×` un hueco
   entero hacia dentro, desalineada del borde de la tarjeta. */
.aviso-instalar[data-modo="ios"]{grid-template-columns:auto minmax(0,1fr) auto}

.aviso-instalar-icono{width:40px;height:40px;border-radius:var(--r-2);display:block}

.aviso-instalar-tit{
  margin:0; font-size:var(--fs-small); font-weight:var(--fw-black);
  color:var(--ink); line-height:var(--lh-head);
}
.aviso-instalar-ayuda{
  margin:var(--sp-1) 0 0; font-size:var(--fs-micro); color:var(--ink-soft);
  line-height:var(--lh-body);
}
/* El icono de compartir va DENTRO de la frase, no delante: «Toca [icono] y
   elige…». Es lo que hace que se entienda sin dibujo. */
.aviso-instalar-ayuda svg{
  width:1em;height:1em;vertical-align:-.15em;margin-inline:.15em;
  color:var(--teal-strong);
}

/* «Ya la tengo instalada», sólo en iOS. Enlace de texto, no botón de acción:
   no es lo que queremos que pulsen, es la salida honesta para quien ya la tiene
   (ver 3.6). */
.aviso-instalar-ya{
  margin-top:var(--sp-1);
  font:inherit;font-size:var(--fs-micro);font-weight:var(--fw-bold);
  color:var(--teal-ink);background:none;border:0;padding:4px 0;
  text-decoration:underline;cursor:pointer;
}
.aviso-instalar-ya:focus-visible{outline:3px solid var(--focus);outline-offset:2px}

/* La `×` es un botón de 44px de área táctil con un glifo de 14, EN SU COLUMNA.

   NO ES `position:absolute`, y eso es el arreglo del fallo #16. Siendo el
   último elemento del DOM, una `×` absoluta ganaba el orden de pintado y se
   llevaba los clics del tercio superior de «Instalar», que queda justo debajo
   en cuanto el título cabe en una línea: quien apuntaba a instalar acababa
   descartando el aviso 14 días. Con su propia columna no se solapan nunca, en
   ninguna altura de tarjeta. */
.aviso-instalar-no{
  width:44px; height:44px;
  display:inline-flex; align-items:center; justify-content:center;
  background:none; border:0; border-radius:var(--r-pill);
  color:var(--ink-soft); cursor:pointer;
}
.aviso-instalar-no:hover{background:var(--tint-teal); color:var(--teal-ink)}
.aviso-instalar-no:active{background:var(--tint-teal-2); color:var(--teal-ink)}
.aviso-instalar-no:focus-visible{outline:3px solid var(--focus); outline-offset:2px}
.aviso-instalar-no svg{width:14px;height:14px}
.aviso-instalar-no,
.aviso-instalar-ya{touch-action:manipulation}
.aviso-instalar-ya:active{color:var(--teal-strong)}

/* ══════════════════════════════════════════════════════════════════════════
   A 320px «INSTALAR» BAJA A SU PROPIA FILA · MEDIDO, NO SUPUESTO

   Las cuatro columnas se comen 218px fijos antes de que el título escriba una
   letra: 40 de icono, 44 de `×`, unos 86 de «Instalar» y tres huecos. En un
   móvil de 320px la tarjeta mide 288, así que al título le quedan 70, y
   `minmax(0,1fr)` deja que una columna se encoja POR DEBAJO de su contenido: la
   palabra «pantalla» se sale de su columna y aterriza encima del botón.
   Comprobado en Chrome a 320px antes de escribir esto.

   Por debajo de 360px, entonces, dos filas: arriba icono, título y `×`; abajo el
   botón, alineado con el título. Las cuatro piezas se colocan a mano porque con
   una sola explícita el resto se recoloca solo y de forma difícil de prever.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width:359px){
  .aviso-instalar[data-modo="boton"]{grid-template-columns:auto minmax(0,1fr) auto}
  .aviso-instalar[data-modo="boton"] .aviso-instalar-icono{grid-area:1/1}
  .aviso-instalar[data-modo="boton"] .aviso-instalar-txt  {grid-area:1/2}
  .aviso-instalar[data-modo="boton"] .aviso-instalar-no   {grid-area:1/3}
  .aviso-instalar[data-modo="boton"] .aviso-instalar-si   {grid-area:2/2/auto/4;justify-self:start}
}

/* ══════════════════════════════════════════════════════════════════════════
   EL HUECO RESERVADO · NO ES OPCIONAL

   `app-ui.css` documenta que `.barra-acciones` del asistente estuvo pegada
   abajo con `position:sticky` y hubo que quitarlo porque tapaba las últimas
   filas de la lista y se comía sus clics, «que es el peor fallo posible en una
   pantalla donde marcar una casilla ES la acción».

   La revisión 1 de este documento repetía ese fallo palabra por palabra: una
   tarjeta fija de ~110px sobre el borde inferior, sin una sola regla que
   reservara sitio para ella. En `/nuevo` —que está en CON_AVISO y donde la
   tarjeta sale de INMEDIATO, sin espera— la tarjeta se plantaba encima de la
   `.barra-acciones` y del final del formulario.

   Se reserva igual que se reserva la barra, y con dos alturas porque la tarjeta
   de iOS lleva dos líneas más. Las alturas se eligen por `data-modo`, que lo
   escribe `pwa.js` como atributo: aquí no se escribe ni un píxel desde
   JavaScript.

   LAS CIFRAS SON MEDIDAS, y las de la revisión anterior —96 y 148— se quedaban
   cortas las dos. Se escribieron antes de que existiera el marcado que va
   dentro, así que no había nada que medir; con la tarjeta montada, en Chrome:

     modo botón   89px a 393 · 107px a 360 · 133px a 320
     modo iOS    172px a 390 · 172px a 360 · 193px a 320

   Con 148px reservados, la tarjeta de iOS se comía 24px de la última línea de la
   portada, que es exactamente el fallo que este bloque existe para evitar. La
   reserva tiene que ser MAYOR O IGUAL que el alto de la tarjeta: las dos cuelgan
   del mismo `--nav-app-hueco + --sp-2`, así que lo que sobra en la resta es
   justo lo que se solapa.

   El alto va en `--aviso-alto` y no repetido en cuatro `calc()`: son cuatro
   sitios que tienen que decir el mismo número, y separados acabarían diciendo
   números distintos. Se declara en `html` para que baje por herencia hasta el
   `body`.

   Y es aquí donde importa el orden de carga del encabezado de esta hoja:
   `body:has(.nav-app)` (cascara.css) y `body:has(.aviso-instalar:not([hidden]))`
   casan las dos a la vez dentro de la aplicación y tienen la misma
   especificidad. Gana ésta por ser la última, y es la única que suma las dos
   alturas.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width:819px){
  html:has(.aviso-instalar:not([hidden])){--aviso-alto:112px}
  html:has(.aviso-instalar[data-modo="ios"]:not([hidden])){--aviso-alto:180px}

  body:has(.aviso-instalar:not([hidden])){
    padding-bottom:calc(var(--nav-app-hueco, 0px) + var(--aviso-alto) + var(--sp-2));
  }

  /* El simétrico, o el último campo del formulario aterriza detrás de la
     tarjeta al recibir el foco. Es además WCAG 2.2 · 2.4.11 «Focus Not
     Obscured (Minimum)», nivel AA. */
  html:has(.aviso-instalar:not([hidden])){
    scroll-padding-bottom:calc(var(--nav-app-hueco, 0px) + var(--aviso-alto) + var(--sp-2));
  }
}

/* Los móviles estrechos, donde el título envuelve más y donde «Instalar» ha
   bajado a su propia fila. Va después del bloque de arriba a propósito: sólo
   redefine el alto, y el `calc()` que lo usa es el mismo. */
@media (max-width:359px){
  html:has(.aviso-instalar:not([hidden])){--aviso-alto:140px}
  html:has(.aviso-instalar[data-modo="ios"]:not([hidden])){--aviso-alto:200px}
}

/* SE APARTA CON EL TECLADO VIRTUAL, exactamente igual que `.nav-app` (5.4).
   Sin esto, rellenando `/nuevo` la tarjeta queda flotando en mitad del área
   visible mientras la barra ya se ha ido. Sólo `input` y `textarea`: con
   cualquier `:focus` la tarjeta desaparecería al tabular por ella misma. */
body:has(.pantalla :is(input,textarea):focus) .aviso-instalar{transform:translateY(150%)}

/* La única animación que se copia de tapstars, y envuelta. Allí ninguna de las
   cuatro lleva guarda y la flecha rebota para siempre. */
@keyframes aviso-instalar-entra{
  from{opacity:0; transform:translateY(12px)}
  to  {opacity:1; transform:none}
}
.aviso-instalar{animation:aviso-instalar-entra .28s ease-out}
@media (prefers-reduced-motion:reduce){
  .aviso-instalar{animation:none;transition:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   NO COINCIDE NUNCA CON EL BANNER DE COOKIES

   El banner es `position:fixed;bottom:0;z-index:70` (`app.css:1122-1129`) y se
   pinta en `/`, que es una de las páginas donde este aviso puede salir. Dos
   tarjetas apiladas en el mismo borde es un muro, y encima le pide a alguien
   que decida dos cosas a la vez. Primero se decide sobre las cookies; el aviso
   de instalar espera a la siguiente página.
   ══════════════════════════════════════════════════════════════════════════ */
body:has(.cookies) .aviso-instalar{display:none}
