/* Paleta de Horme — generada desde web/site.config.mjs. No editar a mano. */
:root{--primary:#7ED20E; --primary-ink:#487B05; --accent:#12a594; --on-primary:#0B1400;}
@media (prefers-color-scheme:dark){:root{--primary:#A5ED45; --primary-ink:#A5ED45; --accent:#2dd4bf; --on-primary:#08130A;}}

/* Fuentes de la marca — generadas desde web/site.config.mjs. No editar a mano. */
@font-face{font-family:'Inter';font-style:normal;font-weight:400 800;font-display:swap;src:url('fonts/interVar-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:'Inter';font-style:normal;font-weight:400 800;font-display:swap;src:url('fonts/interVar-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:'Archivo';font-style:normal;font-weight:400 900;font-stretch:62% 125%;font-display:swap;src:url('fonts/archivo-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:'Archivo';font-style:normal;font-weight:400 900;font-stretch:62% 125%;font-display:swap;src:url('fonts/archivo-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:'Chivo Mono';font-style:normal;font-weight:400 700;font-display:swap;src:url('fonts/chivomono-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;}
:root{--sans:'Inter',system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;--display:'Archivo','Oswald','Inter',system-ui,-apple-system,'Segoe UI',sans-serif;--mono:'Chivo Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}

/* ====================================================================
   Sistema de diseño del sitio público — v2
   --------------------------------------------------------------------
   Un solo archivo para los tres sitios del grupo (Horme, Medorax y
   Somatryx) y los dos idiomas. El generador inyecta DELANTE de este
   fichero la paleta y las fuentes de cada marca, y DETRÁS sus neutros y
   sus tokens propios. El resultado: tres sitios que se reconocen como
   del mismo estudio sin parecer el mismo sitio.

   Dos restricciones mandan sobre todo lo que hay aquí, y conviene
   entenderlas antes de tocar una línea:

     1. La CSP lleva `style-src 'self'` SIN `'unsafe-inline'`. Eso no
        bloquea solo los `<style>`: en CSP 3 `style-src-attr` hereda de
        `style-src`, así que un `style="…"` suelto en una página se
        descarta en producción y en local no se nota. Por eso NO hay ni
        un atributo `style` en las páginas y por eso existen las
        utilidades del final del archivo.

     2. `script-src 'none'` en casi todas las rutas. No hay JavaScript
        que anime nada. Todo el movimiento de este archivo —revelados al
        entrar en pantalla, cabecera que se encoge, barra de progreso,
        marquesina, carruseles con su indicador— es CSS puro, con
        animaciones ligadas al scroll (`animation-timeline`). Donde el
        navegador no las soporta no hay animación y el contenido se ve,
        que es lo único que no se negocia.

   Compatibilidad: este archivo lo consumen ~4.600 páginas ya generadas
   (780 de Horme, 3.780 de Medorax, 20 de Somatryx). Ninguna clase que
   existiera antes se ha eliminado; todas siguen funcionando, reskinadas.
   ==================================================================== */

/* ==================================================================
   1 · TOKENS
   ================================================================== */
:root{
  color-scheme:light;

  /* --- Tipografía ---
     --sans, --display y --mono los inyecta el generador delante de este
     archivo junto con sus @font-face, porque la familia cambia con la
     marca (ver web/fonts.mjs). Aquí solo se usan. */

  /* --- Neutros y superficies ---
     La marca (--primary, --accent, --on-primary) va inyectada justo
     encima; los neutros de abajo son los de Horme y cada marca puede
     sobreescribirlos con `palette.light.neutrals`. */
  --bg:#f6f8f4; --surface:#ffffff; --surface-2:#eef2ea; --surface-3:#e3ebdb;
  /* `--muted` se oscureció de #6b7a63 a #64725d: el original daba 4,28:1 sobre el
     fondo y 4,04:1 sobre la superficie alterna, por debajo del 4,5:1 que pide la AA
     para texto normal. Es el color de los pies de foto, las etiquetas de las cifras y
     los enlaces del pie: texto pequeño y, encima, el más difícil de leer. */
  --ink:#0d1508; --text:#414d3a; --muted:#64725d; --border:#dde4d6;

  /* Tintes derivados del primario. Todo lo que «lleva color de marca»
     sin ser color plano sale de aquí, así que cambiar --primary cambia
     el sitio entero de forma coherente. */
  --tint:color-mix(in srgb, var(--primary) 12%, transparent);
  --tint-2:color-mix(in srgb, var(--primary) 6%, transparent);
  --tint-3:color-mix(in srgb, var(--primary) 22%, transparent);
  --edge:color-mix(in srgb, var(--primary) 32%, var(--border));

  /* Línea de luz superior de las superficies. Es el truco que separa una
     tarjeta «plana con borde» de una tarjeta con material: un pelo de luz
     en el canto de arriba, como el bisel de un objeto real. En claro es
     blanco puro; en oscuro, un blanco al 8 %. */
  --hairline:color-mix(in srgb, #ffffff 92%, transparent);

  /* Banda invertida: la sección que se pone siempre oscura, llueva o
     truene, para partir el ritmo de la página. Es el recurso de Apple y
     de Nike y funciona igual de bien en los tres sitios. */
  --invert-bg:#0b0f09;
  --invert-surface:#151b12;
  --invert-ink:#ffffff;
  --invert-text:#b9c4b2;
  --invert-border:#26301f;
  /* El primario DENTRO de la banda invertida. Tiene que declararse aparte porque el
     primario de marca está elegido para contrastar con el fondo normal, y la banda
     invertida es justo el fondo contrario:

       · en Somatryx el primario claro es #111 — negro sobre la banda negra: invisible;
       · en Horme el verde oscuro #4f8f06 sobre negro se queda en 2,4:1.

     La regla práctica es que cada marca ponga aquí el primario de SU OTRO tema. */
  --invert-primary:var(--primary);
  --invert-primary-ink:var(--primary-ink);

  /* Degradado del texto destacado del titular. Cada marca puede
     redefinirlo (Somatryx lo pone monocromo). */
  --grad:linear-gradient(102deg, var(--primary-ink), var(--accent));

  /* --- Forma --- */
  --radius-xl:30px; --radius:20px; --radius-sm:13px; --radius-xs:9px; --radius-pill:999px;

  /* --- Elevación ---
     Cuatro niveles y no más. Cada uno combina un contacto corto (la
     sombra que pega el objeto al fondo) con una difusa larga y muy
     abierta (la que le da altura). Una sola sombra siempre parece
     pegatina. */
  --shadow-sm:0 1px 2px rgba(13,21,8,.05);
  --shadow:0 1px 2px rgba(13,21,8,.05), 0 14px 34px -16px rgba(13,21,8,.20);
  --shadow-lg:0 2px 5px rgba(13,21,8,.06), 0 32px 66px -26px rgba(13,21,8,.30);
  --shadow-xl:0 4px 10px rgba(13,21,8,.07), 0 50px 100px -40px rgba(13,21,8,.38);

  /* --- Retícula --- */
  --wrap:1140px; --wrap-wide:1320px; --wrap-text:720px;
  --gut:22px;
  --head-h:66px;

  /* --- Espaciado ---
     Escala de 8 con dos medios pasos abajo, donde se nota. */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px;
  --s6:32px; --s7:44px; --s8:60px; --s9:80px; --s10:112px;

  /* --- Movimiento ---
     Tres curvas y nada más:
       --ease-out    salida exponencial: arranca rápido y frena mucho.
                     Es la de iOS y la que hace que algo «aterrice».
       --ease-spring rebota un pelo al final. Solo para lo que aparece.
       --ease-inout  simétrica, para lo que va y vuelve (menús). */
  --ease-out:cubic-bezier(.22,1,.36,1);
  --ease-spring:cubic-bezier(.34,1.42,.64,1);
  --ease-inout:cubic-bezier(.65,0,.35,1);
  --dur-1:.14s; --dur-2:.22s; --dur-3:.4s; --dur-4:.7s; --dur-5:1s;

  /* --- Capas --- */
  --z-bar:30; --z-head:50; --z-sheet:60; --z-skip:100;

  /* --- Personalidad de marca ---
     Estos cuatro los sobreescribe cada marca desde `site.tokens`
     (web/site.config.mjs). Son los que hacen que el mismo CSS suene
     atlético en Horme, clínico en Medorax y de casa matriz en
     Somatryx sin duplicar una sola regla.

       --case-display  MAYÚSCULAS en los antetítulos (Horme, Somatryx)
                       o caja normal (Medorax, que es una marca académica
                       y en versalitas serias parece un pie de foto)
       --track-display espaciado de las letras del titular grande
       --glow          cuánta luz de color hay detrás de la portada
       --grain         cuánto grano tienen las bandas destacadas */
  --case-display:uppercase;
  --track-display:.14em;
  --glow:1;
  --grain:.055;
  /* Familia y espaciado de los TITULARES. Por defecto la de cuerpo, que es lo que
     quieren Horme y Somatryx: una grotesca apretada y muy pesada, que es el registro
     de Apple, Nike o Linear.

     Medorax pone aquí `var(--display)` y sus titulares pasan a la serif. No es un
     capricho: es una marca académica, y una serif en titular la coloca junto a un
     manual en vez de junto a una app de consumo. Una serif necesita además menos
     espaciado negativo que una grotesca — de ahí el segundo mando. */
  --h-font:var(--sans);
  --h-track:-.035em;
  /* ANCHURA del titular. Solo hace algo si la familia de la marca es variable
     y trae eje `wdth` (Archivo en Horme, Instrument Sans en Somatryx); con
     una fuente estática el navegador lo ignora y no pasa nada.

     Es el mando que más separa a las tres marcas y el que no tenía sistema
     hasta ahora: la grotesca ANCHA es el registro de Adidas y de Nike —ocupa
     el ancho de la columna y pesa—, y la CONDENSADA es el de Gymshark, que
     mete cuatro palabras donde caben dos. Con una sola familia variable se
     tienen los dos sin descargar dos archivos.

     --h-stretch      titulares
     --track-stretch  antetítulos, marquesina y cifras (el registro pequeño) */
  --h-stretch:100%;
  --track-stretch:100%;
  /* Y el peso. No es cosmético: si la marca sirve una serif que solo trae 500 y 600 y
     el titular pide 800, el navegador engorda la 600 por software y el resultado es una
     letra sucia y con las gracias empastadas. El peso tiene que caber en lo que se ha
     descargado de verdad. */
  --h-weight:800;

  /* Textura de grano. Es un SVG con ruido fractal en un data-URI: lo
     permite `img-src 'self' data:` (los `background-image` cuelgan de
     img-src, no de style-src) y pesa 300 bytes contra los 40 KB de un
     PNG de ruido. Nunca va fijo a la ventana: solo dentro de las bandas
     que lo piden, porque una capa a pantalla completa cuesta compositor
     en cada scroll. */
  --grain-url:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");

  /* --- Escala tipográfica ---
     Fluida: un solo juego que crece con la ventana, en vez de tres
     saltos bruscos en tres media queries. --t-display es la nueva y solo
     la usan las portadas; --t-hero es la de siempre, subida de tope
     porque 4,2 rem en un monitor de 27" se queda pequeño. */
  --t-display:clamp(2.75rem,1.15rem + 6.6vw,6.6rem);
  --t-hero:clamp(2.3rem,1.35rem + 4.2vw,4.9rem);
  --t-h2:clamp(1.75rem,1.28rem + 1.95vw,2.85rem);
  --t-h3:clamp(1.14rem,1.05rem + .42vw,1.4rem);
  --t-lede:clamp(1.08rem,1rem + .55vw,1.35rem);
  --t-body:1.0325rem;
  --t-sm:.925rem;
  --t-xs:.83rem;
}

@media (prefers-color-scheme:dark){
  :root{
    color-scheme:dark;
    --bg:#080c06; --surface:#111a0e; --surface-2:#16210f; --surface-3:#1d2a15;
    --ink:#ecf5e4; --text:#adbba4; --muted:#82907a; --border:#26331d;
    --hairline:color-mix(in srgb, #ffffff 9%, transparent);
    --shadow-sm:0 1px 2px rgba(0,0,0,.5);
    --shadow:0 1px 2px rgba(0,0,0,.45), 0 18px 44px -20px rgba(0,0,0,.85);
    --shadow-lg:0 2px 6px rgba(0,0,0,.55), 0 36px 76px -30px rgba(0,0,0,.95);
    --shadow-xl:0 4px 12px rgba(0,0,0,.6), 0 56px 110px -44px rgba(0,0,0,1);
    /* En oscuro la banda invertida no puede ser «más oscura»: se
       invierte al revés y se pone clara, que es lo que la sigue
       separando del resto de la página. */
    --invert-bg:#f4f6f2;
    --invert-surface:#ffffff;
    --invert-ink:#0b0f09;
    --invert-text:#4b5646;
    --invert-border:#dbe2d6;
    --grain:.03;
  }
}

/* ==================================================================
   2 · BASE
   ================================================================== */
*,*::before,*::after{box-sizing:border-box}
html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  scroll-padding-top:calc(var(--head-h) + 18px);
  /* `clip` y no `hidden`. La diferencia importa aquí por dos motivos concretos:
       · la hoja del menú móvil se aparca fuera de pantalla con
         `transform:translateX(100%)`, y con `hidden` eso ensancha el documento
         360 px — en un teléfono la página se arrastraba de lado sin motivo;
       · `hidden` convierte al elemento en contenedor de scroll, y eso rompe
         `position:sticky` en sus descendientes (la cabecera, la barra de filtros
         de la biblioteca y la columna lateral de las fichas). `clip` recorta sin
         crear scroll.
     El `hidden` de `body` se queda debajo como respaldo para Safari 15, que no
     conoce `clip` y descarta la línea. */
  overflow-x:clip;
}
body{
  margin:0; background:var(--bg); color:var(--text);
  font:400 var(--t-body)/1.68 var(--sans);
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
  overflow-x:hidden; overflow-x:clip;
  /* Las cifras nunca bailan de ancho al cambiar: en una página llena de
     números un «1» estrecho descoloca la columna entera. */
  font-variant-numeric:tabular-nums lining-nums;
}
/* El atributo `hidden` gana SIEMPRE. La regla `[hidden]{display:none}` que trae el
   navegador es una hoja de USUARIO-AGENTE, así que cualquier `display:` que escribamos
   nosotros —aunque sea de una sola clase— la pisa sin necesidad de más especificidad.
   Pasó de verdad: `.panel-loading{display:grid}` dejaba el «Cargando tus datos…» clavado
   encima del formulario de acceso del panel, con su `hidden` puesto y todo. Se arregla
   una vez aquí en lugar de acordarse en cada clase que se oculte desde JavaScript. */
[hidden]{display:none!important}
img,svg,video{max-width:100%; height:auto; display:block}
a{color:var(--primary-ink); text-underline-offset:3px; text-decoration-thickness:1.5px}
a:hover{text-decoration:underline}

h1,h2,h3,h4{color:var(--ink); font-family:var(--h-font); font-stretch:var(--h-stretch); line-height:1.14; letter-spacing:-.022em; margin:0; text-wrap:balance}
h1{font-size:var(--t-hero); font-weight:var(--h-weight); letter-spacing:var(--h-track); line-height:1.04}
h2{font-size:var(--t-h2); font-weight:var(--h-weight); letter-spacing:calc(var(--h-track) + .005em); line-height:1.08}
h3{font-size:var(--t-h3); font-weight:700}
h4{font-size:1.0325rem; font-weight:700}
p{margin:0 0 1em; text-wrap:pretty}
p:last-child{margin-bottom:0}
strong,b{color:var(--ink); font-weight:640}
em{font-style:italic}
small{font-size:var(--t-xs)}
code{font:500 .88em/1 var(--mono); background:var(--surface-2); border:1px solid var(--border); border-radius:6px; padding:.15em .42em}
hr{border:none; border-top:1px solid var(--border); margin:var(--s6) 0}
::selection{background:var(--tint-3); color:var(--ink)}

.wrap{max-width:var(--wrap); margin:0 auto; padding:0 var(--gut)}
.wrap-wide{max-width:var(--wrap-wide); margin:0 auto; padding:0 var(--gut)}
.wrap-text{max-width:var(--wrap-text); margin:0 auto}
.lede{font-size:var(--t-lede); line-height:1.58; color:var(--text)}
.muted{color:var(--muted)}
.center{text-align:center}
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0}

.skip{
  position:absolute; left:-9999px; top:0; z-index:var(--z-skip);
  background:var(--primary); color:var(--on-primary); padding:12px 18px;
  border-radius:0 0 10px 0; font-weight:650; text-decoration:none;
}
.skip:focus{left:0}
:focus-visible{outline:2.5px solid var(--primary); outline-offset:3px; border-radius:5px}

/* ==================================================================
   3 · MATERIAL
   ------------------------------------------------------------------
   Tres mezcladores que se aplican por composición y no por herencia.
   Están arriba del todo porque los usan casi todos los bloques de
   abajo, y así se lee una vez qué significa «superficie» en este sitio.
   ================================================================== */

/* Superficie base: fondo, borde de un pelo y la línea de luz del canto.
   El `::before` en vez de un `box-shadow: inset` para que no se lo coma
   un `overflow:hidden` ni pinte por encima del contenido. */
.surface,
.card,.plat,.plan,.tile,.steps li,.stats li,.pcard,.kpi,.lib-card,
.log article,.exanim,.facts,.calc,.login-card,.gloss article,.prevnext a,
.faq details,.callout,.table-wrap,.bento>*,.spec div,.pull,.timeline li{
  position:relative;
  background:var(--surface);
  border:1px solid var(--border);
  box-shadow:var(--shadow-sm);
}
/* La línea de luz va en ::after y no en ::before para no pelearse con
   los ::before que ya tienen contenido propio: el contador de los pasos
   numerados y el punto de la línea de tiempo. */
:where(.card,.plat,.plan,.tile,.stats li,.pcard,.kpi,.log article,.calc,
       .login-card,.gloss article,.bento>*,.spec div,.pull,.lib-card)::after{
  content:""; position:absolute; inset:0; border-radius:inherit;
  pointer-events:none; z-index:0;
  box-shadow:inset 0 1px 0 var(--hairline);
}
/* Los que llevan contenido propio en ::after se quedan fuera del truco. */
.phone::after,.crumbs li+li::before{box-shadow:none}

/* Cristal: la cabecera y la barra de filtros. `saturate` antes de `blur`
   porque el desenfoque a secas lava el color de lo que hay debajo. */
.glass{
  background:color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter:saturate(180%) blur(16px);
  -webkit-backdrop-filter:saturate(180%) blur(16px);
}

/* Grano. Solo dentro de bandas concretas. */
.grainy{position:relative; isolation:isolate}
.grainy::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background-image:var(--grain-url); background-size:160px 160px;
  opacity:var(--grain); mix-blend-mode:overlay;
}

/* Malla de color: dos o tres manchas muy diluidas del primario y del
   acento. Pesa cero, escala a cualquier pantalla y en modo oscuro se
   apaga sola porque sale de las mismas variables. */
.mesh{position:relative; isolation:isolate}
.mesh::after{
  content:""; position:absolute; inset:-40% -10% auto; height:150%; z-index:-2; pointer-events:none;
  background:
    radial-gradient(52% 44% at 18% 6%,  color-mix(in srgb, var(--primary) calc(20% * var(--glow)), transparent), transparent 68%),
    radial-gradient(46% 40% at 84% 0%,  color-mix(in srgb, var(--accent)  calc(17% * var(--glow)), transparent), transparent 66%),
    radial-gradient(40% 36% at 52% 30%, color-mix(in srgb, var(--primary) calc(8%  * var(--glow)), transparent), transparent 70%);
}

/* ==================================================================
   4 · ICONOS
   ================================================================== */
.ic{width:24px; height:24px; flex:none}
.ic-sm{width:18px; height:18px}
.ic-lg{width:30px; height:30px}
/* Cuadro tintado del icono: da peso visual sin recurrir al color plano. */
.ic-box{
  width:46px; height:46px; border-radius:14px; display:grid; place-items:center;
  margin-bottom:var(--s4); position:relative; z-index:1;
  background:var(--tint); color:var(--primary-ink);
  border:1px solid var(--tint-3);
  transition:transform var(--dur-2) var(--ease-spring), background var(--dur-2) ease;
}
.card:hover .ic-box,.tile:hover .ic-box{transform:translateY(-2px) rotate(-3deg)}

/* ==================================================================
   5 · CABECERA
   ================================================================== */
.site-head{
  position:sticky; top:0; z-index:var(--z-head);
  background:color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter:saturate(180%) blur(16px); -webkit-backdrop-filter:saturate(180%) blur(16px);
  border-bottom:1px solid transparent;
  transition:border-color var(--dur-2) ease;
}
/* La línea inferior y la sombra solo aparecen cuando la página está
   desplazada: con la página arriba del todo, una raya bajo la cabecera
   parte el diseño sin motivo. Ligado al scroll, sin JavaScript.

   Solo de 941 px para arriba, y esa condición NO es estética: ver el
   bloque de móvil de más abajo. */
@media (min-width:941px){
  @supports (animation-timeline:scroll()){
    .site-head{
      animation:head-settle linear both;
      animation-timeline:scroll();
      animation-range:0 130px;
    }
    @keyframes head-settle{
      to{
        border-bottom-color:var(--border);
        box-shadow:0 10px 30px -22px rgba(0,0,0,.5);
        background:color-mix(in srgb, var(--bg) 88%, transparent);
      }
    }
  }
  @supports not (animation-timeline:scroll()){ .site-head{border-bottom-color:var(--border)} }
}

/* ---------- por qué la cabecera pierde el cristal en móvil ----------
   `backdrop-filter` convierte al elemento en BLOQUE CONTENEDOR de sus
   descendientes `position:fixed`. La hoja del menú móvil es uno de
   ellos, así que con el cristal puesto dejaba de posicionarse respecto a
   la ventana y pasaba a hacerlo respecto a la cabecera: el menú medía
   84 px de alto —la altura de la barra— en vez de la pantalla entera, y
   sus nueve entradas se aplastaban dentro hasta 30 px cada una.

   Es un fallo que no se ve en el HTML, no da error en consola y solo
   aparece en un teléfono. La cabecera de móvil se queda opaca, que
   además cuesta menos compositor en el dispositivo que peor lo lleva. */
@media (max-width:940px){
  .site-head{
    background:var(--bg);
    backdrop-filter:none; -webkit-backdrop-filter:none;
    border-bottom-color:var(--border);
  }
}

/* Barra de progreso de lectura. Un pelo de color que crece con el
   scroll: en las guías largas y en las páginas legales es la única
   pista de cuánto queda. Puramente decorativa, así que va oculta a los
   lectores de pantalla desde el HTML. */
.head-progress{
  position:absolute; left:0; bottom:-1px; height:2px; width:100%;
  transform-origin:0 50%; transform:scaleX(0);
  background:linear-gradient(90deg, var(--primary), var(--accent));
}
@supports (animation-timeline:scroll()){
  @media (prefers-reduced-motion:no-preference){
    .head-progress{animation:progress linear both; animation-timeline:scroll(); animation-range:0 100%}
    @keyframes progress{to{transform:scaleX(1)}}
  }
}

.head-inner{display:flex; align-items:center; gap:var(--s4); min-height:var(--head-h)}
.brand{
  display:flex; align-items:center; gap:10px; font-weight:800; font-size:1.075rem;
  color:var(--ink); text-decoration:none; flex:none; letter-spacing:-.025em;
}
.brand img{border-radius:10px; transition:transform var(--dur-2) var(--ease-spring)}
.brand:hover{text-decoration:none}
.brand:hover img{transform:rotate(-6deg) scale(1.06)}

/* ---------- marca dibujada con máscara ----------
   Para las marcas con wordmark propio (una tipografía que el sitio no
   lleva), el logo se pinta con `mask-image` + `currentColor` en vez de
   con un <img>.

   El motivo es el tema: el original es tinta negra sobre papel blanco.
   Como <img> harían falta dos archivos y acertar con el
   `prefers-color-scheme` en los dos. Con máscara el PNG solo aporta la
   FORMA y el color lo pone el texto de al lado, así que la marca sigue
   al tema sola y siempre contrasta. */
.brand-mark,.brand-word{
  display:block; background:currentColor; flex:none;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-size:contain; mask-size:contain;
  -webkit-mask-position:center; mask-position:center;
}
/* Proporciones tomadas del original: monograma 247×376, wordmark 1044×61. */
.brand-mark{
  width:19px; height:29px;
  -webkit-mask-image:url('/assets/logo-mark.png'); mask-image:url('/assets/logo-mark.png');
}
.brand-word{
  width:188px; height:11px; margin-top:2px;
  -webkit-mask-image:url('/assets/logo-word.png'); mask-image:url('/assets/logo-word.png');
}
.brand-lock{gap:13px}
@media (max-width:420px){
  .brand-mark{width:16px; height:24px}
  .brand-word{width:145px; height:8.5px}
}
.foot-brand .brand-mark{width:23px; height:35px}
.foot-brand .brand-word{width:210px; height:12.3px}

/* ---------- navegación ---------- */
.site-nav{margin-left:auto; display:flex; align-items:center; gap:2px}
.site-nav a{
  position:relative; color:var(--text); text-decoration:none; font-size:.9rem; font-weight:530;
  padding:8px 13px; border-radius:10px; white-space:nowrap;
  transition:color var(--dur-1) ease, background var(--dur-1) ease;
}
.site-nav a:hover{color:var(--ink); background:var(--surface-2); text-decoration:none}
.site-nav a[aria-current="page"]{color:var(--primary-ink); background:var(--tint)}
/* Subrayado de precisión bajo el enlace activo: 2 px que arrancan desde
   el centro. Es el detalle que separa una barra de enlaces de una
   navegación. */
.site-nav a[aria-current="page"]::after{
  content:""; position:absolute; left:13px; right:13px; bottom:3px; height:2px;
  border-radius:2px; background:var(--primary);
  animation:underline var(--dur-3) var(--ease-out) both;
}
@keyframes underline{from{transform:scaleX(0)}to{transform:scaleX(1)}}

.nav-lang{
  margin-left:var(--s2); font-weight:700!important; font-size:.78rem!important; letter-spacing:.05em;
  border:1px solid var(--border); color:var(--muted)!important; padding:6px 10px!important;
}
.nav-lang:hover{color:var(--ink)!important; border-color:var(--edge)}
.nav-login{color:var(--ink)!important; font-weight:620!important}
/* Botón principal de la cabecera, con barrido de luz al pasar por
   encima. El barrido es un pseudoelemento inclinado que cruza el botón:
   no hay imagen ni script, y en reduced-motion se queda quieto. */
.nav-cta{
  position:relative; overflow:hidden;
  background:var(--primary)!important; color:var(--on-primary)!important;
  margin-left:6px; font-weight:660!important; padding:9px 17px!important;
  box-shadow:0 8px 20px -10px var(--primary);
  transition:box-shadow var(--dur-2) ease, transform var(--dur-2) var(--ease-out);
}
.nav-cta:hover{transform:translateY(-1px); box-shadow:0 12px 26px -10px var(--primary)}
.nav-cta::before{
  content:""; position:absolute; top:0; bottom:0; left:-140%; width:60%;
  background:linear-gradient(100deg, transparent, color-mix(in srgb, #fff 45%, transparent), transparent);
  transform:skewX(-18deg); pointer-events:none;
}
@media (prefers-reduced-motion:no-preference){
  .nav-cta:hover::before{animation:sheen .85s var(--ease-out)}
  @keyframes sheen{to{left:150%}}
}

/* ---------- menú móvil ----------
   Hoja a pantalla completa, no un desplegable. Con seis entradas más
   idioma, acceso y descarga, un acordeón bajo la cabecera obliga a
   apuntar a objetivos de 30 px; una hoja da un objetivo de 56 px por
   fila y sitio para separar la navegación de las acciones.

   Todo con un checkbox: la etiqueta del hamburguesa lo marca, el velo
   de fondo lo desmarca. Cero JavaScript, y el foco del teclado sigue el
   orden natural del documento. */
/* El checkbox va invisible pero NO `display:none` ni `hidden`: así sigue en el
   árbol de accesibilidad y se puede abrir y cerrar el menú con Tab + Espacio.
   Con `hidden` —que es como estaba— el menú solo se abría con el ratón. */
.navtoggle{position:absolute; opacity:0; width:1px; height:1px; margin:0; pointer-events:none}
.burger{
  display:none; margin-left:auto; padding:11px; cursor:pointer; border-radius:11px;
  transition:background var(--dur-1) ease;
}
.burger:hover{background:var(--surface-2)}
/* El foco del teclado vive en el checkbox, que es invisible: hay que pintarlo en el
   hamburguesa o quien navega con Tab no sabe dónde está. */
.navtoggle:focus-visible ~ .burger{outline:2.5px solid var(--primary); outline-offset:2px}
.burger span{
  display:block; width:21px; height:2px; background:var(--ink); border-radius:2px;
  transition:transform var(--dur-3) var(--ease-out), opacity var(--dur-2) ease;
}
.burger span+span{margin-top:5px}
/* El hamburguesa se convierte en aspa cuando el menú está abierto. */
.navtoggle:checked ~ .burger span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.navtoggle:checked ~ .burger span:nth-child(2){opacity:0; transform:scaleX(.4)}
.navtoggle:checked ~ .burger span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

.nav-scrim{display:none}

/* Franja estrecha de la barra horizontal. El menú se pliega a hamburguesa a partir
   de 940 px, así que entre 941 y ~1140 px hay que meter SIETE entradas (Cardio entró
   el 11 ago 2026) más el selector de idioma, «Entrar» y «Descargar» en 918 px útiles.
   Medido: 985 px de contenido contra 939 px de contenedor, o sea el botón de descarga
   saliéndose por la derecha.

   Se aprieta el enlace en vez de esconder ninguno: un menú que oculta entradas según
   el ancho es un menú que la mitad de las visitas no ve entero. Con 8 px de padding y
   .85rem sobra holgura, y por debajo de 941 px manda el cajón lateral de siempre. */
@media (min-width:941px) and (max-width:1140px){
  .site-nav a{padding-inline:8px; font-size:.85rem}
  .site-nav a[aria-current="page"]::after{left:8px; right:8px}
  .nav-lang{margin-left:var(--s1)}
}

@media (max-width:940px){
  .burger{display:block; order:3}
  .site-nav{
    position:fixed; inset:var(--head-h) 0 0 auto; width:min(360px,86vw);
    z-index:var(--z-sheet);
    margin:0; flex-direction:column; align-items:stretch; justify-content:flex-start;
    gap:2px; padding:var(--s5) var(--s5) var(--s8);
    background:var(--bg); border-left:1px solid var(--border);
    box-shadow:var(--shadow-xl);
    overflow-y:auto; overscroll-behavior:contain;
    transform:translateX(100%); visibility:hidden;
    transition:transform var(--dur-3) var(--ease-out), visibility var(--dur-3);
  }
  .navtoggle:checked ~ .site-nav{transform:none; visibility:visible}
  .site-nav a{
    padding:15px 14px; font-size:1.06rem; font-weight:600; border-radius:12px;
    border-bottom:1px solid var(--border);
    /* Un menú largo se desplaza, no se comprime. Sin esto, en cuanto las entradas no
       caben, el reparto flexible les quita altura hasta dejarlas por debajo del
       objetivo táctil de 44 px — y las últimas, que son «Entrar» y «Descargar», son
       justo las que hay que poder pulsar. */
    flex:none;
  }
  .site-nav a[aria-current="page"]::after{display:none}
  /* 13 px de relleno y no 6: en la hoja móvil el selector de idioma se quedaba en 35 px
     de alto, por debajo del objetivo táctil de 44 px que piden tanto Apple como las
     pautas WCAG. Es el único control del menú que no era un enlace de ancho completo, y
     por eso se le había escapado. */
  .nav-lang{align-self:flex-start; margin:var(--s4) 0 0; padding:13px 16px!important; font-size:.85rem!important; border-bottom:1px solid var(--border)!important}
  .nav-cta,.nav-login{margin:var(--s3) 0 0; text-align:center; justify-content:center; border-bottom:none!important}
  .nav-cta{padding:15px 18px!important; border-radius:12px; font-size:1.02rem}
  /* Velo. Nace en display:none, así que no hay ninguna capa a pantalla
     completa compositando mientras el menú está cerrado. */
  .navtoggle:checked ~ .nav-scrim{
    display:block; position:fixed; inset:0; z-index:calc(var(--z-sheet) - 1);
    background:color-mix(in srgb, var(--ink) 42%, transparent);
    backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px);
    animation:fade var(--dur-3) ease both; cursor:pointer;
  }
  @keyframes fade{from{opacity:0}to{opacity:1}}
}

/* ==================================================================
   6 · SECCIONES
   ================================================================== */
section{padding:clamp(64px,8.5vw,132px) 0; position:relative}
section.tight{padding:clamp(34px,4.5vw,62px) 0}
section.flush{padding-top:0}
.alt{background:var(--surface-2); border-block:1px solid var(--border)}

.section-head{max-width:720px; margin:0 auto clamp(36px,4.6vw,62px); text-align:center}
.section-head h2{margin-bottom:var(--s4)}
.section-head p{color:var(--muted); font-size:1.09rem; margin:0}
/* Encabezado alineado a la izquierda, para las secciones editoriales
   donde el centrado hace que todo parezca una landing de plantilla. */
.section-head.left{text-align:left; margin-inline:0; max-width:760px}

.eyebrow{
  display:inline-block; font-family:var(--display); font-stretch:var(--track-stretch); font-size:.86rem; font-weight:600;
  letter-spacing:var(--track-display); text-transform:var(--case-display);
  color:var(--primary-ink); margin-bottom:var(--s3);
}
/* Antetítulo con guía: una raya corta delante, como en una portada de
   revista. Se usa donde el antetítulo va solo, sin icono. */
.kicker{
  display:inline-flex; align-items:center; gap:10px;
  font-family:var(--display); font-stretch:var(--track-stretch); font-size:.82rem; font-weight:600;
  letter-spacing:var(--track-display); text-transform:var(--case-display);
  color:var(--muted); margin-bottom:var(--s4);
}
.kicker::before{content:""; width:26px; height:1px; background:var(--primary); flex:none}

/* Regla con etiqueta: separa dos bloques largos sin gritar. */
.rule{display:flex; align-items:center; gap:var(--s4); margin:var(--s7) 0; color:var(--muted); font-size:var(--t-xs); text-transform:var(--case-display); letter-spacing:var(--track-display)}
.rule::before,.rule::after{content:""; height:1px; background:var(--border); flex:1}

/* ---------- banda invertida ----------
   La sección que se pone al revés que el resto de la página. Sirve para
   partir el ritmo cada tres o cuatro secciones; usada dos veces en una
   portada funciona, usada seis parece un tablero de ajedrez. */
section.invert{
  background:var(--invert-bg); border-block:none;
  /* Se reasigna la paleta ENTERA para el subárbol. Todo lo de dentro —tarjetas,
     iconos, tintes, bordes— sale de estas mismas variables, así que no hace falta ni
     una regla `.invert .card{…}`: los componentes se recolorean solos. */
  --primary:var(--invert-primary);
  --primary-ink:var(--invert-primary-ink);
  --surface:var(--invert-surface);
  --surface-2:color-mix(in srgb, var(--invert-surface) 70%, var(--invert-bg));
  --ink:var(--invert-ink);
  --text:var(--invert-text);
  --muted:color-mix(in srgb, var(--invert-text) 78%, transparent);
  --border:var(--invert-border);
  --hairline:color-mix(in srgb, var(--invert-ink) 8%, transparent);
  color:var(--invert-text);
}
section.invert h1,section.invert h2,section.invert h3,section.invert h4{color:var(--invert-ink)}
section.invert .eyebrow{color:var(--primary)}

/* ==================================================================
   7 · PORTADAS
   ================================================================== */
.hero{
  padding:clamp(64px,8vw,124px) 0 clamp(34px,4.4vw,58px);
  text-align:center; position:relative; isolation:isolate;
}
.hero::before{
  content:""; position:absolute; inset:-34% -10% 0; z-index:-1; pointer-events:none;
  background:
    radial-gradient(58% 46% at 22% 8%,  color-mix(in srgb, var(--primary) calc(20% * var(--glow)), transparent), transparent 70%),
    radial-gradient(50% 42% at 82% 0%,  color-mix(in srgb, var(--accent)  calc(17% * var(--glow)), transparent), transparent 68%);
}
/* Grano sobre la malla: sin él, dos degradados grandes en una pantalla
   OLED enseñan bandas de color escalonadas. */
.hero::after{
  content:""; position:absolute; inset:-34% -10% 0; z-index:-1; pointer-events:none;
  background-image:var(--grain-url); background-size:160px 160px;
  opacity:var(--grain); mix-blend-mode:overlay;
}
/* Monograma grande de la portada. Mismo mecanismo de máscara que la
   cabecera, así que sigue al tema. */
.hero-mark{
  display:block; margin:0 auto var(--s5); width:clamp(48px,5vw,66px); height:clamp(73px,7.6vw,100px);
  background:currentColor; color:var(--primary-ink); opacity:.92;
  -webkit-mask-image:url('/assets/logo-mark.png'); mask-image:url('/assets/logo-mark.png');
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-size:contain; mask-size:contain;
  -webkit-mask-position:center; mask-position:center;
}
.hero h1{margin-bottom:var(--s5); max-width:17ch; margin-inline:auto}
.hero .grad{
  background:var(--grad); -webkit-background-clip:text; background-clip:text; color:transparent;
  /* Una descendente de la «g» o la «p» se corta con background-clip si
     la caja va justa: un pelo de relleno abajo lo evita. */
  padding-bottom:.06em;
}
.hero .lede{max-width:680px; margin:0 auto var(--s6)}
.hero-note{font-size:var(--t-sm); color:var(--muted); margin:var(--s4) 0 0}

/* Portada extragrande: la de la home. El titular a --t-display y sin
   límite de 17ch, porque aquí el titular ES el diseño. */
.hero-xl{padding:clamp(78px,10vw,168px) 0 clamp(40px,5vw,72px)}
.hero-xl h1{font-size:var(--t-display); letter-spacing:-.04em; line-height:.98; max-width:19ch}
.hero-xl .lede{max-width:600px; font-size:clamp(1.1rem,1rem + .7vw,1.42rem)}

/* Portada alineada a la izquierda con media al lado. Es la disposición
   editorial: el texto manda y la imagen acompaña, en vez del bloque
   centrado que usa todo el mundo. */
.hero-split{text-align:left}
.hero-split .wrap{
  display:grid; grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:clamp(32px,5vw,76px); align-items:center;
}
/* En la portada partida el titular vive en media retícula, así que no puede usar la
   escala de la portada centrada: 6,6 rem en una columna de 540 px deja cinco letras
   por línea y ocho líneas de titular. Esta escala está calculada para esa columna. */
.hero-split h1{margin-inline:0; max-width:15ch; font-size:clamp(2.3rem,1.2rem + 3.4vw,4.15rem)}
.hero-split .lede{margin-inline:0; max-width:52ch}
.hero-split .cta,.hero-split .trustbar{justify-content:flex-start}
.hero-split .trustbar{gap:10px 22px}
.hero-media{display:flex; justify-content:center; align-items:center; min-width:0}
.hero-media .phone{max-width:290px}
.hero-media .frame{width:100%}
@media (max-width:900px){
  .hero-split{text-align:center}
  .hero-split .wrap{grid-template-columns:1fr}
  .hero-split h1,.hero-split .lede{max-width:none; margin-inline:auto}
  .hero-split .cta,.hero-split .trustbar{justify-content:center}
  /* La media baja debajo del texto y no encima: en un teléfono, empujar el titular
     por debajo del pliegue con una imagen es perder la única frase que se lee seguro. */
  .hero-split .hero-media{order:2; margin-top:var(--s6)}
}

.hero-page{padding:clamp(54px,6.4vw,96px) 0 clamp(24px,3.2vw,42px); text-align:center; position:relative; isolation:isolate}
.hero-page::before{
  content:""; position:absolute; inset:-40% 0 40%; z-index:-1; pointer-events:none;
  background:radial-gradient(48% 60% at 50% 0%, var(--tint), transparent 72%);
}
/* Mismo grano que la portada. Sin él, un degradado radial grande en una pantalla OLED
   enseña anillos escalonados justo detrás del titular. */
.hero-page::after{
  content:""; position:absolute; inset:-40% 0 40%; z-index:-1; pointer-events:none;
  background-image:var(--grain-url); background-size:160px 160px;
  opacity:var(--grain); mix-blend-mode:overlay;
}
.hero-page h1{margin-bottom:var(--s4)}
.hero-page .lede{max-width:720px; margin:0 auto}
.hero-page .cta{margin-top:var(--s6)}
/* Cifras bajo la entradilla de una página de colección. Sin tarjeta y separadas por
   líneas: en una portada interior no compiten con el titular, lo respaldan. */
.hero-stats{
  display:flex; flex-wrap:wrap; justify-content:center; align-items:stretch;
  gap:0; list-style:none; margin:var(--s6) 0 0; padding:0;
}
.hero-stats li{padding:0 clamp(18px,3vw,34px); border-left:1px solid var(--border)}
.hero-stats li:first-child{border-left:none}
.hero-stats b{
  display:block; font-family:var(--display); font-weight:600; line-height:1;
  font-size:clamp(1.5rem,1.2rem + 1.1vw,2.2rem); color:var(--ink);
  letter-spacing:-.01em; font-variant-numeric:tabular-nums;
}
.hero-stats span{display:block; margin-top:7px; font-size:var(--t-xs); color:var(--muted); line-height:1.4}
@media (max-width:600px){
  .hero-stats{gap:var(--s4) 0}
  .hero-stats li{flex:1 0 45%; padding:0 var(--s3)}
  .hero-stats li:nth-child(odd){border-left:none}
}
.hero-page-media{margin-top:clamp(30px,4vw,52px)}

/* Sello de confianza bajo los botones de la portada. */
.trustbar{
  display:flex; flex-wrap:wrap; justify-content:center; gap:10px 28px;
  list-style:none; margin:var(--s6) 0 0; padding:0;
}
.trustbar li{display:flex; align-items:center; gap:8px; font-size:var(--t-sm); color:var(--muted)}
.trustbar .ic{width:19px; height:19px; color:var(--primary-ink)}

/* ---------- entrada de la portada ----------
   Los hijos directos de la portada aparecen escalonados al cargar. Es
   la única animación del sitio que no depende del scroll, y existe
   porque la primera pantalla es la única que nadie desplaza para ver.

   `both` deja el estado inicial aplicado antes de arrancar; si el
   usuario pide menos movimiento, el bloque global del final las reduce
   a 1 ms y el contenido aparece de golpe, que es lo correcto. */
@media (prefers-reduced-motion:no-preference){
  .hero > .wrap > *,
  .hero-copy > *,
  .hero-split .hero-media{animation:enter var(--dur-4) var(--ease-out) both}
  /* En la portada partida el escalonado lo llevan los hijos del texto, no las dos
     columnas: si animas la columna entera, el titular y los botones aparecen a la vez
     y se pierde justo el efecto que se buscaba. */
  .hero-split > .wrap > *{animation:none}
  :where(.hero > .wrap, .hero-copy) > *:nth-child(1){animation-delay:.04s}
  :where(.hero > .wrap, .hero-copy) > *:nth-child(2){animation-delay:.11s}
  :where(.hero > .wrap, .hero-copy) > *:nth-child(3){animation-delay:.18s}
  :where(.hero > .wrap, .hero-copy) > *:nth-child(4){animation-delay:.25s}
  :where(.hero > .wrap, .hero-copy) > *:nth-child(5){animation-delay:.32s}
  .hero-split .hero-media{animation-delay:.22s; animation-duration:var(--dur-5)}
  @keyframes enter{from{opacity:0; transform:translateY(18px)} to{opacity:1; transform:none}}
}

/* ==================================================================
   8 · BOTONES
   ================================================================== */
.cta{display:flex; gap:12px; justify-content:center; flex-wrap:wrap}
.btn{
  position:relative; overflow:hidden;
  display:inline-flex; align-items:center; justify-content:center; gap:9px; text-decoration:none;
  font-weight:650; font-size:.96rem; padding:15px 26px; border-radius:14px; line-height:1.2;
  border:1.5px solid transparent; cursor:pointer;
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) ease,
             filter var(--dur-2) ease, border-color var(--dur-2) ease;
}
.btn .ic{width:19px; height:19px; transition:transform var(--dur-2) var(--ease-out)}
.btn:hover{text-decoration:none; transform:translateY(-2px)}
.btn:active{transform:translateY(0) scale(.985)}
.btn-primary{background:var(--primary); color:var(--on-primary); box-shadow:0 12px 28px -12px var(--primary)}
.btn-primary:hover{filter:brightness(1.06); box-shadow:0 18px 38px -14px var(--primary)}
.btn-ghost{background:var(--surface); color:var(--ink); border-color:var(--border); box-shadow:var(--shadow-sm)}
.btn-ghost:hover{box-shadow:var(--shadow); border-color:var(--edge)}
/* Botón de texto: la tercera acción, la que no compite con las otras
   dos. Solo una flecha que se desplaza. */
.btn-text{
  background:none; border:none; padding:14px 4px; color:var(--primary-ink);
  box-shadow:none; text-decoration:none;
}
.btn-text:hover{transform:none; text-decoration:underline}
.btn-text:hover .ic{transform:translateX(4px)}
.btn-sm{padding:11px 19px; font-size:.9rem; border-radius:11px}
.btn-lg{padding:18px 32px; font-size:1.04rem; border-radius:16px}
.btn-block{width:100%}
/* Barrido de luz, igual que el de la cabecera. */
.btn-primary::before{
  content:""; position:absolute; top:0; bottom:0; left:-140%; width:55%;
  background:linear-gradient(100deg, transparent, color-mix(in srgb, #fff 40%, transparent), transparent);
  transform:skewX(-18deg); pointer-events:none;
}
@media (prefers-reduced-motion:no-preference){
  .btn-primary:hover::before{animation:sheen .85s var(--ease-out)}
}

/* ==================================================================
   9 · CIFRAS
   ================================================================== */
.stats{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:14px; margin:0; padding:0; list-style:none}
.stats li{
  border-radius:var(--radius); padding:var(--s5) var(--s4); text-align:center;
  transition:transform var(--dur-2) var(--ease-out), border-color var(--dur-2) ease;
}
.stats li:hover{transform:translateY(-2px); border-color:var(--edge)}
.stats b{
  display:block; color:var(--ink); font-size:clamp(1.95rem,1.35rem + 1.6vw,2.75rem);
  font-family:var(--display); font-weight:600; letter-spacing:.005em; line-height:1.02;
  font-variant-numeric:tabular-nums; position:relative; z-index:1;
}
.stats span{display:block; font-size:var(--t-xs); color:var(--muted); margin-top:6px; line-height:1.45; position:relative; z-index:1}

/* ---------- banda de cifras grandes ----------
   Las mismas cifras sin tarjeta: números enormes separados por líneas.
   Es lo que hace Stripe con sus métricas y lo que hace que una cifra
   parezca un hecho y no un adorno. */
.numbers{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  list-style:none; margin:0; padding:0; gap:0;
}
.numbers li{padding:var(--s5) var(--s5); border-left:1px solid var(--border)}
.numbers li:first-child{border-left:none; padding-left:0}
@media (max-width:760px){
  .numbers{grid-template-columns:repeat(2,minmax(0,1fr))}
  .numbers li{padding:var(--s4) var(--s4)}
  .numbers li:nth-child(odd){border-left:none; padding-left:0}
  .numbers li:nth-child(n+3){border-top:1px solid var(--border)}
}
.numbers b{
  display:block; font-family:var(--display); font-weight:600;
  font-size:clamp(2.6rem,1.6rem + 3.4vw,4.6rem); line-height:.96;
  letter-spacing:-.02em; color:var(--ink); font-variant-numeric:tabular-nums;
}
.numbers span{display:block; margin-top:10px; font-size:var(--t-sm); color:var(--muted); line-height:1.5; max-width:24ch}
/* Las cifras entran barridas desde abajo, recortadas por su propia
   caja. Sin contador animado a propósito: un contador en JavaScript no
   cabe en esta CSP, y uno en CSS con `counter()` imprimiría «2160» sin
   el separador de millares, que en español es un error de ortografía. */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .numbers li{animation:reveal linear both; animation-timeline:view(); animation-range:entry 0% cover 26%}
    .numbers li:nth-child(2){animation-range:entry 0% cover 32%}
    .numbers li:nth-child(3){animation-range:entry 0% cover 38%}
    .numbers li:nth-child(4){animation-range:entry 0% cover 44%}
  }
}

/* ==================================================================
   10 · CAPTURAS Y MEDIA
   ================================================================== */
.shots{
  display:flex; gap:24px; overflow-x:auto; padding:10px 4px 20px;
  scroll-snap-type:x mandatory; scrollbar-width:thin; scroll-padding-inline:var(--gut);
}
.shots figure{margin:0; flex:0 0 auto; width:244px; scroll-snap-align:center}
.shots figcaption{font-size:var(--t-xs); color:var(--muted); text-align:center; margin-top:var(--s4); line-height:1.5}

/* Marco de teléfono en CSS: un borde grueso muy redondeado. Sin imagen
   de móvil que pesar ni que se vea borrosa en pantallas densas. */
.phone{
  border:7px solid color-mix(in srgb, var(--ink) 88%, transparent);
  border-radius:34px; padding:0; overflow:hidden; background:var(--ink);
  box-shadow:var(--shadow-lg); position:relative;
  transition:transform var(--dur-3) var(--ease-out), box-shadow var(--dur-3) ease;
}
.phone img{border-radius:27px; display:block}
/* Muesca del altavoz. */
.phone::after{
  content:""; position:absolute; top:6px; left:50%; transform:translateX(-50%);
  width:34%; height:4px; border-radius:99px; background:color-mix(in srgb, var(--bg) 34%, transparent);
  z-index:2;
}
/* Reflejo de la pantalla: una diagonal muy tenue que cruza el cristal.
   Es lo que hace que el marco parezca un objeto y no un rectángulo. */
.phone::before{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none; border-radius:27px;
  background:linear-gradient(122deg, color-mix(in srgb,#fff 13%,transparent) 0 26%, transparent 44%);
}
.shots figure:hover .phone,.split .media:hover .phone{transform:translateY(-6px); box-shadow:var(--shadow-xl)}

/* ---------- marco de imagen editorial ----------
   Para la fotografía, no para las capturas. Redondeado, con la misma
   línea de luz que las tarjetas y un zoom lento al pasar por encima. */
.frame{
  position:relative; overflow:hidden; border-radius:var(--radius-xl); margin:0;
  background:var(--surface-2); border:1px solid var(--border); box-shadow:var(--shadow-lg);
}
.frame img{width:100%; height:100%; object-fit:cover; transition:transform var(--dur-5) var(--ease-out)}
.frame:hover img{transform:scale(1.04)}
.frame figcaption{
  position:absolute; left:0; right:0; bottom:0; padding:var(--s6) var(--s5) var(--s4);
  font-size:var(--t-sm); color:#fff; line-height:1.5;
  background:linear-gradient(transparent, rgba(0,0,0,.72));
}
.frame.ratio-wide{aspect-ratio:16/9}
.frame.ratio-tall{aspect-ratio:3/4}
.frame.ratio-square{aspect-ratio:1/1}

/* ---------- banda de media a sangre ----------
   Fotografía a todo lo ancho con el texto encima. Es el recurso de
   portada de Nike y de Adidas: una imagen que ocupa la pantalla y tres
   palabras. Se usa una vez por página como mucho. */
.band{
  position:relative; isolation:isolate; overflow:hidden;
  min-height:min(78vh,660px); display:grid; align-items:center;
  /* Fondo oscuro FIJO detrás de la foto, y no `var(--invert-bg)` ni transparente.
     La imagen va en diferido —está muy por debajo del pliegue—, así que durante un
     instante no hay nada debajo del velo: sin este color, el titular blanco aparece
     sobre el fondo claro de la página y no se lee. El color no sigue al tema porque el
     texto de la banda es blanco en los dos. */
  background:#0a0d0b;
}
.band > img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:-2;
}
/* Velo de contraste. Sin él, un titular blanco sobre una foto clara es
   ilegible justo en la mitad de los casos, y no se sabe en cuál. */
/* DOS velos, no uno, y el motivo está medido.
   El velo vertical tenía su punto MÁS DÉBIL (.34) al 42 % de la altura, que es
   exactamente donde caen el titular y la entradilla. Midiendo los píxeles reales
   de `hero.webp` bajo el titular: aquí la foto es oscura y la media aguanta
   (18,5:1), pero los brillos especulares dejan el PEOR píxel en **1,7:1** — por
   debajo del 3:1 que exige el texto grande, y del 4,5:1 que este mismo proyecto
   se impone (ver la nota de `--muted` arriba y scripts/test-dark-mode.mjs).
   Una media alta no salva un titular: basta con que la línea caiga sobre el
   brillo. Subir el velo vertical hasta taparlos ennegrecería la foto entera.
   La solución es un segundo velo HORIZONTAL en el lado del texto: la columna de
   lectura queda garantizada y la mitad derecha de la imagen respira. Con esto el
   peor píxel sube de 1,7:1 a 4,0:1 y la media a 20:1.
   Es el mismo bloque que Medorax; los tres sitios del grupo comparten técnica. */
.band::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    linear-gradient(90deg, rgba(0,0,0,.72) 0%, rgba(0,0,0,.55) 38%, rgba(0,0,0,0) 72%),
    linear-gradient(180deg, rgba(0,0,0,.58), rgba(0,0,0,.42) 42%, rgba(0,0,0,.74));
}
/* Y una sombra en el texto, que es la parte que el velo NO puede resolver.
   La sombra rodea el TRAZO de la letra de oscuro, así que la legibilidad deja de
   depender de qué píxel de la foto haya detrás. Es la técnica estándar para
   tipografía sobre fotografía, y aquí cubre la superficie que el velo no puede
   alcanzar sin estropear el fondo. */
.band h1, .band h2, .band p, .band .kicker, .band .eyebrow{
  text-shadow: 0 1px 2px rgba(0,0,0,.55), 0 2px 14px rgba(0,0,0,.38);
}
/* Sin foto detrás no hay nada que compensar, y la sombra solo ensuciaría el texto. */
@media (prefers-reduced-transparency: reduce){
  .band h1, .band h2, .band p, .band .kicker, .band .eyebrow{ text-shadow:none }
}
/* En pantallas estrechas el texto ocupa TODO el ancho, así que un velo lateral
   no protege nada: ahí vuelve a mandar uno plano y parejo. */
@media (max-width:760px){
  .band::before{ background:linear-gradient(180deg, rgba(0,0,0,.66), rgba(0,0,0,.58) 42%, rgba(0,0,0,.78)); }
}
.band .wrap{padding-block:clamp(60px,9vw,120px); position:relative}
/* El titular de la banda necesita margen inferior EXPLÍCITO. Los `h*` nacen con
   `margin:0` (sección 2) y el párrafo que va debajo solo trae margen inferior, así
   que sin esto el titular y la entradilla se tocan — y en un titular de tres
   líneas sobre foto, tocarse se lee como un error de maquetación. */
.band h2,.band h1{color:#fff; max-width:15ch; margin-bottom:var(--s4)}
/* Subida de .82 a .94: encima de una foto, ese 18 % de transparencia se come casi
   un escalón entero de contraste justo en el texto de tamaño normal, que es el que
   más exige (4,5:1). Sobre un fondo plano no se nota; aquí sí. */
.band p{color:rgba(255,255,255,.94); max-width:52ch}
.band .eyebrow,.band .kicker{color:#fff}
.band .kicker::before{background:#fff}
/* El borde al 34 % se perdía sobre las zonas claras de la fotografía del hero.
   El 62 % conserva la jerarquía de botón secundario y devuelve un límite visible
   al control, igual que el arreglo ya validado en el sitio hermano. */
.band .btn-ghost{background:rgba(255,255,255,.1); color:#fff; border-color:rgba(255,255,255,.62); backdrop-filter:blur(6px)}
.band .btn-ghost:hover{background:rgba(255,255,255,.18)}
/* Paralaje muy contenido: la imagen se desplaza un 8 % mientras la
   banda cruza la pantalla. Ligado al scroll, así que no hay ni un
   listener ni un `requestAnimationFrame`. */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .band > img{
      animation:parallax linear both; animation-timeline:view(); animation-range:cover 0% cover 100%;
      height:116%; top:-8%;
    }
    @keyframes parallax{from{transform:translateY(-4%)} to{transform:translateY(4%)}}
  }
}

/* ==================================================================
   11 · REJILLAS DE TARJETAS
   ================================================================== */
/* `.grid` es para conjuntos de 3 o 6: `auto-fit` los reparte en 3
   columnas y salen filas llenas. Para conjuntos de 4 usa `.grid-2`, que
   fuerza 2×2: con `auto-fit` saldrían 3+1 y la tarjeta huérfana canta. */
.grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(282px,1fr)); gap:18px}
.grid-2{display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px}
.grid-4{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:14px}
@media (max-width:900px){ .grid-4{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:760px){ .grid-2{grid-template-columns:1fr} }

.card{
  border-radius:var(--radius); padding:var(--s6) 28px;
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) ease, border-color var(--dur-2) ease;
}
.card > *{position:relative; z-index:1}
.card:hover{transform:translateY(-4px); box-shadow:var(--shadow); border-color:var(--edge)}
.card h3{margin-bottom:9px}
.card p{font-size:var(--t-sm); margin-bottom:.8em}
.card ul{margin:var(--s3) 0 0; padding-left:19px; font-size:var(--t-sm)}
.card li{margin-bottom:7px}
.card li::marker{color:var(--primary)}
.card-note{font-size:var(--t-xs)!important; color:var(--muted); margin-top:var(--s3)!important; font-style:italic}
.card-link{margin-top:var(--s4)!important; font-size:var(--t-sm)!important}
.card-link a{display:inline-flex; align-items:center; gap:6px; font-weight:620; text-decoration:none}
.card-link a:hover{text-decoration:underline}
.card-link .ic{width:16px; height:16px; transition:transform var(--dur-2) var(--ease-out)}
.card-link a:hover .ic{transform:translateX(4px)}

/* ---------- bento ----------
   La rejilla de celdas de distinto tamaño que Apple usa desde 2022. No
   es decoración: es jerarquía. La celda ancha lleva la idea principal y
   las pequeñas los detalles, y se lee en ese orden sin necesidad de
   numerarlas.

   Doce columnas para poder decir «esta ocupa 7 y esta 5», que es lo que
   rompe la monotonía del 6+6. */
.bento{display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); gap:16px; grid-auto-rows:minmax(0,auto)}
.bento > *{
  border-radius:var(--radius-xl); padding:clamp(24px,2.4vw,36px); grid-column:span 4;
  display:flex; flex-direction:column; overflow:hidden;
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) ease, border-color var(--dur-2) ease;
}
.bento > *:hover{transform:translateY(-4px); box-shadow:var(--shadow); border-color:var(--edge)}
.bento > * > *{position:relative; z-index:1}
.bento h3{font-size:clamp(1.18rem,1.05rem + .6vw,1.55rem); margin-bottom:10px; letter-spacing:-.025em}
.bento p{font-size:var(--t-sm); color:var(--text)}
.bento .b-num{
  font-family:var(--display); font-weight:600; line-height:1;
  font-size:clamp(2.4rem,1.6rem + 2.6vw,3.6rem); color:var(--ink);
  letter-spacing:-.02em; margin-bottom:10px;
}
.bento .b-media{margin:auto -8px -14px; border-radius:var(--radius-sm); overflow:hidden}
.bento .b-media img{width:100%; display:block}
/* Anchos. Se combinan libremente mientras sumen 12 por fila. */
.b-3{grid-column:span 3} .b-4{grid-column:span 4} .b-5{grid-column:span 5}
.b-6{grid-column:span 6} .b-7{grid-column:span 7} .b-8{grid-column:span 8}
.b-9{grid-column:span 9} .b-12{grid-column:span 12}
.b-tall{grid-row:span 2}
/* Celda destacada: fondo tintado en vez de superficie plana. */
.bento .b-hi{
  background:linear-gradient(160deg, var(--tint), var(--tint-2) 60%, transparent), var(--surface);
  border-color:var(--tint-3);
}
@media (max-width:1000px){
  .bento{grid-template-columns:repeat(6,minmax(0,1fr))}
  .b-3,.b-4,.b-5{grid-column:span 3}
  .b-6,.b-7,.b-8,.b-9,.b-12{grid-column:span 6}
  .b-tall{grid-row:auto}
}
@media (max-width:640px){
  .bento{grid-template-columns:1fr}
  .bento > *,.b-3,.b-4,.b-5,.b-6,.b-7,.b-8,.b-9,.b-12{grid-column:1/-1}
}

/* ---------- rejilla de especificaciones ----------
   Etiqueta arriba, dato grande abajo, sin tarjeta. Para las fichas
   técnicas: qué plataformas, qué idiomas, qué versión. */
.spec{display:grid; grid-template-columns:repeat(auto-fit,minmax(184px,1fr)); gap:1px; background:var(--border); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden}
.spec div{border:none; border-radius:0; box-shadow:none; padding:var(--s5)}
.spec dt,.spec .k{display:block; font-size:var(--t-xs); color:var(--muted); font-weight:600; letter-spacing:.03em; text-transform:var(--case-display); margin-bottom:8px}
.spec dd,.spec .v{margin:0; font-size:1.06rem; color:var(--ink); font-weight:650; line-height:1.35}

/* ---------- lista de verificación ---------- */
.checks{list-style:none; margin:0; padding:0; display:grid; gap:11px}
.checks li{display:flex; gap:11px; align-items:flex-start; font-size:var(--t-sm)}
.checks .ic{color:var(--primary-ink); margin-top:3px}

/* ==================================================================
   12 · EDITORIAL
   ================================================================== */

/* ---------- texto + captura alternos ---------- */
.split{display:grid; grid-template-columns:1.05fr .95fr; gap:clamp(32px,5vw,76px); align-items:center}
.split + .split{margin-top:clamp(60px,8vw,116px)}
.split .media{display:flex; justify-content:center}
.split .phone{max-width:268px}
.split .txt h3{font-size:clamp(1.34rem,1.08rem + 1.2vw,2rem); margin-bottom:var(--s4); letter-spacing:-.028em}
.split .txt ul{margin:var(--s5) 0 0; padding-left:20px}
.split .txt li{margin-bottom:10px}
.split .txt li::marker{color:var(--primary)}
@media (max-width:820px){
  .split{grid-template-columns:1fr; gap:30px}
  .split.rev .media{order:-1}
}

/* ---------- relato con media fija ----------
   La columna de la imagen se queda pegada mientras la de texto pasa por
   delante. Es lo que hace Apple en las páginas de producto y lo que
   convierte tres párrafos sueltos en una secuencia.

   Sin JavaScript: `position:sticky` y ya. En móvil se deshace y vuelve
   a ser una columna, porque una imagen fija ocupando media pantalla en
   un teléfono deja al texto sin sitio. */
.story{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:clamp(32px,5vw,84px); align-items:start}
.story.rev .story-media{order:2}
.story-media{position:sticky; top:calc(var(--head-h) + 40px)}
.story-steps{display:grid; gap:clamp(48px,9vw,120px); padding-block:clamp(20px,4vw,60px)}
.story-steps > li{list-style:none}
.story-steps h3{font-size:clamp(1.3rem,1.1rem + 1vw,1.8rem); margin-bottom:var(--s3); letter-spacing:-.026em}
.story-steps p{font-size:var(--t-sm)}
.story-n{
  display:inline-grid; place-items:center; width:34px; height:34px; border-radius:11px;
  background:var(--tint); color:var(--primary-ink); font-family:var(--display);
  font-weight:700; font-size:.95rem; margin-bottom:var(--s3);
}
@media (max-width:900px){
  .story{grid-template-columns:1fr; gap:32px}
  .story.rev .story-media{order:-1}
  .story-media{position:static}
  .story-steps{gap:44px; padding-block:0}
}

/* ---------- marquesina ----------
   Una tira que se desplaza sin fin. Sirve para enseñar una lista larga
   sin gastar altura: las plataformas soportadas, los grupos musculares,
   las especialidades.

   El truco es duplicar el contenido en el HTML y desplazar el carril
   exactamente la mitad: al llegar al 50 % la copia está donde estaba el
   original y el salto no se ve. La copia va con `aria-hidden`, así que
   un lector de pantalla lee la lista una sola vez. */
.marquee{
  overflow:hidden; position:relative;
  /* Se desvanece por los dos lados en vez de cortarse a hachazo. */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
}
.marquee-track{display:flex; width:max-content; gap:0}
.marquee-track > *{
  display:flex; align-items:center; gap:14px; padding:0;
  list-style:none; margin:0;
}
.marquee li,.marquee-item{
  display:inline-flex; align-items:center; gap:10px; white-space:nowrap;
  padding:0 clamp(20px,3vw,44px);
  font-family:var(--display); font-stretch:var(--track-stretch); font-weight:600;
  font-size:clamp(1.1rem,.9rem + .9vw,1.7rem);
  letter-spacing:var(--track-display); text-transform:var(--case-display);
  color:var(--muted);
}
.marquee li::after,.marquee-item::after{
  content:""; width:6px; height:6px; border-radius:50%; background:var(--primary); flex:none; opacity:.55;
}
.marquee .ic{color:var(--primary-ink)}
@media (prefers-reduced-motion:no-preference){
  .marquee-track{animation:marquee 42s linear infinite}
  .marquee:hover .marquee-track{animation-play-state:paused}
  @keyframes marquee{to{transform:translateX(-50%)}}
}

/* ---------- carrusel con indicador ----------
   Desplazamiento horizontal con anclaje, más una barra que marca por
   dónde va. La barra está ligada al scroll DEL PROPIO carril
   (`scroll(self inline)`), así que refleja la posición real y no una
   estimación. */
.rail{position:relative}
.rail-track{
  display:flex; gap:18px; overflow-x:auto; padding:6px 4px 22px;
  scroll-snap-type:x mandatory; scroll-padding-inline:var(--gut);
  scrollbar-width:none;
}
.rail-track::-webkit-scrollbar{display:none}
.rail-track > *{flex:0 0 auto; scroll-snap-align:start; width:min(340px,78vw)}
.rail-bar{height:3px; border-radius:99px; background:var(--border); overflow:hidden; margin-top:var(--s2)}
.rail-bar::after{content:""; display:block; height:100%; width:34%; border-radius:99px; background:var(--primary); transform-origin:0 50%}
@supports (animation-timeline:scroll()){
  .rail-bar::after{
    width:100%; transform:scaleX(.2);
    animation:rail-progress linear both;
    animation-timeline:scroll(nearest inline);
  }
  /* El carril es el antecesor con scroll horizontal más cercano al
     indicador, así que hay que meterlo dentro para que `nearest` lo
     encuentre. Cuando el indicador va fuera, se usa la versión de
     respaldo de arriba. */
  @keyframes rail-progress{from{transform:scaleX(.2)} to{transform:scaleX(1)}}
}

/* ---------- cita destacada ----------
   El bloque editorial que da voz. En Somatryx va en serif y es lo que
   hace que la casa matriz suene distinta de sus dos productos. */
.pull{
  border-radius:var(--radius-xl); padding:clamp(30px,4vw,56px);
  border-left:3px solid var(--primary);
}
.pull p{
  /* `--serif` solo lo declara la marca que separa los dos roles (Somatryx: grotesca
     para titulares, serif para citas). Las demás caen en su propia display. */
  font-family:var(--serif, var(--display)); font-size:clamp(1.3rem,1.05rem + 1.3vw,2.1rem);
  line-height:1.32; letter-spacing:-.02em; color:var(--ink); font-weight:500;
  margin:0; text-wrap:balance; position:relative; z-index:1;
}
.pull footer,.pull .who{
  margin-top:var(--s5); font-size:var(--t-sm); color:var(--muted);
  font-family:var(--sans); position:relative; z-index:1;
}

/* ---------- línea de tiempo ---------- */
.timeline{list-style:none; margin:0; padding:0; display:grid; gap:14px}
.timeline li{
  border-radius:var(--radius); padding:var(--s5) var(--s5) var(--s5) 56px;
}
.timeline li::before{
  content:""; position:absolute; left:24px; top:30px; width:11px; height:11px;
  border-radius:50%; background:var(--primary); box-shadow:0 0 0 4px var(--tint); z-index:1;
}
.timeline h3{font-size:1.05rem; margin-bottom:6px}
.timeline p{font-size:var(--t-sm); margin:0}
.timeline time{display:block; font-size:var(--t-xs); color:var(--muted); margin-bottom:5px; font-variant-numeric:tabular-nums}

/* ---------- pasos numerados ----------
   El número lo pone el CSS con un contador: si mañana se añade un paso
   en medio, la numeración se recoloca sola. */
.steps{list-style:none; counter-reset:s; margin:0; padding:0; display:grid; gap:18px; grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.steps li{counter-increment:s; border-radius:var(--radius); padding:var(--s6)}
.steps li::before{
  content:counter(s); display:grid; place-items:center; width:34px; height:34px; border-radius:11px;
  background:var(--tint); color:var(--primary-ink); font-family:var(--display);
  font-weight:700; font-size:.95rem; margin-bottom:15px; position:relative; z-index:1;
}
.steps h3{margin-bottom:7px; position:relative; z-index:1}
.steps p{font-size:var(--t-sm); margin:0; position:relative; z-index:1}

/* ---------- cierre de página ---------- */
.closer .lede{max-width:620px; margin:var(--s4) auto var(--s6)}
/* Llamada final con fondo propio: cierra la página en vez de dejarla
   deshilachada contra el pie. */
.ctaband{
  border-radius:var(--radius-xl); padding:clamp(40px,6vw,88px) clamp(24px,4vw,60px);
  text-align:center; position:relative; overflow:hidden; isolation:isolate;
  background:
    radial-gradient(70% 130% at 50% 0%, var(--tint), transparent 70%),
    var(--surface);
  border:1px solid var(--border); box-shadow:var(--shadow);
}
.ctaband::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background-image:var(--grain-url); background-size:160px 160px;
  opacity:var(--grain); mix-blend-mode:overlay;
}
.ctaband h2{margin-bottom:var(--s4)}
.ctaband .lede{max-width:600px; margin:0 auto var(--s6); color:var(--muted)}

/* ==================================================================
   13 · TABLAS
   ================================================================== */
.table-wrap{overflow-x:auto; border-radius:var(--radius); background:var(--surface)}
table{border-collapse:collapse; width:100%; min-width:540px; font-size:var(--t-sm)}
caption{text-align:left; padding:16px 20px 0; color:var(--muted); font-size:var(--t-xs)}
th,td{text-align:left; padding:15px 20px; border-bottom:1px solid var(--border); vertical-align:top}
thead th{
  background:var(--surface-2); color:var(--ink); font-weight:700; font-size:var(--t-xs);
  text-transform:uppercase; letter-spacing:.05em; white-space:nowrap;
  position:sticky; top:0; z-index:1;
}
tbody th{color:var(--ink); font-weight:650}
tbody tr{transition:background var(--dur-1) ease}
tbody tr:hover{background:var(--tint-2)}
tr:last-child th,tr:last-child td{border-bottom:none}
td .yes{color:var(--primary-ink); font-weight:700}
td .no{color:var(--muted)}

/* ==================================================================
   14 · AVISOS
   ================================================================== */
.callout{
  display:flex; gap:15px; align-items:flex-start;
  border-left:3px solid var(--primary);
  border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  padding:var(--s5) var(--s5); font-size:var(--t-sm);
}
.callout .ic{color:var(--primary-ink); margin-top:2px}
.callout.warn{border-left-color:#d98324}
.callout.warn .ic{color:#d98324}
.callout h3{font-size:1rem; margin-bottom:7px}
.callout p:last-child{margin-bottom:0}

/* ==================================================================
   15 · PLATAFORMAS
   ================================================================== */
.plats{display:grid; grid-template-columns:repeat(auto-fit,minmax(232px,1fr)); gap:16px}
.plat{
  border-radius:var(--radius); padding:var(--s6); text-decoration:none; color:inherit; display:block;
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) ease, border-color var(--dur-2) ease;
}
.plat > *{position:relative; z-index:1}
a.plat:hover{transform:translateY(-4px); box-shadow:var(--shadow); text-decoration:none; border-color:var(--edge)}
.plat .ic{color:var(--primary-ink); margin-bottom:var(--s4); width:26px; height:26px; position:relative; z-index:1; transition:transform var(--dur-2) var(--ease-spring)}
a.plat:hover .ic{transform:scale(1.12) rotate(-4deg)}
.plat b{display:block; color:var(--ink); font-size:1.03rem; margin-bottom:5px}
.plat span{font-size:var(--t-sm); color:var(--muted); display:block; line-height:1.5}
.plat[aria-disabled="true"]{opacity:.62}
.badge{
  display:inline-block; font-size:.7rem; font-weight:720; letter-spacing:.05em; text-transform:uppercase;
  background:var(--surface-3); color:var(--primary-ink); border-radius:99px; padding:4px 10px; margin-top:11px;
}

/* ==================================================================
   16 · PLANES DE PRECIO
   ================================================================== */
.plans{display:grid; grid-template-columns:repeat(auto-fit,minmax(268px,1fr)); gap:18px; align-items:start}
.plan{border-radius:var(--radius); padding:32px 28px}
.plan > *{position:relative; z-index:1}
.plan.featured{border-color:var(--primary); box-shadow:var(--shadow); border-width:1.5px}
/* El plan destacado lleva un halo de color muy suave para que se
   distinga del de al lado sin cambiarle el tamaño. */
.plan.featured::after{box-shadow:inset 0 1px 0 var(--hairline), 0 0 0 4px var(--tint-2)}
.plan-tag{
  position:absolute; top:-12px; left:28px; background:var(--primary); color:var(--on-primary);
  font-size:.72rem; font-weight:740; letter-spacing:.05em; text-transform:uppercase;
  padding:5px 12px; border-radius:99px; z-index:2;
}
.plan h3{margin-bottom:12px}
.plan-price{display:flex; align-items:baseline; gap:7px; margin-bottom:10px!important}
.plan-price b{font-size:2.3rem; font-weight:800; letter-spacing:-.038em; color:var(--ink); line-height:1}
.plan-price span{font-size:var(--t-sm); color:var(--muted)}
.plan-blurb{font-size:var(--t-sm); color:var(--muted); margin-bottom:var(--s5)!important}
.plan .checks{margin-bottom:var(--s5)}
.plan-cta{margin:0!important}

/* ==================================================================
   17 · PREGUNTAS FRECUENTES
   ------------------------------------------------------------------
   `<details>` nativo: sin JavaScript, accesible por teclado sin
   programar nada, y el Ctrl+F del navegador encuentra dentro de los
   cerrados en Chrome.
   ================================================================== */
.faq{max-width:840px; margin:0 auto}
.faq details{border-radius:var(--radius-sm); margin-bottom:11px; overflow:hidden}
.faq details[open]{border-color:var(--edge)}
.faq summary{
  cursor:pointer; padding:20px 22px; font-weight:640; color:var(--ink); font-size:1.03rem;
  list-style:none; display:flex; align-items:flex-start; gap:14px;
  transition:background var(--dur-1) ease;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::before{
  content:""; flex:none; width:11px; height:11px; margin-top:8px;
  background:currentColor; color:var(--primary-ink);
  clip-path:polygon(46% 0,54% 0,54% 46%,100% 46%,100% 54%,54% 54%,54% 100%,46% 100%,46% 54%,0 54%,0 46%,46% 46%);
  transition:transform var(--dur-2) var(--ease-out);
}
.faq details[open] summary::before{transform:rotate(135deg)}
.faq summary:hover{background:var(--surface-2)}
.faq .answer{padding:2px 22px 24px 47px; font-size:var(--t-sm)}
.faq .answer ul{padding-left:19px; margin:10px 0 0}
.faq .answer li{margin-bottom:7px}
@media (prefers-reduced-motion:no-preference){
  .faq details[open] .answer{animation:answer var(--dur-3) var(--ease-out) both}
  @keyframes answer{from{opacity:0; transform:translateY(-6px)} to{opacity:1; transform:none}}
}
@media (max-width:540px){ .faq .answer{padding-left:22px} }

/* ==================================================================
   18 · NOVEDADES
   ================================================================== */
.log{max-width:820px; margin:0 auto}
.log article{border-radius:var(--radius); padding:var(--s6) 28px; margin-bottom:16px}
.log article > *{position:relative; z-index:1}
.log h3{display:flex; align-items:baseline; gap:14px; flex-wrap:wrap; margin-bottom:var(--s4)}
.log time{font-size:var(--t-xs); color:var(--muted); font-weight:500}
.log ul{margin:0; padding-left:19px; font-size:var(--t-sm)}
.log li{margin-bottom:8px}
.log li::marker{color:var(--primary)}

/* ==================================================================
   19 · PIE
   ================================================================== */
/* Va por CLASE y no por etiqueta. `<footer>` es un elemento legal dentro de
   `<article>`, `<section>` y `<blockquote>` —la atribución de una cita destacada usa uno,
   que es lo semánticamente correcto—, y un selector `footer{…}` a secas le encasquetaba
   a esa atribución el fondo, el borde superior y los 52 px de relleno del pie del sitio.
   El fallo era invisible en el HTML y evidente en pantalla. */

.site-foot{border-top:1px solid var(--border); background:var(--surface-2); padding:clamp(52px,6vw,80px) 0 40px; position:relative; overflow:hidden}
/* Marca + cinco columnas de enlaces. `minmax(0,1fr)` y no `1fr` a
   secas: sin el mínimo en cero, una etiqueta larga («Recuperación y
   wearables») ensancha su columna y descuadra las otras cuatro. */
.foot-grid{display:grid; grid-template-columns:1.3fr repeat(5,minmax(0,1fr)); gap:32px}
@media (max-width:1080px){ .foot-grid{grid-template-columns:repeat(3,minmax(0,1fr)); gap:30px} .foot-brand{grid-column:1/-1} }
@media (max-width:700px){ .foot-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:460px){ .foot-grid{grid-template-columns:1fr} }
.foot-brand p{font-size:var(--t-sm); color:var(--muted); margin:var(--s4) 0 0; max-width:280px}

.site-foot h2{font-size:.79rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); margin-bottom:var(--s4); font-weight:740}
.site-foot ul{list-style:none; margin:0; padding:0}
.site-foot li{margin-bottom:9px}
.site-foot li a{color:var(--text); text-decoration:none; font-size:var(--t-sm); transition:color var(--dur-1) ease}
.site-foot li a:hover{color:var(--primary-ink); text-decoration:underline}
.foot-legal{
  margin:clamp(34px,4.4vw,52px) 0 0; padding-top:28px; border-top:1px solid var(--border);
  font-size:var(--t-xs); color:var(--muted); line-height:1.65; max-width:900px;
}
/* Crédito de la fotografía. Discreto pero VISIBLE y con el enlace real: los
   términos de la API de Pexels piden exactamente eso, y un crédito que nadie
   puede ver no cumple. Ver `mediaCredit()` en web/components.mjs. */
.foot-credit{margin:var(--s3) 0 0!important; font-size:var(--t-xs); color:var(--muted)}
.foot-credit a{color:var(--muted); text-decoration:underline; text-underline-offset:2px}
.foot-credit a:hover{color:var(--primary-ink)}
.foot-copy{margin:var(--s4) 0 0!important; font-size:var(--t-xs); color:var(--muted)}
/* Nombre de la marca gigante al fondo del pie, recortado por el borde
   inferior. Es la firma de la casa: ocupa el espacio muerto del final y
   no compite con nada porque va detrás de todo y muy apagado. */
.foot-word{
  position:absolute; left:0; right:0; bottom:-.28em; z-index:0; pointer-events:none;
  margin:0; text-align:center; user-select:none;
  font-family:var(--display); font-weight:700; line-height:.8;
  font-size:clamp(4rem,17vw,15rem); letter-spacing:-.045em;
  color:var(--ink); opacity:.045; text-transform:var(--case-display);
}
.site-foot .wrap{position:relative; z-index:1}

/* ==================================================================
   20 · PÁGINAS LEGALES
   ------------------------------------------------------------------
   Contenido largo de una sola columna: privacidad, términos y borrado
   de cuenta.
   ================================================================== */
.legal{max-width:var(--wrap-text); margin:0 auto; padding:clamp(32px,4vw,52px) 0 clamp(56px,6.5vw,88px)}
.legal h2{font-size:1.38rem; margin:48px 0 12px; scroll-margin-top:calc(var(--head-h) + 24px)}
.legal h3{font-size:1.08rem; margin:30px 0 10px}
.legal p,.legal li{font-size:var(--t-sm)}
.legal ul,.legal ol{padding-left:22px; margin:0 0 1em}
.legal li{margin-bottom:7px}
.legal table{min-width:0}
.legal .updated{color:var(--muted); font-size:var(--t-xs); margin-bottom:8px}
.legal .toc{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
  padding:24px 26px; margin:30px 0 44px; box-shadow:var(--shadow-sm);
}
.legal .toc h2{font-size:.79rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); margin:0 0 12px}
.legal .toc ol{margin:0; padding-left:20px; font-size:var(--t-sm); columns:2; column-gap:30px}
@media (max-width:640px){ .legal .toc ol{columns:1} }
.legal .toc li{margin-bottom:6px; break-inside:avoid}
/* Resumen destacado en cabeza del documento: lo que dice el texto legal
   en cuatro frases, para quien no va a leer las cuarenta. */
.legal .tldr{
  background:var(--tint-2); border:1px solid var(--tint-3); border-left:3px solid var(--primary);
  border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  padding:var(--s5) var(--s5); margin:0 0 var(--s6); font-size:var(--t-sm);
}
.legal .tldr strong{color:var(--ink)}

/* ==================================================================
   21 · PANEL WEB (solo /panel — /en/dashboard)
   ================================================================== */
.panel-loading{display:grid; place-items:center; gap:16px; padding:120px 20px; color:var(--muted)}
.spinner{
  width:34px; height:34px; border-radius:50%; border:3px solid var(--border);
  border-top-color:var(--primary); animation:spin .8s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}

.panel-app{padding:clamp(30px,4vw,48px) var(--gut) clamp(56px,6.5vw,88px)}
.panel-head{display:flex; flex-wrap:wrap; gap:18px; align-items:flex-end; justify-content:space-between; margin-bottom:32px}
.panel-head h1{font-size:clamp(1.7rem,1.3rem + 1.6vw,2.4rem); margin-bottom:6px}
.panel-head .muted{font-size:var(--t-sm); margin:0}
.panel-actions{display:flex; gap:10px; flex-wrap:wrap}
.panel-empty{max-width:620px; margin:0 auto; padding:80px var(--gut); text-align:center}
.panel-empty h1{margin-bottom:16px}
.panel-empty .lede{margin-bottom:var(--s5)}

/* Fila de indicadores. `auto-fit` con mínimo pequeño para que en móvil
   salgan de dos en dos en vez de una columna de ocho tarjetas. */
.kpis{display:grid; grid-template-columns:repeat(auto-fit,minmax(178px,1fr)); gap:14px; margin-bottom:var(--s5)}
.kpi{border-radius:var(--radius-sm); padding:18px 20px}
.kpi > *{position:relative; z-index:1}
.kpi-l{display:block; font-size:var(--t-xs); color:var(--muted); font-weight:600; letter-spacing:.02em}
.kpi-v{display:block; font-size:clamp(1.5rem,1.2rem + .9vw,1.9rem); font-weight:800; color:var(--ink); letter-spacing:-.03em; line-height:1.15; margin:6px 0 3px; font-variant-numeric:tabular-nums}
.kpi-s{display:block; font-size:var(--t-xs); color:var(--muted); line-height:1.4}

.kpi-of{font-size:.62em; font-weight:650; color:var(--muted); letter-spacing:0}
/* Tono opcional de una cifra. Solo tres, y siempre acompañados de texto: el
   color no puede ser el único portador del significado. */
.kpi.k-ok{border-color:color-mix(in srgb,var(--primary) 30%,var(--border))}
.kpi.k-warn{border-color:color-mix(in srgb,#d98324 34%,var(--border))}
.kpi.k-bad{border-color:color-mix(in srgb,#d94a3d 34%,var(--border))}
/* Cifras secundarias dentro de una tarjeta: más pequeñas y sin superficie
   propia, para que no compitan con la rejilla de arriba. */
.mini-kpis{display:grid; grid-template-columns:repeat(auto-fit,minmax(118px,1fr)); gap:10px; margin-top:18px}
.mini-kpis .kpi{background:var(--surface-2); border-color:transparent; box-shadow:none; padding:12px 14px}
.mini-kpis .kpi::after{box-shadow:none}
.mini-kpis .kpi-v{font-size:1.12rem; margin:3px 0 2px}

/* Delta con flecha. La dirección «buena» la decide quien llama, porque subir
   de peso no es bueno ni malo sin saber el objetivo: por eso existe `.neu`. */
.dl{font-variant-numeric:tabular-nums; font-weight:660; white-space:nowrap}
.dl.up{color:var(--primary-ink)}
.dl.down{color:#b3372c}
.dl.neu{color:var(--muted)}
@media (prefers-color-scheme:dark){ .dl.down{color:#ff9d92} }

/* ---------- pestañas del panel ---------- */
.ptabs{
  display:flex; gap:4px; overflow-x:auto; scrollbar-width:none;
  border-bottom:1px solid var(--border); margin-bottom:var(--s5);
}
.ptabs::-webkit-scrollbar{display:none}
.ptab{
  flex:0 0 auto; font:inherit; font-size:var(--t-sm); font-weight:620; cursor:pointer;
  color:var(--muted); background:none; border:none; padding:12px 15px;
  border-bottom:2.5px solid transparent; margin-bottom:-1px; white-space:nowrap;
  transition:color var(--dur-1) ease, border-color var(--dur-1) ease;
}
.ptab:hover{color:var(--ink)}
.ptab.on{color:var(--primary-ink); border-bottom-color:var(--primary)}
.ptab:focus-visible{outline:2.5px solid var(--primary); outline-offset:-3px; border-radius:var(--radius-xs)}
.ptab-body:focus{outline:none}

/* Aviso de la demostración. Va arriba del todo y no se puede confundir con
   una tarjeta más: si alguien enseña una captura del panel, tiene que
   quedar claro que esos números no son de nadie. */
.demo-bar{
  display:flex; flex-wrap:wrap; align-items:center; gap:10px 16px;
  background:var(--tint); border:1px solid var(--edge); border-radius:var(--radius-sm);
  padding:14px 18px; margin-bottom:var(--s5); font-size:var(--t-sm); color:var(--text);
}
.demo-bar strong{color:var(--primary-ink)}
.demo-bar .btn{margin-left:auto}

.pgrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(340px,1fr)); gap:18px; margin-bottom:18px; align-items:start}
.pcard{border-radius:var(--radius); padding:var(--s5) var(--s6)}
.pcard > *{position:relative; z-index:1}
.pcard h2{font-size:1.05rem; margin-bottom:6px}
/* Una tarjeta ancha ocupa la rejilla entera. Sin esto, `auto-fit` la mete en
   una columna de 340 px y la tabla de dentro nace con scroll horizontal. */
.pcard.wide{grid-column:1/-1}
.pcard-note{font-size:var(--t-xs)!important; color:var(--muted); margin:16px 0 0!important; line-height:1.55}
.pc-sub{font-size:var(--t-xs)!important; color:var(--muted); margin:0 0 16px!important; line-height:1.5}
.pcard h2 + *:not(.pc-sub){margin-top:18px}
.pcard .table-wrap{box-shadow:none}
.p-empty{font-size:var(--t-sm); padding:26px 0; text-align:center; margin:0!important}

/* Pista de ayuda de una tarjeta: un signo de interrogación con `title`. */
.pc-hint{
  display:inline-grid; place-items:center; width:17px; height:17px; border-radius:50%;
  background:var(--surface-2); color:var(--muted); font-size:11px; font-weight:700;
  vertical-align:middle; cursor:help;
}

/* ---------- tablas ordenables ---------- */
.ptable .th-sort{
  font:inherit; font-weight:inherit; color:inherit; background:none; border:none;
  padding:0; cursor:pointer; display:inline-flex; align-items:center; gap:5px;
}
.ptable .th-sort:hover{color:var(--primary-ink)}
.ptable .th-sort:focus-visible{outline:2px solid var(--primary); outline-offset:3px; border-radius:3px}
.th-arrow{width:0; height:0; border-left:4px solid transparent; border-right:4px solid transparent; opacity:0}
.ptable th[aria-sort] .th-arrow{opacity:1}
.ptable th[aria-sort="ascending"] .th-arrow{border-bottom:5px solid var(--primary)}
.ptable th[aria-sort="descending"] .th-arrow{border-top:5px solid var(--primary)}
.ptable td.n,.ptable th.n{text-align:right; font-variant-numeric:tabular-nums}

/* ---------- selectores de una gráfica ---------- */
/* `.seg` ya existe arriba para el patrón input+label de las calculadoras. El
   panel construye sus segmentos con <button> porque los dibuja en vivo, así
   que necesita su propia pintura con el mismo aspecto. */
.seg-b{
  font:inherit; font-size:var(--t-xs); font-weight:620; cursor:pointer;
  padding:7px 13px; border-radius:99px; border:1px solid var(--border);
  background:var(--surface); color:var(--muted);
  transition:border-color var(--dur-1) ease, color var(--dur-1) ease, background var(--dur-1) ease;
}
.seg-b:hover{border-color:var(--edge); color:var(--ink)}
.seg-b.on{background:var(--tint); border-color:var(--primary); color:var(--primary-ink)}
.seg-b:focus-visible{outline:2.5px solid var(--primary); outline-offset:2px}
.sel-row{display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:16px}
.sel-row label{font-size:var(--t-xs); font-weight:660; color:var(--muted)}
.psel{
  font:inherit; font-size:var(--t-sm); padding:8px 12px; border-radius:var(--radius-xs);
  border:1.5px solid var(--border); background:var(--surface); color:var(--ink);
  max-width:100%; flex:1 1 200px;
}
.psel:focus-visible{border-color:var(--primary); outline:none; box-shadow:0 0 0 3px var(--tint)}

/* ==================================================================
   Gráficas interactivas del panel
   ------------------------------------------------------------------
   Todas comparten el mismo esqueleto: controles, un `.chart-box` que es
   el marco de referencia del globo de datos, el SVG y una leyenda.

   El globo (`.ch-tip`) se posiciona por CSSOM desde el JavaScript —la CSP
   del dominio es `style-src 'self'` y prohíbe el atributo `style=`, pero
   no `el.style.x`—, así que aquí solo se define su aspecto y el hecho de
   que no debe capturar el puntero: si lo capturara, aparecería bajo el
   ratón, robaría el `pointermove` del SVG y parpadearía sin parar.
   ================================================================== */
.chart-fig{margin:0}
.chart-ctl{display:flex; flex-wrap:wrap; gap:6px; margin-bottom:14px}
.chart-box{position:relative; touch-action:pan-y}
.chart{width:100%; height:auto; overflow:visible; display:block}
.chart:focus-visible{outline:2.5px solid var(--primary); outline-offset:4px; border-radius:var(--radius-xs)}
.ch-grid{stroke:var(--border); stroke-width:1}
.ch-lab{fill:var(--muted); font-size:11px; font-family:inherit}
/* COLOR POR CLASE, NO POR ATRIBUTO. El JavaScript solo decide un índice de serie
   y lo escribe como `s0`…`s5`; el color entero vive aquí abajo, en el bloque de
   la paleta. Por eso estas reglas llevan la FORMA y nada más: si alguna declarara
   un `stroke`, ganaría sobre la clase de paleta —misma especificidad, pero va
   antes— y dejaría las seis series del mismo verde. */
.ch-line{fill:none; stroke-width:2.4; stroke-linejoin:round; stroke-linecap:round}
.ch-line.dash{stroke-width:1.8; stroke-dasharray:5 4; opacity:.85}
.ch-area{stroke:none}
.ch-dot{stroke:var(--surface); stroke-width:1.5}
.ch-bar{transition:opacity var(--dur-1) ease}
.ch-goal{stroke:var(--muted); stroke-width:1.5; stroke-dasharray:5 4}
/* Franja de referencia (el rango objetivo de proteína, por ejemplo). */
.ch-band{fill:var(--surface-2)}
.ch-band.good{fill:var(--tint-2)}
/* La rejilla de captura: invisible pero es la que recibe el puntero. Sin
   `fill:transparent` el SVG no le entrega ningún evento —`fill:none` deja
   pasar el ratón a través— y la gráfica queda muerta al señalarla. */
.ch-hit{fill:transparent; cursor:crosshair}
/* Selección de arrastre. `pointer-events:none` es imprescindible: si capturara el
   puntero, el rectángulo se interpondría entre el ratón y la zona de captura en
   cuanto creciera y el arrastre se cortaría solo a los pocos píxeles. */
.ch-brush{fill:var(--tint-3); stroke:var(--primary); stroke-width:1; pointer-events:none}
.ch-cursor{stroke:var(--edge); stroke-width:1.5; stroke-dasharray:3 3; pointer-events:none}
.ch-focus{stroke:var(--surface); stroke-width:2.5; pointer-events:none}
.ch-bg{cursor:pointer}
.ch-bg .ch-bar{opacity:.78}
.ch-bg.on .ch-bar,.ch-bg:hover .ch-bar{opacity:1}
.ch-cap{font-size:var(--t-xs); color:var(--muted); margin-top:12px; line-height:1.5}

/* Globo de datos. `left`/`top` los pone el JavaScript; el `translateX(-50%)`
   lo ancla por su centro para que el cálculo de allí sea el del punto. */
.ch-tip{
  position:absolute; left:0; top:0; transform:translateX(-50%);
  pointer-events:none; z-index:3; min-width:112px; max-width:230px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xs);
  box-shadow:var(--shadow); padding:9px 11px; font-size:var(--t-xs); line-height:1.45;
}
.tip-d{display:block; color:var(--muted); font-weight:640; margin-bottom:5px; text-transform:capitalize}
.tip-r{display:flex; align-items:center; gap:7px; color:var(--text); white-space:nowrap}
.tip-r b{margin-left:auto; color:var(--ink); font-variant-numeric:tabular-nums; font-weight:700}
.tip-dot{width:9px; height:9px; border-radius:50%; flex:0 0 auto; background:var(--primary)}
.tip-n{display:block; margin-top:5px; color:var(--muted); font-style:italic}

/* Leyenda. En una gráfica de varias series los botones apagan y encienden
   cada una; en las apiladas es solo una clave y por eso existe `.static`. */
.chart-legend{display:flex; flex-wrap:wrap; gap:8px 16px; margin-top:14px}
.lg{
  display:inline-flex; align-items:center; gap:7px; font:inherit; font-size:var(--t-xs);
  font-weight:600; color:var(--text); background:none; border:none; padding:2px 0; cursor:pointer;
}
.lg.static{cursor:default}
.lg.off{color:var(--muted); text-decoration:line-through; opacity:.6}
.lg:not(.static):hover{color:var(--ink)}
.lg:focus-visible{outline:2px solid var(--primary); outline-offset:3px; border-radius:4px}
.lg-dot{width:10px; height:10px; border-radius:3px; flex:0 0 auto; background:var(--primary)}

/* ---------- anillo de reparto ---------- */
.donut-fig{margin:0; display:flex; flex-wrap:wrap; align-items:center; gap:22px}
.donut{width:190px; height:190px; flex:0 0 auto}
.dn-arc{cursor:pointer; transition:opacity var(--dur-1) ease}
.dn-arc.on{opacity:1}
.donut:hover .dn-arc:not(.on),.donut:focus-within .dn-arc:not(.on){opacity:.42}
.dn-arc:focus-visible{outline:2px solid var(--primary); outline-offset:2px}
.dn-v{fill:var(--ink); font-size:24px; font-weight:800; font-family:inherit; letter-spacing:-.02em}
.dn-l{fill:var(--muted); font-size:11px; font-family:inherit}
.dn-key{list-style:none; margin:0; padding:0; display:grid; gap:8px; flex:1 1 170px; min-width:160px}
.dn-key li{
  display:grid; grid-template-columns:auto 1fr auto auto; align-items:center; gap:9px;
  font-size:var(--t-xs); padding:4px 7px; margin:0 -7px; border-radius:var(--radius-xs);
  transition:background var(--dur-1) ease;
}
.dn-key li.on{background:var(--surface-2)}
.dn-n{color:var(--text); font-weight:600}
.dn-p{color:var(--ink); font-weight:700; font-variant-numeric:tabular-nums}
.dn-x{color:var(--muted); font-variant-numeric:tabular-nums}

/* ---------- calendario de actividad ---------- */
/* Un año son 53 columnas. En una pantalla estrecha se dejan encoger en vez
   de meter scroll horizontal: el patrón de huecos —que es lo que se viene a
   ver— se lee igual de bien pequeño. */
.cal-c{fill:var(--surface-2); transition:fill var(--dur-1) ease}
.cal-c.lv1{fill:color-mix(in srgb,var(--primary) 26%,var(--surface-2))}
.cal-c.lv2{fill:color-mix(in srgb,var(--primary) 48%,var(--surface-2))}
.cal-c.lv3{fill:color-mix(in srgb,var(--primary) 72%,var(--surface-2))}
.cal-c.lv4{fill:var(--primary)}
.cal-c.on{stroke:var(--ink); stroke-width:1.5}
.cal-m{fill:var(--muted); font-size:10px; font-family:inherit}
.cal-leg{display:flex; align-items:center; gap:5px; margin-top:12px; font-size:var(--t-xs); color:var(--muted)}
.cal-leg i{display:block; width:11px; height:11px; border-radius:2.5px; background:var(--surface-2)}
.cal-leg i.lv1{background:color-mix(in srgb,var(--primary) 26%,var(--surface-2))}
.cal-leg i.lv2{background:color-mix(in srgb,var(--primary) 48%,var(--surface-2))}
.cal-leg i.lv3{background:color-mix(in srgb,var(--primary) 72%,var(--surface-2))}
.cal-leg i.lv4{background:var(--primary)}

/* ---------- barra de volumen con MEV / MAV / MRV ---------- */
/* Las tres marcas se colocan por `left` desde el JavaScript. `overflow:hidden`
   NO: una marca justo en el borde se recortaría a la mitad. */
.vbar{position:relative; display:inline-block; width:100px; height:9px; border-radius:99px; background:var(--surface-2); vertical-align:middle}
.vbar-f{position:absolute; inset:0 auto 0 0; background:var(--primary); border-radius:99px}
.vbar-m{position:absolute; top:-3px; bottom:-3px; width:1.5px; background:var(--muted); opacity:.55}
.vbar-m.mrv{background:#d94a3d; opacity:.8}

/* ---------- medidor genérico ---------- */
.mtr{display:block; height:9px; border-radius:99px; background:var(--surface-2); overflow:hidden}
.mtr-f{display:block; height:100%; background:var(--primary); border-radius:99px; transition:width var(--dur-2) var(--ease-out)}

/* ---------- filas de objetivo ---------- */
.goalrows{list-style:none; margin:0; padding:0; display:grid; gap:14px}
/* `minmax(0,…)` en TODAS las columnas flexibles, no solo un `1fr`. Una columna
   `1fr` a secas tiene mínimo `auto`, que es el ancho del contenido: si el texto no
   cabe, la fila no encoge — se sale de la tarjeta. Es lo que empujaba el «quedan
   40 d» fuera del borde derecho de «Tus objetivos». */
.goalrows li{display:grid; grid-template-columns:minmax(0,1.1fr) minmax(0,2.2fr) auto auto; align-items:center; gap:12px; font-size:var(--t-sm)}
.goalrows.big li{grid-template-columns:minmax(0,1.2fr) minmax(0,2fr) auto auto auto}
.goalrows li.done .gr-l{color:var(--muted)}
.gr-l{color:var(--ink); font-weight:600; display:flex; align-items:center; gap:7px; flex-wrap:wrap; min-width:0}
.gr-v{color:var(--ink); font-variant-numeric:tabular-nums; white-space:nowrap}
.gr-p{font-weight:700; font-variant-numeric:tabular-nums; min-width:46px; text-align:right; white-space:nowrap}
.gr-p.ok{color:var(--primary-ink)}
.gr-p.hi{color:#a4611a}
.gr-p.lo{color:#b3372c}
.gr-x{color:var(--muted); font-size:var(--t-xs); white-space:nowrap; text-align:right}
@media (prefers-color-scheme:dark){ .gr-p.hi{color:#f0b46a} .gr-p.lo{color:#ff9d92} }

/* Cinco columnas no caben en una tarjeta de 340 px por muy ancha que sea la
   pantalla, y el punto de ruptura de abajo mira el VIEWPORT: en un monitor grande
   nunca llega a activarse aunque la tarjeta sea estrechísima. Por eso aquí manda
   el ancho del CONTENEDOR. Es justo el caso para el que existen las consultas de
   contenedor: el problema no es «pantalla pequeña», es «caja pequeña». */
.pcard{container-type:inline-size}
@container (max-width:560px){
  .goalrows li,.goalrows.big li{grid-template-columns:minmax(0,1fr) auto; gap:5px 10px}
  .goalrows .mtr{grid-column:1/-1; order:3}
  .goalrows .gr-v{order:2; text-align:right}
  .goalrows .gr-p{order:1}
  .goalrows .gr-x{grid-column:1/-1; order:4; text-align:left}
}
@media (max-width:640px){
  .goalrows li,.goalrows.big li{grid-template-columns:1fr auto; gap:6px 10px}
  .goalrows .mtr{grid-column:1/-1; order:3}
  .goalrows .gr-x{grid-column:1/-1; order:4; text-align:left}
}

/* ---------- lecturas del resumen ----------
   Cada una lleva una barra de color a la izquierda, pero el color NUNCA es lo
   único que dice el tono: el texto del título ya lo dice con palabras. Es la
   condición para que sirva en escala de grises y con daltonismo. */
.reads{list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(auto-fit,minmax(300px,1fr)); gap:14px}
.rd{display:grid; grid-template-columns:auto 1fr; gap:14px; padding:15px 17px; border-radius:var(--radius-sm); background:var(--surface-2)}
.rd-b{width:4px; border-radius:99px; background:var(--muted)}
.rd.r-good .rd-b{background:var(--primary)}
.rd.r-warn .rd-b{background:#d98324}
.rd.r-bad .rd-b{background:#d94a3d}
.rd.r-info .rd-b{background:var(--edge)}
.rd b{display:block; color:var(--ink); font-size:var(--t-sm); font-weight:680; margin-bottom:5px; line-height:1.35}
.rd p{font-size:var(--t-xs); color:var(--text); line-height:1.55; margin:0!important}
.rd-go{
  font:inherit; font-size:var(--t-xs); font-weight:640; color:var(--primary-ink);
  background:none; border:none; padding:0; margin-top:9px; cursor:pointer;
}
.rd-go:hover{text-decoration:underline}
.rd-go:focus-visible{outline:2px solid var(--primary); outline-offset:3px; border-radius:3px}

/* ---------- las tres coberturas ---------- */
.covers{list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(auto-fit,minmax(270px,1fr)); gap:20px}
.cv{display:grid; grid-template-columns:1fr auto; gap:7px 12px; align-content:start}
.cv-l{color:var(--ink); font-weight:660; font-size:var(--t-sm)}
.cv-v{font-size:var(--t-sm); text-align:right; font-variant-numeric:tabular-nums}
.cv-v b{color:var(--ink); font-size:1.25rem; font-weight:800}
.cv .mtr{grid-column:1/-1}
.cv-h{grid-column:1/-1; font-size:var(--t-xs); color:var(--muted); line-height:1.45}
.cv.none .cv-v{font-size:var(--t-sm)}

/* ---------- señales de recuperación con minigráfica ---------- */
.sigs{list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(auto-fit,minmax(250px,1fr)); gap:16px}
.sigs li{display:grid; grid-template-columns:1fr auto; gap:3px 12px; align-items:baseline; padding:14px 16px; border-radius:var(--radius-sm); background:var(--surface-2)}
.sg-l{color:var(--muted); font-size:var(--t-xs); font-weight:640}
.sg-v{grid-row:2; color:var(--ink); font-size:1.45rem; font-weight:800; letter-spacing:-.03em; font-variant-numeric:tabular-nums}
.sg-u{font-size:.62em; font-weight:650; color:var(--muted); margin-left:2px}
.sg-d{grid-row:3; grid-column:1/-1; font-size:var(--t-xs); margin-top:2px}
.sg-s{grid-row:1/3; grid-column:2; align-self:center}
/* La minigráfica reserva su hueco aunque no haya serie que dibujar (`.empty`),
   para que la fila no dé un salto de 38 px según haya datos o no. */
.spark{display:block; width:110px; height:38px; overflow:visible}
.sp-line{fill:none; stroke-width:2; stroke-linejoin:round; stroke-linecap:round}
.sp-area{stroke:none; opacity:.9}
.sp-dot{stroke:var(--surface-2); stroke-width:1.5}

/* ---------- listas de últimos entrenos y récords ---------- */
.feed{list-style:none; margin:0; padding:0; display:grid; gap:1px; background:var(--border); border-radius:var(--radius-sm); overflow:hidden}
.feed li{display:grid; grid-template-columns:auto 1fr; gap:2px 12px; padding:11px 14px; background:var(--surface); font-size:var(--t-sm)}
.fd-d{color:var(--muted); font-size:var(--t-xs); font-variant-numeric:tabular-nums; white-space:nowrap}
.fd-n{color:var(--ink); font-weight:620}
.fd-x{grid-column:1/-1; color:var(--muted); font-size:var(--t-xs); font-variant-numeric:tabular-nums}
.fd-x b{color:var(--primary-ink); font-weight:700}

/* ---------- fichas sueltas (músculos sin tocar) ---------- */
.chips{list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:8px}
.chip{
  display:inline-flex; align-items:baseline; gap:7px; font-size:var(--t-xs); font-weight:620;
  padding:6px 12px; border-radius:99px; border:1px solid var(--border);
  background:var(--surface-2); color:var(--ink);
}
.chip span{color:var(--muted); font-weight:500}
.chip.warn{border-color:color-mix(in srgb,#d98324 36%,transparent); background:color-mix(in srgb,#d98324 10%,transparent)}

/* ---------- logros ---------- */
.ach{list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:10px}
.ach li{
  display:grid; grid-template-columns:auto 1fr; grid-template-rows:auto auto; gap:2px 12px;
  align-items:center; padding:12px 14px; border-radius:var(--radius-sm);
  background:var(--surface-2); border:1px solid transparent;
}
.ach.locked li{background:none; border-color:var(--border); opacity:.72}
.ach-ic{grid-row:1/3; font-size:1.5rem; line-height:1; filter:saturate(1.1)}
.ach.locked .ach-ic{filter:grayscale(1); opacity:.7}
.ach-n{color:var(--ink); font-weight:660; font-size:var(--t-sm)}
.ach-d{color:var(--muted); font-size:var(--t-xs); line-height:1.4}
.ach-x{grid-column:2; color:var(--primary-ink); font-size:var(--t-xs); font-weight:640; margin-top:3px}
.ach.locked .ach-x{color:var(--muted)}

/* ---------- paleta categórica de las gráficas ----------
   Vive en el CSS y NO en el JavaScript a propósito: así el tema oscuro la
   cambia solo, la leyenda y la línea nunca se desincronizan (el panel lee
   estas mismas variables con `getComputedStyle`) y un cambio de marca no
   obliga a tocar código. Seis colores porque ninguna gráfica del panel
   pinta más series que eso; el séptimo daría la vuelta y repetiría.

   El primero es el primario de la marca, que es el que se lleva la serie
   protagonista. Los otros cinco se eligieron con dos condiciones: 3:1
   contra la superficie (son grafismo, no texto) y distinguibles entre sí
   en deuteranopía, que es la carencia de color más frecuente — por eso no
   hay un rojo y un verde compitiendo, sino que el contraste se apoya
   también en claro/oscuro. */
:root{
  --pc1:var(--primary);        --pc1-fill:color-mix(in srgb,var(--primary) 14%,transparent);
  --pc2:#2f6f8f;               --pc2-fill:color-mix(in srgb,#2f6f8f 14%,transparent);
  --pc3:#b5751b;               --pc3-fill:color-mix(in srgb,#b5751b 14%,transparent);
  --pc4:#8a5cc4;               --pc4-fill:color-mix(in srgb,#8a5cc4 14%,transparent);
  --pc5:#b34a63;               --pc5-fill:color-mix(in srgb,#b34a63 14%,transparent);
  --pc6:#4d6b52;               --pc6-fill:color-mix(in srgb,#4d6b52 14%,transparent);
}
/* Aplicación de la paleta. Una serie lleva la clase `s0`…`s5` y de aquí saca su
   color, sea trazo, relleno o fondo según el elemento. Escrito a mano y no
   generado porque son seis y se leen mejor así que con un bucle de preprocesador
   que este proyecto no tiene. */
.ch-line.s0,.sp-line.s0,.dn-arc.s0{stroke:var(--pc1)}
.ch-line.s1,.sp-line.s1,.dn-arc.s1{stroke:var(--pc2)}
.ch-line.s2,.sp-line.s2,.dn-arc.s2{stroke:var(--pc3)}
.ch-line.s3,.sp-line.s3,.dn-arc.s3{stroke:var(--pc4)}
.ch-line.s4,.sp-line.s4,.dn-arc.s4{stroke:var(--pc5)}
.ch-line.s5,.sp-line.s5,.dn-arc.s5{stroke:var(--pc6)}

.ch-dot.s0,.ch-focus.s0,.ch-bar.s0,.sp-dot.s0{fill:var(--pc1)}
.ch-dot.s1,.ch-focus.s1,.ch-bar.s1,.sp-dot.s1{fill:var(--pc2)}
.ch-dot.s2,.ch-focus.s2,.ch-bar.s2,.sp-dot.s2{fill:var(--pc3)}
.ch-dot.s3,.ch-focus.s3,.ch-bar.s3,.sp-dot.s3{fill:var(--pc4)}
.ch-dot.s4,.ch-focus.s4,.ch-bar.s4,.sp-dot.s4{fill:var(--pc5)}
.ch-dot.s5,.ch-focus.s5,.ch-bar.s5,.sp-dot.s5{fill:var(--pc6)}

.ch-area.s0,.sp-area.s0{fill:var(--pc1-fill)}
.ch-area.s1,.sp-area.s1{fill:var(--pc2-fill)}
.ch-area.s2,.sp-area.s2{fill:var(--pc3-fill)}
.ch-area.s3,.sp-area.s3{fill:var(--pc4-fill)}
.ch-area.s4,.sp-area.s4{fill:var(--pc5-fill)}
.ch-area.s5,.sp-area.s5{fill:var(--pc6-fill)}

.lg-dot.s0,.tip-dot.s0{background:var(--pc1)}
.lg-dot.s1,.tip-dot.s1{background:var(--pc2)}
.lg-dot.s2,.tip-dot.s2{background:var(--pc3)}
.lg-dot.s3,.tip-dot.s3{background:var(--pc4)}
.lg-dot.s4,.tip-dot.s4{background:var(--pc5)}
.lg-dot.s5,.tip-dot.s5{background:var(--pc6)}

/* La barra a cero de una gráfica no es una serie: es el hueco de un día sin
   registrar y se pinta con el borde, no con un color de paleta. */
.ch-bar.zero{fill:var(--border)}

/* En oscuro los mismos tonos se aclaran: un #2f6f8f sobre un fondo casi negro
   se queda en 2:1 y la línea desaparece. */
@media (prefers-color-scheme:dark){
  :root{
    --pc1:#8ccb4e;             --pc1-fill:color-mix(in srgb,#8ccb4e 16%,transparent);
    --pc2:#6cb8d8;             --pc2-fill:color-mix(in srgb,#6cb8d8 16%,transparent);
    --pc3:#e0a84e;             --pc3-fill:color-mix(in srgb,#e0a84e 16%,transparent);
    --pc4:#b492e6;             --pc4-fill:color-mix(in srgb,#b492e6 16%,transparent);
    --pc5:#e58aa1;             --pc5-fill:color-mix(in srgb,#e58aa1 16%,transparent);
    --pc6:#8fb896;             --pc6-fill:color-mix(in srgb,#8fb896 16%,transparent);
  }
}

/* Veredicto de volumen frente a los rangos de referencia. */
.vtag{
  display:inline-block; font-size:var(--t-xs); font-weight:660; border-radius:99px;
  padding:3px 10px; border:1px solid; white-space:nowrap;
}
.vtag.ok{background:var(--tint); color:var(--primary-ink); border-color:color-mix(in srgb,var(--primary) 34%,transparent)}
.vtag.high{background:color-mix(in srgb,#d98324 12%,transparent); color:#a4611a; border-color:color-mix(in srgb,#d98324 30%,transparent)}
.vtag.low,.vtag.over{background:color-mix(in srgb,#d94a3d 12%,transparent); color:#b3372c; border-color:color-mix(in srgb,#d94a3d 30%,transparent)}
@media (prefers-color-scheme:dark){
  .vtag.high{color:#f0b46a} .vtag.low,.vtag.over{color:#ff9d92}
}

/* Ranking de barras horizontales (volumen por músculo). */
.rank{list-style:none; margin:0; padding:0; display:grid; gap:11px}
.rank li{display:grid; grid-template-columns:minmax(96px,1.1fr) 3fr auto; align-items:center; gap:12px; font-size:var(--t-sm)}
.rank-l{color:var(--ink); font-weight:560}
.rank-t{background:var(--surface-2); border-radius:99px; height:11px; overflow:hidden}
.rank-f{display:block; height:100%; background:var(--primary); border-radius:99px; transition:width var(--dur-2) var(--ease-out)}
/* El veredicto tiñe la barra, pero nunca es lo único que lo dice: la tabla de
   debajo lo repite con palabras. El color solo adelanta la lectura. */
.rank-f.t-low{background:#c0632a}
.rank-f.t-over{background:#c04a3d}
.rank-v{color:var(--muted); font-variant-numeric:tabular-nums; min-width:38px; text-align:right}
@media (max-width:520px){ .rank li{grid-template-columns:minmax(78px,1fr) 2fr auto; gap:8px} }

/* ==================================================================
   Informe del panel (el «Descargar PDF»)
   ------------------------------------------------------------------
   Ya no hay estilos de informe en este archivo, y es a propósito.

   El documento lo dibuja FX.Report — el MISMO motor que el informe de la
   app, servido como /assets/report-engine.js — y sale de aquí como
   descarga, no como una sección incrustada que había que ocultar en
   pantalla y desocultar al imprimir. El PDF es vectorial y con texto de
   verdad (seleccionable, buscable, legible por un lector de pantalla), y
   el respaldo HTML se descarga AUTOCONTENIDO: lleva su propia hoja de
   unos 4 KB dentro, así que se abre sin la web y sin red.

   Consecuencia práctica: cambiar el aspecto del informe del sitio se hace
   en www/assets/src/71-report.js, no aquí — y cambia los dos a la vez,
   que es exactamente el punto. Ver docs/INFORME-UNIFICADO.md.
   ================================================================== */

/* ---------- acceso ---------- */
.login-wrap{display:grid; grid-template-columns:minmax(0,420px) minmax(0,1fr); gap:clamp(30px,5vw,64px); align-items:start; max-width:940px; margin:0 auto}
@media (max-width:800px){ .login-wrap{grid-template-columns:1fr} .login-side{order:-1} }
.login-card{border-radius:var(--radius); padding:32px 28px; box-shadow:var(--shadow)}
.login-card > *{position:relative; z-index:1}
.login-card h2{font-size:1.25rem; margin-bottom:6px}
.login-card #pGoogle{margin-top:20px}
.login-card label{display:block; font-size:var(--t-xs); font-weight:660; color:var(--ink); margin:16px 0 6px}
.login-card input{
  width:100%; font:inherit; font-size:.95rem; padding:12px 14px; border-radius:var(--radius-xs);
  border:1.5px solid var(--border); background:var(--bg); color:var(--ink);
}
.login-card input:focus-visible{border-color:var(--primary); outline:none; box-shadow:0 0 0 3px var(--tint)}
.login-card #pSubmit{margin-top:22px}
.login-sep{display:flex; align-items:center; gap:14px; margin:22px 0 4px; color:var(--muted); font-size:var(--t-xs)}
.login-sep::before,.login-sep::after{content:""; flex:1; height:1px; background:var(--border)}
.login-forgot{margin:16px 0 0!important; font-size:var(--t-xs); text-align:center}
.login-foot{margin:20px 0 0!important; padding-top:18px; border-top:1px solid var(--border); font-size:var(--t-xs); color:var(--muted)}
.login-side h2{font-size:1.15rem; margin-bottom:18px}

.panel-msg{margin:14px 0 0!important; font-size:var(--t-sm); padding:11px 14px; border-radius:var(--radius-xs); border:1px solid}
.panel-msg.err{background:color-mix(in srgb,#d94a3d 10%,transparent); border-color:color-mix(in srgb,#d94a3d 34%,transparent); color:#b3372c}
.panel-msg.ok{background:var(--tint); border-color:color-mix(in srgb,var(--primary) 34%,transparent); color:var(--primary-ink)}
@media (prefers-color-scheme:dark){ .panel-msg.err{color:#ff9d92} }

/* ====================================================================
   22 · BIBLIOTECA PÚBLICA
   --------------------------------------------------------------------
   Ejercicios, suplementos, mitos, guías, glosario, síntomas, atlas.
   Estas páginas no son marketing: son el contenido de la app publicado
   tal cual. Por eso priorizan la lectura y el escaneo por encima del
   efecto — sin degradados de portada, con más densidad y con el dato
   siempre delante.
   ==================================================================== */

/* ---------- migas de pan ---------- */
.crumbs{border-bottom:1px solid var(--border); background:var(--surface-2)}
.crumbs ol{
  display:flex; flex-wrap:wrap; align-items:center; gap:6px;
  list-style:none; margin:0; padding:11px 0; font-size:var(--t-xs);
}
.crumbs li{display:flex; align-items:center; gap:6px; color:var(--muted)}
/* El separador lo pone el CSS y no el HTML: así no lo lee un lector de pantalla. */
.crumbs li+li::before{content:"›"; color:var(--border); font-size:1.05rem; line-height:1}
.crumbs a{color:var(--muted); text-decoration:none}
.crumbs a:hover{color:var(--primary-ink); text-decoration:underline}
.crumbs [aria-current]{color:var(--ink); font-weight:600}

/* ---------- barra de filtros ----------
   Pegajosa bajo la cabecera: con 306 tarjetas, perder el buscador al
   desplazarse obliga a subir del todo para cambiar un filtro. */
.lib-bar{
  position:sticky; top:var(--head-h); z-index:var(--z-bar);
  background:color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter:saturate(170%) blur(14px); -webkit-backdrop-filter:saturate(170%) blur(14px);
  border-bottom:1px solid var(--border); padding:14px 0;
}
.lib-bar .wrap{display:flex; flex-wrap:wrap; gap:10px; align-items:center}
.lib-search{
  flex:1 1 260px; min-width:0; font:inherit; font-size:.95rem; padding:12px 14px 12px 40px;
  border-radius:var(--radius-xs); border:1.5px solid var(--border); background:var(--surface); color:var(--ink);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7a63' stroke-width='1.9' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='6.6'/%3E%3Cpath d='M16 16l4.4 4.4'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:12px center; background-size:18px;
  transition:border-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
}
.lib-search:focus-visible{border-color:var(--primary); outline:none; box-shadow:0 0 0 3px var(--tint)}
.lib-sel{
  font:inherit; font-size:.9rem; padding:12px 32px 12px 13px; border-radius:var(--radius-xs);
  border:1.5px solid var(--border); background:var(--surface); color:var(--ink); cursor:pointer;
  appearance:none; -webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7a63' stroke-width='2' stroke-linecap='round'%3E%3Cpolyline points='6 9.5 12 15.5 18 9.5'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 9px center; background-size:16px;
}
.lib-count{font-size:var(--t-xs); color:var(--muted); margin-left:auto; font-variant-numeric:tabular-nums}
/* La barra nace OCULTA y solo la enseña `.js`, que añade el propio
   script al arrancar. Invertido a propósito: si el script no carga, no
   aparece un buscador que no busca — la lista completa sigue ahí abajo,
   enlazada y legible, que es lo que importa. */
.lib-bar{display:none}
.js .lib-bar{display:block}
@media (max-width:640px){ .lib-count{margin-left:0; width:100%} }

/* ---------- rejilla de fichas ---------- */
.lib-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(238px,1fr)); gap:16px}
.lib-card{
  display:flex; flex-direction:column; border-radius:var(--radius-sm);
  overflow:hidden; text-decoration:none; color:inherit;
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) ease, border-color var(--dur-2) ease;
}
.lib-card:hover{transform:translateY(-4px); text-decoration:none; box-shadow:var(--shadow); border-color:var(--edge)}
.lib-card .thumb{background:var(--surface-2); aspect-ratio:1/1; display:grid; place-items:center; overflow:hidden}
.lib-card .thumb img{width:100%; height:100%; object-fit:cover; transition:transform var(--dur-4) var(--ease-out)}
.lib-card:hover .thumb img{transform:scale(1.05)}
.lib-card .thumb.none{color:var(--border)}
.lib-card .thumb.none .ic{width:44px; height:44px}
.lib-card .body{padding:15px 17px 17px; display:flex; flex-direction:column; gap:7px; flex:1; position:relative; z-index:1}
.lib-card b{color:var(--ink); font-size:.97rem; font-weight:650; line-height:1.32; letter-spacing:-.012em}
.lib-card .meta{font-size:var(--t-xs); color:var(--muted); line-height:1.45; margin-top:auto}
.lib-hidden{display:none!important}
.lib-empty{text-align:center; padding:60px 20px; color:var(--muted)}

/* ---------- lista densa ----------
   Variante para las colecciones SIN miniatura: síntomas, fármacos,
   términos del glosario, guías. Con 1.082 fichas, una rejilla de
   tarjetas cuadradas obliga a recorrer tres pantallas de hueco para ver
   lo que en una lista cabe de un vistazo. */
.lib-grid.rows{grid-template-columns:repeat(auto-fill,minmax(min(100%,330px),1fr)); gap:10px}
.lib-grid.rows .lib-card{flex-direction:row; align-items:center}
.lib-grid.rows .lib-card .body{padding:13px 16px; gap:3px}
.lib-grid.rows .lib-card .meta{margin-top:0}
.lib-grid.rows .lib-card:hover{transform:translateX(3px)}

/* Secciones dentro de un índice. El grupo entero es el que se oculta
   cuando el filtro deja su lista a cero: un encabezado «Cardiología»
   encima de ningún resultado es peor que no tener encabezado. */
.lib-sections{display:flex; flex-direction:column; gap:clamp(28px,3.2vw,42px)}
.lib-section > h2{font-size:var(--t-h3); margin:0 0 3px; scroll-margin-top:calc(var(--head-h) + 60px)}
.lib-section > p{font-size:var(--t-xs); color:var(--muted); margin:0 0 13px}

/* ---------- etiquetas ---------- */
.tags{display:flex; flex-wrap:wrap; gap:7px; list-style:none; margin:0; padding:0}
.tag{
  display:inline-flex; align-items:center; gap:5px; font-size:var(--t-xs); font-weight:600;
  background:var(--surface-2); color:var(--text); border:1px solid var(--border);
  border-radius:99px; padding:4px 11px; text-decoration:none; line-height:1.5;
  transition:border-color var(--dur-1) ease, color var(--dur-1) ease, background var(--dur-1) ease;
}
a.tag:hover{border-color:var(--primary); color:var(--primary-ink); background:var(--tint-2); text-decoration:none}
.tag.on{background:var(--tint); color:var(--primary-ink); border-color:var(--tint-3)}
.tag.warn{background:color-mix(in srgb,#d98324 12%,transparent); color:#a4611a; border-color:color-mix(in srgb,#d98324 30%,transparent)}
.tag.bad{background:color-mix(in srgb,#d94a3d 12%,transparent); color:#b3372c; border-color:color-mix(in srgb,#d94a3d 30%,transparent)}
@media (prefers-color-scheme:dark){
  .tag.warn{color:#f0b46a} .tag.bad{color:#ff9d92}
}

/* ---------- ficha de detalle (dos columnas) ---------- */
.detail{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,320px);
  gap:clamp(30px,4.4vw,60px); align-items:start;
  padding:clamp(32px,4vw,52px) 0 clamp(56px,6.5vw,88px);
}
@media (max-width:900px){ .detail{grid-template-columns:1fr} .detail .aside{order:-1} }
.detail h1{font-size:clamp(1.8rem,1.35rem + 1.9vw,2.85rem); margin-bottom:12px}
.detail .lede{margin-bottom:var(--s5)}
.detail h2{font-size:1.3rem; margin:40px 0 14px}
.detail h2:first-child{margin-top:0}
.detail h3{font-size:1.05rem; margin:26px 0 9px}
.detail p,.detail li{font-size:var(--t-sm)}
.detail ol,.detail ul{padding-left:21px; margin:0 0 1em}
.detail li{margin-bottom:9px}
.detail li::marker{color:var(--primary)}

/* Columna lateral pegajosa: la animación y los datos rápidos acompañan
   mientras se lee la técnica. Se despega en móvil. */
.detail .aside{position:sticky; top:calc(var(--head-h) + 22px); display:grid; gap:16px}
@media (max-width:900px){ .detail .aside{position:static} }

/* Animación del ejercicio. Cuadrada porque así vienen las de la app. */
.exanim{border-radius:var(--radius); overflow:hidden; margin:0}
.exanim img{width:100%; object-fit:cover; background:var(--surface-2)}
/* La proporción cuadrada se declara con `:not(.fit)` y no en `.exanim img` a secas.
   Parece un rodeo y es justo lo contrario: es lo que permite que la variante `.fit` NO
   declare `aspect-ratio` en absoluto.
   ------------------------------------------------------------------
   El motivo, comprobado en el navegador y no deducido: cuando una <img> trae `width` y
   `height`, el navegador le asigna `aspect-ratio: auto 900 / 600` — «usa la proporción
   natural y, mientras no la tengas, la de los atributos». Esa segunda mitad es la que
   reserva el hueco durante la descarga. Y `aspect-ratio:auto` escrito en la hoja de
   estilos NO es equivalente: sustituye la declaración entera y se lleva por delante el
   `900/600`, con lo que la imagen se queda en alto 0 hasta que carga.
   Medido en la ficha del atlas: caja de 398×0 con `aspect-ratio:auto`, y 400×267 sin
   declarar nada. Era el salto de maquetación de las 1.112 fichas, y no lo arreglaban los
   atributos por sí solos: hacía falta además dejar de pisarlos desde aquí.
   Y `revert` tampoco sirve — la proporción de los atributos no es una regla de la hoja
   del navegador que se pueda restaurar, sino el tamaño natural del objeto. La única
   forma de conservarla es no declarar nada encima. */
.exanim:not(.fit) img{aspect-ratio:1/1}
.exanim figcaption{font-size:var(--t-xs); color:var(--muted); padding:11px 15px; border-top:1px solid var(--border)}
/* Sin recortar: una animación de ejercicio es cuadrada y se puede
   encajar, pero un ECG de 12 derivaciones o una radiografía de tórax
   son apaisados y `object-fit:cover` les corta justo el borde donde
   está el hallazgo. Aquí la imagen manda sobre la caja. */
.exanim.fit img{object-fit:contain; height:auto}

/* Lista de datos rápidos: etiqueta a la izquierda, valor a la derecha. */
.facts{border-radius:var(--radius); padding:8px 20px; margin:0}
.facts div{display:flex; gap:14px; justify-content:space-between; align-items:baseline; padding:12px 0; border-bottom:1px solid var(--border)}
.facts div:last-child{border-bottom:none}
.facts dt{font-size:var(--t-xs); color:var(--muted); font-weight:600; flex:none}
.facts dd{margin:0; font-size:var(--t-sm); color:var(--ink); font-weight:560; text-align:right}

/* Cues y errores: dos listas con el mismo peso visual y signo opuesto. */
.cues,.errs{list-style:none; padding:0; margin:0; display:grid; gap:11px}
.cues li,.errs li{display:flex; gap:11px; align-items:flex-start; font-size:var(--t-sm)}
.cues li::before,.errs li::before{
  content:""; flex:none; width:19px; height:19px; margin-top:2px; border-radius:50%;
  background-repeat:no-repeat; background-position:center; background-size:13px;
}
.cues li::before{
  background-color:var(--tint);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234f8f06' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 12.5 10 17.5 19 6.5'/%3E%3C/svg%3E");
}
.errs li::before{
  background-color:color-mix(in srgb,#d94a3d 13%,transparent);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23c0392b' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M7 7l10 10M17 7L7 17'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme:dark){
  .cues li::before{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a5ed45' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 12.5 10 17.5 19 6.5'/%3E%3C/svg%3E")}
  .errs li::before{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ff9d92' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M7 7l10 10M17 7L7 17'/%3E%3C/svg%3E")}
}

/* ---------- veredicto de un mito ---------- */
.verdict{
  display:flex; align-items:center; gap:14px; border-radius:var(--radius);
  padding:19px 23px; margin-bottom:var(--s5); border:1px solid; font-weight:650;
  background:color-mix(in srgb,#d94a3d 9%,transparent); border-color:color-mix(in srgb,#d94a3d 28%,transparent); color:#b3372c;
}
.verdict b{font-family:var(--display); font-size:1.4rem; font-weight:600; letter-spacing:.03em; text-transform:uppercase; color:inherit}
.verdict.ok{background:var(--tint); border-color:var(--tint-3); color:var(--primary-ink)}
.verdict.mid{background:color-mix(in srgb,#d98324 11%,transparent); border-color:color-mix(in srgb,#d98324 30%,transparent); color:#a4611a}
@media (prefers-color-scheme:dark){ .verdict{color:#ff9d92} .verdict.mid{color:#f0b46a} }

/* Barra de nivel de evidencia: cinco tramos, los llenos en color de marca. */
.eviscale{display:flex; gap:4px; align-items:center}
.eviscale i{width:20px; height:7px; border-radius:99px; background:var(--border)}
.eviscale i.on{background:var(--primary)}

/* ---------- glosario ----------
   187 términos en una sola página con anclas, y no 187 páginas de dos
   líneas: una definición suelta no sostiene una página propia, y Google
   trata un montón de páginas casi vacías como contenido de relleno. */
.gloss{display:grid; gap:12px}
.gloss article{border-radius:var(--radius-sm); padding:20px 22px; scroll-margin-top:calc(var(--head-h) + 84px)}
.gloss article > *{position:relative; z-index:1}
.gloss h3{font-size:1.05rem; margin-bottom:7px; display:flex; align-items:baseline; gap:10px; flex-wrap:wrap}
.gloss h3 .tag{font-weight:600}
.gloss p{font-size:var(--t-sm); margin:0}
.gloss .syn{font-size:var(--t-xs); color:var(--muted); margin-top:9px!important}
/* Índice alfabético que salta a cada letra. */
.alpha{display:flex; flex-wrap:wrap; gap:5px; margin-bottom:var(--s5); justify-content:center}
.alpha a{
  display:grid; place-items:center; min-width:32px; height:32px; padding:0 7px;
  border-radius:9px; border:1px solid var(--border); background:var(--surface);
  font-size:var(--t-sm); font-weight:650; color:var(--text); text-decoration:none;
  transition:border-color var(--dur-1) ease, color var(--dur-1) ease, transform var(--dur-1) var(--ease-out);
}
.alpha a:hover{border-color:var(--primary); color:var(--primary-ink); text-decoration:none; transform:translateY(-2px)}
.gloss-letter{
  font-family:var(--display); font-size:1.55rem; font-weight:600; color:var(--primary-ink);
  margin:34px 0 12px; scroll-margin-top:calc(var(--head-h) + 84px); letter-spacing:.04em;
}
.gloss-letter:first-child{margin-top:0}

/* ---------- calculadoras ---------- */
.calc{border-radius:var(--radius); padding:clamp(24px,3vw,36px); box-shadow:var(--shadow)}
.calc > *{position:relative; z-index:1}
.calc-fields{display:grid; grid-template-columns:repeat(auto-fit,minmax(158px,1fr)); gap:16px}
.calc label{display:block; font-size:var(--t-xs); font-weight:660; color:var(--ink); margin-bottom:6px}
.calc input,.calc select{
  width:100%; font:inherit; font-size:.95rem; padding:12px 13px; border-radius:var(--radius-xs);
  border:1.5px solid var(--border); background:var(--bg); color:var(--ink);
  transition:border-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
}
.calc input:focus-visible,.calc select:focus-visible{border-color:var(--primary); outline:none; box-shadow:0 0 0 3px var(--tint)}
.calc .hint{font-size:var(--t-xs); color:var(--muted); margin:6px 0 0}
/* Grupo de botones tipo radio (sexo, objetivo, actividad). */
.seg{display:flex; flex-wrap:wrap; gap:6px}
.seg input{position:absolute; opacity:0; width:0; height:0}
.seg label{
  margin:0; cursor:pointer; font-size:var(--t-sm); font-weight:600; padding:10px 16px;
  border-radius:99px; border:1.5px solid var(--border); background:var(--bg); color:var(--text);
  transition:background var(--dur-1) ease, border-color var(--dur-1) ease, color var(--dur-1) ease;
}
.seg label:hover{border-color:var(--edge)}
.seg input:checked + label{background:var(--tint); border-color:var(--primary); color:var(--primary-ink)}
.seg input:focus-visible + label{outline:2.5px solid var(--primary); outline-offset:2px}

.calc-out{margin-top:var(--s5); padding-top:var(--s5); border-top:1px solid var(--border)}
.calc-res{display:grid; grid-template-columns:repeat(auto-fit,minmax(132px,1fr)); gap:13px}
.calc-res div{background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-sm); padding:18px; text-align:center}
.calc-res b{
  display:block; font-family:var(--mono); font-size:clamp(1.4rem,1.15rem + .85vw,1.9rem);
  font-weight:700; color:var(--ink); letter-spacing:-.02em; line-height:1.1; font-variant-numeric:tabular-nums;
}
.calc-res span{display:block; font-size:var(--t-xs); color:var(--muted); margin-top:6px; line-height:1.4}
.calc-res .hi{background:var(--tint); border-color:var(--tint-3)}
.calc-res .hi b{color:var(--primary-ink)}
/* Sin JavaScript una calculadora no calcula. Así que el formulario nace
   oculto y en su lugar se ve un aviso; `.js` invierte las dos cosas.
   Nunca se enseña un formulario muerto que parece roto. */
.calc-live{display:none}
.js .calc-live{display:block}
.js .calc-nojs{display:none}

/* ---------- rejilla de índice de secciones ---------- */
.tiles{display:grid; grid-template-columns:repeat(auto-fit,minmax(252px,1fr)); gap:16px}
.tile{
  display:block; border-radius:var(--radius); padding:var(--s5) var(--s6);
  text-decoration:none; color:inherit;
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) ease, border-color var(--dur-2) ease;
}
.tile > *{position:relative; z-index:1}
.tile:hover{transform:translateY(-4px); box-shadow:var(--shadow); text-decoration:none; border-color:var(--edge)}
.tile .ic{color:var(--primary-ink); margin-bottom:13px; position:relative; z-index:1}
.tile b{display:block; color:var(--ink); font-size:1.06rem; margin-bottom:6px}
.tile span{display:block; font-size:var(--t-sm); color:var(--muted); line-height:1.5}
.tile .n{font-family:var(--display); font-weight:600; color:var(--primary-ink); font-size:.9rem; letter-spacing:.04em}

/* ---------- índice interno de una guía ---------- */
.guide-toc{padding:20px 22px}
.guide-toc h2{font-size:.79rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); margin:0 0 12px; font-weight:740}
.guide-toc ol{margin:0; padding-left:19px; font-size:var(--t-sm)}
.guide-toc li{margin-bottom:8px}
.guide-toc li::marker{color:var(--primary); font-variant-numeric:tabular-nums}
.guide-toc a{color:var(--text); text-decoration:none}
.guide-toc a:hover{color:var(--primary-ink); text-decoration:underline}
/* Cada sección de una guía va separada por una línea fina: son bloques
   largos y sin respiro visual se leen como un muro. */
.guide-sec + .guide-sec{margin-top:36px; padding-top:34px; border-top:1px solid var(--border)}

/* ---------- navegación entre fichas ---------- */
.prevnext{display:grid; grid-template-columns:1fr 1fr; gap:14px; margin-top:48px; padding-top:30px; border-top:1px solid var(--border)}
@media (max-width:600px){ .prevnext{grid-template-columns:1fr} }
.prevnext a{
  display:block; padding:17px 20px; border-radius:var(--radius-sm);
  text-decoration:none; color:inherit;
  transition:border-color var(--dur-2) ease, transform var(--dur-2) var(--ease-out);
}
.prevnext a:hover{border-color:var(--primary); text-decoration:none}
.prevnext a:first-child:hover{transform:translateX(-3px)}
.prevnext .next:hover{transform:translateX(3px)}
.prevnext span{display:block; font-size:var(--t-xs); color:var(--muted); margin-bottom:4px}
.prevnext b{color:var(--ink); font-size:var(--t-sm); font-weight:640}
.prevnext .next{text-align:right}

/* ====================================================================
   23 · MOVIMIENTO AL ENTRAR EN PANTALLA
   --------------------------------------------------------------------
   Solo donde el navegador lo soporta de forma nativa
   (`animation-timeline: view()`). Sin JavaScript, sin
   IntersectionObserver y sin el riesgo clásico de dejar contenido
   invisible para siempre si el script falla: si no hay soporte, no hay
   animación y el contenido se ve, que es lo único que importa.

   El escalonado dentro de una fila se consigue variando el RANGO por
   posición, no con un `animation-delay`: en una animación ligada al
   scroll el retardo no existe (no hay tiempo, hay recorrido), así que
   lo que se retrasa es el tramo en el que cada elemento completa su
   entrada.
   ==================================================================== */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    :where(.stats li,.card,.plat,.steps li,.plan,.split,.log article,.tile,
           .bento > *,.timeline li,.spec div,.pull,.frame,.story-steps > li,
           .ctaband,.numbers li,.table-wrap,.faq details){
      animation:reveal linear both;
      animation-timeline:view();
      animation-range:entry 0% cover 28%;
    }
    :where(.grid,.grid-2,.grid-4,.tiles,.plats,.plans,.stats,.steps,.bento,.spec,.lib-grid) > *:nth-child(2){animation-range:entry 0% cover 34%}
    :where(.grid,.grid-2,.grid-4,.tiles,.plats,.plans,.stats,.steps,.bento,.spec,.lib-grid) > *:nth-child(3){animation-range:entry 0% cover 40%}
    :where(.grid,.grid-2,.grid-4,.tiles,.plats,.plans,.stats,.steps,.bento,.spec,.lib-grid) > *:nth-child(4){animation-range:entry 0% cover 46%}
    @keyframes reveal{from{opacity:0; transform:translateY(18px)} to{opacity:1; transform:none}}

    /* Los encabezados de sección entran igual, pero sin desplazamiento
       lateral y un poco antes: si el titular aparece a la vez que las
       tarjetas, la sección entera parpadea. */
    .section-head{animation:reveal-soft linear both; animation-timeline:view(); animation-range:entry 0% cover 22%}
    @keyframes reveal-soft{from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:none}}
  }
}

/* ====================================================================
   24 · PREFERENCIAS Y MEDIOS
   ==================================================================== */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms!important; animation-iteration-count:1!important;
    transition-duration:.001ms!important; scroll-behavior:auto!important;
  }
  /* La marquesina no se puede «acelerar»: se para y se deja como una
     lista normal que se puede desplazar a mano. */
  .marquee-track{animation:none!important; transform:none!important}
  .marquee{overflow-x:auto}
}

/* Modo de contraste forzado de Windows. El sistema sustituye los
   colores, así que lo único que hay que hacer es devolver los bordes
   que se pierden y asegurar que lo dibujado con `currentColor`
   (iconos, máscaras) sigue el color del sistema. */
@media (forced-colors:active){
  .card,.plat,.plan,.tile,.stats li,.bento > *,.lib-card,.faq details,.table-wrap{border:1px solid CanvasText}
  .btn{border:1px solid ButtonText}
  .brand-mark,.brand-word,.hero-mark{background:CanvasText}
  .head-progress,.rail-bar::after,.rank-f{background:Highlight}
  .hero::before,.hero::after,.mesh::after,.grainy::before,.ctaband::before{display:none}
}

/* ---------- impresión ---------- */
@media print{
  .site-head,.site-foot,.cta,.skip,.trustbar,.lib-bar,.marquee,.ctaband,.head-progress,.foot-word{display:none!important}
  body{background:#fff; color:#000}
  .card,.plan,.plat,.table-wrap,.bento > *{box-shadow:none; border-color:#ccc}
  .band > img{position:static; height:auto}
  .band::before{display:none}
  .band h2,.band p{color:#000}
  a[href^="http"]::after{content:" (" attr(href) ")"; font-size:.75em; color:#555}

  @page{margin:14mm 12mm}

  /* --- Panel: en papel se imprime la pestaña que se esté viendo ---
     El documento de verdad ya no vive en la página: se descarga (PDF o HTML
     autocontenido) y lo dibuja el MISMO motor que el informe de la app, con su
     propia hoja de estilos dentro. Aquí solo se quita el cromo del sitio para
     que quien pulse Ctrl+P se lleve la pestaña limpia, sin cabecera ni pie.
     Antes esto ocultaba el panel entero para imprimir un informe incrustado que
     ya no existe — y habría impreso un folio en blanco. */
  .site-head,.site-foot,.demo-bar,.panel-loading,.panel-actions{display:none!important}
  .panel-app{padding:0}
  /* Las gráficas van sin sus mandos: en papel no se puede pulsar «90 d», y una
     fila de botones impresa solo gasta tinta. */
  .chart-ctl,.ch-tip{display:none!important}
  .pcard,.ptab-body > *{break-inside:avoid}
}

/* ====================================================================
   25 · UTILIDADES DE ESPACIADO Y TIPOGRAFÍA
   --------------------------------------------------------------------
   Existen por una razón de seguridad, no de estilo. La CSP de los tres
   sitios lleva `style-src 'self'` SIN `'unsafe-inline'`, y esa
   directiva no solo bloquea `<style>`: también bloquea los atributos
   `style="…"` sueltos (en CSP 3, `style-src-attr` hereda de
   `style-src`).

   O sea que cada `style="margin-top:30px"` que quedaba en las páginas
   se descartaba en producción: el sitio se veía con el espaciado roto y
   en local, sin cabeceras, no se notaba.

   La alternativa era añadir `'unsafe-inline'` y perder la protección
   entera. Estas clases son la otra: el espaciado vuelve a aplicarse y
   la política se queda igual de cerrada.

   No es un sistema de utilidades al estilo Tailwind ni pretende serlo:
   son exactamente los valores que las páginas ya usaban.
   ==================================================================== */

.mt-18 { margin-top: 18px }
.mt-20 { margin-top: 20px }
.mt-22 { margin-top: 22px }
.mt-24 { margin-top: 24px }
.mt-26 { margin-top: 26px }
.mt-28 { margin-top: 28px }
.mt-30 { margin-top: 30px }
.mt-32 { margin-top: 32px }
.mt-34 { margin-top: 34px }
.mt-36 { margin-top: 36px }
.mt-44 { margin-top: 44px }
.mt-56 { margin-top: 56px }
.mb-24 { margin-bottom: 24px }
.mb-44 { margin-bottom: 44px }
.my-22 { margin-top: 22px; margin-bottom: 22px }
/* El resumen destacado de los documentos legales: párrafos muy juntos. */
.my-tight { margin: .2em 0 }

/* Bloques centrados con ancho de lectura. El `margin:Npx auto 0`
   original se parte en dos: la clase pone el centrado y el margen
   superior lo elige la utilidad `.mt-*` correspondiente. */
.block-600 { max-width: 600px; margin-left: auto; margin-right: auto }
.block-820 { max-width: 820px; margin-left: auto; margin-right: auto }
.block-840 { max-width: 840px; margin-left: auto; margin-right: auto }
/* Entradilla suelta bajo un titular: centrada, con aire arriba y abajo. */
.lede-block { max-width: 600px; margin: 16px auto 28px }
.lede-block-narrow { margin: 18px auto 26px }

.fs-xs { font-size: var(--t-xs) }
.fs-sm { font-size: var(--t-sm) }
.fs-105 { font-size: 1.05rem }
.fs-135 { font-size: 1.35em }
/* `.card-note` va en cursiva por defecto; esta lo devuelve a redonda
   cuando el contenido es una cita bibliográfica y la cursiva estorba. */
.fst-normal { font-style: normal }

/* ====================================================================
   26 · MOVIMIENTO v3
   --------------------------------------------------------------------
   Todo lo que hay de aquí abajo obedece las mismas dos reglas que el
   resto del archivo, y conviene repetirlas porque aquí es donde más
   tienta saltárselas:

     · `script-src 'none'`: no hay JavaScript. Ni un
       IntersectionObserver, ni un `requestAnimationFrame`, ni una
       librería de scroll. Lo que no sepa hacer el navegador solo, no se
       hace.
     · `style-src 'self'` sin `'unsafe-inline'`: tampoco hay atributos
       `style="--x:3"`. Por eso los mandos que en otro sitio serían una
       variable en línea (cuántas láminas tiene un escaparate) aquí son
       clases contadas: `.showcase-3`, `.showcase-4`…

   La consecuencia práctica es que TODO lo de abajo está envuelto en
   `@supports` y en `prefers-reduced-motion:no-preference`. Donde el
   navegador no llega, no hay animación y el contenido se ve. Un
   revelado que se queda a medias y deja una sección en blanco para
   siempre es infinitamente peor que no tener revelado.
   ==================================================================== */

/* ==================================================================
   26.1 · PROPIEDADES REGISTRADAS
   ------------------------------------------------------------------
   Una variable CSS normal no se puede animar: el navegador no sabe si
   `20%` es un número, un color o una cadena, así que salta del valor
   inicial al final sin pasar por el medio. `@property` le dice el tipo
   y a partir de ahí la interpola.

   Es lo que hace posible casi todo lo de abajo —el barrido del titular,
   el degradado que gira, la cortina de las imágenes— sin una sola línea
   de JavaScript.
   ================================================================== */
@property --sweep      { syntax:'<percentage>'; inherits:false; initial-value:130% }
@property --curtain    { syntax:'<percentage>'; inherits:false; initial-value:0% }
@property --grad-angle { syntax:'<angle>';      inherits:false; initial-value:102deg }
@property --shine      { syntax:'<percentage>'; inherits:false; initial-value:-40% }

/* ==================================================================
   26.2 · TRANSICIONES ENTRE PÁGINAS
   ------------------------------------------------------------------
   `@view-transition` con navegación automática: al pasar de una página
   a otra del mismo origen, el navegador hace un fundido cruzado del
   documento entero en vez del parpadeo en blanco de siempre. Dos
   líneas, cero JavaScript, y es lo que separa un sitio estático de algo
   que se siente como una aplicación.

   Importa especialmente aquí: entre las tres webs hay ~4.600 páginas y
   la navegación por la biblioteca —síntoma, ficha, síntoma— es un ir y
   venir constante. Cada salto era un fogonazo en blanco.

   Los elementos con `view-transition-name` quedan FUERA del fundido y
   se animan por su cuenta. La cabecera y el pie llevan nombre porque
   son idénticos en las dos páginas: sin nombre se desvanecerían y
   volverían a aparecer, que es justo lo que delata que ha habido una
   recarga.
   ================================================================== */
@view-transition{ navigation:auto }

@media (prefers-reduced-motion:no-preference){
  ::view-transition-group(root){ animation-duration:.34s; animation-timing-function:var(--ease-out) }
  /* El contenido nuevo entra subiendo un pelo; el viejo solo se apaga.
     Si los dos se mueven, el cruce se ve sucio. */
  ::view-transition-new(root){ animation-name:vt-in }
  ::view-transition-old(root){ animation-name:vt-out }
  @keyframes vt-in { from{ opacity:0; transform:translateY(10px) } }
  @keyframes vt-out{ to  { opacity:0 } }
}
@media (prefers-reduced-motion:reduce){
  /* No se desactiva la transición: se deja instantánea. Quitarla del todo
     devuelve el parpadeo en blanco, que para alguien sensible al
     movimiento es peor que un corte limpio. */
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){ animation:none!important }
}
.site-head{ view-transition-name:head }
.site-foot{ view-transition-name:foot }

/* ==================================================================
   26.3 · REVELADO DEL TITULAR
   ------------------------------------------------------------------
   Dos mecanismos distintos, y no es redundancia: uno es para lo que
   está en la primera pantalla y el otro para lo que hay que ir a
   buscar.

   a) PORTADA — palabra a palabra, al cargar. La primera pantalla es la
      única que nadie desplaza, así que su animación no puede depender
      del scroll. Cada palabra sube desenfocada y se enfoca al aterrizar.
      El desenfoque es lo que lo separa de un fundido de plantilla:
      imita el enfoque de una cámara y el ojo lo lee como profundidad.

   b) INTERIOR — barrido con máscara, al entrar en pantalla. Una franja
      de luz cruza el titular de izquierda a derecha y lo va
      descubriendo. No necesita ni una etiqueta extra en el HTML, así
      que sirve para los cuarenta titulares de sección de una página sin
      engordarla.
   ================================================================== */

/* --- a) palabra a palabra --- */
.w{ display:inline-block }
@media (prefers-reduced-motion:no-preference){
  .h-words .w{ animation:word-in .78s var(--ease-out) both }
  @keyframes word-in{
    from{ opacity:0; transform:translateY(.42em) rotate(1.2deg); filter:blur(7px) }
    to  { opacity:1; transform:none;                             filter:blur(0) }
  }
  /* Escalonado por CLASE NUMERADA y no por `nth-child`, y esto no es manía:
     `nth-child` cuenta entre HERMANOS, así que en cuanto una parte del
     titular va dentro de otra etiqueta —el trozo con degradado de la
     portada, un `<em>`— la cuenta se reinicia dentro y el orden se
     rompe. Con la clase puesta por el generador el número es global y da
     igual cómo esté anidado el titular.

     El paso es de 42 ms: por debajo de 30 no se percibe el orden y por
     encima de 60 la frase tarda en poder leerse. Catorce palabras cubren
     cualquier titular de este sitio; de ahí en adelante todas entran con
     el último retardo, que a esa altura ya no se distingue. */
  .h-words .w-1 { animation-delay:.050s } .h-words .w-2 { animation-delay:.092s }
  .h-words .w-3 { animation-delay:.134s } .h-words .w-4 { animation-delay:.176s }
  .h-words .w-5 { animation-delay:.218s } .h-words .w-6 { animation-delay:.260s }
  .h-words .w-7 { animation-delay:.302s } .h-words .w-8 { animation-delay:.344s }
  .h-words .w-9 { animation-delay:.386s } .h-words .w-10{ animation-delay:.428s }
  .h-words .w-11{ animation-delay:.470s } .h-words .w-12{ animation-delay:.512s }
  .h-words .w-13{ animation-delay:.554s } .h-words .w-n { animation-delay:.596s }

  /* --- c) barrido por TIEMPO, para la portada ---
     Mismo efecto que `.h-sweep` pero sin depender del scroll, porque la
     primera pantalla es la única que nadie desplaza. Existe porque el
     revelado palabra a palabra necesita partir el titular en etiquetas, y
     eso NO se puede hacer cuando el titular ya lleva un trozo con
     degradado: `background-clip:text` recorta el fondo del elemento a SU
     texto, y si los hijos se mueven por separado el recorte deja de
     seguirlos y el degradado se parte.

     Regla, entonces: titular de portada sin marcado dentro → `.h-words`;
     con marcado → `.h-wipe`. `web/components.mjs` lo decide solo. */
  .h-wipe{
    --sweep:130%;
    -webkit-mask-image:linear-gradient(96deg, #000 var(--sweep), transparent calc(var(--sweep) + 20%));
    mask-image:linear-gradient(96deg, #000 var(--sweep), transparent calc(var(--sweep) + 20%));
    padding-bottom:.08em;
    animation:h-wipe 1.15s var(--ease-out) .08s both;
  }
  @keyframes h-wipe{ from{ --sweep:-20% } to{ --sweep:130% } }
  :where(.hero > .wrap, .hero-copy) > h1.h-wipe{ animation:h-wipe 1.15s var(--ease-out) .08s both }
  /* La portada ya escalonaba a sus hijos directos con `enter`. Si además
     el titular hace lo suyo, entra dos veces y se ve el doble salto. Con
     el titular partido en palabras manda el titular.

     Escrito SIN `:where()` a propósito: la regla que hay que ganar es
     `.hero > .wrap > *`, que pesa (0,2,0). `:where()` vale cero, así que
     `:where(…) > h1.h-words` se queda en (0,1,1) y perdía — el titular
     seguía entrando dos veces y no había forma de ver por qué. */
  .hero > .wrap > h1.h-words,
  .hero-copy > h1.h-words{ animation:none }
}

/* --- b) barrido con máscara --- */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .h-sweep{
      /* El valor en reposo descubre el titular entero. Solo mientras la
         animación está en su tramo vale otra cosa. */
      --sweep:130%;
      -webkit-mask-image:linear-gradient(96deg, #000 var(--sweep), transparent calc(var(--sweep) + 18%));
      mask-image:linear-gradient(96deg, #000 var(--sweep), transparent calc(var(--sweep) + 18%));
      /* La máscara recorta la caja exacta del texto y se come las
         descendentes de la «g» y la «p». */
      padding-bottom:.08em;
      animation:h-sweep linear both;
      animation-timeline:view();
      animation-range:entry 12% cover 32%;
    }
    @keyframes h-sweep{ from{ --sweep:-18% } to{ --sweep:130% } }
  }
}

/* ==================================================================
   26.4 · CORTINA DE IMAGEN
   ------------------------------------------------------------------
   La foto no aparece: se descubre. Un recorte que se abre de abajo
   arriba mientras la imagen, dentro, viene de un 9 % más grande y se
   asienta. Los dos movimientos a la vez son lo que hace que parezca que
   la imagen llega, en vez de que se encienda.
   ================================================================== */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .frame,.reveal-curtain{
      clip-path:inset(0 0 var(--curtain) 0 round var(--radius-xl));
      animation:curtain linear both;
      animation-timeline:view();
      animation-range:entry 0% cover 34%;
    }
    @keyframes curtain{ from{ --curtain:88% } to{ --curtain:0% } }

    .frame img,.reveal-curtain img{
      animation:settle linear both;
      animation-timeline:view();
      animation-range:entry 0% cover 46%;
    }
    @keyframes settle{ from{ transform:scale(1.09) } to{ transform:scale(1) } }
    /* El zoom del `:hover` y el del revelado son la misma propiedad. Una
       vez revelada manda el hover: se le quita la animación y la
       transición recupera el control. */
    .frame:hover img{ animation:none; transform:scale(1.04) }
  }
}

/* ==================================================================
   26.5 · PROFUNDIDAD Y LUZ EN LAS SUPERFICIES
   ------------------------------------------------------------------
   Lo que separa una tarjeta de un rectángulo con borde no es la sombra:
   es que reaccione a la luz. Dos capas, las dos en pseudoelementos para
   no tocar el HTML:

     · elevación con perspectiva — la tarjeta se inclina un grado hacia
       el observador, no solo sube;
     · reflejo especular — una franja diagonal que la cruza.

   Un grado de inclinación parece poco escrito y es exactamente lo que
   se nota. A tres grados ya parece un naipe.
   ================================================================== */
@media (hover:hover) and (prefers-reduced-motion:no-preference){
  /* Selectores explícitos y NO `:where()`: las reglas de `:hover` que ya
     existían arriba (`.card:hover`, `a.plat:hover`…) tienen peso propio, y
     `:where()` vale cero. Envueltas en `:where()` estas líneas se escribían
     y las ganaba la regla de más arriba, así que la inclinación no se veía.
     `.lib-grid.rows .lib-card:hover` se queda fuera a propósito: en la vista
     de filas el desplazamiento es lateral y ahí una inclinación estorba. */
  .card:hover,
  a.plat:hover,
  .tile:hover,
  .bento > *:hover,
  .lib-grid:not(.rows) .lib-card:hover{
    transform:perspective(900px) rotateX(1.1deg) translateY(-5px) scale(1.006);
  }
  /* Reflejo. El pseudoelemento hereda el radio y recorta lo que sobra,
     así que no hace falta `overflow` en la tarjeta.
     Ojo: `.card::before` NO existía antes (la línea de luz vive en
     `::after`), así que esto no pisa nada. */
  :where(.card,.plat,.tile,.bento > *)::before{
    content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
    border-radius:inherit; overflow:hidden;
    background:linear-gradient(
      100deg,
      transparent calc(var(--shine) - 18%),
      color-mix(in srgb, var(--primary) 9%, transparent) var(--shine),
      transparent calc(var(--shine) + 18%));
    opacity:0; transition:opacity var(--dur-2) ease;
  }
  :where(.card,.plat,.tile,.bento > *):hover::before{
    opacity:1; animation:shine .9s var(--ease-out) forwards;
  }
  @keyframes shine{ from{ --shine:-40% } to{ --shine:140% } }
}

/* Botón principal: además del barrido que ya tenía, un halo que respira
   mientras el puntero está encima. Es el detalle que hace que el botón
   parezca encendido y no pintado. */
@media (hover:hover) and (prefers-reduced-motion:no-preference){
  .btn-primary:hover{ animation:btn-glow 2.4s var(--ease-inout) infinite alternate }
  @keyframes btn-glow{
    from{ box-shadow:0 18px 38px -14px var(--primary) }
    to  { box-shadow:0 22px 52px -14px var(--primary), 0 0 0 6px var(--tint-2) }
  }
}

/* ==================================================================
   26.6 · DEGRADADOS VIVOS
   ------------------------------------------------------------------
   El texto destacado del titular deja de ser una imagen fija: el ángulo
   del degradado gira muy despacio —un ciclo cada 18 segundos— y eso
   basta para que la portada no parezca una captura de pantalla.

   Muy despacio a propósito: es un movimiento que se ve al mirarlo, no
   uno que persiga la vista mientras se lee.
   ================================================================== */
@media (prefers-reduced-motion:no-preference){
  .hero .grad,.grad-live{
    background:linear-gradient(var(--grad-angle), var(--primary-ink), var(--accent), var(--primary-ink));
    background-size:200% 100%;
    -webkit-background-clip:text; background-clip:text; color:transparent;
    animation:grad-drift 18s linear infinite;
  }
  @keyframes grad-drift{
    0%  { --grad-angle:96deg;  background-position:0% 50% }
    50% { --grad-angle:112deg; background-position:100% 50% }
    100%{ --grad-angle:96deg;  background-position:0% 50% }
  }
}

/* ==================================================================
   26.7 · KEN BURNS Y PARALAJE DE TEXTO
   ------------------------------------------------------------------
   La banda a sangre ya tenía paralaje en la imagen. Le faltaban las dos
   cosas que hacen que una banda de Nike aguante diez segundos en
   pantalla:

     · la imagen además se ACERCA mientras cruza (Ken Burns), y
     · el texto se mueve MENOS que la imagen, no igual.

   La diferencia de velocidad entre los dos planos es literalmente lo
   que el ojo lee como profundidad. Con los dos a la misma velocidad la
   banda es una postal.
   ================================================================== */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .band > img,.band > video{
      animation:kenburns linear both;
      animation-timeline:view(); animation-range:cover 0% cover 100%;
      height:122%; top:-11%;
    }
    @keyframes kenburns{
      from{ transform:translateY(-4.5%) scale(1.12) }
      to  { transform:translateY(4.5%)  scale(1.02) }
    }
    /* El texto, a un tercio del recorrido y en sentido contrario. */
    .band .wrap{
      animation:band-text linear both;
      animation-timeline:view(); animation-range:cover 0% cover 100%;
    }
    /* La opacidad no baja de .78 en los extremos. Con .55 el texto quedaba
       medio apagado cuando la banda es la ÚLTIMA sección de la página: ahí ya
       no queda scroll, la animación nunca llega a su tramo central y el
       titular se quedaba tenue para siempre. */
    @keyframes band-text{
      from{ transform:translateY(26px);  opacity:.78 }
      35% { opacity:1 }
      to  { transform:translateY(-26px); opacity:.78 }
    }
  }
}

/* ==================================================================
   26.8 · BANDA DE VÍDEO
   ------------------------------------------------------------------
   La misma banda, con vídeo en lugar de foto. La CSP ya lo permite
   (`media-src 'self'`): el archivo se sirve desde el propio dominio,
   así que no hay ni una petición a un tercero — que es la razón por la
   que aquí no se incrusta un YouTube.

   El vídeo va SIEMPRE mudo, en bucle, `playsinline` y con cartel. El
   cartel importa más de lo que parece: es lo que se ve mientras el
   vídeo llega y lo único que se ve si el sistema tiene el ahorro de
   batería activo, que en un móvil es la mitad de las visitas.
   ================================================================== */
.band > video{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:-2;
  /* Es decorado, no contenido. `pointer-events:none` evita que un toque
     en el móvil lo pause y deje la banda congelada a media escena. */
  pointer-events:none;
}
/* Con `prefers-reduced-motion` el vídeo se sustituye por su cartel. Un
   `<video autoplay>` no se puede parar desde CSS, así que se esconde y
   queda la imagen fija que ya viaja con él como fondo de la banda. */
@media (prefers-reduced-motion:reduce){
  .band > video{ display:none }
}

/* ==================================================================
   26.9 · ESCENAS FIJADAS
   ------------------------------------------------------------------
   La sección se queda quieta en pantalla mientras el scroll avanza por
   dentro de ella. Es el recurso con el que Apple cuenta un producto: en
   lugar de tres bloques que pasan, una escena que se transforma.

   El alto de la sección decide cuánto dura la parada, y como no puede
   ir en un `style=` en línea, va en clases contadas.
   ================================================================== */
.scene{ position:relative }
.scene-2{ height:200vh } .scene-3{ height:280vh } .scene-4{ height:360vh }
.scene-stage{
  position:sticky; top:var(--head-h);
  height:calc(100vh - var(--head-h));
  display:grid; place-items:center; overflow:hidden;
}
@supports (animation-timeline:view()){
  @media (min-width:901px) and (prefers-reduced-motion:no-preference){
    .scene{ view-timeline-name:--scene; view-timeline-axis:block }
    .scene-zoom{
      animation:scene-zoom linear both;
      animation-timeline:--scene; animation-range:entry 100% exit 0%;
    }
    @keyframes scene-zoom{
      from{ transform:scale(.86);  opacity:.35; filter:blur(3px) }
      22% { transform:scale(1);    opacity:1;   filter:blur(0) }
      78% { transform:scale(1);    opacity:1;   filter:blur(0) }
      to  { transform:scale(1.06); opacity:.2;  filter:blur(3px) }
    }
  }
}
@media (max-width:900px){
  /* En un teléfono una escena fijada roba la pantalla entera durante
     tres deslizamientos y quien lee no sabe si la página se ha colgado.
     Se deshace y vuelve a ser una sección normal. */
  .scene,.scene-2,.scene-3,.scene-4{ height:auto }
  .scene-stage{ position:static; height:auto; padding-block:var(--s8) }
}

/* ==================================================================
   26.10 · ESCAPARATE HORIZONTAL
   ------------------------------------------------------------------
   Mientras la página baja, la fila avanza de lado. Sirve para enseñar
   ocho piezas —capturas, módulos del atlas, ejercicios— sin gastar ocho
   pantallas de alto ni pedir que alguien arrastre a mano una barra
   horizontal, cosa que con un ratón sin rueda lateral no hace nadie.

   El recorrido depende de cuántas láminas hay, y otra vez: no puede ir
   en línea, así que van clases.
   ================================================================== */
.showcase{ position:relative }
.showcase-3{ height:230vh } .showcase-4{ height:300vh }
.showcase-5{ height:370vh } .showcase-6{ height:440vh }
.showcase-stage{
  position:sticky; top:var(--head-h);
  height:calc(100vh - var(--head-h));
  display:flex; align-items:center; overflow:hidden;
}
.showcase-track{ display:flex; gap:clamp(18px,2.4vw,34px); padding-inline:var(--gut) }
.showcase-track > *{ flex:0 0 auto; width:min(430px,74vw) }

@supports (animation-timeline:view()){
  @media (min-width:901px) and (prefers-reduced-motion:no-preference){
    .showcase{ view-timeline-name:--showcase; view-timeline-axis:block }
    .showcase-track{
      animation:showcase linear both;
      animation-timeline:--showcase; animation-range:entry 100% exit 0%;
    }
    /* Se desplaza su propio ancho menos una ventana: así la última
       lámina queda encuadrada al final y no se pasa de largo. */
    @keyframes showcase{ to{ transform:translateX(calc(-100% + 100vw - var(--gut) * 2)) } }
  }
}
/* Sin soporte o en móvil: carril con arrastre y anclaje, que es lo que
   ya sabe hacer `.rail`. */
@media (max-width:900px){
  .showcase,.showcase-3,.showcase-4,.showcase-5,.showcase-6{ height:auto }
  .showcase-stage{ position:static; height:auto; overflow-x:auto; scroll-snap-type:x mandatory; scrollbar-width:none }
  .showcase-stage::-webkit-scrollbar{ display:none }
  .showcase-track > *{ scroll-snap-align:center }
}
@supports not (animation-timeline:view()){
  .showcase,.showcase-3,.showcase-4,.showcase-5,.showcase-6{ height:auto }
  .showcase-stage{ position:static; height:auto; overflow-x:auto; scroll-snap-type:x mandatory }
  .showcase-track > *{ scroll-snap-align:center }
}

/* ==================================================================
   26.11 · CIFRAS QUE SUBEN
   ------------------------------------------------------------------
   No es un contador: es la cifra entera saliendo de debajo de una
   línea. Un contador de verdad necesitaría JavaScript —y aquí no lo
   hay— o un `counter()` animado, que imprimiría «2434» sin el punto de
   los millares, y eso en español está mal escrito.

   Este efecto da lo mismo que se buscaba —la sensación de que la cifra
   se está calculando— sin mentir sobre el número ni romper la
   ortografía.
   ================================================================== */
/* La cifra no se recorta con `overflow` en su propia caja: el recorte se
   aplica al elemento y luego el `transform` mueve las dos cosas juntas, así
   que no esconde nada. Quien tendría que recortar es el `<li>`, pero el
   número es su PRIMER hijo y bajarlo lo mete encima de la etiqueta, no fuera
   de la caja. Lo que oculta el recorrido es la opacidad, y por eso el
   desplazamiento se queda en media línea: subir una línea entera con el
   número ya visible se ve como un salto, no como un revelado. */
.numbers b,.hero-stats b{ padding-bottom:.06em }
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .numbers li b,.hero-stats li b{
      animation:digits linear both;
      animation-timeline:view(); animation-range:entry 10% cover 34%;
    }
    @keyframes digits{
      from{ transform:translateY(.42em) skewY(2.5deg); opacity:0 }
      to  { transform:none;                            opacity:1 }
    }
    .numbers li:nth-child(2) b{ animation-range:entry 10% cover 40% }
    .numbers li:nth-child(3) b{ animation-range:entry 10% cover 46% }
    .numbers li:nth-child(4) b{ animation-range:entry 10% cover 52% }
  }
}

/* ==================================================================
   26.12 · MARQUESINA DOBLE
   ------------------------------------------------------------------
   Dos carriles a distinta velocidad y en sentido contrario. Con uno
   solo la tira es un adorno; con dos cruzándose, la sección tiene
   textura y aguanta ocupar 200 px de alto.
   ================================================================== */
.marquee-slow .marquee-track{ animation-duration:64s }
.marquee-fast .marquee-track{ animation-duration:28s }
@media (prefers-reduced-motion:no-preference){
  .marquee-rev .marquee-track{ animation-direction:reverse }
}
/* Marquesina de imágenes: la misma mecánica con fotos en vez de texto.
   Sirve para el muro de capturas y para el mosaico del atlas. */
.marquee-img .marquee-item{ padding:0 8px }
/* Dos carriles cruzándose necesitan aire entre ellos o se leen como una rejilla
   de dos filas —que es justo lo contrario del efecto— hasta que arrancan. */
.marquee + .marquee{ margin-top:14px }
.marquee-img img{
  width:clamp(150px,17vw,250px); height:clamp(100px,11.5vw,168px);
  object-fit:cover; border-radius:var(--radius-sm);
  border:1px solid var(--border); box-shadow:var(--shadow-sm);
  transition:transform var(--dur-3) var(--ease-out), box-shadow var(--dur-3) ease;
}
.marquee-img .marquee-item::after{ display:none }
@media (hover:hover){
  .marquee-img img:hover{ transform:scale(1.05); box-shadow:var(--shadow) }
}

/* ==================================================================
   26.13 · MOSAICO EDITORIAL
   ------------------------------------------------------------------
   Cuatro fotos de proporciones distintas encajadas. Es lo que hace
   Gymshark en su portada y lo que evita la fila de tres rectángulos
   iguales, que es la maquetación que grita «plantilla».

   Cada pieza entra con su propio retardo, así que el bloque se compone
   a la vista en vez de aparecer de golpe.
   ================================================================== */
.mosaic{
  display:grid; grid-template-columns:repeat(12,minmax(0,1fr));
  grid-auto-rows:clamp(74px,8.2vw,120px); gap:clamp(10px,1.3vw,18px);
}
.mosaic > *{ margin:0; overflow:hidden; border-radius:var(--radius); position:relative }
.mosaic img{ width:100%; height:100%; object-fit:cover; display:block }
.mosaic .m-a{ grid-column:span 7; grid-row:span 3 }
.mosaic .m-b{ grid-column:span 5; grid-row:span 2 }
.mosaic .m-c{ grid-column:span 5; grid-row:span 2 }
.mosaic .m-d{ grid-column:span 7; grid-row:span 2 }
@media (max-width:760px){
  .mosaic{ grid-template-columns:repeat(6,minmax(0,1fr)) }
  .mosaic .m-a{ grid-column:span 6; grid-row:span 3 }
  .mosaic .m-b,.mosaic .m-c{ grid-column:span 3; grid-row:span 2 }
  .mosaic .m-d{ grid-column:span 6; grid-row:span 2 }
}
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .mosaic > *{ animation:mosaic-in linear both; animation-timeline:view(); animation-range:entry 0% cover 32% }
    .mosaic .m-b{ animation-range:entry 0% cover 38% }
    .mosaic .m-c{ animation-range:entry 0% cover 44% }
    .mosaic .m-d{ animation-range:entry 0% cover 50% }
    @keyframes mosaic-in{ from{ opacity:0; transform:translateY(30px) scale(.965) } to{ opacity:1; transform:none } }
    .mosaic img{ animation:settle linear both; animation-timeline:view(); animation-range:entry 0% cover 60% }
  }
}

/* ==================================================================
   26.14 · DESENFOQUE PROGRESIVO BAJO LA CABECERA
   ------------------------------------------------------------------
   El cristal de la cabecera corta en seco: por encima hay desenfoque y
   por debajo no, y esa línea recta se ve. Una segunda capa que se
   desvanece hacia abajo con una máscara reparte el corte en 38 px y
   deja de notarse.

   Solo de 941 px para arriba: por debajo la cabecera renuncia al
   cristal a propósito (ver el bloque de la sección 5), y una capa a
   pantalla completa desenfocando es justo lo que peor lleva un móvil.
   ================================================================== */
@media (min-width:941px){
  .site-head::after{
    content:""; position:absolute; left:0; right:0; top:100%; height:38px;
    pointer-events:none; z-index:-1;
    backdrop-filter:blur(7px); -webkit-backdrop-filter:blur(7px);
    -webkit-mask-image:linear-gradient(#000, transparent);
    mask-image:linear-gradient(#000, transparent);
  }
}

/* ==================================================================
   26.15 · MICROINTERACCIÓN
   ================================================================== */

/* Enlaces de navegación: el subrayado ya no aparece solo en el activo,
   también crece bajo el que se señala. */
@media (hover:hover) and (prefers-reduced-motion:no-preference){
  .site-nav a::after{
    content:""; position:absolute; left:13px; right:13px; bottom:3px; height:2px;
    border-radius:2px; background:var(--primary);
    transform:scaleX(0); transform-origin:50% 50%;
    transition:transform var(--dur-2) var(--ease-out);
  }
  .site-nav a:hover::after{ transform:scaleX(1) }
  .site-nav a[aria-current="page"]::after{ transform:scaleX(1) }
}

/* Las preguntas frecuentes se despliegan con altura animada de verdad.
   `interpolate-size` es lo que permite interpolar hasta `auto`; sin él
   el navegador salta, y no hay forma de saber la altura sin JavaScript. */
@supports (interpolate-size:allow-keywords){
  :root{ interpolate-size:allow-keywords }
  @media (prefers-reduced-motion:no-preference){
    .faq details::details-content{
      block-size:0; overflow:clip;
      transition:block-size var(--dur-3) var(--ease-out), content-visibility var(--dur-3) allow-discrete;
    }
    .faq details[open]::details-content{ block-size:auto }
  }
}

/* Foco visible con anillo animado: quien navega con teclado merece la
   misma calidad de interacción que quien usa el ratón. */
@media (prefers-reduced-motion:no-preference){
  :focus-visible{ transition:outline-offset var(--dur-1) var(--ease-out) }
  a:focus-visible,button:focus-visible,summary:focus-visible{ outline-offset:4px }
}

/* ==================================================================
   26.16 · CINTURÓN DE SEGURIDAD DEL MOVIMIENTO
   ------------------------------------------------------------------
   El bloque de `prefers-reduced-motion:reduce` de la sección 24 recorta
   las duraciones a 1 ms, y eso basta para las animaciones de tiempo.
   NO basta para las ligadas al scroll: en una animación con
   `animation-timeline` la duración se ignora —no hay tiempo, hay
   recorrido—, así que una regla de duración no la para.

   De ahí que cada bloque de arriba lleve su propio
   `prefers-reduced-motion:no-preference`. Esto es el cinturón por si
   alguna se escapa: se apaga la línea de tiempo y la animación deja de
   existir, dejando el elemento en su estado normal.
   ================================================================== */
@media (prefers-reduced-motion:reduce){
  *{ animation-timeline:auto!important; view-timeline-name:none!important }
  .h-sweep{ -webkit-mask-image:none!important; mask-image:none!important }
  .frame,.reveal-curtain{ clip-path:none!important }
  .scene-stage,.showcase-stage{ position:static!important; height:auto!important }
  .scene,.showcase{ height:auto!important }
}

/* Fila de redes del pie. Solo aparece si la marca declara `site.social`
   (ver web/site.config.mjs); vacía no pinta nada. */
.foot-social{display:flex; gap:6px; list-style:none; margin:clamp(30px,3.8vw,44px) 0 0; padding:0}
.foot-social a{
  display:grid; place-items:center; width:40px; height:40px; border-radius:12px;
  color:var(--muted); border:1px solid var(--border); background:var(--surface);
  transition:color var(--dur-2) ease, border-color var(--dur-2) ease, transform var(--dur-2) var(--ease-out);
}
.foot-social a:hover{color:var(--primary-ink); border-color:var(--edge); transform:translateY(-2px)}
.foot-social .ic{width:19px; height:19px}


/* ==================================================================
   27 · REDISEÑO DEL ECOSISTEMA 2026
   ------------------------------------------------------------------
   Portadas inspiradas en los modelos aprobados: mensaje de producto a
   la izquierda, producto real a la derecha y una banda de capacidades
   que permite entender el sistema antes del primer scroll largo.
   ================================================================== */
.hero-kicker{
  display:inline-flex; align-items:center; gap:9px; width:max-content;
  margin:0 0 var(--s4); padding:8px 12px; border:1px solid color-mix(in srgb,var(--primary) 40%,var(--border));
  border-radius:999px; color:var(--primary-ink); background:color-mix(in srgb,var(--primary) 8%,var(--surface));
  font-family:var(--display); font-size:.76rem; font-weight:700; line-height:1;
  letter-spacing:var(--track-display); text-transform:var(--case-display);
}
.hero-kicker::before{content:""; width:7px; height:7px; border-radius:50%; background:var(--primary); box-shadow:0 0 16px var(--primary)}
.hero-split .hero-kicker{margin-inline:0}

.device-stage{
  position:relative; width:min(100%,620px); min-height:560px; display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr)); align-items:center; isolation:isolate;
  padding:24px 0 38px;
}
.device-stage::before{
  content:""; position:absolute; inset:5% -8% 0; z-index:-2; border-radius:50%;
  background:radial-gradient(circle at 52% 48%,color-mix(in srgb,var(--primary) 24%,transparent),transparent 58%);
  filter:blur(12px); opacity:.9;
}
.device-stage::after{
  content:""; position:absolute; left:2%; right:2%; bottom:2%; height:22%; z-index:-1;
  background:radial-gradient(ellipse,color-mix(in srgb,var(--primary) 22%,transparent),transparent 68%);
  filter:blur(18px); transform:perspective(420px) rotateX(66deg);
}
.device-frame{margin:0; position:relative; width:100%; z-index:1; transform:translateX(18%) rotate(-3deg) scale(.86); transform-origin:center bottom}
.device-frame:nth-child(2){z-index:3; transform:translateY(-10px) scale(1.07)}
.device-frame.is-primary{z-index:3}
.device-frame:nth-child(3){z-index:2; transform:translateX(-18%) rotate(3deg) scale(.86)}
.device-frame .phone{width:100%; border-width:5px; border-radius:28px; box-shadow:0 28px 72px rgba(0,0,0,.28)}
.device-frame .phone img{border-radius:22px}
.device-frame figcaption{
  position:absolute; left:50%; bottom:-31px; transform:translateX(-50%); width:max-content; max-width:150%;
  padding:7px 11px; border:1px solid var(--border); border-radius:999px; background:color-mix(in srgb,var(--surface) 88%,transparent);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); color:var(--text); font-size:.72rem; font-weight:650;
  box-shadow:var(--shadow-sm);
}
@media (hover:hover){
  .device-frame{transition:transform var(--dur-3) var(--ease-out),filter var(--dur-3) ease}
  .device-stage:hover .device-frame:not(:hover){filter:saturate(.72) brightness(.88)}
  .device-frame:first-child:hover{transform:translateX(12%) rotate(-1deg) scale(.91)}
  .device-frame:nth-child(2):hover{transform:translateY(-18px) scale(1.1)}
  .device-frame:nth-child(3):hover{transform:translateX(-12%) rotate(1deg) scale(.91)}
}

.feature-strip{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(170px,1fr)); list-style:none;
  margin:0; padding:0; overflow:hidden; border:1px solid var(--border); border-radius:var(--radius-xl);
  background:linear-gradient(135deg,color-mix(in srgb,var(--surface) 96%,var(--primary)),var(--surface));
  box-shadow:var(--shadow);
}
.feature-strip li{display:flex; align-items:flex-start; gap:12px; min-height:108px; padding:22px 20px; border-left:1px solid var(--border)}
.feature-strip li:first-child{border-left:0}
.feature-strip-icon{
  flex:0 0 38px; height:38px; display:grid; place-items:center; border-radius:12px;
  color:var(--primary-ink); background:color-mix(in srgb,var(--primary) 12%,var(--surface));
}
.feature-strip-icon .ic{width:20px;height:20px}
.feature-strip b{display:block; color:var(--ink); font-size:.9rem; line-height:1.25}
.feature-strip small{display:block; margin-top:5px; color:var(--muted); font-size:.76rem; line-height:1.45}

.ecosystem-stage{
  position:relative; width:min(100%,610px); aspect-ratio:1/1; display:grid; place-items:center; isolation:isolate;
}
.ecosystem-orbit{position:absolute; inset:10%; border:1px solid color-mix(in srgb,var(--primary) 28%,var(--border)); border-radius:50%; box-shadow:inset 0 0 90px color-mix(in srgb,var(--primary) 8%,transparent)}
.ecosystem-orbit::before,.ecosystem-orbit::after{content:"";position:absolute;border:1px solid color-mix(in srgb,var(--primary) 20%,transparent);border-radius:50%}
.ecosystem-orbit::before{inset:13%;transform:rotate(32deg)}
.ecosystem-orbit::after{inset:28%;transform:rotate(-20deg)}
.ecosystem-core{
  width:42%; aspect-ratio:1; display:grid; place-items:center; border-radius:30%;
  color:var(--ink); background:linear-gradient(145deg,var(--surface),var(--surface-2));
  border:1px solid var(--edge); box-shadow:0 34px 90px color-mix(in srgb,var(--primary) 17%,transparent),inset 0 1px #fff4;
  transform:rotate(45deg);
}
.ecosystem-core .hero-mark{display:block; width:62%; height:62%; margin:0; transform:rotate(-45deg)}
.ecosystem-chip{
  position:absolute; display:flex; align-items:center; gap:9px; max-width:190px; padding:11px 13px;
  color:var(--text); background:color-mix(in srgb,var(--surface) 86%,transparent); border:1px solid var(--border);
  border-radius:15px; backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px); box-shadow:var(--shadow);
  font-size:.75rem; line-height:1.35;
}
.ecosystem-chip .ic{flex:0 0 21px;width:21px;height:21px;color:var(--primary-ink)}
.ecosystem-chip b{display:block;color:var(--ink);font-size:.8rem}
.ecosystem-chip:nth-of-type(1){top:12%;right:0}.ecosystem-chip:nth-of-type(2){left:0;top:44%}.ecosystem-chip:nth-of-type(3){right:2%;bottom:10%}

@media (max-width:900px){
  .hero-split .hero-kicker{margin-inline:auto}
  .device-stage{min-height:0; width:min(94vw,610px); margin-inline:auto}
  .ecosystem-stage{width:min(90vw,560px);margin-inline:auto}
}
@media (max-width:620px){
  .device-stage{display:flex; overflow-x:auto; scroll-snap-type:x mandatory; gap:14px; padding:14px var(--gut) 42px; scrollbar-width:none}
  .device-stage::-webkit-scrollbar{display:none}
  .device-frame,.device-frame:nth-child(2),.device-frame:nth-child(3){flex:0 0 62vw;max-width:245px;transform:none;scroll-snap-align:center}
  .device-frame:first-child{margin-left:auto}.device-frame:last-child{margin-right:auto}
  .feature-strip{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;border-radius:var(--radius);scrollbar-width:none}
  .feature-strip::-webkit-scrollbar{display:none}
  .feature-strip li{flex:0 0 78vw;min-height:96px;border-left:1px solid var(--border);scroll-snap-align:start}
  .feature-strip li:first-child{border-left:0}
  .ecosystem-chip{position:static;max-width:none;width:100%}
  .ecosystem-stage{aspect-ratio:auto;display:grid;grid-template-columns:1fr;gap:10px;padding-top:260px}
  .ecosystem-orbit{inset:0 8% auto;height:240px}.ecosystem-core{position:absolute;top:55px;width:145px}
}
@media (prefers-reduced-motion:no-preference){
  .ecosystem-orbit{animation:orbit-drift 18s ease-in-out infinite alternate}
  @keyframes orbit-drift{to{transform:rotate(7deg) scale(1.025)}}
}

/* ====================================================================
   Portada Horme 2026 — negra, técnica y orientada al producto
   ==================================================================== */
.model-home.fit-model-home{background:#020709;color:#f5f8f6;color-scheme:dark;font-family:Inter,system-ui,sans-serif}
.fit-model-home .site-head{top:0;margin:0;background:#02080a;border-color:#14241d;box-shadow:none}
.fit-model-home .head-inner{width:min(1420px,calc(100% - 80px));min-height:78px;margin:10px auto;border:1px solid #173126;border-radius:16px;background:rgba(4,15,16,.9);padding-inline:24px}
.fit-model-home .brand,.fit-model-home .site-nav a{color:#f4f8f5}.fit-model-home .site-nav a{font-size:.84rem;font-weight:600}.fit-model-home .site-nav a[aria-current="page"]{color:#91e222}.fit-model-home .nav-lang,.fit-model-home .nav-login{border-color:#21362e;background:#08110e;color:#dbe5df}.fit-model-home .nav-cta{border-color:#8ce418;background:#8de224;color:#001309}
.fit-model-home main{overflow:hidden;background:#020709}.fit-model-home .fit-ref-wrap{width:min(1420px,calc(100% - 80px));margin-inline:auto}
.fit-model-home .fit-ref-hero{position:relative;min-height:635px;overflow:hidden;background:radial-gradient(circle at 72% 46%,rgba(131,220,19,.11),transparent 33%),linear-gradient(180deg,#020709,#03090b);border-bottom:1px solid #173126}
.fit-model-home .fit-glow{position:absolute;border-radius:50%;filter:blur(70px);pointer-events:none}.fit-model-home .fit-glow-one{right:12%;bottom:-20%;width:530px;height:380px;background:rgba(127,221,12,.13)}.fit-model-home .fit-glow-two{left:18%;top:38%;width:260px;height:220px;background:rgba(91,162,9,.08)}
.fit-model-home .fit-hero-grid{position:relative;display:grid;grid-template-columns:.93fr 1.07fr;min-height:635px;align-items:start;gap:16px}
.fit-model-home .fit-hero-copy{position:relative;z-index:5;padding:74px 0 70px;max-width:650px}
.fit-model-home .fit-eyebrow{margin:0 0 23px;color:#91e226;font-size:.79rem;font-weight:750;letter-spacing:.04em}.fit-model-home .fit-hero-copy h1{margin:0;color:#f4f7f5;font-size:clamp(3.2rem,4.3vw,4.8rem);line-height:1.02;letter-spacing:-.055em}.fit-model-home .fit-hero-copy h1 span,.fit-model-home .fit-section-copy h2 span{color:#8de224}.fit-model-home .fit-lede{max-width:610px;margin:25px 0 0;color:#9ba7a2;font-size:1.08rem;line-height:1.72}
.fit-model-home .fit-actions{display:flex;gap:16px;margin-top:29px}.fit-model-home .fit-btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:52px;padding:0 24px;border:1px solid #476524;border-radius:10px;color:#ecf5ef;text-decoration:none;font-weight:700;font-size:.89rem;transition:.2s ease}.fit-model-home .fit-btn:hover{transform:translateY(-2px);box-shadow:0 12px 36px rgba(131,227,12,.16)}.fit-model-home .fit-btn .ic{width:19px;height:19px}.fit-model-home .fit-btn-primary{border-color:#90e222;background:linear-gradient(135deg,#7fd313,#9ce740);color:#00160a}.fit-model-home .fit-btn-outline{background:#04100d;color:#8ee027}
.fit-model-home .fit-rating{display:flex;align-items:center;gap:16px;margin-top:30px}.fit-model-home .fit-avatars{display:flex}.fit-model-home .fit-avatars span{display:grid;place-items:center;width:33px;height:33px;margin-left:-7px;border:2px solid #07100d;border-radius:50%;background:linear-gradient(145deg,#e8b48e,#815d4f);color:#0b0d0c;font-size:.68rem;font-weight:800}.fit-model-home .fit-avatars span:first-child{margin-left:0}.fit-model-home .fit-rating strong{display:block;font-size:1rem}.fit-model-home .fit-rating em{color:#8de224;font-style:normal;letter-spacing:.12em}.fit-model-home .fit-rating small{display:block;margin-top:4px;color:#7f8d86;font-size:.75rem}
.fit-model-home .fit-device-stage{position:relative;align-self:stretch;min-height:635px}.fit-model-home .fit-athlete-photo{position:absolute;left:3%;bottom:0;width:40%;height:86%;background:linear-gradient(90deg,rgba(2,7,9,.1),rgba(2,7,9,.78)),url('/assets/photo/portrait-lift-1600.webp') center 22%/cover;border-radius:46% 46% 0 0;filter:saturate(.65) contrast(1.12);opacity:.7}.fit-model-home .fit-orbit{position:absolute;border:1px solid rgba(143,227,31,.42);border-radius:50%;transform:rotate(-12deg)}.fit-model-home .fit-orbit-a{inset:10% -18% 8% 12%}.fit-model-home .fit-orbit-b{inset:25% -5% 19% 2%;opacity:.45}
.fit-model-home .fit-phone{position:absolute;bottom:-86px;width:246px;height:520px;margin:0;padding:9px;border:2px solid #65706b;border-radius:42px;background:#050707;box-shadow:0 28px 70px rgba(0,0,0,.72);overflow:hidden;z-index:2}.fit-model-home .fit-phone img{width:100%;height:100%;object-fit:cover;object-position:top;border-radius:31px;filter:brightness(.8) contrast(1.05) saturate(.9)}.fit-model-home .fit-notch{position:absolute;top:15px;left:50%;z-index:4;width:84px;height:19px;border-radius:20px;background:#030505;transform:translateX(-50%)}.fit-model-home .fit-phone-left{left:23%;width:220px;height:470px;transform:rotate(-3deg);z-index:1}.fit-model-home .fit-phone-main{left:48%;width:264px;height:555px;bottom:-60px;z-index:3}.fit-model-home .fit-phone-right{right:-2%;width:224px;height:480px;transform:rotate(3deg);z-index:1}
.fit-model-home .fit-feature-rail{position:relative;z-index:6;margin-top:-1px;padding:0 0 18px;background:#020709}.fit-model-home .fit-feature-grid{display:grid;grid-template-columns:repeat(6,1fr);border:1px solid #183128;border-radius:18px;background:rgba(5,19,17,.96);box-shadow:0 24px 55px rgba(0,0,0,.32);overflow:hidden}.fit-model-home .fit-feature-grid article{display:flex;gap:13px;min-height:118px;padding:24px 19px;border-left:1px solid #1b3029}.fit-model-home .fit-feature-grid article:first-child{border-left:0}.fit-model-home .fit-feature-grid article>.ic{flex:0 0 28px;width:28px;height:28px;padding:6px;border-radius:9px;background:#0a2a1d;color:#91e126}.fit-model-home .fit-feature-grid h3{margin:0 0 7px;font-size:.79rem;line-height:1.35}.fit-model-home .fit-feature-grid p{margin:0;color:#7f9088;font-size:.7rem;line-height:1.5}
.fit-model-home .fit-ref-section{padding:55px 0 68px;background:#03080a}.fit-model-home .fit-all-in-one{border-top:1px solid #14221c}.fit-model-home .fit-all-grid{display:grid;grid-template-columns:.72fr 2.25fr;gap:35px;align-items:center}.fit-model-home .fit-section-copy h2{margin:0;color:#f4f7f5;font-size:clamp(2.2rem,3.3vw,3.6rem);line-height:1.06;letter-spacing:-.04em}.fit-model-home .fit-section-copy p{margin:20px 0 28px;color:#8b9892;line-height:1.65}.fit-model-home .fit-card-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:15px}.fit-model-home .fit-visual-card{overflow:hidden;border:1px solid #1c3028;border-radius:15px;background:#09110f;color:#f1f6f3;text-decoration:none}.fit-model-home .fit-visual-card img{width:100%;height:170px;object-fit:cover;filter:saturate(.83) brightness(.73);transition:.35s ease}.fit-model-home .fit-visual-card:hover img{transform:scale(1.035);filter:saturate(1) brightness(.86)}.fit-model-home .fit-visual-card>div{position:relative;padding:17px 18px 21px}.fit-model-home .fit-visual-card h3{margin:0 28px 8px 0;font-size:1rem;line-height:1.2}.fit-model-home .fit-visual-card p{margin:0;color:#87958e;font-size:.75rem;line-height:1.5}.fit-model-home .fit-visual-card span{position:absolute;right:16px;bottom:18px;display:grid;place-items:center;width:29px;height:29px;border:1px solid #466f12;border-radius:50%;color:#91e02b}
.fit-model-home .fit-proof{padding:34px 0 45px;border-top:1px solid #17221e;background:#020608;text-align:center}.fit-model-home .fit-proof p{color:#69766f;font-size:.66rem;letter-spacing:.16em}.fit-model-home .fit-proof-row{display:grid;grid-template-columns:repeat(5,1fr);gap:20px;margin-top:22px;color:#53605a;font-size:1.02rem;font-weight:800;letter-spacing:-.02em}.fit-model-home .site-foot{background:#03080a;border-color:#18241f;color:#7f8e86}.fit-model-home .site-foot a,.fit-model-home .site-foot h2{color:#dfe8e3}.fit-model-home .foot-word{color:#07100d}
@media(max-width:1120px){.fit-model-home .fit-ref-wrap,.fit-model-home .head-inner{width:min(100% - 42px,1000px)}.fit-model-home .fit-hero-grid{grid-template-columns:1fr}.fit-model-home .fit-hero-copy{padding-bottom:10px}.fit-model-home .fit-device-stage{min-height:570px}.fit-model-home .fit-feature-grid{grid-template-columns:repeat(3,1fr)}.fit-model-home .fit-feature-grid article:nth-child(4){border-left:0;border-top:1px solid #1b3029}.fit-model-home .fit-all-grid{grid-template-columns:1fr}.fit-model-home .fit-section-copy{max-width:620px}.fit-model-home .fit-phone-left{left:12%}.fit-model-home .fit-phone-main{left:39%}.fit-model-home .fit-phone-right{right:10%}}
@media(max-width:720px){.fit-model-home .fit-ref-wrap,.fit-model-home .head-inner{width:min(100% - 28px,650px)}.fit-model-home .fit-hero-copy h1{font-size:3rem}.fit-model-home .fit-actions{flex-direction:column;align-items:stretch}.fit-model-home .fit-device-stage{min-height:490px;margin-inline:-80px}.fit-model-home .fit-phone{transform:scale(.76)}.fit-model-home .fit-phone-left{left:2%;transform:scale(.7) rotate(-4deg)}.fit-model-home .fit-phone-main{left:32%;transform:scale(.78)}.fit-model-home .fit-phone-right{right:0;transform:scale(.7) rotate(4deg)}.fit-model-home .fit-feature-grid{display:flex;overflow-x:auto;scroll-snap-type:x mandatory}.fit-model-home .fit-feature-grid article{flex:0 0 72vw;scroll-snap-align:start}.fit-model-home .fit-card-grid{grid-template-columns:1fr 1fr}.fit-model-home .fit-proof-row{grid-template-columns:1fr 1fr}.fit-model-home .fit-proof-row span:last-child{grid-column:1/-1}}
@media(max-width:470px){.fit-model-home .fit-card-grid{grid-template-columns:1fr}.fit-model-home .fit-device-stage{margin-inline:-135px}.fit-model-home .fit-phone-main{left:35%}}

/* Sistema visual interno Horme 2026: verde electrico sobre negro. */
body:not(.model-home){
  color-scheme:dark;--primary:#91e02a;--primary-ink:#a5ed45;--accent:#8cff38;--on-primary:#001009;
  --bg:#020708;--surface:#07100e;--surface-2:#0a1512;--surface-3:#0d1c17;
  --ink:#f5faf7;--text:#b9c8c0;--muted:#788a81;--border:#173027;
  --tint:color-mix(in srgb,var(--primary) 12%,transparent);--tint-2:color-mix(in srgb,var(--primary) 7%,transparent);--tint-3:color-mix(in srgb,var(--primary) 25%,transparent);
  --edge:color-mix(in srgb,var(--primary) 40%,var(--border));--grad:linear-gradient(100deg,#f7fff9 4%,#93e229 63%,#a1ff3f 100%);
  --radius-xl:20px;--radius:15px;--radius-sm:11px;
  background:radial-gradient(850px 480px at 82% -8%,rgba(143,225,35,.13),transparent 65%),radial-gradient(600px 500px at -4% 38%,rgba(91,124,43,.09),transparent 72%),#020708;color:var(--text)
}
body:not(.model-home)::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;background:linear-gradient(112deg,transparent 0 69%,rgba(145,224,42,.035) 69% 69.15%,transparent 69.15%),linear-gradient(70deg,transparent 0 83%,rgba(140,255,56,.025) 83% 83.12%,transparent 83.12%)}
body:not(.model-home) main{overflow:hidden;background:transparent}
body:not(.model-home) .site-head{background:rgba(2,7,8,.87);border-bottom-color:#11251e;backdrop-filter:blur(20px) saturate(150%)}
body:not(.model-home) .head-inner{width:min(1390px,calc(100% - 64px));min-height:72px}
body:not(.model-home) .brand{color:#f5faf7;font-size:1.12rem}
body:not(.model-home) .brand img{box-shadow:0 0 24px rgba(145,224,42,.13)}
body:not(.model-home) .site-nav a{color:#a6b6ad;font-size:.84rem;font-weight:620}
body:not(.model-home) .site-nav a:hover{color:#fff;background:#0b1713}
body:not(.model-home) .site-nav a[aria-current="page"]{color:#9fea39;background:rgba(145,224,42,.09)}
body:not(.model-home) .nav-lang{border-color:#20362d!important;background:#07110e;color:#91a198!important}
body:not(.model-home) .nav-login{color:#eaf3ee!important}
body:not(.model-home) .nav-cta{background:linear-gradient(135deg,#9ae633,#86d41c)!important;color:#001009!important;box-shadow:0 12px 30px rgba(143,226,31,.20)}
body:not(.model-home) .head-progress{background:linear-gradient(90deg,#91e02a,#9aff39)}
body:not(.model-home) .crumbs{background:#040a08;border-color:#13251f}body:not(.model-home) .crumbs a{color:#a5ed45}
body:not(.model-home) .hero-page{min-height:510px;padding:clamp(92px,10vw,154px) 0 clamp(64px,7vw,104px);text-align:left;display:grid;align-items:center;border-bottom:1px solid #163027;background:radial-gradient(560px 410px at 82% 46%,rgba(145,224,42,.18),transparent 66%),linear-gradient(125deg,#030808 0 61%,#061711 61% 78%,#020708 78%)}
body:not(.model-home) .hero-page::before{inset:0;background:repeating-linear-gradient(115deg,transparent 0 52px,rgba(141,216,42,.022) 53px 54px)}
body:not(.model-home) .hero-page::after{content:"";position:absolute;right:5%;top:14%;width:min(38vw,520px);height:72%;border:1px solid rgba(159,229,59,.21);border-radius:50%;transform:rotate(-13deg);box-shadow:inset 0 0 90px rgba(145,224,42,.035)}
body:not(.model-home) .hero-page>.wrap{position:relative;z-index:2;margin-left:max(24px,calc((100vw - 1180px)/2));margin-right:auto;width:min(820px,calc(100% - 48px))}
body:not(.model-home) .hero-page .eyebrow{display:inline-flex;padding:8px 13px;border:1px solid rgba(159,231,57,.34);border-radius:999px;background:rgba(143,226,31,.065);color:#a5ed45;font-size:.72rem;letter-spacing:.12em}
body:not(.model-home) .hero-page h1{max-width:14ch;margin:0 0 23px;color:#f6faf8;font-family:Inter,system-ui,sans-serif;font-size:clamp(3.2rem,6.8vw,6.5rem);font-weight:780;line-height:.93;letter-spacing:-.063em}
body:not(.model-home) .hero-page h1::after{content:"";display:block;width:86px;height:5px;margin-top:27px;border-radius:9px;background:linear-gradient(90deg,#91e02a,#8cff38);box-shadow:0 0 24px rgba(145,224,42,.30)}
body:not(.model-home) .hero-page .lede{max-width:680px;margin:0;color:#a7b8af;font-size:clamp(1.04rem,1.5vw,1.25rem);line-height:1.7}
body:not(.model-home) .hero-stats{justify-content:flex-start;margin:43px 0 0}
body:not(.model-home) .hero-stats li{border-color:#1b372d;text-align:left}body:not(.model-home) .hero-stats b{color:#a5ed45;font-family:Inter,system-ui,sans-serif}
body:not(.model-home) section{border-color:#14271f}
body:not(.model-home) section.alt{background:linear-gradient(180deg,#07100e,#050c0a)}
body:not(.model-home) section.invert{background:linear-gradient(135deg,#0a1a14,#04100d);--surface:#0c1915;--surface-2:#10231c;--ink:#fff;--text:#bdcbc4;--muted:#7c8e85;--border:#1d392e;--primary:#a5ed45;--primary-ink:#a5ed45}
body:not(.model-home) .section-head h2{color:#f3f8f5;font-family:Inter,system-ui,sans-serif;font-size:clamp(2.35rem,4.4vw,4.65rem);font-weight:780;line-height:.98;letter-spacing:-.055em}
body:not(.model-home) .section-head p{color:#8fa198}body:not(.model-home) .eyebrow,body:not(.model-home) .kicker{color:#a5ed45}
body:not(.model-home) :where(.card,.plat,.plan,.tile,.bento>*,.steps li,.stats li,.pcard,.kpi,.lib-card,.calc,.callout,.login-card){background:linear-gradient(145deg,rgba(10,22,18,.97),rgba(5,12,10,.99));border-color:#1a3329;box-shadow:0 18px 55px rgba(0,0,0,.26),inset 0 1px rgba(255,255,255,.025)}
body:not(.model-home) :where(.card,.plat,.plan,.tile,.bento>*,.lib-card):hover{transform:translateY(-5px);border-color:rgba(161,231,61,.48);box-shadow:0 26px 72px rgba(0,0,0,.38),0 0 38px rgba(145,224,42,.055)}
body:not(.model-home) :where(.card,.plat,.plan,.tile,.bento>*,.steps li,.stats li,.pcard,.kpi,.lib-card,.calc) :where(h2,h3){color:#f1f7f4}
body:not(.model-home) .ic-box{background:rgba(145,224,42,.10);border-color:rgba(145,224,42,.20);color:#a5ed45}
body:not(.model-home) .btn-primary,body:not(.model-home) .plan-cta{background:linear-gradient(135deg,#9ae633,#86d41c);color:#001009;border-color:#9de638;box-shadow:0 14px 34px rgba(145,224,42,.18)}
body:not(.model-home) .btn:not(.btn-primary){background:#08120f;color:#edf6f1;border-color:#214036}
body:not(.model-home) .lib-bar{background:rgba(4,10,8,.94);border-color:#1a3329;box-shadow:0 17px 48px rgba(0,0,0,.28)}
body:not(.model-home) :where(input,select,textarea,.lib-search,.lib-sel){background:#06100d!important;border-color:#224237!important;color:#edf7f2!important}
body:not(.model-home) :where(input,select,textarea):focus{border-color:#91e02a!important;box-shadow:0 0 0 4px rgba(145,224,42,.11)!important}
body:not(.model-home) .lib-card img{filter:saturate(.78) brightness(.78);transition:.3s ease}body:not(.model-home) .lib-card:hover img{filter:saturate(1) brightness(.92)}
body:not(.model-home) .feature-strip,body:not(.model-home) .spec{background:#183128;border-color:#1e3a30}body:not(.model-home) .feature-strip li,body:not(.model-home) .spec div{background:#07110e;border-color:#1a3329}
body:not(.model-home) .table-wrap{border-color:#1b352b;background:#06100d;box-shadow:0 22px 60px rgba(0,0,0,.26)}body:not(.model-home) table tr{border-color:#1b352b}body:not(.model-home) table th{background:#0b1a15;color:#eef7f2}
body:not(.model-home) .faq details{background:#07110e;border-color:#1a3329}body:not(.model-home) .faq details[open]{border-color:rgba(159,229,57,.42)}
body:not(.model-home) :where(.detail,.guide-sec,.body){color:#b4c3bb}body:not(.model-home) :where(.detail,.guide-sec,.body) :where(h2,h3){color:#f2f8f5}body:not(.model-home) :where(.detail,.guide-sec,.body) a{color:#a5ed45}
body:not(.model-home) .guide-toc,body:not(.model-home) .aside{background:#07110e;border-color:#1a3329}
body:not(.model-home) .ctaband{background:linear-gradient(125deg,#0b2418,#071410 58%,#102619);border-color:rgba(160,230,59,.26);box-shadow:0 30px 90px rgba(0,0,0,.30)}
body:not(.model-home) .site-foot{background:#010404;border-color:#14271f;color:#7e9087}body:not(.model-home) .site-foot::before{content:"";position:absolute;inset:0;background:radial-gradient(720px 240px at 14% 0,rgba(145,224,42,.075),transparent 70%);pointer-events:none}body:not(.model-home) .site-foot a,body:not(.model-home) .site-foot h2{color:#cbd8d1}body:not(.model-home) .foot-word{color:#06100d}
@media(max-width:940px){body:not(.model-home) .head-inner{width:min(100% - 30px,720px)}body:not(.model-home) .site-head,body:not(.model-home) .site-nav{background:#030908}body:not(.model-home) .hero-page{min-height:auto;padding:86px 0 70px}body:not(.model-home) .hero-page::after{right:-100px;top:13%;width:300px;height:330px;opacity:.55}body:not(.model-home) .hero-page h1{font-size:clamp(2.75rem,12.5vw,4.9rem)}body:not(.model-home) .hero-stats{display:grid;grid-template-columns:1fr 1fr;width:100%}body:not(.model-home) .hero-stats li{border-left:0;border-top:1px solid #1b372d;padding:17px 0}}
@media(max-width:620px){body:not(.model-home) .hero-page>.wrap{width:calc(100% - 32px);margin-inline:16px}body:not(.model-home) section{padding-block:60px}body:not(.model-home) .section-head h2{font-size:2.4rem}}

/* Tokens de la marca — generados desde web/site.config.mjs. No editar a mano. */
:root{--case-display:uppercase; --track-display:.16em; --glow:1; --grain:.06; --h-font:var(--display); --h-weight:800; --h-track:-.028em; --h-stretch:114%; --track-stretch:76%;}
:root{--invert-bg:#07090a; --invert-surface:#12160f; --invert-ink:#ffffff; --invert-text:#a9b5a2; --invert-border:#232a1e; --invert-primary:#A5ED45; --invert-primary-ink:#A5ED45;}
@media (prefers-color-scheme:dark){:root{--invert-bg:#f4f7f1; --invert-surface:#ffffff; --invert-ink:#0b1006; --invert-text:#4a5644; --invert-border:#dde4d6; --invert-primary:#487B05; --invert-primary-ink:#487B05;}}
