/* public/inicio.css -- CSS propio de la landing de marketing (etapa 3 del rediseno).
   Antes la landing cargaba styles.css COMPLETO (~449 KB en disco) para usar menos del
   5%: este archivo es autonomo, con solo lo que "/" y "/en/" necesitan.

   4 capas, en orden:
   A) Tokens de tema -- copia LITERAL de styles.css:2-27 y :1309-1316 (claro/oscuro).
      Protegida por evaluacion/paridad-tokens-inicio.mjs: si un token compartido diverge
      de styles.css, `npm run verificar` falla. Cambiar el acento del CRM y no aqui deja
      de ser un error silencioso.
   B) Marca -- copia de styles.css:4854-4930 (tokens xarpen-*, .xarpen-logo, las dos
      animaciones del isotipo). marca.js la necesita tal cual para pintar el lockup.
   C) Componentes .ld-* propios de la landing + los ganchos que YA usa inicio.js
      (.login-card, .btn/.btn-p): no se tocan sus IDs ni su JS.
   D) Motion: reveals, sticky de las secciones narrativas, @supports para scroll-driven
      nativo, y el bloque de prefers-reduced-motion (obligatorio, 14 sitios del repo). */

/* ============================== CAPA A: TOKENS ============================== */
:root{
  --ease-entra: cubic-bezier(.16, 1, .3, 1);
  --ease-estado: cubic-bezier(.4, 0, .2, 1);
  --dur-1: 120ms; --dur-2: 180ms; --dur-3: 240ms;
  --bone:#F3F1EA; --paper:#FCFBF7; --deep:#EFEBE0; --ink:#2B2926; --muted:#6A675D;
  --hair:#E7E3D8; --line2:#DCD7CA; --red:#C96442; --blue:#6E7F63; --yellow:#C79A4E;
  --accent:#C96442; --r:4px;
  /* Modo claro: el relleno solido conserva los valores de SIEMPRE (naranja + texto
     blanco). El rediseno minimalista solo redefine estos dos en el bloque oscuro, asi
     que el tema claro queda exactamente como estaba. */
  --accent-solido:#C96442; --accent-tinta:#fff;
  --serif:'Instrument Sans',system-ui,sans-serif;
  --sans:'Instrument Sans',system-ui,sans-serif;
  --mono:'Instrument Sans',system-ui,sans-serif;

  /* Tokens propios de la landing (no reclamados por la paridad: no existen en styles.css) */
  --dur-esc: 400ms;   /* cambio de escena (cruces de captura, escenas de galeria) */
  --dur-rev: 600ms;   /* reveals grandes al entrar en viewport */
}
/* Espejo del bloque oscuro de styles.css -- paridad-tokens-inicio.mjs falla si se
   desincronizan. Ver ahi el porque de cada valor del rediseno minimalista. */
:root[data-theme="dark"]{
  color-scheme:dark;
  --bone:#0F0F0E; --paper:#171716; --deep:#141413;
  --ink:#C9C6BF; --muted:#A6A39C;
  --hair:#1E1E1C; --line2:#2A2A28;
  --red:#D97757; --accent:#C9C6BF;
  --accent-solido:#2A2A28; --accent-tinta:#C9C6BF;
  --blue:#8fb0d4; --yellow:#cfa15e;
}

/* ============================== CAPA B: MARCA ============================== */
:root{
  --xarpen-ink:#2a2520; --xarpen-ink-2:#6a6158;
  --xarpen-accent:var(--xarpen-ink); --xarpen-surface:#f4efe9;
}
:root[data-theme="dark"]{
  --xarpen-ink:#f5f4ee; --xarpen-ink-2:#bdbbb0;
  --xarpen-accent:var(--xarpen-ink); --xarpen-surface:#262624;
}
.xarpen-logo{ display:inline-flex; align-items:center; color:var(--xarpen-ink); }
.xarpen-logo__txt{ display:flex; flex-direction:column; justify-content:center; min-width:0; }
.xarpen-logo__name{ font-family:var(--sans), "Instrument Sans", system-ui, sans-serif; font-weight:600; letter-spacing:-.045em; line-height:1; }
.xarpen-logo__slogan{ font-family:var(--sans), "Instrument Sans", system-ui, sans-serif; font-weight:500; letter-spacing:.24em; text-transform:uppercase; color:var(--xarpen-ink-2); margin-top:.5em; }
.xarpen-logo__sub{ font-family:var(--sans), system-ui, sans-serif; font-size:10px; font-weight:500; letter-spacing:.12em; text-transform:uppercase; color:var(--xarpen-ink-2); margin-top:4px; line-height:1.25; overflow:hidden; text-overflow:ellipsis; }
.xarpen-logo--apilado{ flex-direction:column; text-align:center; }
.xarpen-logo--apilado .xarpen-logo__txt{ align-items:center; }
.xarpen-mark{ flex:none; display:block; }

@keyframes xarpen-spin{ to{ transform:rotate(360deg); } }
.xarpen-mark--procesando circle:nth-child(1){ animation:xarpen-spin 6s linear infinite; transform-origin:50% 50%; }
.xarpen-mark--procesando circle:nth-child(2){ animation:xarpen-spin 4s linear infinite reverse; transform-origin:50% 50%; }
@media (prefers-reduced-motion: reduce){ .xarpen-mark--procesando circle{ animation:none; } }

@keyframes xarpen-pulso{
  0%   { opacity:calc(var(--op) * .18); transform:scale(.9); }
  45%  { opacity:1; transform:scale(1.04); }
  100% { opacity:var(--op); transform:scale(1); }
}
.xarpen-mark--enfoque circle{
  transform-box:fill-box; transform-origin:center;
  animation:xarpen-pulso .62s cubic-bezier(.22,.61,.36,1) backwards;
}
.xarpen-mark--enfoque circle[r="3"] { --op:1;  animation-delay:0ms; }
.xarpen-mark--enfoque circle[r="9"] { --op:1;  animation-delay:70ms; }
.xarpen-mark--enfoque circle[r="15"]{ --op:.6; animation-delay:140ms; }
.xarpen-mark--enfoque circle[r="21"]{ --op:.3; animation-delay:210ms; }
@media (prefers-reduced-motion: reduce){ .xarpen-mark--enfoque circle{ animation:none; } }

/* ============================== CAPA C: COMPONENTES ============================== */
*{ box-sizing:border-box; }
/* overflow-x:clip y NO hidden: hidden crea un contenedor de scroll implicito y mata
   TODO position:sticky descendiente (S3/S6 lo necesitan). clip recorta sin ese efecto. */
html,body{ margin:0; overflow-x:clip; }
body{ background:var(--bone); color:var(--ink); font-family:var(--sans); font-size:15px; line-height:1.6; -webkit-font-smoothing:antialiased; }
img{ max-width:100%; display:block; }
a{ color:inherit; }
::selection{ background:var(--accent); color:var(--paper); }

.skip-link{ position:fixed; left:10px; top:-60px; z-index:100; background:var(--accent-solido); color:var(--accent-tinta); padding:10px 16px; border-radius:9px; font-size:14px; text-decoration:none; transition:top .15s ease; }
.skip-link:focus{ top:10px; }

/* -- Nav fija -- */
.ld-nav{ position:sticky; top:0; z-index:20; display:flex; align-items:center; justify-content:space-between; gap:16px; padding:16px 22px; background:color-mix(in oklab, var(--bone) 88%, transparent); backdrop-filter:blur(10px); border-bottom:1px solid var(--hair); }
.ld-nav-links{ display:flex; align-items:center; gap:18px; font-size:13px; }
.ld-nav-links a{ color:var(--muted); text-decoration:none; }
.ld-nav-links a:hover{ color:var(--ink); }
.ld-nav-lang{ font-family:var(--mono); font-size:11px; letter-spacing:.08em; text-transform:uppercase; }
/* Bug real cazado con scrollWidth en el preview movil (375px): .ld-nav es un flex row
   sin flex-wrap, y logo + 2 links + boton CTA no caben -- el nav se desbordaba a 536px
   y quedaba recortado en silencio por el overflow-x:clip del body. En vez de flex-wrap
   (el boton cae a su propia linea, se ve descuadrado) se ocultan los 2 links de salto:
   son atajos a secciones de esta MISMA pagina (no rutas distintas), el visitante los
   alcanza igual bajando. El boton (unico <a> con .btn) se queda. */
@media (max-width:520px){
  .ld-nav-links a:not(.btn){ display:none; }
  .ld-nav{ padding:14px 16px; gap:10px; }
  /* Red de mas abajo para el logo del nav: inicio.js ya genera el lockup mas chico y
     SIN slogan en este mismo ancho (xarpenLockup(34,{slogan:false}), ver el comentario
     alla -- tam=64 con eslogan median 278px y desbordaban el nav). Esta regla es
     puro cinturon-y-tirantes por si algun dia alguien vuelve a llamarlo con
     slogan:true aqui: el icono+wordmark solos ya identifican la marca, y el eslogan
     completo ("Enfoca tu estudio") se sigue leyendo en el cierre (S11) y el pie. */
  .ld-nav .xarpen-logo__slogan{ display:none; }
}

/* -- Boton, tarjeta de login (ganchos de inicio.js: NO renombrar) -- */
.btn{ display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:11px 20px; border-radius:var(--r); border:1px solid var(--line2); background:var(--paper); color:var(--ink); font:inherit; font-size:14px; font-weight:600; cursor:pointer; text-decoration:none; transition:transform var(--dur-1) var(--ease-estado), border-color var(--dur-1); }
.btn:hover{ border-color:var(--accent); }
.btn:active{ transform:scale(.98); }
.btn-p{ background:var(--accent-solido); border-color:var(--accent-solido); color:var(--accent-tinta); }
.btn-p:hover{ border-color:var(--accent-solido); filter:brightness(1.06); }

.login-card{ background:var(--paper); border:1px solid var(--hair); border-radius:16px; padding:28px 26px; box-shadow:0 24px 60px -24px rgba(20,18,14,.35); }
.login-card h1{ font-family:var(--serif); font-weight:400; font-size:21px; margin:0 0 4px; text-align:center; letter-spacing:-.01em }
.login-card .sub{ text-align:center; color:var(--muted); font-size:12.5px; letter-spacing:.04em; text-transform:uppercase; margin-bottom:20px; }
.login-card input{ width:100%; padding:12px 14px; margin-bottom:10px; border:1px solid var(--line2); border-radius:10px; background:var(--bone); color:var(--ink); font:inherit; font-size:14px; }
.login-card input:focus{ outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent); }
.login-card .btn{ width:100%; margin-top:4px; padding:13px; }
.login-err{ color:#c0433a; font-size:12.5px; margin-top:8px; min-height:1em; }
.ld-note{ font-size:12.5px; color:var(--muted); text-align:center; margin-top:14px; line-height:1.5; }
.ld-note a{ color:var(--accent); text-decoration:underline; }

/* -- Tipografia de seccion -- */
.ld-eyebrow{ font-family:var(--mono); font-size:10.5px; letter-spacing:.2em; text-transform:uppercase; color:var(--accent); margin:0 0 14px; }
.ld-h1{ font-family:var(--serif); font-weight:400; font-size:clamp(26.5px,4.2vw,40.5px); line-height:1.08; margin:0 0 18px; text-wrap:balance; letter-spacing:-.01em }
.ld-h2{ font-family:var(--serif); font-weight:400; font-size:clamp(23px,3.4vw,31.5px); line-height:1.12; margin:0 0 14px; text-wrap:balance; letter-spacing:-.01em }
.ld-lead{ font-size:16.5px; color:var(--muted); line-height:1.6; max-width:56ch; margin:0; }
.ld-sub{ font-size:15px; color:var(--muted); line-height:1.6; max-width:60ch; margin:0 0 32px; }

.ld-section{ max-width:1120px; margin:0 auto; padding:88px 22px; }
.ld-section-tight{ padding-top:48px; padding-bottom:48px; }
.ld-section-head{ max-width:680px; margin:0 0 40px; }
.ld-hr{ border:0; border-top:1px solid var(--hair); max-width:1120px; margin:0 auto; }

/* -- Tarjetas (S3, S8) -- */
.ld-cards{ display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:16px; }
.ld-card{ background:var(--paper); border:1px solid var(--hair); border-radius:16px; padding:26px 24px; }
.ld-card h3{ font-family:var(--serif); font-weight:400; font-size:16.5px; margin:0 0 8px; letter-spacing:-.01em }
.ld-card p{ font-size:13.5px; color:var(--muted); line-height:1.6; margin:0; }
.ld-card-num{ font-family:var(--mono); font-size:11px; color:var(--accent); margin-bottom:10px; }

/* -- Mosaico chico (S8) -- */
.ld-mosaico{ display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:14px; margin-top:36px; }
.ld-mosaico-item{ background:var(--paper); border:1px solid var(--hair); border-radius:14px; padding:16px 18px; }
.ld-mosaico-item h4{ font-family:var(--serif); font-weight:400; font-size:13px; margin:0 0 5px; letter-spacing:-.01em }
.ld-mosaico-item p{ font-size:12px; color:var(--muted); line-height:1.5; margin:0; }
.ld-linea{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:0; }
.ld-linea li{ padding:14px 0; border-top:1px solid var(--hair); font-size:14px; line-height:1.5; display:flex; gap:12px; }
.ld-linea li:first-child{ border-top:0; }
.ld-linea b{ flex:0 0 auto; color:var(--ink); }
.ld-linea span{ color:var(--muted); }

/* -- Precio (S10) -- */
.ld-plans{ display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-top:8px; }
.ld-plan{ border:1px solid var(--hair); border-radius:16px; padding:24px 20px; background:var(--paper); }
.ld-plan.on{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
.ld-plan-tag{ font-family:var(--mono); font-size:10px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }
.ld-plan-tag.on{ color:var(--accent); }
.ld-plan h4{ font-family:var(--serif); font-weight:400; font-size:17.5px; margin:6px 0 4px; letter-spacing:-.01em }
.ld-plan-precio{ font-family:var(--serif); font-size:24.5px; margin:0 0 12px; letter-spacing:-.01em }
.ld-plan-precio span{ font-family:var(--sans); font-size:12px; color:var(--muted); }
.ld-plan ul{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:8px; }
.ld-plan li{ font-size:12.5px; color:var(--muted); line-height:1.4; display:flex; gap:7px; }
.ld-plan li.no{ opacity:.5; }
.ld-plan .ck{ flex:0 0 auto; color:var(--accent); font-family:var(--mono); font-size:11px; }
@media (max-width:820px){ .ld-plans{ grid-template-columns:1fr 1fr; } }
@media (max-width:520px){ .ld-plans{ grid-template-columns:1fr; } }

/* -- Grids de 2 columnas (hero, S3, S4, S5, S7) -- las 3 variantes vivian con su
   grid-template-columns EN LINEA en el html (hero: 1.05fr/.95fr; S3 interno: 48px de
   separacion y align-items:start; S4/S5/S7: 1fr/1fr con 56px). Bug real que ya se
   repitio dos veces esta sesion en otras partes de este mismo archivo: una regla de
   @media apuntando a una clase NUNCA le gana a un estilo inline, sin importar
   especificidad. Se sacan a estas 3 clases para que el colapso a 1 columna de abajo
   si tenga algo que pisar. Nada de esto es motion: aplica igual con o sin JS. */
.ld-hero-grid{ display:grid; grid-template-columns:1.05fr .95fr; gap:56px; align-items:center; }
.ld-xarpie-grid{ display:grid; grid-template-columns:1fr 1fr; gap:48px; align-items:start; }
.ld-2col{ display:grid; grid-template-columns:1fr 1fr; gap:56px; align-items:center; }
@media (max-width:820px){
  /* S4/S5 usan order:1/2 EN LINEA para invertir texto/imagen en escritorio -- a una
     columna eso no hace falta tocarlo: el orden natural del html ya pone el texto
     primero en las 2 (S4 no lo usa, S5 sí: su texto ya es order:1, menor que el
     order:2 del panel, asi que cae texto-primero solo con colapsar el grid). */
  .ld-hero-grid, .ld-xarpie-grid, .ld-2col{ grid-template-columns:1fr; gap:32px; }
}

/* -- Franja de numeros (S9) -- */
.ld-numeros{ display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:24px; margin:36px 0; padding:28px 0; border-top:1px solid var(--hair); border-bottom:1px solid var(--hair); }
.ld-numero{ text-align:left; }
.ld-numero b{ display:block; font-family:var(--serif); font-size:28px; color:var(--accent); letter-spacing:-.01em }
.ld-numero span{ font-size:12px; color:var(--muted); }

/* -- Cierre (S11) -- */
.ld-final{ text-align:center; padding:100px 22px 60px; }
.ld-final .xarpen-mark{ margin:0 auto 20px; }
.ld-final h2{ font-family:var(--serif); font-weight:400; font-size:clamp(24.5px,4vw,35px); margin:0 0 14px; letter-spacing:-.01em }
.ld-final p{ color:var(--muted); font-size:15px; margin:0 0 36px; }
.ld-form-wrap{ max-width:420px; margin:0 auto; }

.ld-footer{ text-align:center; padding:40px 22px 60px; border-top:1px solid var(--hair); }
.ld-footer-links{ display:flex; justify-content:center; gap:18px; margin-top:12px; font-size:13px; }
.ld-footer-links a{ color:var(--muted); text-decoration:none; }
.ld-footer-links a:hover{ color:var(--accent); }

/* ================================================================================
   Recreaciones fieles de UI real (no capturas: el panel de navegador no puede
   guardar screenshots a disco -- ver la nota en el plan). Mismas clases/tokens que
   el CRM real donde comparten vocabulario visual; prefijo .ldu- para lo propio de
   estas recreaciones, que no debe chocar con ninguna clase real del CRM. */
.ldu-frame{ background:var(--paper); border:1px solid var(--hair); border-radius:16px; overflow:hidden; box-shadow:0 30px 70px -30px rgba(20,18,14,.4); }
.ldu-frame-bar{ display:flex; align-items:center; gap:6px; padding:10px 14px; border-bottom:1px solid var(--hair); }
.ldu-frame-dot{ width:8px; height:8px; border-radius:50%; background:var(--line2); }
.ldu-frame-body{ padding:20px; background:var(--deep); }

/* El Foco (S5) */
.ldu-foco{ display:flex; flex-direction:column; gap:1px; background:var(--hair); border-radius:12px; overflow:hidden; border:1px solid var(--hair); }
.ldu-foco-row{ background:var(--paper); padding:13px 14px; display:flex; align-items:flex-start; justify-content:space-between; gap:10px; }
.ldu-foco-tx b{ display:block; font-size:13px; }
.ldu-foco-tx span{ display:block; font-size:11.5px; color:var(--muted); margin-top:2px; }
.ldu-foco-btn{ flex:0 0 auto; font-size:11px; padding:6px 10px; border-radius:8px; border:1px solid var(--line2); background:transparent; color:var(--muted); white-space:nowrap; }
.ldu-foco-btn.on{ border-color:var(--accent); color:var(--accent); }

/* Escalera de autonomia (S4) */
.ldu-niv{ background:var(--paper); border:1px solid var(--hair); border-radius:12px; padding:14px 16px; margin-bottom:10px; }
.ldu-niv-h{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.ldu-niv-nom{ font-size:13px; font-weight:600; }
.ldu-niv-sel{ font-size:11.5px; padding:5px 10px; border-radius:7px; border:1px solid var(--line2); color:var(--muted); }
.ldu-niv-sel.candado{ border-color:var(--accent); color:var(--accent); display:flex; align-items:center; gap:5px; }
.ldu-niv-stat{ font-size:11px; color:var(--muted); margin-top:5px; }

/* Chat de Xarpie (hero, S1) */
.ldu-chat{ display:flex; flex-direction:column; gap:12px; }
.ldu-msg{ max-width:82%; padding:11px 14px; border-radius:14px; font-size:13px; line-height:1.5; }
.ldu-msg-user{ align-self:flex-end; background:var(--accent-solido); color:var(--accent-tinta); border-bottom-right-radius:4px; }
.ldu-msg-xarpie{ align-self:flex-start; background:var(--paper); border:1px solid var(--hair); border-bottom-left-radius:4px; }
.ldu-chips{ display:flex; flex-direction:column; gap:6px; margin-top:4px; }
.ldu-chip{ font-size:12px; padding:8px 12px; border-radius:9px; background:var(--paper); border:1px solid var(--hair); color:var(--ink); display:flex; align-items:center; gap:8px; }
.ldu-chip::before{ content:"✓"; color:var(--accent); font-weight:700; }
.ldu-cliente{ background:var(--paper); border:1px solid var(--hair); border-radius:12px; padding:14px 16px; margin-top:6px; }
.ldu-cliente-nom{ font-family:var(--serif); font-size:14px; letter-spacing:-.01em }
.ldu-cliente-fila{ display:flex; justify-content:space-between; font-size:12px; color:var(--muted); margin-top:6px; }
.ldu-barra{ height:5px; background:var(--hair); border-radius:99px; margin-top:8px; overflow:hidden; }
.ldu-barra i{ display:block; height:100%; background:var(--accent); border-radius:99px; transform-origin:left; }

/* ============================== CAPA D: MOTION ============================== */
/* html.anim lo pone SOLO inicio-anim.js, y solo si hay JS Y no hay reduced-motion.
   Todo lo que empieza oculto vive bajo este selector: sin JS o con RM, es visible
   directo (estado base = estado final, en TODA la pagina, no solo el hero). */
.ld-reveal{ opacity:1; transform:none; }
html.anim .ld-reveal{ opacity:0; transform:translateY(18px); transition:opacity var(--dur-rev) var(--ease-entra), transform var(--dur-rev) var(--ease-entra); }
html.anim .ld-reveal.es-visible{ opacity:1; transform:none; }
/* Red de rescate: si el IO nunca llega a marcar es-visible (JS cargo pero algo fallo
   despues), esto revela solo a los 3.2s. es-visible (mas especifico via transition ya
   corrida) gana si actua primero; si no, esto es lo que salva el contenido. */
html.anim .ld-reveal{ animation:ld-autorevela .6s var(--ease-entra) 3.2s forwards; }
@keyframes ld-autorevela{ to{ opacity:1; transform:none; } }

/* Escenas cruzadas (S3 narrativa): visible solo la escena con .on. */
.ld-escenas{ position:relative; }
.ld-escena{ position:absolute; inset:0; opacity:0; transition:opacity var(--dur-esc) var(--ease-estado); pointer-events:none; }
.ld-escena.on{ opacity:1; pointer-events:auto; }
html:not(.anim) .ld-escena{ opacity:1; pointer-events:auto; position:static; margin-bottom:20px; }
html:not(.anim) .ld-escenas{ display:flex; flex-direction:column; gap:0; }

/* Sticky solo cuando hay JS y motion: sin el, la narrativa se apila normal (mejor
   para no-JS/RM que un sticky vacio de contenido que nunca cambia). */
html.anim .ld-sticky{ position:sticky; top:8vh; }

html:not(.anim) .ld-sticky{ position:static; }

/* ---- El rio de plantillas (S6) ----
   Las 23 del catalogo real en dos filas de marquee (referencia Ramotion: superficie
   viva, no un carrusel de flechas). Las tarjetas las construye inicio-rio.js desde
   LD_RIO; su paleta llega en custom properties inline (--c-*), que un @media no puede
   pisar -- la trampa inline-vs-@media de este archivo no aplica a variables.

   El bucle sin costura: cada cinta contiene DOS sets identicos (el clon aria-hidden lo
   agrega el JS solo con motion) y el gap vive DENTRO del set como padding-right, asi
   translateX(-50%) cae exacto en un periodo y el salto del loop es invisible.
   Direcciones opuestas y duraciones distintas por fila: el ojo nunca ve el patron.
   Pausa en hover Y focus-within (el teclado tambien necesita leer), y .corre lo pone
   un IO solo con la seccion a la vista (fuera del viewport la GPU no gasta). */
.ld-rio-chips{ list-style:none; margin:0 0 8px; padding:0; display:flex; flex-wrap:wrap; gap:8px 10px; justify-content:center; }
.ld-rio-chips li{ font-size:13px; padding:7px 13px; border:1px solid var(--hair); border-radius:99px; background:var(--paper); color:var(--muted); }
.rio-ok .ld-rio-chips{ display:none; } /* con tarjetas construidas, los chips sobran */
.ld-rio{ display:flex; flex-direction:column; gap:16px; overflow:clip; }
.ld-rio-cinta{ display:flex; width:max-content; }
.ld-rio-set{ display:flex; gap:16px; padding-right:16px; }
html.anim .rio-ok .ld-rio-cinta{ will-change:transform; animation:ld-rio 64s linear infinite; animation-play-state:paused; }
html.anim .rio-ok .ld-rio-fila[data-dir="izq"] .ld-rio-cinta{ animation-direction:reverse; animation-duration:52s; }
html.anim .rio-ok .ld-rio.corre .ld-rio-cinta{ animation-play-state:running; }
html.anim .rio-ok .ld-rio-fila:hover .ld-rio-cinta,
html.anim .rio-ok .ld-rio-fila:focus-within .ld-rio-cinta{ animation-play-state:paused; }
@keyframes ld-rio{ to{ transform:translateX(-50%); } }
/* Sin motion (y con JS): mismo contenido como rejilla quieta, sin clones ni cintas. */
html:not(.anim) .ld-rio-cinta{ width:auto; }
html:not(.anim) .ld-rio-set{ flex-wrap:wrap; justify-content:center; padding-right:0; }

/* La tarjeta: una sola anatomia para las 23 -- el MOCK real de su plantilla (portada +
   disposicion, ver gxm-mockups.css e inicio-rio.js), vestido con fotos de SU muestrario,
   nombre en la fuente display de SU par y la paleta de SU plantilla. Antes era una foto
   sola; la queja real era que una foto no dice si la plantilla trae capitulos, moodboard
   o portada partida -- la disposicion ES el producto, y estas son las MISMAS cajas que
   ya prueba el fotografo al elegir diseno en el CRM, no una recreacion aparte.

   Los mocks .gxm-* estan calibrados para ~380px de ancho (los font-size mas chicos ya
   bajan a 6.5px ahi -- ver el comentario de pintarFotosMock en galeria-estilos.js). La
   tarjeta del rio es mas chica que eso a proposito (compacta, muchas en un marquee), asi
   que en vez de re-escribir 128 clases para otro ancho, .ld-carta-mock se dibuja a su
   tamano NATURAL (380x261, la proporcion 16/11 real del selector del CRM) y se encoge
   con transform:scale -- preserva las proporciones/relaciones internas del mock, que
   reflow-ar a un ancho menor no. --ld-esc la fija cada breakpoint. */
.ld-carta{ --ld-esc:.684; width:260px; flex:none; display:flex; flex-direction:column; text-align:left; cursor:pointer; padding:0; overflow:clip; border-radius:14px; font:inherit; background:var(--c-bg); color:var(--c-tinta); border:1px solid color-mix(in oklab, var(--c-tinta) 14%, var(--c-bg)); transition:transform var(--dur-2) var(--ease-estado), box-shadow var(--dur-2) var(--ease-estado); }
.ld-carta:hover, .ld-carta:focus-visible{ transform:translateY(-4px) scale(1.02); box-shadow:0 18px 40px -18px rgba(20,18,14,.45); }
.ld-carta:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.ld-carta-foto{ position:relative; width:100%; aspect-ratio:16/11; overflow:clip; display:block; background:var(--c-bg); }
.ld-carta-mock{ position:absolute; top:0; left:0; width:380px; height:261.25px; transform:scale(var(--ld-esc)); transform-origin:top left; }
.ld-carta-nom{ padding:11px 13px 2px; font-size:16px; line-height:1.2; }
.ld-carta-linea{ height:3px; width:34px; margin:7px 13px 13px; border-radius:2px; background:var(--c-acc); }
@media (max-width:520px){ .ld-carta{ --ld-esc:.526; width:200px; } .ld-carta-nom{ font-size:14px; } }

/* Los 9 pares tipograficos, espejo de GT_DISPLAY (galeria.js) con respaldo generico:
   si la fuente subset no llega (o el glifo falta), el nombre sigue legible. */
.ld-f-editorial{ font-family:'Cormorant Garamond',Georgia,serif; }
.ld-f-modern{ font-family:'Instrument Sans',system-ui,sans-serif; }
.ld-f-romantic{ font-family:'Newsreader',Georgia,serif; }
.ld-f-display{ font-family:'Instrument Serif',Georgia,serif; }
.ld-f-mono{ font-family:'JetBrains Mono',ui-monospace,monospace; }
.ld-f-script{ font-family:'Alex Brush',cursive; }
.ld-f-bold{ font-family:'Fraunces',Georgia,serif; }
.ld-f-geometric{ font-family:'Space Grotesk',system-ui,sans-serif; }
.ld-f-elegante{ font-family:'Playfair Display',Georgia,serif; }
.ld-f-script .ld-carta-nom{ font-size:21px; } /* la caligrafica a 16px es ilegible */

/* La lupa: overlay propio (mismo esqueleto que el selector del CRM: dialog, Esc,
   click al fondo, foco restaurado -- todo eso vive en inicio-rio.js). */
.ld-lupa{ position:fixed; inset:0; z-index:60; display:flex; align-items:center; justify-content:center; padding:20px; background:color-mix(in oklab, var(--ink) 45%, transparent); backdrop-filter:blur(6px); }
.ld-lupa-caja{ position:relative; width:min(680px, 94vw); max-height:92svh; display:flex; flex-direction:column; border-radius:18px; overflow:clip; background:var(--c-bg); color:var(--c-tinta); box-shadow:0 50px 120px -40px rgba(10,8,6,.6); }
.ld-lupa-x{ position:absolute; top:10px; right:10px; z-index:2; width:34px; height:34px; border-radius:99px; border:0; cursor:pointer; background:color-mix(in oklab, var(--c-bg) 72%, transparent); color:var(--c-tinta); font-size:15px; }
.ld-lupa-media{ aspect-ratio:16/10; overflow:clip; background:var(--c-panel); }
.ld-lupa-media img, .ld-lupa-iframe{ width:100%; height:100%; object-fit:cover; border:0; display:block; }
.ld-lupa-pie{ display:flex; align-items:center; gap:14px; padding:16px 18px; flex-wrap:wrap; }
.ld-lupa-nom{ font-size:24px; line-height:1.1; }
.ld-lupa-linea{ height:3px; width:34px; border-radius:2px; background:var(--c-acc); }
.ld-lupa-btns{ margin-left:auto; display:flex; gap:10px; flex-wrap:wrap; }

/* ---- Zoom-out del hero a tarjeta (S1 -> S2) ----
   El gesto central de la referencia (Ramotion/HackerRank): al hacer scroll el hero se
   encoge y redondea a una tarjeta flotando sobre un resplandor terracota, y S2 sube
   POR ENCIMA como una hoja con esquinas redondeadas.

   Doble compuerta a proposito:
   1. `html.anim.motor` -- .motor lo agrega SOLO el conductor de inicio-motion.js al
      engancharse con exito. Si ese archivo nunca corre (aunque html.anim este puesto),
      nada de esto aplica y la pagina queda en flujo normal: es la misma garantia que
      las redes de rescate dan a los reveals, pero estructural en vez de por tiempo --
      un pin de 178svh que nadie conduce no se puede "rescatar" con un keyframe.
   2. El @media de escritorio: en movil el hero apilado a 1 columna mide mas que la
      pantalla (~1200px vs 812), y un sticky de 100svh con clip lo CORTARIA. El pin
      solo existe donde el hero cabe completo en el viewport.

   El conductor escribe --hero-p (0..1) en .ld-hero-pin; todo lo demas es CSS
   consumiendola. Solo transform/opacity/box-shadow/border-radius: nada de layout. */
.ld-hero{ padding-top:56px; } /* antes inline en el HTML; migrado por la regla del feature */
@media (min-width:821px) and (min-height:601px){
  html.anim.motor .ld-hero-pin{ height:178svh; }
  html.anim.motor .ld-hero-sticky{ position:sticky; top:0; min-height:100svh; display:flex; align-items:center; overflow:clip; } /* clip, no hidden: hidden mataria el proximo sticky ancestro */
  html.anim.motor .ld-hero-stage{
    width:100%;
    background:var(--bone);
    transform:scale(calc(1 - .10 * var(--hero-p, 0))) translateY(calc(var(--hero-p, 0) * -12px));
    border-radius:calc(var(--hero-p, 0) * 26px);
    box-shadow:0 40px 90px -40px rgba(20,18,14,calc(var(--hero-p, 0) * .5));
    will-change:transform;
  }
  /* El fondo que el zoom-out revela: un lavado profundo + radial terracota. Invisible
     en reposo (opacity 0 = el fondo normal de la pagina), crece con el progreso. */
  .ld-hero-glow{ position:absolute; inset:0; opacity:0; pointer-events:none;
    background:var(--deep) radial-gradient(58% 46% at 50% 42%, color-mix(in oklab, var(--accent) 30%, transparent), transparent 72%); }
  html.anim.motor .ld-hero-glow{ opacity:var(--hero-p, 0); }
  html.anim.motor .ld-hero-stage{ position:relative; } /* pinta encima del glow absoluto */
  /* S2 sube por encima del hero encogido, como hoja redondeada. El margen negativo
     come parte del pin (la mitad baja del segundo viewport del pin). */
  html.anim.motor .ld-cubre{ position:relative; z-index:2; margin-top:-46svh; background:var(--bone);
    border-radius:26px 26px 0 0; padding-top:96px; box-shadow:0 -30px 60px -44px rgba(20,18,14,.4); }
  html.anim.motor .ld-hr-hero{ display:none; }
}

/* ---- Frases que se iluminan (S3) + micro-parallax ----
   Los parrafos de cada .ld-paso pasan de apagado a tinta con el avance del scroll
   (referencia Ramotion). El texto es un gradiente de 2 mitades recortado al texto;
   --frase-p (0..1, del conductor) desliza la ventana: 0 = todo --muted (el estado
   historico de estos parrafos), 1 = todo --ink. Fallback --frase-p:1 y compuerta
   html.anim.motor: sin conductor, tinta plena estatica -- nunca texto a medias.
   El estilo base del parrafo vivia INLINE con color:var(--muted); se migro a esta
   clase porque un color en linea le gana al gradiente (misma trampa inline-vs-clase
   ya sufrida dos veces en este archivo). */
.ld-paso-tx{ color:var(--muted); font-size:14.5px; line-height:1.6; max-width:40ch; }
html.anim.motor .ld-paso .ld-paso-tx{
  color:transparent; -webkit-background-clip:text; background-clip:text;
  background-image:linear-gradient(90deg, var(--ink) 50%, var(--muted) 50%);
  background-size:200% 100%;
  background-position-x:calc(100% * (1 - var(--frase-p, 1)));
}
/* Micro-parallax: `translate` y no `transform` A PROPOSITO -- es propiedad
   independiente y COMPONE con el transform que .ld-reveal ya anima en el mismo
   elemento; con transform, el ultimo pisaria al otro. --par lo escribe el conductor
   (desplazamiento normalizado del centro del elemento vs el del viewport). */
html.anim.motor .ld-flota{ translate:0 calc(var(--par, 0) * 18px); }

/* ---- Titular que se enciende (S1) ----
   Cada palabra es un span.ld-luz con su indice en --i (inline). Base = final: el color
   pleno (--ink, o --accent en .ld-luz-acc) es el estado sin animar; bajo html.anim el
   keyframe solo aporta el `from` en --muted y el navegador interpola hacia el color
   natural del span -- un solo keyframe sirve para tinta y acento. Los chips de icono
   entran con un pop de escala. Todo termina ~1.2s tras cargar: muy por debajo del
   rescate del hero (6.5s) y sin depender de inicio-motion.js (CSS puro bajo html.anim,
   que pone inicio-anim.js sync en el head). */
.ld-luz{ color:var(--ink); }
.ld-luz-acc{ color:var(--accent); }
.ld-chip-h1{ display:inline-flex; align-items:center; justify-content:center; width:1.02em; height:1.02em; border-radius:.26em; background:var(--paper); border:1px solid var(--hair); vertical-align:-.14em; box-shadow:0 4px 14px -6px rgba(20,18,14,.3); }
.ld-chip-h1 svg{ width:.62em; height:.62em; color:var(--accent); }
@keyframes ld-enciende{ from{ color:var(--muted); } }
@keyframes ld-enciende-chip{ from{ opacity:0; transform:scale(.6); } }
html.anim .ld-enciende .ld-luz{ animation:ld-enciende .55s var(--ease-entra) both; animation-delay:calc(240ms + var(--i, 0) * 85ms); }
html.anim .ld-enciende .ld-chip-h1{ animation:ld-enciende-chip .45s var(--ease-entra) both; animation-delay:calc(240ms + var(--i, 0) * 85ms); }

/* ---- Coreografia del hero (S1) ----
   Cada pieza arranca oculta y `inicio-motion.js` añade .corre al contenedor 350ms
   despues de cargar. Fill-mode `both` (backwards+forwards), NO solo `backwards`:
   durante el retardo aplica el fotograma 0 (oculto) -- eso es la mitad "backwards".
   Pero a diferencia de .ld-reveal o xarpen-mark--enfoque (donde backwards solo
   BASTA porque el estado no-animado de esos elementos YA es el final deseado),
   aqui SI hay una regla mas especifica (html.anim .ld-hero-p{opacity:0}, 2 clases)
   forzando oculto de forma incondicional bajo html.anim -- asi que sin la mitad
   "forwards", en cuanto la animacion propia de cada pieza termina, el elemento le
   devuelve el control a esa regla de 2 clases y vuelve a opacity:0 PARA SIEMPRE.
   Bug real, cazado con getComputedStyle en el preview: toda la demo aparecia y
   luego se desvanecia por completo a los pocos segundos. `both` lo corrige: el
   ultimo fotograma (opacity:1) se retiene despues de terminar, no se abandona. */
.ld-hero-p{ opacity:1; }
html.anim .ld-hero-p{ opacity:0; }
/* Red de rescate propia del hero (mismo espiritu que la de .ld-reveal): si por lo
   que sea inicio-motion.js nunca llega a añadir .corre (error de red en ESE
   archivo puntual, aunque inicio-anim.js si corrio), esto revela todo a los 6.5s de
   cualquier forma. Especificidad: las reglas de abajo (html.anim .ld-demo.corre
   .ld-hero-X, 4 clases) le ganan a esta (html.anim .ld-hero-p, 2 clases) cuando
   .corre SI llega a existir, asi que la entrada coreografiada manda y esto nunca
   se nota en el camino feliz -- solo entra si .corre de verdad nunca aparece. */
html.anim .ld-hero-p{ animation:ld-hero-rescate .5s var(--ease-entra) 6.5s forwards; }
@keyframes ld-hero-rescate{ to{ opacity:1; } }
@keyframes ld-hero-entra{ from{ opacity:0; transform:translateY(10px); } to{ opacity:1; transform:none; } }
html.anim .ld-demo.corre .ld-hero-msg{ animation:ld-hero-entra .4s var(--ease-entra) 0ms both; }
html.anim .ld-demo.corre .ld-hero-thinking{ animation:ld-hero-entra .4s var(--ease-entra) 2200ms both; }
html.anim .ld-demo.corre .ld-hero-chip1{ animation:ld-hero-entra .3s var(--ease-entra) 3400ms both; }
html.anim .ld-demo.corre .ld-hero-chip2{ animation:ld-hero-entra .3s var(--ease-entra) 3680ms both; }
html.anim .ld-demo.corre .ld-hero-chip3{ animation:ld-hero-entra .3s var(--ease-entra) 3960ms both; }
html.anim .ld-demo.corre .ld-hero-chip4{ animation:ld-hero-entra .3s var(--ease-entra) 4240ms both; }
html.anim .ld-demo.corre .ld-hero-cliente{ animation:ld-hero-entra .35s var(--ease-entra) 4400ms both; }
html.anim .ld-demo.corre .ld-hero-ask{ animation:ld-hero-entra .4s var(--ease-entra) 5600ms both; }
/* La barra de pagos: crece de 0 a su valor real (30%), nunca anima `width` (layout) --
   scaleX sobre un elemento con transform-origin:left hace el mismo efecto compositado.
   `both` por la misma razon que arriba: sin retener el fotograma final, la barra
   llegaria a scaleX(1) y en el instante siguiente se le notaria un salto (aunque
   aqui el salto es casi invisible porque el `transform` base sin animar tambien
   equivale a scaleX(1) -- pero apoyarse en esa coincidencia es fragil, `both` lo
   hace explicito y correcto sin depender de que nadie mas toque esta regla). */
.ld-hero-barra{ transform-origin:left; }
html.anim .ld-demo.corre .ld-hero-barra{ animation:ld-hero-barra .5s var(--ease-estado) 4700ms both; }
@keyframes ld-hero-barra{ from{ transform:scaleX(0); } to{ transform:scaleX(1); } }

@media (prefers-reduced-motion: reduce){
  html.anim .ld-reveal{ opacity:1; transform:none; transition:none; animation:none; }
  .ld-escena{ transition:none; }
  html.anim .ld-enciende .ld-luz, html.anim .ld-enciende .ld-chip-h1{ animation:none; }
}
