/* ============================================================================
   CANINEUS · panel.css
   Sistema de diseño. Fase 1.

   Cómo está organizado:
     1. Tokens              — neutros fijos, claro y oscuro
     2. Paletas             — lo único que cambia con la marca del colegio
     3. Compatibilidad      — traduce las variables viejas mientras migramos
     4. Base                — reset y tipografía
     5. Armazón             — sidebar (escritorio) / appbar + tabbar (móvil)
     6. Componentes         — botones, campos, listas, hojas, etc.
     7. Legado              — clases antiguas, retocadas hasta que se migren

   Reglas que no se rompen:
     · Los fondos y el texto NUNCA usan el color de la paleta.
     · Las sombras son grises, nunca de color.
     · Ningún objetivo táctil por debajo de 44 px en móvil.
   ========================================================================== */


/* ============================================================
   0. LETRAS
   Alojadas en la plataforma (fuentes/, subconjunto latino) y no en
   Google Fonts. Con Google llegaban después del primer pintado: el
   texto se veía un instante en otra letra y saltaba de tamaño al
   cambiar, en cada pantalla. Aquí van precargadas desde el <head> y el
   service worker las guarda, así que están desde el primer fotograma.

   font-display:optional: si una letra no llegó a tiempo (la primera
   visita con mala señal), esa pantalla se queda entera con el
   respaldo en vez de cambiar a mitad de lectura; la siguiente ya la
   tiene. Los respaldos («… Respaldo») son Arial y Times ajustados a las
   medidas de cada letra (ancho medido en Chromium con un texto de la
   plataforma; alturas del propio archivo), para que ni siquiera
   entonces cambie el tamaño de nada.
   Las tres son de licencia libre OFL: ver fuentes/LICENCIAS.txt.
   ============================================================ */
@font-face{
  font-family:'Geist'; font-style:normal; font-weight:100 900; font-display:optional;
  src:url('/fuentes/geist-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Bricolage Grotesque'; font-style:normal; font-weight:700; font-display:optional;
  src:url('/fuentes/bricolage-grotesque-700-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Instrument Serif'; font-style:italic; font-weight:400; font-display:optional;
  src:url('/fuentes/instrument-serif-italic-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Geist Respaldo';
  src:local('Arial'), local('Helvetica Neue'), local('Helvetica'), local('Roboto');
  size-adjust:101%; ascent-override:99.5%; descent-override:29.2%; line-gap-override:0%;
}
@font-face{
  font-family:'Bricolage Respaldo'; font-weight:700;
  src:local('Arial Bold'), local('Arial-BoldMT'), local('Helvetica Neue Bold'), local('Roboto Bold');
  size-adjust:90.7%; ascent-override:102.5%; descent-override:29.8%; line-gap-override:0%;
}
@font-face{
  font-family:'Instrument Respaldo'; font-style:italic;
  src:local('Times New Roman Italic'), local('TimesNewRomanPS-ItalicMT'), local('Georgia Italic');
  size-adjust:86.1%; ascent-override:115%; descent-override:36%; line-gap-override:0%;
}


/* ============================================================
   1. TOKENS
   ============================================================ */
/* Estilo «Pastel» (27 de septiembre de 2026): fondo cálido, tarjetas
   blancas sin bordes marcados, sombras difusas y tintes suaves por tema.
   Los nombres de las variables son los de siempre —las pantallas los
   usan todos—; solo cambian sus valores. */
:root{
  /* Neutros cálidos. --bg-base es el cálido puro; --bg lo tiñe con el
     color del colegio (sección 9). */
  --bg-base:     #F7F5F2;
  --bg:          var(--bg-base);
  --surface:     #FFFFFF;
  --surface-2:   #F3F0EB;
  --surface-3:   #E9E5DF;
  /* Bordes casi invisibles: separan filas sin dibujar cajas. */
  --border:      #EEEAE4;
  --border-2:    #DDD8D1;
  --text:        #1C1B1F;
  --text-2:      #5E5B63;
  /* La maqueta pedía #7A7780, pero sobre blanco da 4,4:1 y sobre el
     fondo 4,0:1: por debajo del mínimo de lectura (4,5:1). Un punto
     más oscuro lo cumple en los dos (5,1:1 y 4,7:1) y se ve igual. */
  --text-3:      #6F6C75;

  /* Estado */
  --ok:        #2E6B52;  --ok-bg:     #DDEBE2;  --ok-br:     #C4DCCC;
  --warn:      #8A3F0A;  --warn-bg:   #F9E3D4;  --warn-br:   #F0CBB2;
  --danger:    #A8321F;  --danger-bg: #FBE5E1;  --danger-br: #F2C6BE;

  /* La acción principal: píldora casi negra. No usa el acento del
     colegio a propósito: el acento marca lo elegido (pestañas, chips,
     interruptores) y el negro, lo que se hace. Si los dos fueran del
     mismo color no se distinguiría «seleccionado» de «guardar». */
  --tinta:     #1C1B1F;
  --tinta-hover:#34323A;
  --tinta-on:  #FFFFFF;

  /* El verde de Canineus, para lo que es de la plataforma y no del
     colegio (el círculo del «hecho», el inicio de sesión sin marca). */
  --canineus:       #2E6B52;
  --canineus-suave: #DDEBE2;

  /* Tintes por tema, para las tarjetas destacadas de cada sección.
     Cada uno lleva su color de texto e icono, con contraste de sobra
     sobre su propio fondo. */
  --tinte-rutas:    #DDEBE2;  --tinte-rutas-txt:    #245440;
  --tinte-avisos:   #F9E3D4;  --tinte-avisos-txt:   #8A3F0A;
  --tinte-hotel:    #E7E3F6;  --tinte-hotel-txt:    #4A3D8F;
  --tinte-cartera:  #F1EAD9;  --tinte-cartera-txt:  #6B5418;
  --tinte-alumnos:  #DCE9F3;  --tinte-alumnos-txt:  #1F5480;

  /* Texto sobre --acento (botones, chips activos...). Blanco por
     defecto: todas las paletas hasta ahora son lo bastante oscuras
     para leerse en blanco. Una paleta clara (como Rosa) la cambia a
     un color oscuro dentro de su propio bloque. */
  --acento-on: #fff;

  /* Texto sobre los colores de estado cuando van de fondo (contadores,
     botón de borrar, marcas de «hecho»). En claro son oscuros y el
     blanco se lee; en oscuro se aclaran y el blanco desaparecía
     (contraste 2:1). El bloque oscuro los cambia a casi negro. */
  --danger-on: #fff;
  --ok-on:     #fff;
  --warn-on:   #fff;

  /* Las insignias pequeñas (la placa del plan sobre la foto, un
     contador en una pestaña): letra de 10–11 px, así que piden más
     contraste que un botón. Van sobre --acento-txt, que cada paleta ya
     tiene oscuro en claro y claro en oscuro, y la letra al revés. Con
     --acento y blanco, en Pastel oscuro salía blanco sobre blanco, y en
     Ámbar, Cian o Bronce oscuros por debajo de 3:1. */
  --insignia:    var(--acento-txt);
  --insignia-on: #fff;

  /* Tipografía: Geist para leer, Bricolage para títulos y cifras,
     Instrument Serif en cursiva para una palabra suelta. */
  --font: 'Geist', 'Geist Respaldo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-titulo: 'Bricolage Grotesque', 'Bricolage Respaldo', 'Geist', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-acento: 'Instrument Serif', 'Instrument Respaldo', Georgia, 'Times New Roman', serif;
  --t-xs:12px; --t-sm:13px; --t-base:14px; --t-md:16px;
  --t-lg:18px; --t-xl:22px; --t-2xl:28px; --t-3xl:34px;

  /* Espaciado, base 4 */
  --s-1:4px;  --s-2:8px;  --s-3:12px; --s-4:16px; --s-5:20px;
  --s-6:24px; --s-8:32px; --s-10:40px; --s-14:56px; --s-18:72px;

  /* Forma: más redonda. --r-sm lo usan campos y botones pequeños,
     --r-lg las tarjetas y --r-xl las ventanas. */
  --r-sm:12px; --r-md:18px; --r-lg:24px; --r-xl:28px; --r-full:999px;
  --r-campo:14px;

  /* Sombras — grises, nunca de color. Muy abiertas y muy suaves: se
     intuyen más que se ven. */
  --sh-1: 0 1px 2px rgba(28,27,31,.04);
  --sh-2: 0 1px 2px rgba(28,27,31,.03), 0 8px 24px -8px rgba(28,27,31,.08);
  --sh-3: 0 28px 64px -16px rgba(28,27,31,.22), 0 4px 14px rgba(28,27,31,.06);
  --sh-alza: 0 2px 4px rgba(28,27,31,.04), 0 22px 44px -14px rgba(28,27,31,.18);

  /* Movimiento: una sola curva para todo, que frena al llegar. */
  --curva: cubic-bezier(.22,1,.36,1);
  /* La de la píldora y la marca del menú: un resorte que se pasa un
     3,8 % y vuelve. Sin linear() (navegadores viejos), una bezier que
     se pasa algo parecido. */
  --resorte: cubic-bezier(.3,1.25,.5,1);
  /* Salto con un pequeño rebote (1 de octubre de 2026, el «Fundido
     rápido» elegido por el dueño): 260 ms, se pasa un 20 % y
     vuelve. Lo usan la píldora de la barra de abajo y la marca del menú
     lateral; arranca en el pointerdown, no en la llegada de la pantalla. */
  --rebote: cubic-bezier(.2,.9,.3,1.2);
  --rebote-t: .26s;

  /* Medidas del armazón */
  --sidebar-w: 248px;
  --appbar-h:  58px;
  --tabbar-h:  60px;
  --safe-b:    env(safe-area-inset-bottom, 0px);
  /* Alto que la barra de pestañas le quita a la pantalla desde abajo,
     sin contar la zona segura. En el teléfono la cápsula flota: se
     suman su relleno (12) y el aire que la separa del borde (10). Lo
     usan las piezas pegadas abajo, como el pie de acciones de Rutas. */
  --tabbar-ocupa: var(--tabbar-h);
}
/* Teléfono o computador: no lo decide solo el ancho.

   Hasta el 27 de septiembre el diseño de teléfono entraba por debajo de
   1024 px en cualquier aparato. Un portátil con Windows al 150 % o con
   el zoom del navegador alto se queda con menos de eso y pasaba a la
   barra de abajo, con ratón y todo. Ahora, en toda la plataforma:

     teléfono    (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse)
     computador  (min-width:1024px), (min-width:900px) and (pointer:fine),
                 (min-width:900px) and (pointer:none)

   Con ratón, el menú lateral aguanta hasta 900 px (con la escala de
   theme-inline.js cabe entero); una tableta táctil sigue como siempre:
   teléfono por debajo de 1024, computador desde ahí. Las dos listas son
   complementarias —toda ventana cae en una y solo una— y por eso van
   escritas enteras y no con not(...), que los Safari de antes de 2023
   no entienden. Los ,98 tapan el hueco de los anchos con decimales
   (un Windows al 150 % da ventanas de 1023,33 px). Si se cambia una,
   cambiar todas: `grep -n "899.98px"` y `grep -n "(pointer:none)"`. */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  :root{ --tabbar-ocupa: calc(var(--tabbar-h) + 12px + 10px); }
}
/* El resorte de verdad (amortiguación 0,72), muestreado a 20 ms. */
@supports (transition-timing-function:linear(0, 1)){
  :root{ --resorte: linear(0,.039,.133,.257,.391,.523,.642,.746,.832,.9,.951,.988,1.013,1.028,1.036,1.038,1.037,1.034,1.029,1.024,1.019,1.014,1.01,1.007,1.004,1.002,1); }
}

[data-theme="dark"]{
  --bg-base:   #121113;
  --bg:        var(--bg-base);
  --surface:   #1C1B1F;
  --surface-2: #26252A;
  --surface-3: #323036;
  --border:    #2A292E;
  --border-2:  #3D3B42;
  --text:      #F2EFEA;
  --text-2:    #B7B3BC;
  --text-3:    #97939D;

  --ok:        #8FD0AE;  --ok-bg:     #1B2B23;  --ok-br:     #2A4436;
  --warn:      #F2B48C;  --warn-bg:   #33231A;  --warn-br:   #4E3424;
  --danger:    #F29A8C;  --danger-bg: #36201D;  --danger-br: #53302A;

  --tinta:     #F2EFEA;
  --tinta-hover:#FFFFFF;
  --tinta-on:  #1C1B1F;

  --danger-on:   #1C1B1F;
  --ok-on:       #1C1B1F;
  --warn-on:     #1C1B1F;
  --insignia-on: #1C1B1F;

  --canineus:       #8FD0AE;
  --canineus-suave: #1B2B23;

  --tinte-rutas:    #1B2B23;  --tinte-rutas-txt:    #9BD4B6;
  --tinte-avisos:   #33231A;  --tinte-avisos-txt:   #F2B48C;
  --tinte-hotel:    #25223A;  --tinte-hotel-txt:    #C6BCF4;
  --tinte-cartera:  #2D281C;  --tinte-cartera-txt:  #E3CF97;
  --tinte-alumnos:  #1A2733;  --tinte-alumnos-txt:  #9FC9EC;

  --sh-1: 0 1px 2px rgba(0,0,0,.35);
  --sh-2: 0 1px 2px rgba(0,0,0,.3), 0 10px 28px -10px rgba(0,0,0,.5);
  --sh-3: 0 30px 70px -14px rgba(0,0,0,.7), 0 4px 14px rgba(0,0,0,.4);
  --sh-alza: 0 2px 4px rgba(0,0,0,.35), 0 24px 48px -14px rgba(0,0,0,.7);
}


/* ============================================================
   2. PALETAS
   Las claves son las mismas de siempre ('azul-original', etc.)
   para no tener que migrar nada en configuracion_negocio.
   Cada paleta define cuatro valores:
     --acento        fondo de la acción principal (texto blanco encima)
     --acento-hover  su estado al pasar el cursor
     --acento-suave  fondo teñido para selección y avisos
     --acento-txt    versión legible sobre fondo claro
   ============================================================ */
/* Pastel, la predeterminada: sin color de marca. Lo elegido y lo que
   se hace van en casi negro, y el color lo ponen los tintes de cada
   tema (sección 9). */
[data-paleta="pastel"]       { --acento:#1C1B1F; --acento-hover:#34323A; --acento-suave:#ECE8E2; --acento-txt:#1C1B1F; }
[data-paleta="azul-original"]{ --acento:#1D63D1; --acento-hover:#1852AF; --acento-suave:#EAF1FD; --acento-txt:#14509E; }
/* Esmeralda es el verde de Canineus desde el estilo Pastel. */
[data-paleta="esmeralda"]    { --acento:#2E6B52; --acento-hover:#245842; --acento-suave:#DDEBE2; --acento-txt:#245440; }
[data-paleta="purpura"]      { --acento:#6D3BD1; --acento-hover:#5A2FB0; --acento-suave:#F0EAFD; --acento-txt:#5A2FB0; }
[data-paleta="vino"]         { --acento:#B32741; --acento-hover:#951F35; --acento-suave:#FCEBEF; --acento-txt:#921F35; }
[data-paleta="ambar"]        { --acento:#B26A00; --acento-hover:#935700; --acento-suave:#FDF1E0; --acento-txt:#8A5200; }
[data-paleta="cian"]         { --acento:#0A7C90; --acento-hover:#086476; --acento-suave:#E4F4F7; --acento-txt:#086476; }
[data-paleta="grafito"]      { --acento:#3E4651; --acento-hover:#2C333C; --acento-suave:#EEF0F3; --acento-txt:#333A44; }
[data-paleta="bronce"]       { --acento:#8A6A16; --acento-hover:#6E5411; --acento-suave:#F8F1DE; --acento-txt:#6E5411; }
/* Rosa es la única paleta clara: el acento es un pastel, no un color
   oscuro, así que el texto que va encima tiene que ser oscuro
   (--acento-on) en vez del blanco de las demás. Esto solo hace falta
   declararlo aquí, en el bloque claro: el bloque oscuro no lo repite
   porque el acento sigue siendo pastel en los dos modos y hereda el
   mismo valor. */
[data-paleta="rosa"]         { --acento:#F4A0B5; --acento-hover:#EE6D8D; --acento-suave:#FBE9EE; --acento-txt:#701028; --acento-on:#430A18; }

/* En oscuro el acento sube de luminosidad: el mismo tono que
   funciona sobre blanco se apaga sobre un fondo oscuro. */
[data-theme="dark"][data-paleta="pastel"]       { --acento:#F2EFEA; --acento-hover:#FFFFFF; --acento-suave:#2A282E; --acento-txt:#F2EFEA; --acento-on:#1C1B1F; }
[data-theme="dark"][data-paleta="azul-original"]{ --acento:#3B82F6; --acento-hover:#5B9AFB; --acento-suave:#14243B; --acento-txt:#84B4FF; }
[data-theme="dark"][data-paleta="esmeralda"]    { --acento:#367F61; --acento-hover:#3F8E6D; --acento-suave:#1B2B23; --acento-txt:#9BD4B6; }
[data-theme="dark"][data-paleta="purpura"]      { --acento:#8B5CF6; --acento-hover:#A277FF; --acento-suave:#1F1837; --acento-txt:#BFA0FF; }
[data-theme="dark"][data-paleta="vino"]         { --acento:#D93E5C; --acento-hover:#E85874; --acento-suave:#2E1620; --acento-txt:#FF93A8; }
[data-theme="dark"][data-paleta="ambar"]        { --acento:#D98A0B; --acento-hover:#EDA02A; --acento-suave:#2C2110; --acento-txt:#F5C069; }
[data-theme="dark"][data-paleta="cian"]         { --acento:#0EA5BE; --acento-hover:#20BDD6; --acento-suave:#0E272D; --acento-txt:#5FD5E8; }
[data-theme="dark"][data-paleta="grafito"]      { --acento:#7C8899; --acento-hover:#93A0B0; --acento-suave:#1F252D; --acento-txt:#AEB9C6; }
[data-theme="dark"][data-paleta="bronce"]       { --acento:#B8912E; --acento-hover:#CFA742; --acento-suave:#282112; --acento-txt:#E0C275; }
/* En oscuro el pastel se desatura (79% → 55% de saturación) en vez de
   subir de luminosidad como las demás: ya es un color claro, y
   subirlo más lo haría brillar sobre el fondo oscuro. --acento-txt sí
   sube de luminosidad, como en las demás paletas, porque aquí hace de
   texto legible sobre un fondo oscuro, no de fondo de botón. */
[data-theme="dark"][data-paleta="rosa"]         { --acento:#E3A0B1; --acento-hover:#D6718A; --acento-suave:#2D161B; --acento-txt:#F4A0B5; }


/* ============================================================
   3. COMPATIBILIDAD
   Las páginas que todavía no se han migrado siguen pidiendo
   --navy-panel, --mist, --line… Aquí se traducen al sistema
   nuevo para que sigan funcionando y se vean decentes mientras
   las vamos pasando una por una.
   BORRAR ESTE BLOQUE cuando no quede ninguna página sin migrar.
   ============================================================ */
:root{
  --navy-deep:   var(--bg);
  --navy-panel:  var(--surface);
  --navy-panel-2:var(--surface-2);
  --ink:         var(--bg);
  --white:       var(--text);
  --mist:        var(--text-2);
  --mist-dim:    var(--text-3);
  --line:        var(--border);
  --line-strong: var(--border-2);
  --blue-deep:   var(--acento);
  --blue-mid:    var(--acento);
  --blue-bright: var(--acento);
  --blue-glow:   var(--acento-txt);
  --radius:      var(--r-lg);
}


/* ============================================================
   4. BASE
   ============================================================ */
*{ box-sizing:border-box; margin:0; padding:0; }

html{
  -webkit-text-size-adjust:100%;
  background:var(--bg);
  /* Reserva el hueco de la barra de desplazamiento: sin esto, al abrir
     un modal el contenido salta unos píxeles a la derecha. */
  scrollbar-gutter:stable;
}

body{
  /* Entre --zoom: con la escala de computador (theme-inline.js) 100dvh
     mediría solo una parte de la ventana. */
  min-height:calc(100dvh / var(--zoom, 1));
  font-family:var(--font);
  font-size:var(--t-base);
  line-height:1.5;
  color:var(--text);
  background:var(--bg);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  /* El doble toque para hacer zoom se desactiva; el pellizco sigue. */
  touch-action:manipulation;
  /* Sin rebote elástico en el armazón: es de lo que más delata
     que una app instalada es en realidad una página web. */
  overscroll-behavior-y:none;
}

h1,h2,h3,h4{ line-height:1.2; letter-spacing:-0.02em; font-weight:700; }
h1{ font-size:26px; letter-spacing:-0.03em; }
h2{ font-size:var(--t-xl); }
h3{ font-size:var(--t-md); letter-spacing:-0.01em; }
h4{ font-size:var(--t-base); }

a{ color:var(--acento-txt); text-decoration:none; }
button, input, select, textarea{ font-family:inherit; }
img{ max-width:100%; display:block; }

/* Fotos que aparecen enteras (fotos-suaves.js).
   Estas reglas viven aquí y no en el módulo porque tienen que estar desde
   el primer fotograma: con red lenta el módulo llega segundos después, y
   las fotos que el armazón restaura de la foto de pantalla (las de la
   Tienda van con su dirección pública) se estaban pintando a trozos en
   ese hueco. shell-inline.js las marca al restaurarlas; el módulo sigue
   desde ahí.

   Cargando, o esperando su dirección firmada (fotos.js): object-position
   la saca de su caja —la <img> sigue midiendo y recortando lo mismo, pero
   lo que llega a trozos queda fuera y no se dibuja—; color:transparent
   tapa el texto alternativo. El gris es el de las siluetas del armazón, y
   el brillo, un SVG animado por dentro con transform (un degradado movido
   con background-position repintaría el marcador en cada fotograma). */
:root{ --fs-gris:color-mix(in srgb, var(--text) 8%, var(--surface)); --fs-brillo:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22g%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%22.5%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220.55%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-60%22%20y%3D%220%22%20width%3D%2260%22%20height%3D%22100%22%20fill%3D%22url(%23g)%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22translate%22%20from%3D%220%200%22%20to%3D%22220%200%22%20dur%3D%221.3s%22%20repeatCount%3D%22indefinite%22%2F%3E%3C%2Frect%3E%3C%2Fsvg%3E"); }
[data-theme="dark"]{ --fs-gris:color-mix(in srgb, var(--text) 9%, var(--surface)); --fs-brillo:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22g%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%22.5%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220.07%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-60%22%20y%3D%220%22%20width%3D%2260%22%20height%3D%22100%22%20fill%3D%22url(%23g)%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22translate%22%20from%3D%220%200%22%20to%3D%22220%200%22%20dur%3D%221.3s%22%20repeatCount%3D%22indefinite%22%2F%3E%3C%2Frect%3E%3C%2Fsvg%3E"); }
img[data-fs="cargando"], img[data-foto-url]:not([src]){
  object-position:-99999px -99999px !important; color:transparent !important;
  background:var(--fs-gris) var(--fs-brillo) 0 0 / 100% 100% no-repeat !important; }
/* Falló: el marcador de siempre, quieto. */
img[data-fs="fallo"]{ color:transparent !important; background:var(--fs-gris) !important; }
/* Sin dirección, Chrome dibuja el icono de imagen rota si la <img> tiene
   texto alternativo. content la sustituye por una transparente de 1 × 1
   (la misma VACIA de fotos-suaves.js) y el marcador queda limpio. */
img[data-foto-url]:not([src]){ content:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); }
@media (prefers-reduced-motion: reduce){
  img[data-fs="cargando"], img[data-foto-url]:not([src]){ background-image:none !important; } }

::selection{ background:var(--acento-suave); color:var(--acento-txt); }

:focus-visible{ outline:2px solid var(--acento); outline-offset:2px; border-radius:4px; }
:focus:not(:focus-visible){ outline:none; }

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


/* ============================================================
   5. ARMAZÓN
   Escritorio: barra lateral fija a la izquierda.
   Móvil: barra superior fija + barra de pestañas abajo.
   Son dos estructuras distintas, no una encogida.
   ============================================================ */

/* ---- Barra lateral (solo escritorio) ----
   El menú es lo único que se desplaza; la marca arriba y el pie con la
   cuenta abajo quedan siempre visibles. Antes toda la barra se
   desplazaba junta y en pantallas bajas "Cerrar sesión" se salía. */
.sidebar{
  position:fixed; top:0; left:0;
  width:var(--sidebar-w); height:100dvh;
  background:var(--surface);
  border-right:1px solid var(--border);
  display:flex; flex-direction:column;
  padding:var(--s-3) var(--s-2);
  overflow:hidden;
  z-index:110;
}
.sidebar .nav-groups{ flex:1; min-height:0; overflow-y:auto; scrollbar-width:none; }
.sidebar .nav-groups::-webkit-scrollbar{ width:0; }
.sidebar .brand, .sidebar .sidebar-footer{ flex-shrink:0; }

/* ---- Barra lateral en ventanas bajas ----
   Los escalones por altura (los de la #165, que apretaban el menú en
   ventanas de 700-900 px) viven ahora en la sección 11, junto con el
   resto de lo que se ajusta a la pantalla. */

/* ---- Contenido ----
   Ocupa todo el espacio a la derecha de la barra lateral. El relleno
   sube un poco en pantallas muy anchas para que no quede pegado a los
   bordes, pero el contenido no se limita a una columna central. */
main{
  margin-left:var(--sidebar-w);
  min-width:0;
  padding:var(--s-8) var(--s-6) var(--s-14);
}
@media (min-width:1600px){
  main{ padding-left:var(--s-10); padding-right:var(--s-10); }
}

/* ---- Barra superior de móvil ---- */
.appbar{
  display:none;
  position:sticky; top:0; z-index:100;
  height:var(--appbar-h);
  align-items:center; gap:var(--s-2);
  padding:0 var(--s-2) 0 var(--s-4);
  background:var(--surface);
  border-bottom:1px solid var(--border);
}
.appbar .appbar-title{
  flex:1; min-width:0;
  font-size:var(--t-lg); font-weight:700; letter-spacing:-0.02em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.appbar-logo{ width:30px; height:30px; border-radius:9px; object-fit:cover; flex-shrink:0; }

/* Botón de icono con área táctil cómoda */
.icon-btn{
  width:44px; height:44px; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border:none; background:none; border-radius:var(--r-full);
  color:var(--text-2); cursor:pointer; position:relative;
  -webkit-tap-highlight-color:transparent;
}
.icon-btn:hover{ background:var(--surface-2); color:var(--text); }
.icon-btn:active{ background:var(--surface-3); }
.icon-btn svg{ width:21px; height:21px; }
/* Punto de aviso, sin número: el número va dentro del módulo */
.icon-btn .dot-alert{
  position:absolute; top:9px; right:10px;
  width:9px; height:9px; border-radius:50%;
  background:var(--danger); border:2px solid var(--surface);
}

/* ---- Barra de pestañas (solo móvil) ---- */
.tabbar{
  display:none;
  position:fixed; left:0; right:0; bottom:0; z-index:100;
  background:var(--surface);
  border-top:1px solid var(--border);
  padding-bottom:var(--safe-b);
}
.tabbar-inner{ display:grid; grid-auto-flow:column; grid-auto-columns:1fr; position:relative; }

/* Píldora que se desliza de una pestaña a otra. Se mueve en el momento
   del toque, no al llegar la página nueva: así el movimiento se ve
   entero y el cambio se siente inmediato. */
/* Un toque en una pestaña nunca se vuelve desplazamiento: en iPhone, unos
   píxeles de movimiento al apoyar cancelaban el toque (pointercancel) y la
   píldora volvía sin que la pantalla cambiara. La barra es fija: no hay
   nada que desplazar desde ella. */
.tabbar .tab{ touch-action:none; }
.tab-indicador{
  position:absolute; top:6px; left:0;
  height:calc(var(--tabbar-h) - 12px);
  border-radius:var(--r-full);
  background:var(--acento-suave);
  pointer-events:none; z-index:0;
  transition:transform .26s cubic-bezier(.34,.9,.3,1),
             width .26s cubic-bezier(.34,.9,.3,1),
             opacity .18s ease;
}
/* Al entrar en una página debe aparecer ya en su sitio, no llegar
   deslizándose desde el borde izquierdo. */
.tab-indicador.sin-animar{ transition:none; }

.tab{
  position:relative; z-index:1;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
  height:var(--tabbar-h);
  border:none; background:none; cursor:pointer;
  color:var(--text-3); font-size:10.5px; font-weight:500;
  text-decoration:none;
  -webkit-tap-highlight-color:transparent;
}
.tab svg{ width:23px; height:23px; stroke-width:1.8; }
.tab.active{ color:var(--acento-txt); font-weight:600; }
.tab.active svg{ stroke-width:2.2; }
/* Respuesta al dedo antes de que llegue la página nueva. */
.tab:active{ opacity:.6; }
.tab .dot-alert{
  position:absolute; top:7px; left:50%; margin-left:6px;
  width:9px; height:9px; border-radius:50%;
  background:var(--danger); border:2px solid var(--surface);
}

/* ---- Cambio a móvil ---- */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .sidebar{ display:none; }
  .appbar{ display:flex; }
  .tabbar{ display:block; }
  main{
    margin-left:0;
    max-width:none;
    padding:var(--s-4) var(--s-4);
    /* Justo lo necesario para que la última fila no quede bajo la
       barra: más margen que este deja un hueco visible al final. */
    padding-bottom:calc(var(--tabbar-h) + var(--safe-b) + var(--s-4));
  }
  h1{ font-size:var(--t-xl); }
}

/* ---- Aviso superpuesto (layout.js) ----
   "Sin conexión, viendo lo último guardado" y cosas por el estilo: se
   superpone al contenido, nunca a la barra lateral ni a la de arriba,
   así que no empuja nada ni tapa un botón que alguien esté a punto de
   tocar. Se anima al entrar y al salir en vez de aparecer/desaparecer
   de golpe. */
#avisoSinConexion{
  position:fixed; top:0; left:0; right:0; z-index:120;
  background:var(--warn-bg); color:var(--warn);
  border-bottom:1px solid var(--warn-br);
  text-align:center; font-size:var(--t-sm); font-weight:600;
  padding:8px 16px;
  transform:translateY(-100%); opacity:0; pointer-events:none;
  transition:transform .22s ease, opacity .22s ease;
}
#avisoSinConexion.visible{
  transform:translateY(0); opacity:1; pointer-events:auto;
}
@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  #avisoSinConexion{ left:var(--sidebar-w); }
}
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  #avisoSinConexion{ top:var(--appbar-h); }
}
@media (prefers-reduced-motion:reduce){
  #avisoSinConexion{ transition:none; }
}


/* ============================================================
   6. COMPONENTES
   ============================================================ */

/* ---- Bienvenida al iniciar sesión ----
   La plataforma se ve por detrás, desenfocada, y encima solo el trazo
   del logo: el fondo del vídeo se recorta en tiempo real desde el
   armazón. Por eso aquí la capa no lleva color sólido, solo un velo. */
.bienvenida{
  position:fixed; inset:0; z-index:400;
  display:flex; align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--bg) 55%, transparent);
  -webkit-backdrop-filter:blur(18px) saturate(.9);
          backdrop-filter:blur(18px) saturate(.9);
  opacity:1; transition:opacity .45s ease;
}
.bienvenida.saliendo{ opacity:0; pointer-events:none; }

/* El vídeo original no se muestra: es solo la fuente de la que el
   lienzo toma cada fotograma.

   Se saca fuera de la pantalla en lugar de encogerlo u ocultarlo. Safari
   en iPhone se niega a reproducir un vídeo que considera invisible —un
   píxel, opacidad cero o display:none— y entonces no hay fotogramas que
   copiar. Fuera de la pantalla, con su tamaño real, sí lo reproduce. */
.bienvenida-fuente{
  position:absolute; top:0; left:-10000px;
  width:240px; height:135px;
  pointer-events:none;
}

/* Diagnóstico en pantalla, solo con ?diag=1 en la dirección. */
.bienvenida-diag{
  position:absolute; left:0; right:0; bottom:0;
  max-height:45%; overflow:auto;
  padding:10px 12px calc(10px + var(--safe-b));
  background:rgba(0,0,0,.82); color:#9fe870;
  font-family:ui-monospace, monospace; font-size:11px; line-height:1.6;
  white-space:pre-wrap; word-break:break-all;
}

.bienvenida-lienzo{
  width:min(78vw, 460px);
  height:auto;
  /* El lienzo es 16:9 y el logo va centrado, así que el aire de los
     lados es transparente y no molesta. */
}

@media (prefers-reduced-motion:reduce){
  /* A quien pide menos movimiento no se le pone ninguna animación: solo
     el velo desenfocado mientras carga, que dura lo mismo. */
  .bienvenida-lienzo, .bienvenida-fuente{ display:none; }
}

/* ---- Indicador de carga entre páginas ----
   z-index por debajo del armazón a propósito: la barra lateral (110),
   la superior y las pestañas (100) quedan encima y nítidas, así que
   solo se atenúa el contenido y nunca se pierde la referencia de
   dónde estás. */
.cargando-overlay{
  position:fixed; inset:0; z-index:90;
  display:none; align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--bg) 55%, transparent);
  -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px);
}
.cargando-overlay.on{ display:flex; }
.spinner{
  width:34px; height:34px; border-radius:50%;
  border:3px solid var(--border-2); border-top-color:var(--acento);
  animation:girar .7s linear infinite;
}
@keyframes girar{ to{ transform:rotate(360deg); } }
@media (prefers-reduced-motion:reduce){
  .spinner{ animation-duration:1.8s; }
}

/* ---- Encabezado de página ---- */
.page-head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--s-5); flex-wrap:wrap; margin-bottom:var(--s-6);
}
.page-head .sub{ color:var(--text-2); margin-top:4px; }

/* ---- Botones ---- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s-2);
  height:40px; padding:0 var(--s-4);
  border:1px solid transparent; border-radius:var(--r-sm);
  font-size:var(--t-base); font-weight:600; line-height:1;
  cursor:pointer; white-space:nowrap; text-decoration:none;
  transition:background .13s ease, border-color .13s ease, color .13s ease;
  -webkit-tap-highlight-color:transparent;
}
.btn svg{ width:16px; height:16px; flex-shrink:0; }
.btn:disabled{ opacity:.5; cursor:default; }

.btn-primary{ background:var(--acento); color:var(--acento-on); }
.btn-primary:hover:not(:disabled){ background:var(--acento-hover); }
.btn-secondary{ background:var(--surface); color:var(--text); border-color:var(--border-2); box-shadow:var(--sh-1); }
.btn-secondary:hover:not(:disabled){ background:var(--surface-2); }
.btn-ghost{ background:none; color:var(--text-2); }
.btn-ghost:hover:not(:disabled){ background:var(--surface-2); color:var(--text); }
.btn-danger{ background:var(--surface); color:var(--danger); border-color:var(--danger-br); }
.btn-danger:hover:not(:disabled){ background:var(--danger-bg); }

.btn-sm{ height:34px; padding:0 var(--s-3); font-size:var(--t-sm); }
.btn-lg{ height:48px; padding:0 var(--s-6); font-size:var(--t-md); }
.btn-block{ width:100%; }

/* En móvil ningún botón baja de 44 px de alto */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .btn{ height:46px; padding:0 var(--s-5); }
  .btn-sm{ height:44px; }
}

/* ---- Campos ---- */
.field{ margin-bottom:var(--s-4); }
.field > label{ display:block; font-size:var(--t-sm); font-weight:500; color:var(--text); margin-bottom:6px; }
.field .help{ font-size:var(--t-sm); color:var(--text-3); margin-top:6px; }
.field .err{ font-size:var(--t-sm); color:var(--danger); margin-top:6px; }

.input{
  width:100%; height:42px; padding:0 var(--s-3);
  border:1px solid var(--border-2); border-radius:var(--r-sm);
  background:var(--surface); color:var(--text);
  font-size:var(--t-base); outline:none;
  transition:border-color .13s ease, box-shadow .13s ease;
}
textarea.input{ height:auto; min-height:88px; padding:11px var(--s-3); resize:vertical; line-height:1.5; }
select.input{ appearance:none; padding-right:36px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238B95A2' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 12px center; background-size:16px; }
.input::placeholder{ color:var(--text-3); }
.input:hover:not(:disabled){ border-color:var(--text-3); }
.input:focus{ border-color:var(--acento); box-shadow:0 0 0 3px var(--acento-suave); }
.input:disabled{ background:var(--surface-2); color:var(--text-3); cursor:default; }
.input.error{ border-color:var(--danger); }
.input.error:focus{ box-shadow:0 0 0 3px var(--danger-bg); }

/* ---- El zoom automático de iOS ----
   iOS acerca la pantalla al enfocar cualquier campo cuya letra mida
   menos de 16 px, y después no vuelve solo: hay que pellizcar para
   deshacerlo. Peor aún, mientras está acercado la barra lateral y las
   pestañas (que están fijas) se salen de la vista.

   Va con !important a propósito: cada página define sus propios campos
   con más peso que esta regla, y basta con que uno se escape para que
   el problema vuelva. Es el único sitio del proyecto donde se usa. */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .input{ height:48px; }
  input, select, textarea,
  .input, .field input, .field select, .field textarea{
    font-size:16px !important;
  }
}

/* ---- Interruptor ---- */
.switch{ position:relative; display:inline-block; width:44px; height:26px; flex-shrink:0; }
.switch input{ position:absolute; opacity:0; width:100%; height:100%; margin:0; cursor:pointer; z-index:1; }
.switch .slider{ position:absolute; inset:0; background:var(--surface-3); border-radius:var(--r-full); transition:background .16s ease; }
.switch .slider::before{
  content:''; position:absolute; width:20px; height:20px; left:3px; top:3px;
  background:#fff; border-radius:50%; box-shadow:var(--sh-1);
  transition:transform .16s ease;
}
.switch input:checked + .slider{ background:var(--acento); }
.switch input:checked + .slider::before{ transform:translateX(18px); }

.toggle-row{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-4);
  min-height:52px; padding:var(--s-3) var(--s-4);
  border:1px solid var(--border); border-radius:var(--r-sm);
  background:var(--surface); margin-bottom:var(--s-4);
}
.toggle-row .hint{ display:block; font-size:var(--t-sm); color:var(--text-2); margin-top:2px; }

/* ---- Selector segmentado ---- */
.segmented{ display:inline-flex; background:var(--surface-2); border-radius:var(--r-sm); padding:3px; gap:2px; }
.segmented button{
  border:none; background:none; cursor:pointer;
  padding:8px 14px; border-radius:6px;
  font-size:var(--t-sm); font-weight:500; color:var(--text-2);
}
.segmented button[aria-pressed="true"], .segmented button.active{
  background:var(--surface); color:var(--text); font-weight:600; box-shadow:var(--sh-1);
}

/* ---- Insignias ---- */
.badge{
  display:inline-flex; align-items:center; gap:5px;
  height:22px; padding:0 9px; border-radius:var(--r-full);
  font-size:var(--t-xs); font-weight:600; white-space:nowrap;
  background:var(--surface-2); color:var(--text-2); border:1px solid var(--border);
}
.badge .dot{ width:6px; height:6px; border-radius:50%; background:currentColor; }
.badge-ok{ background:var(--ok-bg); color:var(--ok); border-color:var(--ok-br); }
.badge-warn{ background:var(--warn-bg); color:var(--warn); border-color:var(--warn-br); }
.badge-danger{ background:var(--danger-bg); color:var(--danger); border-color:var(--danger-br); }
.badge-marca{ background:var(--acento-suave); color:var(--acento-txt); border-color:transparent; }

/* La etiqueta de un perro sin acudiente con cuenta ("perro suelto").
   Una sola para todas las pantallas: si cada una la pintara a su
   manera, el mismo perro parecería estar en situaciones distintas.
   Pequeña y en línea, porque va pegada al nombre del dueño y no debe
   empujar la fila a una segunda línea. */
.etq-sin-cuenta{
  display:inline-flex; align-items:center; vertical-align:middle;
  height:18px; padding:0 7px; margin-left:6px; border-radius:var(--r-full);
  font-size:10.5px; font-weight:700; line-height:1; white-space:nowrap;
  background:var(--warn-bg); color:var(--warn); border:1px solid var(--warn-br);
}
.etq-sin-cuenta:first-child{ margin-left:0; }

/* Perro archivado: gris y no ámbar, porque no pide nada a nadie; solo
   avisa de que ese perro ya no está en el día a día. */
.etq-archivado{
  display:inline-flex; align-items:center; vertical-align:middle;
  height:18px; padding:0 7px; margin-left:6px; border-radius:var(--r-full);
  font-size:10.5px; font-weight:700; line-height:1; white-space:nowrap;
  background:var(--surface-3); color:var(--text-2); border:1px solid var(--border-2);
}
.etq-archivado:first-child{ margin-left:0; }

/* Contador numérico, para módulos con pendientes */
.count{
  min-width:20px; height:20px; padding:0 6px;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--r-full); background:var(--danger); color:var(--danger-on);
  font-size:11px; font-weight:700; line-height:1;
}

/* ---- Retratos ---- */
.avatar{
  width:40px; height:40px; border-radius:var(--r-full); flex-shrink:0;
  display:flex; align-items:center; justify-content:center; overflow:hidden;
  background:var(--surface-3); color:#fff;
  font-size:var(--t-base); font-weight:700; letter-spacing:-0.02em;
}
.avatar img{ width:100%; height:100%; object-fit:cover; }
.avatar-sm{ width:30px; height:30px; font-size:var(--t-xs); }
.avatar-lg{ width:64px; height:64px; font-size:var(--t-xl); }
.avatar-xl{ width:88px; height:88px; font-size:var(--t-2xl); }

/* ---- Tarjetas ---- */
.card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); }
.card-pad{ padding:var(--s-6); }
.card-head{
  display:flex; align-items:flex-start; justify-content:space-between; gap:var(--s-4);
  padding:var(--s-5) var(--s-6); border-bottom:1px solid var(--border);
}
.card-head .sub{ font-size:var(--t-sm); color:var(--text-2); margin-top:2px; }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .card-pad{ padding:var(--s-4); }
  .card-head{ padding:var(--s-4); }
}

/* ---- Cifras ---- */
.stat{ padding:var(--s-5); background:var(--surface); border:1px solid var(--border); border-radius:var(--r-md); }
.stat .lbl{ font-size:var(--t-sm); color:var(--text-2); margin-bottom:6px; }
.stat .val{ font-size:var(--t-2xl); font-weight:700; letter-spacing:-0.03em; font-variant-numeric:tabular-nums; }
.stat .val.debe{ color:var(--danger); }
.stat .delta{ font-size:var(--t-sm); color:var(--text-3); margin-top:4px; }
.stats-row{ display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:var(--s-3); }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .stat{ padding:var(--s-3) var(--s-4); }
  .stat .val{ font-size:var(--t-lg); }
  .stats-row{ grid-template-columns:1fr 1fr; }
  /* Un número impar de cifras deja la última sola: que ocupe el ancho
     en vez de quedar media tarjeta vacía al lado. */
  .stats-row > .stat:last-child:nth-child(odd){ grid-column:1 / -1; }
}

/* ---- Listas ---- */
.list{ background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); overflow:hidden; }
.list-row{
  display:flex; align-items:center; gap:var(--s-4);
  min-height:64px; padding:var(--s-3) var(--s-5);
  border-bottom:1px solid var(--border);
  transition:background .12s ease;
}
.list-row:last-child{ border-bottom:none; }
.list-row:hover{ background:var(--surface-2); }
.list-row.clickable{ cursor:pointer; }
/* Flecha del aviso: indica que lleva a otro sitio, no que decide aquí. */
.list-row .ir{ width:18px; height:18px; color:var(--text-3); flex-shrink:0; }
a.list-row{ text-decoration:none; color:inherit; }
.list-row.clickable:active{ background:var(--surface-3); }

/* Sin destello gris del navegador: el estado pulsado lo definimos
   nosotros, y el suyo llega tarde y con otro color. */
a, button, .fila, .acceso, .list-row, .pet-card, .item-card, .cliente-card{
  -webkit-tap-highlight-color:transparent;
}
@media (hover:none){
  .fila:active, .acceso:active, .cliente-card:active{ background:var(--surface-2); }
  .item-card:active, .pet-card:active{ opacity:.7; }
}
.list-main{ flex:1; min-width:0; }
.list-title{ font-weight:600; font-size:var(--t-base); }
.list-meta{ font-size:var(--t-sm); color:var(--text-2); margin-top:1px; }
.list-actions{ display:flex; gap:var(--s-2); opacity:0; transition:opacity .12s ease; }
.list-row:hover .list-actions,
.list-row:focus-within .list-actions{ opacity:1; }
/* En táctil no existe "pasar el cursor": las acciones se ven siempre */
@media (hover:none){ .list-actions{ opacity:1; } }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .list-row{ padding:var(--s-3) var(--s-4); }
  .list-actions{ display:none; } /* pasan al botón de tres puntos */
}

/* Filas de las listas largas: las que quedan lejos de la pantalla no se
   estilizan, maquetan ni pintan hasta que se acercan al desplazar. Al
   cambiar de sección, la pantalla nueva y la que se va (el fantasma de
   shell-inline.js) se pintaban enteras en el mismo fotograma: con 300
   filas en Inicio, medio segundo con la CPU 6× más lenta (Android de
   gama media). El alto de reserva solo cuenta hasta que la fila se pinta
   una vez (auto): luego se recuerda el suyo. Solo en filas sin nada que
   se salga de ellas ni posicionado dentro (la contención recorta). */
/* El alto de reserva es el del contenido (sin relleno ni borde) de la
   fila típica en el teléfono; una pantalla con filas más altas lo pone
   en su <style> (Alumnos). */
.list > .list-row{ content-visibility:auto; contain-intrinsic-size:auto 60px; }
.table-wrap > .fila{ content-visibility:auto; contain-intrinsic-size:auto 44px; }

/* ---- Tiras de pestañas horizontales ----
   Van con "body" delante para ganarle a las reglas de cada página.

   Dos cosas que se arreglan aquí:
   · overflow-x:auto por sí solo convierte también el eje vertical en
     desplazable, y la tira se movía hacia arriba y abajo con el dedo.
   · Cuando hay más pestañas de las que caben, no había ninguna señal:
     el borde se desvanece para que se vea que sigue. */
body .toolbar-tabs, body .subtabs, body .tabs, body .det-tabs, body .vistas{
  overflow-x:auto;
  /* Sin esto, declarar overflow-x:auto convierte también el eje
     vertical en desplazable y la tira se movía arriba y abajo. Con
     hidden, el gesto vertical pasa a la página, que es lo natural. */
  overflow-y:hidden;
  -webkit-overflow-scrolling:touch;
  overscroll-behavior-x:contain;
  scrollbar-width:none;
}
body .toolbar-tabs::-webkit-scrollbar,
body .subtabs::-webkit-scrollbar,
body .tabs::-webkit-scrollbar,
body .det-tabs::-webkit-scrollbar,
body .vistas::-webkit-scrollbar{ height:0; }

.con-mas-der{
  -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 36px), transparent);
          mask-image:linear-gradient(to right, #000 calc(100% - 36px), transparent);
}
.con-mas-izq{
  -webkit-mask-image:linear-gradient(to right, transparent, #000 36px);
          mask-image:linear-gradient(to right, transparent, #000 36px);
}
.con-mas-izq.con-mas-der{
  -webkit-mask-image:linear-gradient(to right, transparent, #000 36px, #000 calc(100% - 36px), transparent);
          mask-image:linear-gradient(to right, transparent, #000 36px, #000 calc(100% - 36px), transparent);
}

/* ---- Pestañas dentro de una pantalla ---- */
.tabs{
  display:flex; gap:var(--s-5);
  border-bottom:1px solid var(--border);
  overflow-x:auto; scrollbar-width:none;
}
.tabs::-webkit-scrollbar{ height:0; }
.tabs button{
  border:none; background:none; cursor:pointer; white-space:nowrap;
  padding:var(--s-3) 0; font-size:var(--t-base); font-weight:500; color:var(--text-2);
  border-bottom:2px solid transparent; margin-bottom:-1px;
}
.tabs button[aria-selected="true"], .tabs button.active{
  color:var(--text); font-weight:600; border-bottom-color:var(--acento);
}
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .tabs{ margin:0 calc(var(--s-4) * -1); padding:0 var(--s-4); }
}

/* ---- Pestañas de una pantalla en el teléfono: todas a la vista ----
   Visto en un iPhone (28 de septiembre): en la ficha de un vehículo de
   Mantenimiento «Documentos» quedaba escondida a la derecha y nadie
   sabía que existía; el borde que se desvanece no bastaba. En el
   teléfono las tiras dejan de desplazarse: se reparten en el ancho y,
   si no caben, bajan a otra fila. Sin desplazamiento no hay nada que
   se mueva en vertical.

   Van como segmentos (pista gris y la activa rellena en --tinta, como
   la píldora de la barra de abajo) y no con la raya de abajo: con dos
   filas, una raya bajo una pestaña de la primera fila parece separar
   las dos filas y no marcar nada.

   body:not(#_) para ganarle a los <style> de cada pantalla (Cartera,
   Acudientes, Trabajadores, Solicitudes, Rutas, Configuración,
   Entrenamiento y Mantenimiento declaran su tira). Una pestaña oculta
   con style="display:none" sigue oculta: el estilo en línea gana. */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  body:not(#_) :is(.toolbar-tabs, .subtabs, .det-tabs, .vistas){
    display:flex; flex-wrap:wrap; gap:4px;
    overflow:visible;
    margin-left:0; margin-right:0;
    padding:4px;
    background:var(--surface-2);
    border:none; border-radius:var(--r-md);
    -webkit-mask-image:none; mask-image:none;
  }
  body:not(#_) :is(.toolbar-tabs, .subtabs, .det-tabs, .vistas)
      > :is(.tab-pill, .subtab, .det-tab, .vista){
    /* Crecen para llenar su fila: una fila de dos queda en mitades y la
       última no se queda sola en una esquina. */
    flex:1 1 auto;
    display:inline-flex; align-items:center; justify-content:center; gap:6px;
    min-width:0; min-height:38px;
    margin:0; padding:6px 10px;
    border:none; border-radius:calc(var(--r-md) - 3px);
    background:none;
    font-size:13px; font-weight:600; line-height:1.2;
    white-space:nowrap; text-align:center;
    color:var(--text-2);
    -webkit-tap-highlight-color:transparent;
  }
  body:not(#_) :is(.toolbar-tabs, .subtabs, .det-tabs, .vistas)
      > :is(.tab-pill.active, .subtab.active, .det-tab.on, .vista.on){
    background:var(--tinta); color:var(--tinta-on); font-weight:700;
    box-shadow:var(--sh-1);
  }
  /* El contador dentro de la activa: sobre --tinta, en su mismo color de
     texto y translúcido, en cualquier paleta y en oscuro. */
  body:not(#_) :is(.tab-pill.active, .det-tab.on) :is(.count, .det-cont){
    background:color-mix(in srgb, var(--tinta-on) 22%, transparent);
    color:var(--tinta-on); border-color:transparent;
  }
}

/* ---- Estado vacío ---- */
.empty{ text-align:center; padding:var(--s-14) var(--s-6); }
.empty .ico{
  width:44px; height:44px; margin:0 auto var(--s-4);
  border-radius:var(--r-md); background:var(--surface-2);
  display:flex; align-items:center; justify-content:center; color:var(--text-3);
}
.empty .ico svg{ width:21px; height:21px; }
.empty h3{ margin-bottom:6px; }
.empty p{ color:var(--text-2); max-width:44ch; margin:0 auto var(--s-5); }

/* ---- Bloqueo del fondo con un modal abierto ----
   Sin esto, al desplazarse dentro de un modal el gesto "se escapa" y
   acaba moviendo la página de detrás. Se aplica sola: el armazón vigila
   si hay algún modal abierto y pone esta clase. */
body.sin-scroll{ overflow:hidden; }

/* Y aunque el fondo esté bloqueado, el desplazamiento dentro de una
   caja no debe propagarse al llegar a su tope. */
.modal-overlay, .modal, .dialog, .dialog-body, .detail-scroll,
.desc-list, .picker-list, .sidebar .nav-groups{
  overscroll-behavior:contain;
}

/* En móvil, un solo contenedor con desplazamiento por modal: el propio
   modal. Cada página define su .modal-overlay con desplazamiento propio,
   y al quedar dos anidados el gesto caía a veces en el de fuera —que no
   tiene nada que mover— y parecía que la pantalla se trababa.
   El selector lleva "body" delante para ganarle a las reglas de página. */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  body .modal-overlay.active{ overflow:hidden; }
  body .modal-overlay.active > .modal{
    touch-action:pan-y;
    -webkit-overflow-scrolling:touch;
  }
}

/* ---- Capa oscura compartida ---- */
.scrim{
  position:fixed; inset:0; z-index:200;
  background:rgba(9,12,17,.45);
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px);

  /* Se funde, no aparece. Antes era display:none/block, y un elemento
     que nace en el momento no se puede animar: el navegador no tiene
     desde dónde empezar. Con visibilidad y opacidad sí, y con
     pointer-events se evita que estorbe mientras está apagado. */
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .3s ease, visibility 0s .3s;
}
.scrim.open{
  opacity:1; visibility:visible; pointer-events:auto;
  transition:opacity .3s ease, visibility 0s;
}

/* ---- Salida de los modales ----
   La añade el armazón a todos. Va con "body" delante para ganarle a las
   reglas de cada página, que definen su propio .modal. */
body .modal-overlay .modal{ position:relative; }
.modal-cerrar{
  position:absolute; top:10px; right:10px; z-index:3;
  width:38px; height:38px;
  display:flex; align-items:center; justify-content:center;
  border:none; border-radius:var(--r-full);
  background:var(--surface-2); color:var(--text-2);
  cursor:pointer; transition:background .13s ease, color .13s ease;
}
.modal-cerrar:hover{ background:var(--surface-3); color:var(--text); }
.modal-cerrar svg{ width:18px; height:18px; }
/* Sitio para la equis, que si no se monta sobre títulos largos. */
body .modal-overlay .modal > h2{ padding-right:44px; }

@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  /* En la hoja, la equis sube a la altura del asa. */
  .modal-cerrar{ top:var(--s-3); right:var(--s-3); width:42px; height:42px; }
  .modal-cerrar svg{ width:20px; height:20px; }
}

/* ---- Diálogo (escritorio) / hoja (móvil) ----
   Mismo marcado en los dos: solo cambia la presentación. */
.dialog{
  position:fixed; z-index:210;
  top:50%; left:50%;
  width:min(560px, calc(100vw / var(--zoom, 1) - 40px)); max-height:calc(100dvh / var(--zoom, 1) - 80px);
  display:flex; flex-direction:column;
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r-lg); box-shadow:var(--sh-3);

  /* Siempre montado, apagado con visibilidad. Con display:none no había
     animación posible y el diálogo salía de golpe. */
  transform:translate(-50%,-50%) scale(.96);
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .2s ease,
             transform .24s cubic-bezier(.22,.61,.36,1),
             visibility 0s .24s;
}
.dialog.open{
  transform:translate(-50%,-50%) scale(1);
  opacity:1; visibility:visible; pointer-events:auto;
  transition:opacity .2s ease,
             transform .24s cubic-bezier(.22,.61,.36,1),
             visibility 0s;
}
.dialog-head{ padding:var(--s-5) var(--s-6) var(--s-3); }
.dialog-head h2{ font-size:var(--t-lg); }
.dialog-head .sub{ font-size:var(--t-sm); color:var(--text-2); margin-top:4px; }
.dialog-body{ padding:0 var(--s-6); overflow-y:auto; flex:1; }
.dialog-foot{ padding:var(--s-4) var(--s-6) var(--s-5); display:flex; gap:var(--s-3); }
.dialog-foot .btn{ flex:1; }
.dialog-grip{ display:none; }

@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .dialog{
    top:auto; bottom:0; left:0;
    width:100%; max-height:92dvh;
    border-radius:var(--r-xl) var(--r-xl) 0 0;
    border-left:none; border-right:none; border-bottom:none;
    padding-bottom:var(--safe-b);

    /* En el teléfono sube desde abajo, con la misma curva y el mismo
       tiempo que el panel de avisos: dos cosas que se abren igual tienen
       que sentirse igual. */
    transform:translateY(100%);
    opacity:1;
    transition:transform .34s cubic-bezier(.22,.61,.36,1), visibility 0s .34s;
  }
  .dialog.open{
    transform:translateY(0);
    transition:transform .34s cubic-bezier(.22,.61,.36,1), visibility 0s;
  }

  /* El agarre es la pista de que se puede arrastrar hacia abajo. */
  .dialog-grip{ touch-action:none; }
  .dialog-grip{
    display:block; width:38px; height:4px; border-radius:var(--r-full);
    background:var(--surface-3); margin:10px auto 2px;
  }
  .dialog-head{ padding:var(--s-3) var(--s-4) var(--s-3); }
  .dialog-body{ padding:0 var(--s-4); }
  .dialog-foot{ padding:var(--s-4); flex-direction:column-reverse; }
  .dialog-foot .logout-link{ height:50px; }
}

@media (prefers-reduced-motion:reduce){
  .dialog, .scrim{ transition-duration:.01s !important; }
}

/* ---- Menú de acciones (los tres puntos) ---- */
.action-sheet .dialog-body{ padding-bottom:var(--s-2); }
.action-item{
  display:flex; align-items:center; gap:var(--s-3);
  width:100%; min-height:52px; padding:0 var(--s-1);
  border:none; background:none; cursor:pointer; text-align:left;
  font-size:var(--t-base); font-weight:500; color:var(--text);
  border-bottom:1px solid var(--border);
}
.action-item:last-child{ border-bottom:none; }
.action-item svg{ width:20px; height:20px; color:var(--text-2); flex-shrink:0; }
.action-item.danger{ color:var(--danger); }
.action-item.danger svg{ color:var(--danger); }

/* ---- Esqueleto de carga ---- */
.skeleton{
  background:linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size:400% 100%;
  animation:sk 1.3s ease infinite;
  border-radius:var(--r-sm);
}
@keyframes sk{ 0%{ background-position:100% 50%; } 100%{ background-position:0 50%; } }

/* ---- Buscador ---- */
.search-box{
  display:flex; align-items:center; gap:var(--s-2);
  height:42px; padding:0 var(--s-3);
  background:var(--surface); border:1px solid var(--border-2); border-radius:var(--r-sm);
}
.search-box:focus-within{ border-color:var(--acento); box-shadow:0 0 0 3px var(--acento-suave); }
.search-box svg{ width:17px; height:17px; color:var(--text-3); flex-shrink:0; }
.search-box input{ flex:1; min-width:0; border:none; background:none; outline:none; color:var(--text); font-size:var(--t-base); }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){ .search-box{ height:48px; } }

/* ---- Utilidades mínimas ---- */
.muted{ color:var(--text-2); }
.dim{ color:var(--text-3); }
.num{ font-variant-numeric:tabular-nums; }
.stack{ display:flex; flex-direction:column; gap:var(--s-5); }
.row-gap{ display:flex; gap:var(--s-3); flex-wrap:wrap; align-items:center; }
.sr-only{ position:absolute; width:1px; height:1px; padding:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }


/* ============================================================
   7. LEGADO
   Clases de las páginas todavía sin migrar. Se dejan usables y
   con el aspecto nuevo, no bonitas. Se borran a medida que cada
   pantalla pasa a los componentes de arriba.
   ============================================================ */
.sidebar-overlay{ position:fixed; inset:0; background:rgba(9,12,17,.45); z-index:105; display:none; }
.sidebar-overlay.active{ display:block; }
.menu-toggle{ display:none; }

.brand{ display:flex; align-items:center; gap:9px; padding:var(--s-2) var(--s-3) var(--s-3); }
.brand-logo{ width:30px; height:30px; border-radius:9px; object-fit:cover; flex-shrink:0; }
.brand-text{ min-width:0; }
.brand-text .name{ font-size:var(--t-sm); font-weight:700; letter-spacing:-0.01em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.brand-text .sub{ font-size:11px; color:var(--text-3); margin-top:1px; }

.nav-label, .nav-groups > .nav-label{
  font-size:11px; font-weight:600; color:var(--text-3);
  padding:var(--s-3) var(--s-3) 4px; text-transform:none; letter-spacing:0;
}
.nav-groups{ display:flex; flex-direction:column; gap:1px; }
.nav-item{
  display:flex; align-items:center; gap:9px;
  padding:7px var(--s-3); border-radius:var(--r-sm);
  color:var(--text-2); font-size:13.5px; font-weight:500;
  text-decoration:none; cursor:pointer;
  transition:background .12s ease, color .12s ease;
}
.nav-item svg{ width:18px; height:18px; flex-shrink:0; }
.nav-item:hover{ background:var(--surface-2); color:var(--text); }
.nav-item:active{ background:var(--surface-3); }
.nav-item.active{ background:var(--acento-suave); color:var(--acento-txt); font-weight:600; box-shadow:none; }
.nav-item.disabled{ color:var(--text-3); cursor:default; }
.nav-item.disabled:hover{ background:none; color:var(--text-3); }
.nav-item .tag{
  margin-left:auto; font-size:11px; font-weight:500; color:var(--text-3);
  border:none; padding:0; text-transform:none; letter-spacing:0;
}

.sidebar-footer{ margin-top:auto; padding-top:var(--s-2); border-top:1px solid var(--border); }
.role-card{ display:flex; align-items:center; gap:9px; padding:var(--s-2) var(--s-3); }
.avatar-ring{ width:32px; height:32px; border-radius:50%; background:none; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.avatar-ring .avatar{ width:32px; height:32px; background:var(--acento); color:var(--acento-on); font-size:11px; }
.role-card .who{ min-width:0; }
.role-card .who .n{ font-size:var(--t-sm); font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.role-card .who .r{ font-size:var(--t-xs); color:var(--text-3); }

/* Apariencia: la elige cada usuario y está a mano en todas las páginas,
   no escondida en un ajuste. */
.modo-row{ display:flex; align-items:center; justify-content:space-between; gap:var(--s-2); padding:2px var(--s-3) var(--s-2); }
.modo-label{ font-size:var(--t-sm); color:var(--text-2); }
.segmented-iconos button{ width:32px; height:28px; padding:0; display:inline-flex; align-items:center; justify-content:center; }
.segmented-iconos svg{ width:15px; height:15px; }

/* Cerrar sesión: fondo propio en vez de un borde fino que se pierde. */
.logout-link{
  width:100%; height:40px;
  background:var(--surface-2); border:none; border-radius:var(--r-sm);
  color:var(--text-2); font-family:inherit; font-size:var(--t-base); font-weight:600;
  cursor:pointer; transition:background .13s ease, color .13s ease;
}
.logout-link:hover{ background:var(--danger-bg); color:var(--danger); }

.topbar{ display:flex; align-items:center; justify-content:space-between; gap:var(--s-4); margin-bottom:var(--s-6); }
.topbar-left{ display:flex; align-items:center; gap:var(--s-3); min-width:0; }
.crumbs{ font-size:var(--t-sm); color:var(--text-3); letter-spacing:0; }
.crumbs b{ color:var(--text); font-weight:600; }
.top-right{ display:flex; align-items:center; gap:var(--s-3); }
.clock{
  font-size:var(--t-sm); color:var(--text-2); font-variant-numeric:tabular-nums;
  background:var(--surface); border:1px solid var(--border);
  padding:9px var(--s-3); border-radius:var(--r-sm); min-width:96px; text-align:center;
}
.search{
  display:flex; align-items:center; gap:var(--s-2);
  background:var(--surface); border:1px solid var(--border);
  padding:9px var(--s-3); border-radius:var(--r-sm);
  color:var(--text-3); font-size:var(--t-sm); width:220px;
}
.search svg{ width:16px; height:16px; }
.section-label{
  font-size:var(--t-sm); font-weight:600; color:var(--text-2);
  margin:var(--s-8) 0 var(--s-4); text-transform:none; letter-spacing:0;
}

@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .topbar{ display:none; }  /* la reemplaza .appbar */
}


/* ---- Recortador de imágenes (photocrop.js) ----
   El área se ve redonda porque así se mostrará después: en el menú, en
   las fichas y en la pantalla de acceso. Un recuadro cuadrado aquí haría
   que el encuadre elegido no coincidiera con el resultado. */
.recorte-capa{
  position:fixed; inset:0; z-index:300;
  display:flex; align-items:center; justify-content:center;
  padding:var(--s-5);
  background:rgba(9,12,17,.62);
  -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
}
.recorte-panel{
  width:100%; max-width:420px;
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r-lg); box-shadow:var(--sh-3);
  padding:var(--s-6) var(--s-5) calc(var(--s-5) + var(--safe-b));
  text-align:center;
}
.recorte-titulo{ font-size:var(--t-md); font-weight:700; letter-spacing:-0.01em; }
.recorte-ayuda{ font-size:var(--t-sm); color:var(--text-2); margin-top:4px; line-height:1.5; }

.recorte-marco{
  position:relative; margin:var(--s-5) auto var(--s-4);
  border-radius:var(--r-full); overflow:hidden;
  background:var(--surface-2);
  cursor:grab; touch-action:none;   /* el gesto lo maneja el recortador */
  user-select:none; -webkit-user-select:none;
}
.recorte-marco:active{ cursor:grabbing; }
.recorte-marco canvas{ display:block; }
/* Un aro tenue que recuerda dónde está el borde del círculo. */
.recorte-mascara{
  position:absolute; inset:0; border-radius:var(--r-full);
  box-shadow:inset 0 0 0 2px rgba(255,255,255,.55);
  pointer-events:none;
}

.recorte-zoom{ display:flex; align-items:center; gap:var(--s-3); margin-bottom:var(--s-5); color:var(--text-3); }
.recorte-zoom svg{ width:18px; height:18px; flex-shrink:0; }
.recorte-zoom input{ flex:1; accent-color:var(--acento); height:22px; }

.recorte-botones{ display:flex; gap:var(--s-3); }
.recorte-botones button{ flex:1; height:46px; border-radius:var(--r-sm); font-family:inherit; font-size:var(--t-base); font-weight:600; cursor:pointer; }
.recorte-cancelar{ border:1px solid var(--border-2); background:var(--surface); color:var(--text-2); }
.recorte-guardar{ border:none; background:var(--acento); color:var(--acento-on); }
.recorte-guardar:hover{ background:var(--acento-hover); }


/* ---- Calendario de rango (daterange.js) ----
   El tramo elegido se ve de un vistazo: extremos marcados y los días
   de en medio con fondo suave. Es la forma en que ya lo entiende
   cualquiera, porque es la de reservar un hotel. */
.rf-panel{
  position:absolute; z-index:400; display:none;
  width:300px; padding:var(--s-4);
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r-lg); box-shadow:var(--sh-3);
}
.rf-panel.abierto{ display:block; }

/* En móvil se pega al fondo, que es donde llega el pulgar. */
.rf-panel.rf-hoja{
  position:fixed; inset:auto 0 0 0; width:auto;
  border-radius:var(--r-xl) var(--r-xl) 0 0;
  border-left:none; border-right:none; border-bottom:none;
  padding:var(--s-5) var(--s-4) calc(var(--s-5) + var(--safe-b));
}

/* Antes eran tres botones con justify-content:space-between, centrados
   a base de que los dos de los extremos pesaran lo mismo. Con la X de
   cerrar como cuarto botón ese truco ya no cuadra: el mes ahora es el
   que reparte el espacio sobrante (flex:1) y se centra por su cuenta. */
.rf-cabecera{ display:flex; align-items:center; gap:6px; margin-bottom:var(--s-3); }
/* El nombre del mes es un botón: al pulsarlo se despliega el salto a
   cualquier mes y año. La flechita indica que hace algo. */
.rf-mes{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  flex:1; min-width:0; height:36px; padding:0 var(--s-3);
  border:none; background:none; border-radius:var(--r-sm);
  font-family:inherit; font-size:var(--t-base); font-weight:700;
  color:var(--text); text-transform:capitalize; cursor:pointer;
}
.rf-mes:hover{ background:var(--surface-2); }
.rf-mes::after{
  content:''; width:0; height:0; flex-shrink:0;
  border-left:4px solid transparent; border-right:4px solid transparent;
  border-top:5px solid var(--text-3);
}

/* Las dos vistas ocupan el mismo sitio: o el calendario, o los meses.
   Nunca las dos, que era lo que hacía parecer dos calendarios. */
.rf-vista-meses{ display:none; }
.rf-panel.en-meses .rf-vista-dias{ display:none; }
.rf-panel.en-meses .rf-vista-meses{ display:block; }

.rf-meses{ display:grid; grid-template-columns:repeat(3, 1fr); gap:6px; padding:var(--s-2) 0; }
.rf-mes-op{
  height:52px; border:1px solid var(--border-2); background:var(--surface);
  border-radius:var(--r-sm); font-family:inherit; font-size:var(--t-sm);
  color:var(--text); cursor:pointer; text-transform:capitalize;
}
/* Solo con ratón: en el teléfono el toque en un año dejaba marcado el
   mes que aparece debajo del dedo. */
@media (hover:hover){ .rf-mes-op:hover:not(:disabled){ background:var(--surface-2); } }
.rf-mes-op:disabled{ opacity:.35; cursor:default; }
/* El año o el mes de hoy, para orientarse al saltar lejos. */
.rf-mes-op.actual{ font-weight:700; border-color:var(--text-3); }
.rf-mes-op.activo, .rf-mes-op.activo:hover{ background:var(--acento); border-color:transparent; color:var(--acento-on); font-weight:700; }
/* En los años, la flechita apunta arriba: el título lleva de vuelta. */
.rf-panel[data-vista="anios"] .rf-mes::after{ transform:rotate(180deg); }
.rf-nav{
  width:36px; height:36px; display:flex; align-items:center; justify-content:center;
  border:1px solid var(--border-2); background:var(--surface);
  border-radius:var(--r-sm); color:var(--text-2); cursor:pointer;
}
.rf-nav:hover{ background:var(--surface-2); color:var(--text); }
.rf-nav svg{ width:16px; height:16px; }

.rf-cerrar{
  width:30px; height:30px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  border:none; background:var(--surface-2); color:var(--text-3);
  border-radius:var(--r-full); cursor:pointer;
}
.rf-cerrar:hover{ background:var(--border); color:var(--text); }
.rf-cerrar svg{ width:14px; height:14px; }

.rf-ayuda{ font-size:var(--t-xs); color:var(--text-2); line-height:1.4; margin-bottom:var(--s-3); }

.rf-dias{ display:grid; grid-template-columns:repeat(7, 1fr); gap:2px; margin-bottom:4px; }
.rf-dias span{ text-align:center; font-size:var(--t-xs); font-weight:600; color:var(--text-3); padding:4px 0; }

.rf-rejilla{ display:grid; grid-template-columns:repeat(7, 1fr); gap:2px; }
.rf-vacio{ height:38px; }

.rf-dia{
  height:38px; border:none; background:none; border-radius:var(--r-sm);
  font-family:inherit; font-size:var(--t-sm); color:var(--text); cursor:pointer;
}
.rf-dia:hover{ background:var(--surface-2); }
.rf-dia.futuro{ color:var(--text-3); }
.rf-dia.hoy{ font-weight:700; text-decoration:underline; }
.rf-dia.deshabilitado{ color:var(--text-3); opacity:.4; cursor:default; }
.rf-dia.deshabilitado:hover{ background:none; }

/* Los días de en medio, con fondo suave y sin esquinas: así el tramo
   se lee como una banda continua y no como días sueltos. */
.rf-dia.entre{ background:var(--acento-suave); border-radius:0; }
.rf-dia.inicio, .rf-dia.fin{ background:var(--acento); color:var(--acento-on); font-weight:700; }
.rf-dia.inicio{ border-radius:var(--r-sm) 0 0 var(--r-sm); }
.rf-dia.fin{ border-radius:0 var(--r-sm) var(--r-sm) 0; }
.rf-dia.inicio.fin{ border-radius:var(--r-sm); }

.rf-pie{ display:flex; gap:var(--s-2); margin-top:var(--s-4); }
.rf-pie button{
  flex:1; height:42px; border-radius:var(--r-sm);
  font-family:inherit; font-size:var(--t-sm); font-weight:600; cursor:pointer;
}
.rf-limpiar{ border:1px solid var(--border-2); background:var(--surface); color:var(--text-2); }
.rf-limpiar:hover{ background:var(--surface-2); }
.rf-listo{ border:none; background:var(--acento); color:var(--acento-on); }
.rf-listo:hover{ background:var(--acento-hover); }


/* ============================================================
   CONFIRMACIÓN AL GUARDAR (mostrarHecho, en ui.js)
   La píldora está en la sección 8. Aquí queda lo que hace el
   modal mientras tanto: se apaga un poco y deja de responder,
   para que nadie vuelva a pulsar «Guardar». El prefijo «hecho-»
   no choca con nada: `.hecho` a secas sí existe en rutas.html y
   mi-ruta.html, como estado de otras piezas.
   ============================================================ */
.hecho-bloqueo{
  pointer-events:none;
  opacity:.55;
  transition:opacity .3s ease;
}

@keyframes hecho-rebote{
  0%{ transform:scale(.4); opacity:0; }
  70%{ transform:scale(1.1); opacity:1; }
  100%{ transform:scale(1); opacity:1; }
}
@keyframes hecho-dibujar{ to{ stroke-dashoffset:0; } }


/* ============================================================
   8. ESTILO PASTEL
   La reforma visual del 27 de septiembre de 2026. Va al final a
   propósito: cambia el aspecto de piezas que están definidas más
   arriba y, sobre todo, de las que define cada pantalla en su
   propio <style>, sin tocar ese marcado.

   Por eso muchos selectores empiezan por `body:not(#_)`. Ese #_ no
   existe: el :not() solo aporta el peso de un identificador, que es
   lo que hace falta para ganarle a las reglas de cada pantalla
   (algunas llegan a tres clases) sin llenar esto de !important.

   Nada de aquí cambia qué se ve ni dónde: solo cómo.
   ============================================================ */

/* ---------- Tipografía ---------- */
body{ font-feature-settings:'ss01' 0; letter-spacing:-0.005em; }

h1, h2,
.page-head h1, .page-header h1,
.saludo, .modal h2, .dialog-head h2{
  font-family:var(--font-titulo);
  font-weight:700; letter-spacing:-0.04em;
}
h1{ font-size:28px; line-height:1.1; }
h2{ line-height:1.15; }
h3, h4{ letter-spacing:-0.015em; }
body:not(#_) .page-header h1{ font-size:28px; line-height:1.1; }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  h1, body:not(#_) .page-header h1{ font-size:24px; }
}

/* Palabra destacada en cursiva: el nombre del saludo, una fecha. */
.acento-serif{
  font-family:var(--font-acento); font-style:italic; font-weight:400;
  letter-spacing:-0.01em;
}

/* Las cifras grandes van en la letra de los títulos. */
.stat .val, .cifra, .kpi-val, .resumen-num{
  font-family:var(--font-titulo); letter-spacing:-0.04em;
}

/* ---------- Armazón: barra lateral ----------
   Una tarjeta flotante con aire alrededor, no un panel pegado al
   borde. La columna reservada (--sidebar-w) es la misma de siempre,
   así que el contenido no se mueve.

   Compacta a propósito: el menú de un administrador tiene trece
   entradas y cinco grupos, y tiene que caber entero en la ventana útil
   de un portátil (unos 650 px de alto con las barras del navegador).
   Las medidas que cambian con la altura de la ventana están en la
   sección 11; aquí queda el aspecto. */
.sidebar{
  top:12px; left:12px; bottom:12px;
  height:auto; width:calc(var(--sidebar-w) - 12px);
  border:none; border-radius:var(--r-xl);
  background:var(--surface); box-shadow:var(--sh-2);
  padding:14px 10px;
}
.sidebar .brand{ padding:4px 8px 12px; gap:10px; }
.brand-logo, .sidebar .brand-logo{ width:34px; height:34px; border-radius:11px; }
.brand-text .name{ font-family:var(--font-titulo); font-size:14.5px; letter-spacing:-0.03em; }
.brand-text .sub{ color:var(--text-3); }

.nav-label, .nav-groups > .nav-label{
  font-size:10.5px; font-weight:600; color:var(--text-3);
  text-transform:uppercase; letter-spacing:.06em; line-height:16px;
  padding:12px var(--s-3) 4px;
}
.sidebar .nav-groups > .nav-label:first-child{ padding-top:2px; }
.nav-groups{ gap:1px; }
.nav-item{
  padding:7px var(--s-3); line-height:20px; border-radius:var(--r-full);
  font-size:14px; color:var(--text-2);
  /* Sin transición de color: al tocar una entrada, la píldora oscura
     salta a ella en el acto. Con 0,2 s de fundido, la foto de la
     pantalla que se va (la transición entre pantallas) se tomaba a
     medio camino y el menú se veía unos fotogramas con dos entradas
     grises. El paso del ratón sí se suaviza, solo al entrar. */
  transition:transform .2s var(--curva);
}
.nav-item:hover{ background:var(--surface-2); color:var(--text); }
.nav-item:not(.active):hover{ transition:background .15s ease, color .15s ease, transform .2s var(--curva); }
.nav-item:active{ transform:scale(.98); }
/* La sección activa en píldora oscura, igual que en la barra de abajo
   del teléfono: las dos dicen «estás aquí» con el mismo gesto. */
.nav-item.active{ background:var(--tinta); color:var(--tinta-on); font-weight:600; }
.nav-item.active .count{ background:var(--danger); }
/* La píldora del menú se desliza de una entrada a otra, como la de la
   barra de abajo. Es una pieza aparte detrás de las entradas; shell-inline
   la coloca y solo entonces pone .con-marca, que quita el fondo de la
   entrada activa (sin JS, la entrada activa sigue con el suyo). */
.sidebar .nav-groups{ position:relative; }
.sidebar .nav-item{ position:relative; z-index:1; }
.nav-marca{
  position:absolute; left:0; top:0; z-index:0; display:none;
  border-radius:var(--r-full); background:var(--tinta);
  pointer-events:none;
  transition:transform var(--rebote-t) var(--rebote), height var(--rebote-t) var(--curva);
}
.nav-marca.sin-animar{ transition:none; }
.sidebar .nav-groups.con-marca .nav-marca{ display:block; }
.sidebar .nav-groups.con-marca .nav-item.active{ background:transparent; }
/* La letra cambia de color mientras la marca viaja (0,2 s), no en seco:
   en el menú el viaje es largo y, en seco, la entrada de destino se
   quedaba blanca sobre blanco hasta que llegaba la marca. */
.sidebar .nav-groups.con-marca .nav-item{ transition:transform .2s var(--curva), color .12s ease, background .15s ease; }

.sidebar-footer{ border-top:none; padding-top:8px; }
.sidebar .role-card{ padding:6px 8px; border-radius:var(--r-md); background:var(--surface-2); margin-bottom:6px; }
.sidebar .modo-row{ padding:2px 8px 6px; }
.sidebar .logout-link{ border-radius:var(--r-full); height:40px; }

/* Aire medido: con más, Inicio no cabía en un portátil (1366×768). */
main{ padding-top:var(--s-6); }
@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  main{ padding-left:var(--s-8); padding-right:var(--s-8); padding-bottom:var(--s-6); }
}

/* ---------- Armazón: barra de arriba del teléfono ----------
   Translúcida sobre el fondo, sin raya: el contenido pasa por debajo
   y se intuye. */
.appbar{
  background:color-mix(in srgb, var(--bg) 82%, transparent);
  -webkit-backdrop-filter:saturate(1.4) blur(18px);
          backdrop-filter:saturate(1.4) blur(18px);
  border-bottom:none;
}
.appbar .appbar-title{ font-family:var(--font-titulo); font-size:20px; letter-spacing:-0.04em; }
.appbar-logo{ width:32px; height:32px; border-radius:11px; }

/* ---------- Armazón: barra de pestañas del teléfono ----------
   Una cápsula que flota sobre el contenido, con la sección activa en
   píldora oscura. La píldora sigue siendo .tab-indicador: shell-inline
   la coloca midiendo cada pestaña, así que no hay que tocar nada más. */
/* El fondo no va en la barra sino en dos capas (sección 10): así, al
   recogerse, se puede encoger y volver cristal moviendo solo transform
   y opacity. Aquí, en reposo, se ven igual que el fondo de siempre. */
.tabbar{
  left:12px; right:12px;
  bottom:calc(10px + var(--safe-b));
  padding:6px;
  border:none; border-radius:var(--r-full);
  background:none;
}
.tabbar-inner{ z-index:1; }
.tab-indicador{
  top:0; height:var(--tabbar-h);
  background:var(--tinta);
  /* Se va una sola vez, con un pequeño rebote: arranca rápido y se asienta. */
  transition:transform var(--rebote-t) var(--rebote), width var(--rebote-t) var(--curva), opacity .2s ease;
}
.tab{ height:var(--tabbar-h); color:var(--text-2); font-size:11px; gap:2px; min-width:0; }
/* El nombre, siempre en un renglón y dentro de la píldora: partido en
   dos se salía de ella («Mis / ascotas»). 7 px por lado y no 2 (lo que
   deja la píldora): es casi redonda y abajo, donde va el nombre, se
   estrecha.
   Si una palabra del colegio no cabe, shell-inline baja la letra de
   todos por igual (--tab-letra, ajustarNombres); solo si ni así cabe,
   «…». */
.tab > span:not([class]){
  display:block; max-width:calc(100% - 14px);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  font-size:var(--tab-letra, inherit);
}
.tab.active{ color:var(--tinta-on); }
.tab .dot-alert{ border-color:var(--surface); }
.tab.active .dot-alert{ border-color:var(--tinta); }

@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  main{
    padding-top:var(--s-2);
    /* La cápsula flota 10 px por encima del borde: se reserva también ese aire. */
    padding-bottom:calc(var(--tabbar-ocupa) + var(--safe-b) + var(--s-6));
  }
}

/* ---------- Botones ----------
   Todos en píldora. El principal, casi negro; el secundario, blanco
   con una sombra mínima. */
.btn{
  border-radius:var(--r-full); height:40px; padding:0 var(--s-5);
  font-weight:600; letter-spacing:-0.005em;
  transition:background .2s var(--curva), color .2s var(--curva),
             border-color .2s var(--curva), box-shadow .2s var(--curva),
             transform .2s var(--curva);
}
.btn:active:not(:disabled){ transform:scale(.97); }
.btn-primary{ background:var(--tinta); color:var(--tinta-on); }
.btn-primary:hover:not(:disabled){ background:var(--tinta-hover); }
.btn-secondary{ background:var(--surface); border-color:var(--border-2); box-shadow:var(--sh-1); }
.btn-sm{ height:34px; padding:0 var(--s-4); }
.btn-lg{ height:48px; padding:0 var(--s-8); }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .btn{ height:46px; }
  .btn-sm{ height:44px; }
}

/* Los botones que cada pantalla define por su cuenta. Los de guardar,
   añadir o aprobar son la acción principal: van en tinta. */
body:not(#_) :is(.btn-save, .btn-add:not(.secundario), .btn-approve, .btn-aprobar, .btn-aprobar-plan,
  .pv-guardar, .btn-pagar, .btn-pagar-total, .btn-marcar-total, .btn-chico.principal,
  .modal-actions .prim, .det-acciones button.principal, .link-box button, .enlace-caja button,
  .recorte-guardar, .rf-listo){
  background:var(--tinta); color:var(--tinta-on);
  border-color:transparent; border-radius:var(--r-full);
  transition:background .2s var(--curva), transform .2s var(--curva), opacity .2s ease;
}
body:not(#_) :is(.btn-save, .btn-add:not(.secundario), .btn-approve, .btn-aprobar, .btn-aprobar-plan,
  .pv-guardar, .btn-pagar, .btn-pagar-total, .btn-marcar-total, .btn-chico.principal,
  .modal-actions .prim, .det-acciones button.principal, .link-box button, .enlace-caja button,
  .recorte-guardar, .rf-listo):hover:not(:disabled){
  background:var(--tinta-hover);
}
body:not(#_) :is(.btn-save, .btn-add, .btn-approve, .btn-aprobar, .btn-pagar, .btn-pagar-total,
  .btn-marcar-total, .btn-secondary, .btn-icon, .btn-deny, .btn-denegar, .modal-close,
  .btn-delete-confirm, .btn-resolver, .btn-texto, .btn-mini, .btn-notif, .btn-ver-mapa,
  .recorte-cancelar, .rf-limpiar):active:not(:disabled){
  transform:scale(.97);
}
body:not(#_) :is(.btn-save, .btn-delete-confirm){ height:46px; }
/* Los de las guías y el permiso de notificaciones (sus estilos van
   dentro de guia.js y permiso-notificaciones.js). */
body:not(#_) :is(.previa-notif-si, .guia-pie .seguir:not(:disabled), .guia-accion){
  background:var(--tinta); color:var(--tinta-on); box-shadow:none;
}
body:not(#_) .previa-notif-si{ border-radius:var(--r-full); }

/* Secundarios y de icono: píldora clara. */
body:not(#_) :is(.btn-secondary, .btn-add.secundario, .btn-icon, .btn-deny, .btn-denegar, .modal-close,
  .btn-delete-confirm, .btn-resolver, .btn-mini, .btn-notif, .btn-ver-mapa,
  .filtro-archivados, .toggle-pill, .recorte-cancelar, .rf-limpiar, .rf-pie button,
  .recorte-botones button, .back-link){
  border-radius:var(--r-full);
}
body:not(#_) .modal-close{ height:44px; border-color:var(--border-2); }

/* ---------- Campos ----------
   Más altos, con radio de 14 px y aire dentro. Incluye teléfono,
   número, fecha y los desplegables, que antes se quedaban con el
   aspecto del navegador en algunas pantallas. */
.input{ border-radius:var(--r-campo); height:44px; padding:0 var(--s-4); border-color:var(--border-2); }
textarea.input{ padding:13px var(--s-4); }
.input:focus, .search-box:focus-within{
  border-color:var(--acento);
  box-shadow:0 0 0 4px color-mix(in srgb, var(--acento) 16%, transparent);
}
body:not(#_) :is(input:not([type]), input[type="text"], input[type="email"], input[type="tel"],
  input[type="number"], input[type="date"], input[type="time"], input[type="datetime-local"],
  input[type="month"], input[type="password"], input[type="search"], input[type="url"],
  select, textarea){
  border-radius:var(--r-campo);
}
body:not(#_) :is(.field, .campo, .modal .field, .plan-row, .pago-row, .dplan-row, .link-box, .enlace-caja)
  :is(input:not([type]), input[type="text"], input[type="email"], input[type="tel"],
  input[type="number"], input[type="date"], input[type="time"], input[type="datetime-local"],
  input[type="month"], input[type="password"], input[type="url"], select){
  min-height:44px;
  padding-left:var(--s-4); padding-right:var(--s-4);
  border-color:var(--border-2);
  background-color:var(--surface);
}
body:not(#_) :is(.field, .campo) select{ padding-right:40px; }
body:not(#_) :is(.field, .campo) textarea{ padding:13px var(--s-4); border-color:var(--border-2); }
body:not(#_) :is(.field, .campo) :is(input, select, textarea):focus{
  border-color:var(--acento);
  box-shadow:0 0 0 4px color-mix(in srgb, var(--acento) 16%, transparent);
}
body:not(#_) :is(.field, .campo) > label{ font-weight:600; margin-bottom:8px; }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .input{ height:48px; }
  body:not(#_) :is(.field, .campo, .modal .field, .plan-row, .pago-row, .dplan-row, .link-box, .enlace-caja)
    :is(input:not([type]), input[type="text"], input[type="email"], input[type="tel"],
    input[type="number"], input[type="date"], input[type="time"], input[type="datetime-local"],
    input[type="month"], input[type="password"], input[type="url"], select){
    min-height:48px;
  }
}
.field{ margin-bottom:var(--s-4); }

/* El botón del selector de archivos del navegador, en píldora como
   los demás en vez del botón gris del sistema. */
input[type="file"]::file-selector-button{
  height:36px; margin-right:10px; padding:0 16px;
  border:1px solid var(--border-2); border-radius:var(--r-full);
  background:var(--surface); color:var(--text);
  font-family:inherit; font-size:var(--t-sm); font-weight:600; cursor:pointer;
}
input[type="file"]{ color:var(--text-2); font-size:var(--t-sm); }

.search-box{ height:44px; border-radius:var(--r-full); padding:0 var(--s-4); border-color:transparent; box-shadow:var(--sh-2); }
[data-theme="dark"] .search-box{ border-color:var(--border); }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){ .search-box{ height:48px; } }

/* Interruptor: un poco más grande y con el mismo resorte. */
.switch .slider::before, body:not(#_) .slider::before{ transition:transform .3s var(--curva); }
.toggle-row{ border-radius:var(--r-md); border-color:transparent; background:var(--surface-2); }
body:not(#_) .toggle-row{ border-radius:var(--r-md); border-color:transparent; background:var(--surface-2); }

.segmented{ border-radius:var(--r-full); padding:4px; }
.segmented:not(.segmented-iconos) button{ border-radius:var(--r-full); padding:8px 16px; }
.segmented-iconos button{ border-radius:var(--r-full); width:34px; }

/* ---------- Tarjetas, listas y cifras ----------
   Sin borde: se separan del fondo por el color y una sombra que casi
   no se ve. En oscuro la sombra no se nota, así que ahí vuelve un
   borde muy tenue. */
.card, .list, .stat{
  border-color:transparent; border-radius:var(--r-lg);
  box-shadow:var(--sh-2);
}
body:not(#_) :is(.card, .table-wrap, .list, .pending-list, .resumen-card, .compra-card,
  .hoy-tarjeta, .pet-card, .pet-mini-card, .cliente-cards,
  .choice-card, .formulario-card, .chequeo-card-movil){
  border-color:transparent;
  border-radius:var(--r-lg);
  box-shadow:var(--sh-2);
}
[data-theme="dark"] body:not(#_) :is(.card, .table-wrap, .list, .stat, .pending-list,
  .resumen-card, .compra-card, .hoy-tarjeta, .pet-card,
  .pet-mini-card, .cliente-cards, .choice-card, .formulario-card, .chequeo-card-movil){
  border-color:var(--border);
}
.card-head{ border-bottom-color:var(--border); }
.stat{ padding:var(--s-5); border-radius:var(--r-lg); }
.stat .lbl{ font-weight:500; }
.stat .val{ font-size:30px; line-height:1.05; }
.stats-row{ gap:var(--s-3); }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .stat{ padding:var(--s-4); }
  .stat .val{ font-size:24px; }
}
.list-row{ border-bottom-color:var(--border); }
.list-row:hover{ background:color-mix(in srgb, var(--surface-2) 70%, transparent); }

/* Tarjetas que se tocan: suben un poco al pasar el ratón y se hunden
   al pulsarlas. Solo con ratón de verdad: en el teléfono el «pasar
   por encima» se queda pegado después del toque. */
:is(.acceso, a.card, .card.clickable, .pet-card, .pet-mini-card, .item-card,
  .choice-card, .formulario-card, .chequeo-card-movil, .bento-tile[href]){
  transition:transform .32s var(--curva), box-shadow .32s var(--curva), background .2s ease;
  will-change:transform;
}
@media (hover:hover){
  :is(.acceso, a.card, .card.clickable, .pet-card, .pet-mini-card, .item-card,
    .choice-card, .formulario-card, .bento-tile[href]):hover{
    transform:translateY(-4px);
    box-shadow:var(--sh-alza);
  }
}
:is(.acceso, a.card, .card.clickable, .pet-card, .pet-mini-card, .item-card,
  .choice-card, .formulario-card, .chequeo-card-movil, .bento-tile[href]):active{
  transform:translateY(0) scale(.98);
  box-shadow:var(--sh-1);
  transition-duration:.12s;
}

/* ---------- Insignias y etiquetas ---------- */
.badge{ height:24px; padding:0 10px; border-color:transparent; }
.badge-ok{ border-color:transparent; }
.badge-warn{ border-color:transparent; }
.badge-danger{ border-color:transparent; }
.etq-sin-cuenta, .etq-archivado{ border-color:transparent; }

/* ---------- Pestañas dentro de una pantalla ---------- */
.tabs{ border-bottom-color:var(--border); }
.tabs button[aria-selected="true"], .tabs button.active{ border-bottom-color:var(--tinta); }

/* ---------- Estado vacío ---------- */
.empty .ico{ border-radius:var(--r-full); width:52px; height:52px; background:var(--canineus-suave); color:var(--canineus); }

/* ---------- Ventanas ----------
   Radio de 28 px, sin borde y una entrada suave: suben 12 px y crecen
   un pelo mientras aparecen. En el teléfono son hojas que suben desde
   abajo. */
.scrim, .sidebar-overlay{ background:rgba(20,18,22,.38); }
.scrim{ -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px); }
.dialog{
  border:none; border-radius:var(--r-xl);
  transition:opacity .28s var(--curva), transform .4s var(--curva), visibility 0s .4s;
}
.dialog.open{ transition:opacity .28s var(--curva), transform .4s var(--curva), visibility 0s; }
.dialog-head{ padding:var(--s-6) var(--s-6) var(--s-3); }
.dialog-head h2{ font-size:22px; }
.dialog-foot{ padding:var(--s-5) var(--s-6) var(--s-6); }

body:not(#_) .modal-overlay{
  background:rgba(20,18,22,.38);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
}
body:not(#_) .modal-overlay .modal{
  border:none; border-radius:var(--r-xl);
  box-shadow:var(--sh-3);
}
body:not(#_) .modal-overlay .modal > h2, body:not(#_) .modal h2{ font-size:24px; letter-spacing:-0.04em; }
/* Abren y cierran con escala y fundido. Las pantallas las abren y
   cierran quitando y poniendo .active, que cambia display: con
   «allow-discrete» el navegador espera a terminar el fundido antes de
   quitarla, y @starting-style le da de dónde partir al abrir. Donde no
   se entiende, se abren y cierran de golpe, como antes. */
body:not(#_) .modal-overlay{
  opacity:0;
  transition:opacity .24s var(--curva), display .24s allow-discrete, overlay .24s allow-discrete;
}
body:not(#_) .modal-overlay.active{ opacity:1; }
body:not(#_) .modal-overlay > .modal{
  opacity:0; transform:translateY(8px) scale(.97);
  transition:opacity .22s var(--curva), transform .34s var(--curva);
}
body:not(#_) .modal-overlay.active > .modal{ opacity:1; transform:none; }
/* Para las ventanas que no son .modal-overlay (autorizacion-datos.js). */
@keyframes pastel-velo{ from{ opacity:0; } to{ opacity:1; } }
@keyframes pastel-ventana{ from{ opacity:0; transform:translateY(12px) scale(.98); } to{ opacity:1; transform:none; } }
@starting-style{
  body:not(#_) .modal-overlay.active{ opacity:0; }
  body:not(#_) .modal-overlay.active > .modal{ opacity:0; transform:translateY(12px) scale(.96); }
}
.modal-cerrar{ background:var(--surface-2); }

@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .dialog{ border-radius:var(--r-xl) var(--r-xl) 0 0; }
  .dialog-grip{ width:40px; height:5px; background:var(--border-2); }
  body:not(#_) .modal-overlay .modal{ border-radius:var(--r-xl) var(--r-xl) 0 0; }
  /* En el teléfono son hojas: suben desde abajo y bajan al cerrarse. */
  body:not(#_) .modal-overlay > .modal{ transform:translateY(48px); }
  body:not(#_) .modal-overlay.active > .modal{ transform:none; }
  @starting-style{
    body:not(#_) .modal-overlay.active > .modal{ opacity:0; transform:translateY(48px); }
  }
}

/* Otras capas flotantes con el mismo acabado. */
.rf-panel, .recorte-panel{ border:none; border-radius:var(--r-xl); }
.rf-panel.rf-hoja{ border-radius:var(--r-xl) var(--r-xl) 0 0; }
.rf-dia{ border-radius:var(--r-full); }
.rf-dia.entre{ border-radius:0; }
.rf-dia.inicio{ border-radius:var(--r-full) 0 0 var(--r-full); }
.rf-dia.fin{ border-radius:0 var(--r-full) var(--r-full) 0; }
.rf-dia.inicio.fin{ border-radius:var(--r-full); }
.rf-nav, .rf-mes{ border-radius:var(--r-full); }

/* Aviso de sin conexión: una píldora, no una franja. */
#avisoSinConexion{
  left:50%; right:auto; top:12px;
  transform:translate(-50%, -140%);
  border:none; border-radius:var(--r-full);
  padding:10px 18px; box-shadow:var(--sh-3);
  transition:transform .4s var(--curva), opacity .3s ease;
}
#avisoSinConexion.visible{ transform:translate(-50%, 0); }
@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  #avisoSinConexion{ left:calc(50% + var(--sidebar-w) / 2); }
}
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  #avisoSinConexion{ top:calc(var(--appbar-h) + 8px); max-width:calc(100vw - 32px); }
}

/* ---------- Título letra a letra ----------
   Cada letra entra de desenfocada a nítida. Las letras las envuelve
   inicio.html en <span class="pastel-letra"> con su turno (--i). */
.pastel-letra{ display:inline-block; white-space:pre; }
.pastel-palabra{ display:inline-block; white-space:nowrap; }
/* Siempre que se entra, con foto o sin ella. Mientras las siluetas
   de la pantalla la tapan (html.con-silueta, de shell-inline.js) espera
   en pausa en su primer fotograma, y arranca cuando se destapa. */
.pastel-letra{
  animation:pastel-enfocar .46s var(--curva) both;
  animation-delay:calc(var(--i, 0) * 18ms);
}
html.con-silueta .pastel-letra{ animation-play-state:paused; }
/* Terminada la escritura, inicio.html pone .saludo-hecho: sin animación,
   las letras quedan fijas y nada (un <body> que se oculta y se vuelve a
   mostrar, un repintado) puede reescribir el saludo otra vez. */
.saludo-hecho .pastel-letra{ animation:none; }
@keyframes pastel-enfocar{
  from{ opacity:0; filter:blur(8px); transform:translateY(4px); }
  to{ opacity:1; filter:blur(0); transform:none; }
}

/* ---------- Aviso «hecho» (mostrarHecho, en ui.js) ----------
   Una píldora oscura que flota abajo al centro: el círculo verde con
   el chulito, y el texto entrando letra a letra. Se encoge y se va
   sola. Vive en <body>, no dentro del modal: así sigue a la vista
   aunque el modal ya se haya cerrado. */
.hecho-pildora{
  position:fixed; z-index:1000;
  left:50%; bottom:calc(28px + var(--safe-b));
  display:flex; align-items:center; gap:10px;
  max-width:calc(100vw - 32px);
  padding:8px 20px 8px 8px;
  border-radius:var(--r-full);
  background:var(--tinta); color:var(--tinta-on);
  box-shadow:0 18px 44px -12px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.18);
  font-size:15px; font-weight:600; letter-spacing:-0.01em;
  pointer-events:none;
  transform:translateX(-50%);
  animation:hecho-entra .44s var(--curva) var(--retraso, 0s) both;
}
[data-theme="dark"] .hecho-pildora{ box-shadow:0 18px 44px -12px rgba(0,0,0,.8); }
.hecho-pildora.saliendo{ animation:hecho-sale .36s var(--curva) both; }
/* En el teléfono, por encima de la cápsula de pestañas. */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  body:has(#tabbar) .hecho-pildora{ bottom:calc(var(--tabbar-ocupa) + var(--safe-b) + 14px); }
}
/* En el computador, centrada en el contenido y no en la ventana. */
@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  body:has(#sidebar) .hecho-pildora{ left:calc(50% + var(--sidebar-w) / 2); }
}
.hecho-pildora .hecho-ok{
  width:30px; height:30px; flex-shrink:0; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:#2E6B52; color:#fff;
  animation:hecho-rebote .5s var(--curva) calc(var(--retraso, 0s) + .06s) both;
}
.hecho-pildora .hecho-ok svg{ width:16px; height:16px; }
.hecho-pildora .hecho-ok path{
  fill:none; stroke:currentColor; stroke-width:3.2;
  stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:1; stroke-dashoffset:1;
  animation:hecho-dibujar .32s ease-out calc(var(--retraso, 0s) + .2s) forwards;
}
.hecho-pildora .hecho-letras{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.hecho-pildora .hecho-letras span{
  display:inline-block; white-space:pre;
  animation:pastel-enfocar .4s var(--curva) both;
  animation-delay:calc(var(--retraso, 0s) + .12s + var(--i, 0) * 16ms);
}
@keyframes hecho-entra{
  from{ opacity:0; transform:translateX(-50%) translateY(18px) scale(.9); }
  to{ opacity:1; transform:translateX(-50%); }
}
@keyframes hecho-sale{
  to{ opacity:0; transform:translateX(-50%) translateY(6px) scale(.82); filter:blur(4px); }
}

/* ---------- Botones de guardar (ui.js, estadoBotonGuardar) ----------
   El botón se recorta hasta un círculo (clip-path, sin cambiar su
   ancho, así nada de alrededor se mueve), gira mientras guarda y
   termina en un ✓ que se expande; si falla, se sacude. El texto se
   vuelve transparente en vez de cambiarse: la pantalla sigue siendo
   dueña de él. */
.boton-preparado{
  position:relative;
  clip-path:inset(0 0 0 0 round 999px);
  transition:clip-path .38s var(--curva), background-color .25s ease, color .15s ease !important;
}
.boton-guardando{
  clip-path:inset(0 var(--boton-recorte, 0px) 0 var(--boton-recorte, 0px) round 999px);
  color:transparent !important;
  /* Desactivado mientras guarda, pero sin el gris de «apagado» que le
     ponen las pantallas: el círculo sigue siendo el botón. */
  opacity:1 !important;
  pointer-events:none;
}
.boton-guardando > *{ visibility:hidden; }
/* El anillo que gira, del color que tenía el texto. */
.boton-guardando::after{
  content:''; position:absolute; left:50%; top:50%;
  width:18px; height:18px; margin:-9px 0 0 -9px;
  border-radius:50%;
  border:2.5px solid color-mix(in srgb, var(--boton-color, #fff) 30%, transparent);
  border-top-color:var(--boton-color, #fff);
  animation:boton-girar .7s linear infinite, boton-aparecer .2s ease .15s both;
}
/* El ✓: fondo verde de Canineus y el chulito que crece con un rebote,
   con un aro que se abre detrás. */
.boton-guardando.boton-hecho{ background:#2E6B52 !important; border-color:#2E6B52 !important; }
.boton-guardando.boton-hecho::after{
  width:20px; height:20px; margin:-10px 0 0 -10px;
  border:none; border-radius:0;
  background:#fff;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / contain no-repeat;
  animation:boton-chulito .42s var(--curva) both;
}
.boton-guardando.boton-hecho::before{
  content:''; position:absolute; left:50%; top:50%;
  width:100%; aspect-ratio:1; max-width:none; transform:translate(-50%, -50%) scale(.4);
  border-radius:50%; background:rgba(255,255,255,.35);
  animation:boton-aro .5s var(--curva) both;
  pointer-events:none;
}
.boton-sacudir{ animation:boton-sacudir .42s cubic-bezier(.36,.07,.19,.97) both; }

@keyframes boton-girar{ to{ transform:rotate(360deg); } }
@keyframes boton-aparecer{ from{ opacity:0; } to{ opacity:1; } }
@keyframes boton-chulito{
  0%{ transform:scale(.2) rotate(-12deg); opacity:0; }
  60%{ transform:scale(1.25); opacity:1; }
  100%{ transform:scale(1); opacity:1; }
}
@keyframes boton-aro{
  from{ transform:translate(-50%, -50%) scale(.3); opacity:1; }
  to{ transform:translate(-50%, -50%) scale(1.1); opacity:0; }
}
@keyframes boton-sacudir{
  10%, 90%{ transform:translateX(-1px); }
  20%, 80%{ transform:translateX(3px); }
  30%, 50%, 70%{ transform:translateX(-5px); }
  40%, 60%{ transform:translateX(5px); }
}

/* ---------- Menos movimiento ----------
   Quien lo pidió en su teléfono no ve ninguna de las animaciones del
   estilo: ni entradas, ni letras, ni tarjetas que suben. */
@media (prefers-reduced-motion:reduce){
  .pastel-letra, .hecho-pildora, .hecho-pildora *{
    animation:none !important;
  }
  body:not(#_) .modal-overlay, body:not(#_) .modal-overlay > .modal{ transition:none; transform:none; }
  .hecho-pildora .hecho-ok path{ stroke-dashoffset:0; }
  :is(.acceso, a.card, .card.clickable, .pet-card, .pet-mini-card, .item-card,
    .choice-card, .formulario-card, .chequeo-card-movil, .bento-tile[href]){
    transition:none; transform:none !important;
  }
  .tab-indicador, .nav-marca, .nav-item, .btn{ transition:none; }
  /* ui.js tampoco pone el botón a girar; esto es por si se manda a mano. */
  .boton-preparado{ transition:none !important; }
  .boton-guardando::after, .boton-guardando::before, .boton-sacudir{ animation:none !important; }
}

/* ---------- Páginas sin armazón ----------
   Acceso, registro, recuperar contraseña, política de datos,
   asistente… No tienen barra lateral: el fondo lleva los tintes de
   la plataforma, muy difuminados, y la tarjeta flota encima. */
body:not(:has(#sidebar)){
  background:
    radial-gradient(640px 440px at 8% -8%, color-mix(in srgb, var(--tinte-rutas) 85%, transparent), transparent 70%),
    radial-gradient(520px 400px at 100% 6%, color-mix(in srgb, var(--tinte-avisos) 75%, transparent), transparent 70%),
    radial-gradient(620px 460px at 78% 108%, color-mix(in srgb, var(--tinte-hotel) 80%, transparent), transparent 70%),
    var(--bg);
  background-attachment:fixed;
}
body:not(#_) :is(body > .card-auth, body > .card-legal, body > .card){
  border:none; border-radius:32px;
  box-shadow:0 1px 2px rgba(28,27,31,.04), 0 30px 70px -24px rgba(28,27,31,.18);
  animation:pastel-subir .55s var(--curva) both;
}
body:not(#_) > .card-auth{ padding:var(--s-10) var(--s-8); max-width:420px; }
[data-theme="dark"] body:not(#_) :is(body > .card-auth, body > .card-legal, body > .card){
  box-shadow:0 0 0 1px var(--border), 0 30px 70px -20px rgba(0,0,0,.7);
}
body:not(#_) :is(.card-auth, .card-legal, body > .card) h1{
  font-size:30px; line-height:1.1; margin-bottom:10px;
}
body:not(#_) :is(.card-auth, body > .card) .brand img,
body:not(#_) .card-auth .brand-logo-inicial{ border-radius:14px; }
body:not(#_) .card-auth .brand .name{ font-family:var(--font-titulo); letter-spacing:-0.03em; font-size:17px; }
body:not(#_) :is(.card-auth, body > .card) .msg{ border-radius:var(--r-md); border-color:transparent; }
@media (max-width:520px){
  body:not(#_) > .card-auth{ padding:var(--s-8) var(--s-6); }
  body:not(#_) :is(body > .card-auth, body > .card-legal, body > .card){ border-radius:28px; }
}
@media (prefers-reduced-motion:reduce){
  body:not(#_) :is(body > .card-auth, body > .card-legal, body > .card){ animation:none; }
}


/* ============================================================
   9. EL COLOR DEL COLEGIO
   Con una paleta de color (Zafiro, Esmeralda…), ese color manda: la
   acción principal, la entrada activa del menú y de la barra de abajo
   y la píldora de «Guardado» van en él con texto blanco (--tinta pasa
   a ser el acento); el menú lateral lleva un 9 % de él sobre el fondo,
   la página un 4 %, la tarjeta principal de Inicio un 16 %, y la barra
   de progreso es un degradado suyo.

   Con «Pastel» (la predeterminada) nada de eso: lo principal en casi
   negro y cada tarjeta con el tinte de su tema.

   Los tintes se sacan con color-mix() del acento, así cada paleta y
   cada modo salen solos sin escribir un color más. `:root` y no
   [data-paleta] suelto: las tarjetitas del selector de paletas llevan
   también data-paleta, y no deben recibir el fondo ni el menú.
   ============================================================ */
:root{
  /* El fondo de la tarjeta principal de Inicio (y de su silueta). */
  --tinte-principal: var(--tinte-rutas);
  --tinte-principal-txt: var(--tinte-rutas-txt);
}
:root:not([data-paleta="pastel"]){
  --tinta: var(--acento);
  --tinta-hover: var(--acento-hover);
  --tinta-on: var(--acento-on);
  --bg: color-mix(in srgb, var(--acento) 4%, var(--bg-base));
  --tinte-menu: color-mix(in srgb, var(--acento) 9%, var(--bg-base));
  --tinte-principal: color-mix(in srgb, var(--acento) 16%, var(--surface));
  --tinte-principal-txt: var(--text);
}

/* El menú lateral, teñido. Sin sombra: el tinte ya lo separa. */
:root:not([data-paleta="pastel"]) .sidebar{ background:var(--tinte-menu); box-shadow:none; }
:root:not([data-paleta="pastel"]) .sidebar .nav-item:not(.active):hover{
  background:color-mix(in srgb, var(--acento) 10%, var(--surface));
}
:root:not([data-paleta="pastel"]) .sidebar .role-card{
  background:color-mix(in srgb, var(--acento) 8%, var(--surface));
}
[data-theme="dark"]:not([data-paleta="pastel"]) .sidebar{ box-shadow:0 0 0 1px var(--border); }

/* La barra de pestañas del teléfono: la cápsula, con un poco del
   color para que case con el menú (el color lo pintan sus capas, en la
   sección 10). */
:root:not([data-paleta="pastel"]){
  --tabbar-base: color-mix(in srgb, var(--acento) 6%, var(--surface));
}

/* La píldora de «Guardado»: el círculo del ✓ en blanco con el ✓ del
   color, que sobre la píldora de color el verde no se leía. */
:root:not([data-paleta="pastel"]) .hecho-pildora .hecho-ok{ background:var(--tinta-on); color:var(--tinta); }

/* Tarjeta principal de Inicio y el saludo. */
body:not(#_) .bento-hero{ background:var(--tinte-principal); color:var(--tinte-principal-txt); }
body:not(#_) .bento-hero .sub{ color:var(--tinte-principal-txt); }
:root:not([data-paleta="pastel"]) body:not(#_) .bento-hero .sub{ color:var(--text-2); opacity:1; }
.saludo .nombre{ color:var(--acento-txt); }
/* En Pastel, el saludo entero en casi negro: el nombre se distingue
   por la cursiva, no por el color. */
:root[data-paleta="pastel"] body:not(#_) .bento-hero .saludo{ color:var(--text); }
:root[data-paleta="pastel"] .saludo .nombre{ color:var(--text); }

/* Barras de progreso: degradado del color del colegio; en Pastel,
   tinta lisa. Llena al 100 % sigue en verde (Tareas). */
body:not(#_) :is(.barra > i:not(.lleno), .avance-relleno){ background:var(--tinta); }
:root:not([data-paleta="pastel"]) body:not(#_) :is(.barra > i:not(.lleno), .avance-relleno){
  background:linear-gradient(90deg, color-mix(in srgb, var(--acento) 45%, var(--surface)), var(--acento));
}

/* Las páginas sin armazón (registro, recuperar contraseña…): con
   paleta de color, las manchas del fondo son de ese color; en Pastel,
   los tintes de la plataforma. */
:root:not([data-paleta="pastel"]) body:not(:has(#sidebar)){
  background:
    radial-gradient(640px 440px at 8% -8%, color-mix(in srgb, var(--acento) 16%, transparent), transparent 70%),
    radial-gradient(520px 400px at 100% 6%, color-mix(in srgb, var(--acento) 10%, transparent), transparent 70%),
    radial-gradient(620px 460px at 78% 108%, color-mix(in srgb, var(--tinte-hotel) 70%, transparent), transparent 70%),
    var(--bg);
  background-attachment:fixed;
}


/* ============================================================
   10. BARRAS SIEMPRE VISIBLES (solo teléfono)
   Desde canineus-754 la cabecera y la cápsula de abajo NO se recogen al
   desplazarse: estaban siempre ahí y la cápsula titilaba al llegar al
   final de Rutas (Android e iPhone, app instalada). No volver a poner
   nada que esconda, encoja o mueva las barras al hacer scroll (ver
   CLAUDE.md, «Las barras no se recogen»).

   El fondo de la cápsula son dos capas (::before cristal, ::after
   relleno), no la barra: no volver a ponerle `background` a `.tabbar`.
   ============================================================ */
:root{
  --tabbar-base: var(--surface);
  --vidrio-borde: rgba(255,255,255,.7);
  --vidrio-sombra: 0 1px 2px rgba(28,27,31,.05), 0 18px 40px -12px rgba(28,27,31,.28);
}
[data-theme="dark"]{
  --vidrio-borde: rgba(255,255,255,.14);
  --vidrio-sombra: 0 0 0 1px rgba(255,255,255,.06), 0 18px 40px -12px rgba(0,0,0,.8);
}

/* Las dos capas del fondo. Suman lo de siempre: 64 % de cristal con un
   67 % de relleno encima ≈ 88 % de superficie. */
.tabbar::before,
.tabbar::after{
  content:''; position:absolute; inset:0; z-index:0;
  border-radius:inherit; pointer-events:none;
}
.tabbar::before{
  background:color-mix(in srgb, var(--tabbar-base) 64%, transparent);
  -webkit-backdrop-filter:saturate(1.8) blur(24px);
          backdrop-filter:saturate(1.8) blur(24px);
  box-shadow:inset 0 0 0 1px var(--vidrio-borde), var(--vidrio-sombra);
}
.tabbar::after{ background:color-mix(in srgb, var(--tabbar-base) 67%, transparent); }
/* Sin desenfoque, el cristal sería un 64 % liso y el contenido se
   leería a través de los iconos: casi opaco. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .tabbar::before{ background:color-mix(in srgb, var(--tabbar-base) 94%, transparent); }
}

@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  #avisoSinConexion{ transition:transform .22s ease, opacity .22s ease; }

  /* El color de la letra cambia en seco, sin transición: la píldora se
     va en 0,26 s y, si la letra tardara en oscurecerse, la pestaña que
     se deja quedaría ese rato blanca sobre blanco. */
  .tab-indicador{
    transition:transform var(--rebote-t) var(--rebote), width var(--rebote-t) var(--curva),
               opacity .2s ease;
  }
}

@media (prefers-reduced-motion:reduce){
  #avisoSinConexion, .tab-indicador{ transition:none; }
}


/* ============================================================
   11. AJUSTE A LA PANTALLA
   Dos capas:

   1. La escala (theme-inline.js). En un computador con ratón, <html>
      lleva `zoom` = min(ancho / 1536, alto / 860): la plataforma se
      diseña para una ventana de 1536 × 860 (un portátil de 1920 × 1080
      con Windows al 125 %) y en cualquier otra todo escala junto, como
      con el zoom del navegador. Ese portátil la ve a escala 1; uno con
      Windows al 150 % (unos 1280 × 650), al 76 %; un monitor de
      1920 × 950, al 110 %; uno de 2560 × 1440, al 160 %. El teléfono y
      la tableta no llevan escala.

      Con escala, 100vh y 100vw NO son la ventana: son la ventana por
      el zoom. Todo lo que mida la ventana va entre var(--zoom, 1).

   2. Lo fluido (esto). Por si no hay escala —tableta apaisada,
      navegador sin zoom— o la ventana se sale de los topes de la
      escala, las medidas se siguen interpolando con el tamaño de la
      ventana EN UNIDADES DE DISEÑO. Con escala, esa ventana mide
      siempre 1536 × 860 o más, así que los factores quedan como en el
      portátil de referencia (0,4 de ancho, 0,7 de alto) o por encima:
      todo se ve igual que ahí, más grande o más pequeño. Sin escala, se
      aprieta hasta el compacto.

   Los factores van de 0 a 1 (en px, porque CSS no deja dividir una
   longitud entre otra) y cada medida es calc(MIN + factor × (MAX − MIN)):
     --alto-f   0 con 650 px de alto o menos, 1 con 950 o más.
     --ancho-f  0 con 1280 px de ancho o menos, 1 con 1920 o más.
     --pantalla-f  el menor de los dos.

   En el teléfono los tamaños siguen fijos.
   ============================================================ */
:root{
  --alto-f:  clamp(0px, calc((100vh / var(--zoom, 1) - 650px) / 300), 1px);
  --ancho-f: clamp(0px, calc((100vw / var(--zoom, 1) - 1280px) / 640), 1px);
  --pantalla-f: min(var(--alto-f), var(--ancho-f));
}

@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  :root{
    /* El valor pleno de cada medida es el de siempre; el de la
       referencia (1536 × 860) queda entre medias; el compacto, para
       cuando no hay escala. Letra: se
       mueve poco; la de párrafo no baja de 13,5. */
    --t-xs:   calc(11.5px + var(--pantalla-f) * .5);
    --t-sm:   calc(12.5px + var(--pantalla-f) * .5);
    --t-base: calc(13.5px + var(--pantalla-f) * .5);
    --t-md:   calc(15px   + var(--pantalla-f) * 1);
    --t-lg:   calc(17px   + var(--pantalla-f) * 1);
    --t-xl:   calc(20px   + var(--pantalla-f) * 2);
    --t-2xl:  calc(24px   + var(--pantalla-f) * 4);
    --t-3xl:  calc(28px   + var(--pantalla-f) * 6);

    /* Espacios: 4 y 8 fijos (son los de dentro de las piezas); los
       grandes, los que separan bloques, son los que más se mueven. */
    --s-3:  calc(10px + var(--pantalla-f) * 2);
    --s-4:  calc(14px + var(--pantalla-f) * 2);
    --s-5:  calc(16px + var(--pantalla-f) * 4);
    --s-6:  calc(18px + var(--pantalla-f) * 6);
    --s-8:  calc(24px + var(--pantalla-f) * 8);
    --s-10: calc(30px + var(--pantalla-f) * 10);
    --s-14: calc(40px + var(--pantalla-f) * 16);
    --s-18: calc(52px + var(--pantalla-f) * 20);

    /* Radios: la tarjeta más pequeña, menos redonda; si no, parece
       una píldora. Los de campos y botones no cambian. */
    --r-md: calc(16px + var(--pantalla-f) * 2);
    --r-lg: calc(20px + var(--pantalla-f) * 4);
    --r-xl: calc(22px + var(--pantalla-f) * 6);

    /* La columna del menú: algo más estrecha si no hay escala y falta
       ancho. */
    --sidebar-w: calc(236px + var(--ancho-f) * 12);
  }
  h1, body:not(#_) .page-header h1{ font-size:calc(24px + var(--pantalla-f) * 4); }
}

/* ---------- El menú lateral ----------
   Los escalones por altura de la #165, rehechos para el estilo Pastel.
   En una ventana baja se achica la letra y los iconos, no el aire: la
   primera versión juntaba los espacios y, en el portátil de verdad, el
   menú se veía apretado. El aire entre la cabecera y «GENERAL», entre
   cada título y sus entradas, entre entradas y entre secciones es lo
   que deja leer el menú de un vistazo.

   Con la escala de computador la ventana de diseño mide 860 px de alto
   o más. En la de referencia (1536 × 860), el menú más largo
   (administrador: trece entradas en cinco grupos) con el pie en dos
   renglones deja unos 70 px libres entre la última entrada y el pie
   (medido el 27 de septiembre). Sin escala, en 650 px de alto: 10 de hueco + 14 de
   relleno + 32 de cabecera + 31 del primer título + 4 × 29 de los otros
   + 13 × 27 de entradas + 8 × 2 entre entradas + 72 del pie compacto =
   642. Cabe.

   Límites que no se cruzan: letra de las entradas de 12,5 px y 27 px de
   alto como mínimo (con ratón basta; en táctil el menú no existe). Si
   un menú más largo que ese no cabe, se desplaza solo el centro y la
   cabecera y el pie siguen a la vista (sección 5). */
@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  .sidebar{
    --sb-hueco:   calc(5px  + var(--alto-f) * 5);
    --sb-relleno: calc(7px  + var(--alto-f) * 5);
    --sb-fila:    calc(27px + var(--alto-f) * 8);
    --sb-letra:   calc(12.5px + var(--alto-f) * 1.5);
    --sb-icono:   calc(16px + var(--alto-f) * 2);
    --sb-entre:   calc(2px + var(--alto-f) * 1);
    --sb-logo:    calc(26px + var(--alto-f) * 6);
    --sb-cabecera:calc(14px + var(--alto-f) * 8);
    --sb-titulo-arriba: calc(12px + var(--alto-f) * 4);
    --sb-titulo-abajo:  calc(5px + var(--alto-f) * 2);
    --sb-titulo-alto:   calc(12px + var(--alto-f) * 2);
    --sb-salir:   calc(30px + var(--alto-f) * 10);

    top:var(--sb-hueco); bottom:var(--sb-hueco); left:var(--sb-hueco);
    width:calc(var(--sidebar-w) - var(--sb-hueco));
    padding:var(--sb-relleno) calc(var(--sb-relleno) + 2px);
  }
  .sidebar .brand{ padding:0 2px 0 6px; gap:8px; min-height:calc(var(--sb-logo) + 4px); }
  .sidebar .brand-logo{ width:var(--sb-logo); height:var(--sb-logo); border-radius:calc(var(--sb-logo) * .32); }
  .sidebar .brand .campana{ width:calc(30px + var(--alto-f) * 4); height:calc(30px + var(--alto-f) * 4); }
  .sidebar .brand-text .name{ font-size:calc(13.5px + var(--alto-f) * .5); line-height:1.25; }
  .sidebar .brand-text .sub{ margin-top:0; line-height:1.25; }
  .sidebar .role-card .who .n{ line-height:calc(16px + var(--alto-f) * 3); }
  .sidebar .role-card .who .r{ line-height:calc(14px + var(--alto-f) * 3); }

  .sidebar .nav-groups{ gap:0; }
  .sidebar .nav-groups > .nav-label{
    line-height:var(--sb-titulo-alto);
    font-size:calc(9.5px + var(--alto-f) * 1.5);
    padding:var(--sb-titulo-arriba) var(--s-3) var(--sb-titulo-abajo);
  }
  .sidebar .nav-groups > .nav-label:first-child{ padding-top:var(--sb-cabecera); }
  .sidebar .nav-item{
    padding-top:0; padding-bottom:0; line-height:20px;
    min-height:var(--sb-fila); font-size:var(--sb-letra);
  }
  .sidebar .nav-item svg{ width:var(--sb-icono); height:var(--sb-icono); }
  /* Aire entre entradas: que la píldora de la activa no toque a la de
     al lado ni al pasar el ratón. */
  .sidebar .nav-item + .nav-item{ margin-top:var(--sb-entre); }

  .sidebar .sidebar-footer{ padding-top:calc(6px + var(--alto-f) * 4); }
  .sidebar .avatar-ring, .sidebar .avatar-ring .avatar{
    width:calc(28px + var(--alto-f) * 6); height:calc(28px + var(--alto-f) * 6);
  }
  .sidebar .logout-link{ height:var(--sb-salir); }
}

/* Con menos de 1000 px de alto de diseño (html.ventana-baja, que pone
   theme-inline.js midiendo la ventana entre la escala), el pie pasa a
   dos renglones. Antes era una media query (max-height:1000px) limitada
   a html:not(.escalado); con la referencia en 1536 × 860 la ventana de
   diseño de casi todos los portátiles se queda por debajo de 1000, con
   escala o sin ella, y el pie compacto es el que deja el menú entero.
   Aquí: la cuenta con el selector claro/oscuro a su lado, y «Cerrar
   sesión» debajo. Es lo que más alto devuelve (unos 50 px) sin quitar
   nada: la palabra «Apariencia» se oculta a la vista, pero los botones
   la siguen diciendo a los lectores de pantalla y en su título. */
@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  html.ventana-baja .sidebar .sidebar-footer{
    display:grid; grid-template-columns:minmax(0, 1fr) auto;
    align-items:center; column-gap:6px; row-gap:4px;
  }
  html.ventana-baja .sidebar .role-card{ grid-column:1; margin:0; padding:calc(1px + var(--alto-f) * 4) 6px; }
  html.ventana-baja .sidebar .modo-row{ grid-column:2; padding:0 4px 0 0; }
  html.ventana-baja .sidebar .modo-label{
    position:absolute; width:1px; height:1px; overflow:hidden;
    clip:rect(0 0 0 0); white-space:nowrap;
  }
  html.ventana-baja .sidebar .logout-link{ grid-column:1 / -1; }
  /* La tarjeta de la cuenta, en vez de una píldora propia, se funde
     con la fila entera: el pie se lee como una sola pieza. */
  html.ventana-baja .sidebar .sidebar-footer::before{
    content:''; grid-area:1 / 1 / 2 / -1; align-self:stretch;
    border-radius:var(--r-md); background:var(--surface-2);
  }
  html.ventana-baja .sidebar :is(.role-card, .modo-row){ grid-row:1; position:relative; }
  /* Con body:not(#_) para ganar al tinte de la sección 9. */
  html.ventana-baja body:not(#_) .sidebar .role-card{ background:none; }
  :root.ventana-baja:not([data-paleta="pastel"]) .sidebar .sidebar-footer::before{
    background:color-mix(in srgb, var(--acento) 8%, var(--surface));
  }
  html.ventana-baja .sidebar .segmented-iconos button{ width:28px; height:26px; }
}

/* ============================================================
   12. INSTALAR LA APP (1 de octubre de 2026)
   El botón y la tarjeta de «Instalar»: acceso, Inicio, «Más» y
   Configuración. Quién los enseña y qué hace el toque, en
   theme-inline.js (CanineusInstalacion); las guías, en
   permiso-notificaciones.js. Aquí solo el aspecto, compartido.
   El logo es el del colegio; sin logo, el dibujo de la descarga.
   ============================================================ */
[data-instalar-app][hidden]{ display:none !important; }
.instalar-logo{
  position:relative; flex:none; width:28px; height:28px;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:8px; overflow:hidden;
  background:var(--acento-suave); color:var(--acento-txt);
}
.instalar-logo img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; background:var(--surface); }
.instalar-logo img[hidden]{ display:none; }
.instalar-logo svg{ width:60%; height:60%; }
.boton-instalar{
  display:flex; align-items:center; justify-content:center; gap:10px;
  width:100%; min-height:46px; padding:8px 16px;
  border:1px solid var(--border-2); border-radius:var(--r-full);
  background:var(--surface); color:var(--text);
  font:inherit; font-size:var(--t-base); font-weight:600; cursor:pointer;
}
.boton-instalar:hover{ background:var(--surface-2); }
.boton-instalar .instalar-texto{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* La tarjeta de Inicio: discreta, una fila con su botón y «Ahora no». */
.instalar-tarjeta{
  display:flex; align-items:center; gap:var(--s-3);
  margin-bottom:var(--s-4); padding:12px 14px;
  border:1px solid var(--border); border-radius:var(--r-xl);
  background:var(--surface);
}
.instalar-tarjeta .instalar-logo{ width:40px; height:40px; border-radius:11px; }
.instalar-tarjeta .it-txt{ flex:1; min-width:0; }
.instalar-tarjeta .it-t{ font-weight:600; font-size:var(--t-base); line-height:1.3; overflow-wrap:anywhere; }
.instalar-tarjeta .it-d{ font-size:var(--t-sm); line-height:1.45; color:var(--text-2); margin-top:2px; }
.instalar-tarjeta .it-acciones{ display:flex; align-items:center; gap:var(--s-2); flex:none; }
.instalar-tarjeta .it-acciones .btn{ white-space:nowrap; }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .instalar-tarjeta{ flex-wrap:wrap; align-items:flex-start; }
  .instalar-tarjeta .it-txt{ flex:1 1 calc(100% - 56px); }
  .instalar-tarjeta .it-acciones{ width:100%; padding-left:calc(40px + var(--s-3)); }
}
@media (min-width:1024px), (min-width:900px) and (pointer:fine), (min-width:900px) and (pointer:none){
  .instalar-tarjeta{ flex-shrink:0; margin-bottom:calc(8px + var(--pantalla-f) * 8); }
}

/* ============================================================
   13. AVISO DE CARGA: SIN CONEXIÓN Y REINTENTAR (1 de octubre de 2026)
   Lo pinta errores.js (avisarFalloCarga) donde una pantalla no pudo
   cargar algo: «Sin conexión. Reintentando…» y «Reintentar». Encima
   de lo último que se había cargado, si lo hay. De red, en el tono de
   aviso (no es culpa de nadie); lo demás, en el de peligro. Colores
   con su pareja (-bg/-br): legibles en las diez paletas, claro y oscuro.
   ============================================================ */
.aviso-carga{
  display:flex; align-items:center; flex-wrap:wrap; gap:8px 10px;
  margin:0 0 var(--s-3); padding:10px 12px;
  border:1px solid var(--danger-br, var(--border)); border-radius:var(--r-md, 12px);
  background:var(--danger-bg, var(--surface-2)); color:var(--danger, var(--text));
  font-size:var(--t-sm); line-height:1.4; text-align:left;
}
.aviso-carga.de-red{
  border-color:var(--warn-br, var(--border)); background:var(--warn-bg, var(--surface-2)); color:var(--warn, var(--text));
}
.aviso-carga-icono{ flex:none; font-size:1.05em; line-height:1; }
.aviso-carga-txt{ flex:1 1 120px; min-width:0; overflow-wrap:anywhere; font-weight:500; }
.aviso-carga-btn{
  flex:none; min-height:36px; padding:6px 14px;
  border:1px solid currentColor; border-radius:var(--r-full, 999px);
  background:transparent; color:inherit;
  font:inherit; font-weight:600; cursor:pointer;
}
.aviso-carga-btn:hover:not(:disabled){ background:color-mix(in srgb, currentColor 10%, transparent); }
.aviso-carga-btn:disabled{ opacity:.6; cursor:default; }
/* Dentro de una fila de tabla o una lista muy pegada, sin margen. */
td > .aviso-carga, li > .aviso-carga{ margin:0; }
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  .aviso-carga-btn{ min-height:44px; }
}

/* ============================================================
   14. Estado del perro: 🏠 En casa · 🏫 En el colegio · 🏨 En el hotel
   (estado-perro.js; solo lo ve el colegio). Los colores salen de los
   tintes de tema, que no cambian con la paleta y traen su pareja de
   texto: colegio = el verde de Rutas, hotel = el violeta de Hotel, casa
   = neutro. Contraste del texto sobre su fondo: 7,1 / 7,1 / 5,9 en claro
   y 8,8 / 8,7 / 7,4 en oscuro. El icono acompaña al texto: el color
   nunca es lo único que lo dice.
   ============================================================ */
.estado-perro{
  display:inline-flex; align-items:center; gap:5px; flex:none;
  height:22px; padding:0 9px; border-radius:var(--r-full, 999px);
  border:1px solid var(--border); background:var(--surface-2); color:var(--text-2);
  font-size:var(--t-xs); font-weight:600; line-height:1; white-space:nowrap;
}
/* Vacío hasta que llegan los estados: no sale una etiqueta que luego cambia. */
.estado-perro:empty{ display:none; }
.estado-perro-icono{ font-size:1.05em; line-height:1; }
.estado-perro[data-estado="colegio"]{
  background:var(--tinte-rutas); color:var(--tinte-rutas-txt);
  border-color:color-mix(in srgb, var(--tinte-rutas-txt) 22%, transparent);
}
.estado-perro[data-estado="hotel"]{
  background:var(--tinte-hotel); color:var(--tinte-hotel-txt);
  border-color:color-mix(in srgb, var(--tinte-hotel-txt) 22%, transparent);
}
/* En Hotel, una sola etiqueta: «Salida vencida» (aviso) y «Por llegar»
   (neutra, con su reloj). */
.estado-perro[data-estado="vencida"]{
  background:var(--warn-bg); color:var(--warn); border-color:var(--warn-br);
}
/* «Salida vencida»: en el hotel con la fecha de salida ya pasada. Tinte de
   aviso con su pareja de texto (contraste de sobra en las diez paletas). */
.estado-perro-vencida{
  margin-left:2px; padding:2px 7px; border-radius:var(--r-full, 999px);
  background:var(--warn-bg); color:var(--warn); border:1px solid var(--warn-br);
  font-size:.92em; font-weight:700; line-height:1.2;
}
/* Más grande, junto al nombre de la ficha. */
.estado-perro.grande{ height:28px; padding:0 12px; gap:6px; font-size:var(--t-sm); }
/* La cifra de cada filtro de estado (Alumnos). */
.cuenta-estado{ margin-left:6px; font-variant-numeric:tabular-nums; opacity:.75; font-weight:700; }
/* El resumen de Inicio: cada estado, con su icono. */
.resumen-estados{ display:flex; flex-wrap:wrap; gap:6px 8px; align-items:center; }
.resumen-estados .estado-perro{ height:26px; font-size:var(--t-sm); }
.resumen-estados .estado-perro b{ font-weight:800; font-variant-numeric:tabular-nums; }

/* ============================================================
   15. CALENDARIO DE LA FAMILIA (5 de octubre de 2026, canineus-722)
   calendario-familia.js: lo usan Mi ruta (la familia) y la ficha del perro
   (el colegio). Cada día lleva color, símbolo y leyenda: el color nunca es
   lo único que lo dice. Los colores son los tintes de tema (cambian con la
   paleta y el modo oscuro, con su pareja de texto medida).
   ============================================================ */
.cf{ display:flex; flex-direction:column; gap:var(--s-4); }
.cf-carga{ color:var(--text-2); font-size:var(--t-base); padding:var(--s-8, 32px) var(--s-4); text-align:center; }
.cf-banda{
  background:var(--text); color:var(--bg, var(--surface)); border-radius:var(--r-md);
  padding:10px 14px; font-size:var(--t-sm); font-weight:600;
}
.cf-perros{ display:flex; flex-wrap:wrap; gap:8px; }
.cf-perro{
  min-height:44px; padding:0 16px; border-radius:var(--r-full); border:1px solid var(--border-2);
  background:var(--surface); color:var(--text); font:inherit; font-size:var(--t-base); font-weight:600;
  cursor:pointer; display:inline-flex; align-items:center; gap:8px;
}
.cf-perro.on{ background:var(--acento); color:var(--acento-on); border-color:transparent; }
.cf-punto{ width:8px; height:8px; border-radius:50%; background:var(--warn); }
.cf-cab-tit{ display:flex; flex-direction:column; gap:2px; }
.cf-cab-tit b{ font-family:var(--font-titulo, inherit); font-size:var(--t-lg, 20px); }
.cf-cab-tit span{ font-size:var(--t-sm); color:var(--text-2); }

.cf-aviso{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  background:var(--warn-bg); color:var(--warn); border:1px solid var(--warn-br);
  border-radius:var(--r-md); padding:12px 14px; font-size:var(--t-base); font-weight:600;
}
.cf-aviso.ok{ background:var(--ok-bg); color:var(--ok); border-color:var(--ok-br); }
.cf-aviso.error{ background:var(--danger-bg); color:var(--danger); border-color:var(--danger-br); }

.cf-btn-sec, .cf-btn-ok{
  min-height:44px; padding:0 14px; border-radius:var(--r-full); border:1px solid var(--border-2);
  background:var(--surface); color:var(--text); font:inherit; font-size:var(--t-sm); font-weight:700; cursor:pointer;
}
.cf-btn-ok{ background:var(--acento); color:var(--acento-on); border-color:transparent; }

.cf-mes, .cf-resumen, .cf-pend{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg);
  padding:var(--s-4) var(--s-3); display:flex; flex-direction:column; gap:10px;
}
.cf-resumen, .cf-pend{ padding:var(--s-4); }
.cf-mes h3, .cf-resumen h3, .cf-pend h3{ font-family:var(--font-titulo, inherit); font-size:var(--t-md); }
.cf-mes-cab{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.cf-nav{
  width:44px; height:44px; border-radius:50%; border:1px solid var(--border-2); background:var(--surface);
  color:var(--text); font-size:22px; line-height:1; cursor:pointer;
}
.cf-nav:disabled{ opacity:.35; cursor:default; }
.cf-semana, .cf-rejilla{ display:grid; grid-template-columns:repeat(7, minmax(0, 1fr)); gap:4px; }
.cf-semana{ text-align:center; font-size:var(--t-xs); font-weight:700; color:var(--text-3); }
.cf-hueco{ display:block; }

.cf-dia{
  height:54px; border-radius:12px; border:2px solid transparent; padding:0;
  background:var(--surface); color:var(--text); font:inherit; font-size:15px; font-weight:700;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; cursor:pointer;
  box-shadow:inset 0 0 0 1px var(--border);
}
.cf-dia .cf-sim{ font-size:13px; line-height:1; min-height:13px; }
.cf-dia:disabled{ opacity:.4; cursor:default; }
.cf-dia.hoy .cf-num{ text-decoration:underline; text-underline-offset:3px; }
.cf-dia.sel{ border-color:var(--text); }
.cf-dia.borrador{ border-style:dashed; border-color:var(--acento-txt, var(--text)); }
.cf-dia.pendiente{ border-style:dashed; border-color:var(--warn); }

.cf-dia.e-colegio, .cf-dia.e-cambio, .cf-chip.e-colegio, .cf-chip.e-cambio, .cf-op-ico.e-colegio, .cf-op-ico.e-cambio{
  background:var(--tinte-alumnos); color:var(--tinte-alumnos-txt); box-shadow:none;
}
.cf-dia.e-extra, .cf-chip.e-extra, .cf-op-ico.e-extra{ background:var(--tinte-rutas); color:var(--tinte-rutas-txt); box-shadow:none; }
.cf-dia.e-entra, .cf-dia.e-hotel, .cf-dia.e-vuelve, .cf-chip.e-entra, .cf-chip.e-hotel, .cf-chip.e-vuelve,
.cf-op-ico.e-hotel{ background:var(--tinte-hotel); color:var(--tinte-hotel-txt); box-shadow:none; }
.cf-dia.e-repuesto{ background:var(--tinte-cartera); color:var(--tinte-cartera-txt); box-shadow:none; }
.cf-dia.e-novas, .cf-dia.e-movido, .cf-chip.e-novas, .cf-op-ico.e-novas, .cf-op-ico.e-neutro, .cf-op-ico.e-apagado{
  background:var(--surface-3); color:var(--text-2); box-shadow:none;
}
.cf-dia.e-novas .cf-num, .cf-dia.e-movido .cf-num{ text-decoration:line-through; }
.cf-dia.e-cerrado{ background:var(--surface-2); color:var(--text-3); }
.cf-dia.e-libre{ background:var(--surface); }
.cf-dia.borrador.e-novas, .cf-dia.borrador.e-movido{ border-color:var(--text-2); }

.cf-leyenda{ display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:6px 10px; padding:6px 6px 0; font-size:var(--t-xs); color:var(--text-2); }
.cf-ley{ display:inline-flex; align-items:center; gap:6px; }
.cf-chip{
  width:22px; height:18px; border-radius:5px; font-size:11px; flex:none;
  display:inline-flex; align-items:center; justify-content:center;
}

.cf-nada, .cf-nota{ font-size:var(--t-base); color:var(--text-2); }
.cf-nota{ font-size:var(--t-sm); }
.cf-linea{ display:flex; justify-content:space-between; align-items:baseline; gap:12px; font-size:var(--t-base); }
.cf-linea b{ white-space:nowrap; }
.cf-total{ border-top:1px solid var(--border); padding-top:10px; display:flex; justify-content:space-between; align-items:baseline; font-weight:600; }
.cf-total b{ font-family:var(--font-titulo, inherit); font-size:22px; font-weight:800; }
.cf-guardados summary{ cursor:pointer; font-size:var(--t-sm); color:var(--text-2); padding:6px 0; }
.cf-acciones{ display:flex; gap:8px; justify-content:flex-end; align-items:center; }
.cf-guardar{
  flex:1; min-height:52px; border-radius:var(--r-full); border:0; background:var(--acento); color:var(--acento-on);
  font:inherit; font-size:var(--t-md); font-weight:700; cursor:pointer;
}
.cf-guardar:disabled{ opacity:.45; cursor:default; }
.cf-pend-fila{ display:flex; justify-content:space-between; align-items:center; gap:10px; font-size:var(--t-base); flex-wrap:wrap; }
.cf-pend-acc{ display:inline-flex; gap:8px; }

/* La hoja de un día: sube desde abajo en teléfono (la regla de .modal de cada
   pantalla); en computador, una ventana. */
.cf-hoja{ display:flex; flex-direction:column; gap:12px; max-width:520px; }
.cf-hoja-cab{ display:flex; justify-content:space-between; align-items:flex-start; gap:12px; }
.cf-hoja-dia{ font-size:var(--t-sm); font-weight:600; color:var(--text-2); }
.cf-hoja h3{ font-family:var(--font-titulo, inherit); font-size:21px; line-height:1.2; margin:4px 0 0; }
.cf-cerrar{
  width:44px; height:44px; border-radius:50%; border:1px solid var(--border-2); background:var(--surface);
  color:var(--text-2); font-size:16px; cursor:pointer; flex:none;
}
.cf-hoja-sub{ font-size:var(--t-base); font-weight:600; color:var(--text-2); }
.cf-ops{ display:flex; flex-direction:column; gap:8px; }
.cf-op{
  display:flex; align-items:center; gap:12px; width:100%; min-height:60px; text-align:left; padding:10px 14px;
  border-radius:var(--r-md); border:2px solid var(--border); background:var(--surface); color:var(--text);
  font:inherit; cursor:pointer;
}
.cf-op.tarde{ opacity:.6; cursor:default; }
.cf-op-ico{ width:36px; height:36px; border-radius:10px; flex:none; display:inline-flex; align-items:center; justify-content:center; font-size:17px; }
.cf-op-txt{ display:flex; flex-direction:column; gap:2px; flex:1; min-width:0; }
.cf-op-txt b{ font-size:var(--t-md); }
.cf-op-txt span{ font-size:var(--t-sm); color:var(--text-2); }
.cf-hoja-nada{ font-size:var(--t-base); color:var(--text-2); }
.cf-motivo{ display:flex; flex-direction:column; gap:6px; font-size:var(--t-base); font-weight:600; }
.cf-motivo textarea{ border:1px solid var(--border-2); border-radius:var(--r-sm); padding:10px; font:inherit; background:var(--surface); color:var(--text); resize:vertical; }

/* La ficha del perro: el botón grande y el resumen del mes. */
.btn-calendario{
  display:flex; align-items:center; gap:12px; width:100%; min-height:64px; padding:12px 16px; border:0;
  border-radius:var(--r-md); background:var(--acento); color:var(--acento-on); font:inherit; text-align:left; cursor:pointer;
}
.btn-calendario svg{ width:26px; height:26px; flex:none; }
.btn-calendario span{ display:flex; flex-direction:column; gap:2px; }
.btn-calendario b{ font-size:var(--t-md); }
.btn-calendario small{ font-size:var(--t-sm); opacity:.8; }
.cf-ficha-resumen{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-md);
  padding:12px 16px; display:flex; flex-direction:column; gap:4px; font-size:var(--t-base);
}
.cf-ficha-resumen b{ font-size:var(--t-base); }
.cf-ficha-resumen:empty{ display:none; }

@media (prefers-reduced-motion:reduce){ .cf-dia, .cf-op{ transition:none; } }
/* Mi ruta: el título de la sección del calendario. */
.seccion-calendario{ margin-top:var(--s-6); }
.cf-seccion-titulo{ font-family:var(--font-titulo, inherit); font-size:var(--t-lg, 20px); margin:0 0 2px; }
.cf-seccion-sub{ color:var(--text-2); font-size:var(--t-sm); margin:0 0 var(--s-3); }
.cf-dia.e-va, .cf-chip.e-va{ background:var(--tinte-alumnos); color:var(--tinte-alumnos-txt); box-shadow:none; }
.cf-nota-alerta{ color:var(--danger); font-weight:600; }
.cf-msg-error{ color:var(--danger); font-size:var(--t-sm); margin:0; }
.cf-motivo input{ border:1px solid var(--border-2); border-radius:var(--r-sm); padding:10px; font:inherit; background:var(--surface); color:var(--text); min-height:44px; }
.cf-guardar.cf-peligro{ background:var(--danger); color:var(--danger-on, #fff); }

/* ============================================================
   16. CABECERA COMÚN DE LAS PANTALLAS (canineus-732)

   Todas las pestañas y fichas con título usan el mismo esqueleto:

     <div class="page-header">
       <div>  <h1>…</h1>  <p>la frase</p>  </div>
       <div class="cab-acciones">
         <button class="btn-notif|btn-menor|…">…</button>   ← secundarias
         <button class="btn-add cab-principal">+ …</button>  ← la principal
       </div>
     </div>

   Computador: título a la izquierda, acciones a la derecha en una fila
   (secundarias y, al final, la principal).
   Teléfono (≤ 600 px): el título en su línea, la frase debajo más
   pequeña y en la suya, la principal como botón ancho y debajo las
   demás en una fila. Con MÁS DE DOS secundarias a la vista, todas van
   dentro de «Más», que abre una hoja (cabecera.js). Un botón nunca se
   queda en un icono solo: el texto siempre se ve, y todo toque mide
   44 px o más.

   Los selectores llevan body:not(#_) para ganar a lo que cada pantalla
   escribe en su <style> sin !important; solo lleva !important lo que
   las pantallas cambian con `style.display` en línea.
   ============================================================ */
body:not(#_) .page-header{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--s-4); flex-wrap:wrap;
}
body:not(#_) .page-header > :first-child{ flex:1 1 280px; min-width:0; }
body:not(#_) .cab-acciones{
  display:flex; align-items:center; justify-content:flex-end;
  gap:var(--s-2); flex-wrap:wrap; flex-shrink:0;
}
/* El texto del botón se ve siempre: en ningún ancho queda un icono solo. */
body:not(#_) .cab-acciones .btn-texto{ display:inline !important; }
/* Ningún icono de la cabecera se ve desproporcionado: tope de 20 px (los pequeños no se tocan). */
body:not(#_) .cab-acciones svg{ max-width:20px; max-height:20px; }
body:not(#_) .cab-acciones > .btn-descargar-excel{ width:auto; padding:0 var(--s-4); }
.cab-mas{ display:none; }
/* Lo que la pantalla esconde con `hidden` o con display en línea sigue
   escondido aunque el teléfono le dé su propio display. */
body:not(#_) .cab-acciones > [hidden]{ display:none !important; }
/* Sin ninguna acción a la vista (una familia en una pantalla del colegio)
   no deja ni el hueco. */
body:not(#_) .cab-acciones:not(:has(> :not([hidden]):not([style*="display:none"]):not([style*="display: none"]))){ display:none; }

/* La hoja de «Más» (se crea al abrirla y se quita al cerrarla). */
.cab-hoja-fondo{
  position:fixed; inset:0; z-index:450;
  display:flex; align-items:flex-end; justify-content:center;
  background:rgba(9,12,17,.45);
  animation:cab-fondo .16s ease-out;
}
.cab-hoja{
  width:100%; max-width:480px;
  display:flex; flex-direction:column; gap:var(--s-2);
  padding:var(--s-4) var(--s-4) calc(var(--s-4) + var(--safe-b));
  background:var(--surface); color:var(--text);
  border-radius:var(--r-lg) var(--r-lg) 0 0;
  box-shadow:var(--sh-3);
  animation:cab-sube .2s ease-out;
}
.cab-hoja-titulo{
  font-family:var(--font-titulo, inherit); font-weight:700;
  font-size:var(--t-md); padding:0 var(--s-1) var(--s-1);
}
.cab-hoja-fila, .cab-hoja-cerrar{
  display:flex; align-items:center; gap:var(--s-3);
  width:100%; min-height:48px; padding:0 var(--s-4);
  border:1px solid var(--border-2); border-radius:var(--r-md);
  background:var(--surface); color:var(--text);
  font:inherit; font-size:var(--t-md); font-weight:600; text-align:left; cursor:pointer;
}
.cab-hoja-fila svg{ width:20px; height:20px; flex:none; }
.cab-hoja-fila.peligro{ color:var(--danger); border-color:var(--danger-br); }
.cab-hoja-fila span{ flex:1; min-width:0; }
.cab-hoja-n{
  font-style:normal; font-size:12px; font-weight:700;
  min-width:22px; height:22px; padding:0 6px; border-radius:var(--r-full);
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--insignia, var(--acento)); color:var(--insignia-on, var(--acento-on));
}
.cab-hoja-cerrar{ justify-content:center; border-color:transparent; background:var(--surface-2); color:var(--text-2); }
@keyframes cab-fondo{ from{ opacity:0; } to{ opacity:1; } }
@keyframes cab-sube{ from{ transform:translateY(24px); opacity:.6; } to{ transform:none; opacity:1; } }
@media (prefers-reduced-motion:reduce){
  .cab-hoja-fondo, .cab-hoja{ animation:none; }
}

@media (max-width:600px){
  body:not(#_) .page-header{
    flex-direction:column; align-items:stretch; gap:var(--s-3);
  }
  body:not(#_) .page-header > :first-child{ flex:none; width:100%; }
  body:not(#_) .page-header h1{ font-size:28px; line-height:1.1; }
  body:not(#_) .page-header p{
    font-size:var(--t-sm); line-height:1.45; margin-top:6px;
    overflow-wrap:anywhere;
  }
  body:not(#_) .cab-acciones{
    width:100%; justify-content:stretch; gap:var(--s-2);
  }
  /* La principal: ancha, arriba y con su texto. */
  body:not(#_) .cab-acciones > .cab-principal{
    order:-1; flex:1 1 100%; width:100%; min-height:48px;
    justify-content:center; font-size:var(--t-md);
  }
  /* Las demás: una fila ordenada, todas del mismo estilo. */
  body:not(#_) .cab-acciones > :not(.cab-principal){
    flex:1 1 140px; min-width:0; min-height:44px; height:auto;
    display:inline-flex; align-items:center; justify-content:center; gap:6px;
    padding:0 var(--s-3);
    border:1px solid var(--border-2); border-radius:var(--r-sm);
    background:var(--surface); color:var(--text);
    font:inherit; font-size:var(--t-base); font-weight:600; white-space:normal; text-align:center;
    text-decoration:none;
  }
  body:not(#_) .cab-acciones > :not(.cab-principal) svg{ width:16px; height:16px; flex:none; }
  /* Más de dos a la vista: se recogen en «Más». */
  body:not(#_) .cab-acciones.cab-colapsada > :not(.cab-principal):not(.cab-mas){ display:none !important; }
  body:not(#_) .cab-acciones.cab-colapsada > .cab-mas{ display:inline-flex; flex:1 1 100%; }
}

/* ---------------------------------------------------------------------------
   Ventanas con el teclado puesto (canineus-731, 757, 759; rehecho en la 760)
   ---------------------------------------------------------------------------
   En el teléfono la caja de la app (<body>, sección 17) mide lo que se ve:
   con el teclado, theme-inline.js la encoge al alto visible y la sube lo que
   WebKit desplazó el viewport visual. Como toda capa position:fixed se refiere
   a <body>, las ventanas terminan solas encima del teclado y empiezan en lo
   visible: no hay nada que acortar ni que desplazar. Lo único que queda es
   que lo que tiene tope de alto en «dvh» (las ventanas de cada pantalla) no
   se pase del alto visible, y que la capa pueda desplazarse si algo desborda
   (con overflow:hidden, WebKit movía el contenido para enseñar el campo y no
   había forma de volver). theme-inline.js solo pone html.teclado-abierto con
   un campo de texto enfocado y el alto visible encogido. */
html.teclado-abierto body:not(#_) .modal-overlay,
html.teclado-abierto body:not(#_) .cc-overlay,
html.teclado-abierto body:not(#_) .cl-overlay,
html.teclado-abierto body:not(#_) .cp-overlay{ overflow-y:auto; }
html.teclado-abierto body:not(#_) .modal-overlay > .modal,
html.teclado-abierto body:not(#_) .cl-overlay .cl-hoja,
html.teclado-abierto body:not(#_) .cp-overlay .cp-modal,
html.teclado-abierto body:not(#_) .dialog{
  max-height:calc(var(--alto-app, 100dvh) - env(safe-area-inset-top, 0px) - 8px);
}
html.teclado-abierto body:not(#_) .cl-overlay.cl-elegir .cl-hoja.cl-pantalla{ max-height:none; height:100%; }
html.teclado-abierto body:not(#_) .cl-overlay .cl-hoja{ padding-bottom:0; }


/* ============================================================
   17. CÓMO ESTÁ ARMADA LA PANTALLA EN EL TELÉFONO (7 de octubre de 2026, canineus-760)

   Seis arreglos seguidos (705, 706, 731, 755, 757, 759) midieron el
   viewport y corrigieron la posición de la barra de abajo y de las
   ventanas. Todos fallaron por lo mismo: en WebKit (iPhone, sobre todo
   con la app instalada) el viewport «de lo fijo» —al que se pegan
   position:fixed, bottom:0 y 100dvh— se queda a veces con el alto viejo
   (el del teclado, el de antes de girar, el de antes de irse a segundo
   plano), y una corrección calculada sobre ese dato sale mal.

   Desde la 760 la barra NO PUEDE quedar mal aunque WebKit mida mal,
   porque nada de la pantalla se pega ya a ese viewport:

     · <body> es la caja de la app: una columna flex que mide
       --alto-app (theme-inline.js, «La caja de la app»: lo que se ve,
       nunca lo que dice el viewport de lo fijo) y no se desplaza.
     · Arriba la cabecera (#appbar), en medio <main>, que es EL ÚNICO que
       se desplaza (overflow-y:auto), y abajo la barra (#tabbar) como
       último hijo de la columna, en el flujo normal, con su margen de
       seguridad. Está pegada al final de la columna, no a un viewport.
     · <body> lleva un transform. Con él, todo lo position:fixed del
       documento (ventanas, hojas, velos, siluetas, el fantasma de la
       transición, la píldora de «Guardado», el visor de fotos…) se
       refiere a <body> y no al viewport de WebKit: cubren la caja de la
       app entera y terminan donde termina ella. Ninguna pantalla tuvo
       que cambiar su CSS.
     · Con el teclado, la caja mide el alto visible y se sube lo que
       WebKit desplazó el viewport visual (--vv-top): el contenido llega
       hasta arriba y todo vuelve a su sitio al cerrarlo.

   Reglas para no volver atrás:
     · Nada de position:fixed PARA LA BARRA ni para corregir capas:
       lo fijo ya se refiere a <body>; con eso basta.
     · Nada que mida la barra, el viewport o el teclado para «corregir»
       nada (--tabbar-ajuste, translate, esconder <body>…). Si algo
       queda mal, se arregla la medida de --alto-app en theme-inline.js,
       en un solo sitio.
     · El scroll es de <main>: en JavaScript, nada de window.scrollTo ni
       scrollY en el teléfono (desplazadorDePantalla, subirPantalla y
       scrollDePantalla en ui.js; el armazón, CanineusShell.subir).
       scrollIntoView sigue sirviendo: desplaza el contenedor que toque.
     · Lo que vive dentro de <main> y se pega abajo (position:sticky)
       se pega a bottom:0, no al hueco de la barra: <main> termina
       encima de ella.
     · El computador no cambia: menú lateral, zoom y la ventana se
       desplazan como siempre (estas reglas van solo en la lista de
       teléfono y solo con el armazón, html.con-armazon).
   ============================================================ */
@media (max-width:899.98px), (max-width:1023.98px) and (pointer:coarse){
  /* La ventana no se desplaza: lo que se desplaza es <main>. */
  html.con-armazon{ overflow:hidden; scrollbar-gutter:auto; }
  html.con-armazon body{
    /* Lo que se ve. El respaldo de 100vh solo cuenta si theme-inline.js
       no llegó a medir (no es el viewport dinámico, pero es un alto). */
    height:100vh;
    height:var(--alto-app, 100vh);
    min-height:0;
    /* clip, no hidden: lo fijo que se guarda fuera de la caja (la hoja de
       «Más» cerrada, translateY(100%); el panel de avisos, a la derecha)
       ya se refiere a <body> y con hidden le daba a la ventana 776 px de
       desplazamiento (clip no crea contenedor de desplazamiento: ni el
       dedo ni un focus() pueden mover la caja). !important: varias
       ventanas ponen body.style.overflow = 'hidden' al abrirse, y eso
       volvería a hacer de <body> un contenedor de desplazamiento. */
    overflow:hidden;
    overflow:clip !important;
    display:flex; flex-direction:column;
    /* (1) Con el teclado sigue al viewport visual; (2) y es lo que
       convierte a <body> en la referencia de todo lo position:fixed.
       Aunque --vv-top sea 0, el transform tiene que estar. */
    transform:translateY(var(--vv-top, 0px));
  }
  html.con-armazon body > .appbar{ flex:none; order:-1; }
  html.con-armazon body > main{
    flex:1 1 auto; min-height:0; order:0;
    overflow-y:auto; overflow-x:hidden;
    -webkit-overflow-scrolling:touch;
    /* Al llegar al tope no se lo pasa a nadie (ni rebote de la ventana). */
    overscroll-behavior:contain;
    /* Ya no hay barra encima: solo el aire de siempre al final. */
    padding-bottom:var(--s-6);
  }
  /* Con una ventana abierta, lo de atrás no se mueve (antes era el
     overflow de <body>). hidden conserva el punto de desplazamiento. */
  html.con-armazon body.sin-scroll > main{ overflow-y:hidden; }
  html.con-armazon body > .tabbar{
    position:relative; flex:none; order:1;
    left:auto; right:auto; bottom:auto; translate:none;
    /* margin-top:auto la deja pegada abajo AUNQUE NO HAYA <main>
       (canineus-773). Con <main> no hace nada: <main> crece y no sobra
       espacio. Sin él (al cambiar sin recargar el <main> viejo pasa al
       fantasma y el nuevo no entra hasta que cargan sus hojas: en Rutas,
       la de MapLibre; o al cargar, entre shell-inline.js y <main>) la
       barra quedaba justo debajo de la cabecera, arriba, medio segundo.
       No quitarlo: es lo que hace que la barra no dependa de <main>. */
    margin:auto 12px calc(10px + var(--safe-b));
  }
}

/* ============================================================
   18. CHEQUEOS DE BIENESTAR EN LA FICHA (12 de octubre de 2026, canineus-788)

   chequeos-vista.js: ficha de la mascota y Mis mascotas. Primero el
   chequeo y sus respuestas, a la vista; la foto, solo como botón
   «📷 Ver foto» con una miniatura de 48 px al final (en el visor, entera).
   Cada chequeo es un <details>: lo abre y lo pliega el navegador.
   ============================================================ */
.bnv-lista{ border:1px solid var(--border); border-radius:var(--r-md); overflow:hidden; }
.bnv{ border-bottom:1px solid var(--border); }
.bnv:last-child{ border-bottom:none; }
.bnv-cab{ display:flex; align-items:flex-start; gap:var(--s-3); padding:var(--s-3) var(--s-4); cursor:pointer; list-style:none; }
.bnv-cab::-webkit-details-marker{ display:none; }
.bnv-cab::marker{ content:''; }
.bnv-cab:hover{ background:var(--surface-2); }
.bnv-cab:focus-visible{ outline:2px solid var(--acento); outline-offset:-2px; }
.bnv-cab-txt{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.bnv-nombre{ font-size:var(--t-base); font-weight:600; color:var(--text); overflow-wrap:anywhere; }
.bnv-meta{ font-size:var(--t-sm); color:var(--text-2); overflow-wrap:anywhere; }
.bnv-etiqueta{ align-self:flex-start; margin-top:4px; font-size:var(--t-xs); font-weight:600; color:var(--acento-txt); background:var(--acento-suave); border-radius:999px; padding:2px 8px; }
.bnv-flecha{ width:18px; height:18px; flex-shrink:0; margin-top:2px; color:var(--text-3); transition:transform .15s; }
.bnv[open] > .bnv-cab .bnv-flecha{ transform:rotate(180deg); }
.bnv-cuerpo{ padding:0 var(--s-4) var(--s-4); }
/* Pregunta a la izquierda (gris), respuesta a la derecha (fuerte); una
   respuesta larga baja debajo de su pregunta, a todo lo ancho. */
/* En computador la tarjeta es ancha: sin tope, la respuesta quedaba lejos
   de su pregunta, al otro lado de la pantalla. */
.bnv-resp{ margin:0; max-width:640px; border-top:1px solid var(--border); }
.bnv-fila{ display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1.2fr); gap:var(--s-3); padding:8px 0; border-bottom:1px dashed var(--border); font-size:var(--t-sm); }
.bnv-fila:last-child{ border-bottom:none; }
.bnv-fila dt{ color:var(--text-2); font-weight:500; overflow-wrap:anywhere; }
.bnv-fila dd{ margin:0; color:var(--text); font-weight:600; overflow-wrap:anywhere; white-space:pre-line; }
.bnv-fila.larga{ grid-template-columns:minmax(0, 1fr); gap:2px; }
.bnv-vacio{ margin:var(--s-2) 0 0; font-size:var(--t-sm); color:var(--text-2); }
.bnv-foto{ display:inline-flex; align-items:center; gap:var(--s-3); margin-top:var(--s-3); min-height:56px; padding:4px var(--s-4) 4px 4px; border:1px solid var(--border-2); border-radius:var(--r-md); background:var(--surface); color:var(--text); font:600 var(--t-sm)/1.2 inherit; cursor:zoom-in; }
.bnv-foto:hover{ background:var(--surface-2); }
.bnv-foto:focus-visible{ outline:2px solid var(--acento); outline-offset:2px; }
.bnv-mini{ width:48px; height:48px; flex-shrink:0; border-radius:var(--r-sm); overflow:hidden; background:var(--surface-2); }
.bnv-mini img{ display:block; width:100%; height:100%; object-fit:cover; }
@media (prefers-reduced-motion: reduce){ .bnv-flecha{ transition:none; } }
