/* ============================================================================
   ROBRICKS ROBÓTICA — main.css
   Concepto: "Movimiento controlado". Sin librerías, sin CDN, sin build.
   ========================================================================= */

/* ---------- 1. FUENTES (locales, subset latin) --------------------------- */
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-Medium.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-SemiBold.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-ExtraBold.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/IBMPlexMono-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/IBMPlexMono-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}

/* ---------- 2. TOKENS ---------------------------------------------------- */
:root {
  /* ===== NEUTROS (teñidos hacia navy, matiz ~205-210°) ===== */
  --ink-0: #050607;   /* fondo hero y footer — el más profundo */
  --ink-1: #0A0C0E;   /* fondo base del documento */
  --ink-2: #101418;   /* superficie de sección alterna */
  --ink-3: #161B21;   /* tarjetas, drawer, inputs */
  --ink-4: #232A32;   /* bordes, divisores, hover de tarjeta */
  --ink-5: #313A44;   /* bordes activos, líneas de gráfica */
  --ink-6: #4A5560;   /* iconografía secundaria, ticks */

  /* ===== MARCA ===== */
  --navy: #002B40;                       /* solo como profundidad/tinte */
  --navy-glow: rgba(0, 90, 130, 0.10);   /* radial de atmósfera */
  --green-core: #5C7A3E;                 /* oficial: fills grandes, fondos claros, texto >=24px */
  --green-bright: #8FBF4D;               /* texto/UI/CTA sobre oscuro — 9.1:1 en ink-1 */
  --green-tint: rgba(143, 191, 77, 0.14);/* fondo de chips y tags */
  --green-line: rgba(143, 191, 77, 0.35);/* bordes activos, focus interno */
  --whatsapp: #25D366;

  /* ===== TEXTO ===== */
  --text-hi:  #F2F5F7;
  --text-mid: #A8B3BC;
  --text-low: #76828D;   /* metadatos, captions mono — 4.99:1 sobre ink-1, usar >=13px */
  --text-on-green: #0A0C0E;
  /* Derivado (no altera los valores de §2.2): el rol "low" sobre superficies más
     claras que ink-1 falla AA con --text-low. Medido con la fórmula WCAG 2.x:
       --text-low #76828D sobre ink-3 #161B21 .................... 4.41:1  FALLA
       --text-low sobre ink-2 + tinte navy 0.16 = rgb(13,31,41) .. 4.30:1  FALLA
       --text-low-card #828E99 sobre ink-3 ....................... 5.18:1  AA
       --text-low-card sobre ink-2 + tinte navy 0.16 ............. 5.04:1  AA
     Mismo matiz (~205°). Es el rol "low" de toda superficie elevada o teñida:
     ink-3 (drawer, inputs) y .section--navy. §6.8 pide verificar cada par nuevo. */
  --text-low-card: #828E99;

  /* ===== TIPOGRAFÍA ===== */
  --font-sans: "Montserrat", -apple-system, "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", "SF Mono", ui-monospace, monospace;

  --fs-display: clamp(2.625rem, 1.1rem + 6.4vw, 5.75rem);
  --fs-h2:      clamp(1.875rem, 1rem + 3.2vw, 3.25rem);
  --fs-h3:      clamp(1.25rem, 1rem + 1.1vw, 1.625rem);
  --fs-lead:    clamp(1.0625rem, 1rem + 0.45vw, 1.3125rem);
  --fs-body:    1.0625rem;
  --fs-small:   0.875rem;
  --fs-mono:    0.8125rem;
  --fs-mono-xs: 0.6875rem;
  /* MODERNIZACIÓN v2 — el peldaño de escala que faltaba entre lead y h2, y la
     escala grande de acrónimos/números (blancos, no verdes: §P1). */
  --fs-index: clamp(1.625rem, 1rem + 2.6vw, 2.75rem);  /* 26→44px */
  --fs-acr:   clamp(2.75rem, 1.8rem + 3.6vw, 4.5rem);  /* 44→72px */

  --lh-display: 1.04;
  --lh-h2: 1.12;
  --lh-h3: 1.3;
  --lh-body: 1.65;
  --lh-mono: 1.5;

  --track-display: -0.025em;
  --track-h2: -0.015em;
  --track-body: 0;
  --track-mono-label: 0.09em;

  /* ===== ESPACIADO (base 4px) ===== */
  --sp-1: 0.25rem;  --sp-2: 0.5rem;   --sp-3: 0.75rem;  --sp-4: 1rem;
  --sp-6: 1.5rem;   --sp-8: 2rem;     --sp-12: 3rem;    --sp-16: 4rem;
  --sp-24: 6rem;    --sp-32: 8rem;
  --sp-section: clamp(6rem, 5rem + 8vw, 10rem);

  /* ===== RADIOS ===== */
  --r-xs: 2px;    --r-sm: 4px;    --r-md: 8px;    --r-lg: 12px;
  --r-pill: 999px;

  /* ===== BORDES Y ELEVACIÓN ===== */
  --border-1: 1px solid var(--ink-4);
  --border-2: 1px solid var(--ink-5);
  --shadow-drawer: 0 24px 64px rgba(0, 0, 0, 0.55);
  --shadow-card: 0 8px 28px rgba(0, 0, 0, 0.35);
  --glow-cta: 0 0 0 1px rgba(143,191,77,0.4), 0 8px 24px rgba(143,191,77,0.18);

  /* ===== SISTEMA DE PROFUNDIDAD Y LUZ v2 (§P2) =====
     "Planta de noche donde brilla lo que opera": superficies con canto de luz y
     un radial suave; atmósfera navy por sección; viñeta que hunde los bordes.
     Ninguna capa sube la luminancia de fondo > 0.03 (contraste AA intacto). */
  --lum-edge:  rgba(255,255,255,0.055);
  --lum-soft:  rgba(255,255,255,0.03);
  --navy-spot: rgba(0,90,130,0.14);
  --vignette:  radial-gradient(120% 90% at 50% 0%, transparent 55%, rgba(5,6,7,0.55) 100%);

  /* ===== MOTION ===== */
  --ease-out: cubic-bezier(0.215, 0.61, 0.355, 1);
  --ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-move: cubic-bezier(0.77, 0, 0.175, 1);
  --dur-micro: 120ms;  --dur-hover: 180ms;  --dur-ui: 240ms;
  --dur-panel: 320ms;  --dur-reveal: 550ms; --dur-draw: 700ms;

  /* ===== FÍSICA DE MOTION v2 (MOTION §4.2) =====
     Espejados en motion.js. NO sustituyen a los tokens de arriba: los extienden.
     Springs SOLO críticamente amortiguados (ζ=1.0). Un robot industrial que
     rebota al llegar a posición es un robot descalibrado. */
  --servo-move:   cubic-bezier(0.645, 0.045, 0.355, 1);  /* power3.inOut — viajes con destino */
  --servo-settle: cubic-bezier(0.165, 0.84, 0.44, 1);    /* power4.out  — el elemento LLEGA */
  --servo-brake:  cubic-bezier(0.215, 0.61, 0.355, 1);   /* power2.out  — salidas y cierres */
  --follower-k: 170;      /* spring del carro y de la barra: ζ = 26/(2·√170) ≈ 1.0 */
  --follower-c: 26;
  --follower-m: 1;
  /* NO hay token de inercia de scroll: el scroll es NATIVO y no se interpola
     (MOTION §2.2/§4.3, decisión del dueño 16-07-2026). No reintroducir. */
  --stagger-el: 70ms;     /* elementos */
  --stagger-card: 90ms;   /* tarjetas y grids */
  --stagger-chip: 40ms;   /* chips dentro de una tarjeta */
  --stagger-line: 90ms;   /* líneas de titular (SplitText) */
  --stagger-char: 16ms;   /* SOLO índices mono de sección */
  --dur-drawer-in: 380ms;
  --dur-drawer-out: 240ms;

  /* ===== LAYOUT ===== */
  --container: 75rem;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --grid-gap: 1.5rem;

  /* ===== Z-INDEX ===== */
  --z-raised: 10;   --z-nav: 100;    --z-whatsapp: 150;
  --z-backdrop: 200; --z-drawer: 210; --z-skiplink: 400;

  color-scheme: dark;
  accent-color: var(--green-bright);
}

::selection { background: var(--green-bright); color: var(--ink-0); }

/* ---------- 3. RESET ----------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
/* `scroll-behavior: smooth` SOLO fuera del tier con motion, y solo para el salto
   de ancla (no afecta a la rueda). Bajo `.has-motion` el viaje de las anclas lo
   escribe el tween de motion.js frame a frame: si el navegador además suavizara
   cada `scrollTo()`, los dos se pelearían y volvería la sensación de arrastre.
   Ningún otro `scroll-behavior: smooth` en el sitio. */
html:not(.has-motion) { scroll-behavior: smooth; }
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }
@media (prefers-reduced-motion: reduce) { html, html:not(.has-motion) { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ink-1);
  color: var(--text-hi);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
body.is-locked { overflow: hidden; }

/* Capa de partículas de atmósfera (js/ambient.js la crea). Fixed detrás del
   contenido: z-index 0 la deja bajo el texto (.container va a z-index 1) y bajo
   la nav; las secciones de fondo transparente la dejan ver, las opacas la tapan.
   Se crea solo con JS y sin reduced-motion; igual se oculta por CSS por si el
   ajuste cambia en caliente. */
.ambient {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .ambient { display: none; } }

/* ---------- Banner de consentimiento de cookies (js/consent.js) --------- */
.cookie-banner {
  position: fixed;
  left: 50%;
  bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 300;
  width: min(52rem, calc(100% - 2rem));
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  flex-wrap: wrap;
  padding: var(--sp-4) var(--sp-6);
  background: var(--ink-3);
  border: var(--border-1);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-card);
  animation: cookie-in 400ms var(--ease-out) both;
}
@keyframes cookie-in {
  from { opacity: 0; transform: translateX(-50%) translateY(14px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.cookie-banner__text { flex: 1 1 18rem; margin: 0; font-size: var(--fs-small); color: var(--text-mid); }
.cookie-banner__actions { display: flex; gap: var(--sp-3); flex: 0 0 auto; }
@media (max-width: 559px) {
  .cookie-banner { flex-direction: column; align-items: stretch; }
  .cookie-banner__actions { justify-content: stretch; }
  .cookie-banner__actions .btn { flex: 1; }
}
@media (prefers-reduced-motion: reduce) { .cookie-banner { animation: none; } }

/* ---------- Página legal (política de privacidad) ---------------------- */
.legal { max-width: 44rem; margin: 0 auto; padding: calc(72px + var(--sp-16)) var(--gutter) var(--sp-24); }
.legal__back { display: inline-block; margin-bottom: var(--sp-8); }
.legal h1 { font-size: var(--fs-h2); letter-spacing: var(--track-h2); margin-bottom: var(--sp-3); }
.legal__meta { color: var(--text-low); margin-bottom: var(--sp-12); }
.legal h2 { font-size: var(--fs-h3); margin: var(--sp-12) 0 var(--sp-4); }
.legal p { color: var(--text-mid); margin-bottom: var(--sp-4); }
.legal ul { margin: 0 0 var(--sp-4); padding-left: var(--sp-6); display: grid; gap: var(--sp-2); }
.legal li { color: var(--text-mid); }
/* Al bloquear el scroll desaparece la barra clásica (Windows/Linux) y el viewport
   se ensancha `--sbw`. El <body> ya se compensa con padding-right desde JS, pero
   los elementos `fixed` se estiran al viewport nuevo y su contenido saltaría
   lateralmente en el frame de apertura. Se les recorta el mismo ancho por la
   derecha. Con overlay scrollbars (macOS/móvil) --sbw = 0 y no hay efecto. */
body.is-locked .nav,
body.is-locked .progress { right: var(--sbw, 0px); }

h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg, canvas { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
i, em { font-style: normal; }

:focus-visible {
  outline: 2px solid var(--green-bright);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}

/* ---------- 4. UTILIDADES ------------------------------------------------ */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 400;
  line-height: var(--lh-mono);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}
.skip-link {
  position: fixed;
  top: var(--sp-2); left: var(--sp-2);
  z-index: var(--z-skiplink);
  background: var(--green-bright);
  color: var(--text-on-green);
  font-weight: 600;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  transform: translateY(-160%);
  transition: transform var(--dur-ui) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

/* Grano de planta — overlay fijo, hero y footer */
.grain::after {
  content: "";
  position: absolute; inset: 0;
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAMFBMVEX8/PzY2Ni0tLSQkJBsbGxISEgkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFZSEFAAAGVUlEQVR42g2UN7dEABhEP2FpxdXKWrsWrawVVytrhcXff6+eYuacO+dCore4bddeaPzo2FAthsqnbtrkBV/kWOM1iNz11se36/SnWGnYcrSv9IbLO9bXHPEBAlR/bVkJV0onCD7OcbkRVtLKqUe2H45XLUhj90cME5XsutdziWwXPYtUG4+7+iad2xO4pZOZoy3pWfuZtBTRIeoOJVlzkJpYHT5g9H+ijfgl5mPKjV4eevZPiuE7yUW4bqQJ2Gji0aGmn/xP4pUsdH/3oznoZX373ehrJ6R3LkV80WESz91ya1vf81NNOldFyDwQLgWR/zo/vfpI9OzdzD08Zh2x24j6hVkMDnLAU7Hvpv+KaCRJ+iarGaoIwsfRZWfY7e78QJrpgVf1S1f8EEVrg1ckhEX/Qb2IiVBixkF0HkF46SjKrofgDdWQbj6bm0cdok+c1T4Qx4EjfX2hDcL4ndJv15eTcQrjKSvxsN4niDsJES0buy+kVMPgELps2CorRwWa4W0vg2Pr3/Uwfs4BVYqnaQTXAZ9UE+PYyY4gD6CzqDJFG/SnKxfcMsMKXVmv/otwSH3dggBC01ZEzOe9LQjZ6vTWcDfZ72iRi/NbsQ1g9rP4P7xyB7WxKDog/zH8KvCSh4Wi/MiBaQruwyK1swdozbrijKSoEKi+3wuHcfoX9CFpEvo7ktHM/JWDtsBleHnISrVw+mhQgTN+J+SpiBm9fCDXiJK9zqVcz2XPcD5iI3CWf5Q3/VKl3Zg4IjywL34Gn1Rh2m1MWhqwx2H7/51PzC+TIRlXK0bw+bakz7DKid5BvYn5V35OCbcXprwtWO5LT2kQo/Y2xxw34SPe93TU20I+LQfTxI71GXrVo744DqN3eXivFGNolUOueBCGUf7NaCOPGkmq2t997TUkIrzU5uVRruOQRziVdTzSooKmORZM87ZDJvq9plfvo3QqfOCTGf26qA6HYL+cGBkUkJ3RXtvvhR5TNYhBsNRvQkhQzFahZr52CmpBBmcb8c2Rjo2+lP9E1N2dWvfIT/beBcjnlh7IKbJ/T1+wKRPhj48yn/GtkW+9eojwbKxZa49pi+Oi4C//nqg2IroFpZxdHrAcSpOL8Zn5HupVS8XbqjlSnlK3XyvZz77fCt5Qo9gejIGQEmPG/zi/J42YSdVJmsoFGnCZ9TyioA/D1ZyCmXpdiIFl6tffDQUKkYCpddJSTA1M7vu3MTU/JytEQK0hD3N3qzxAmlYDfs6J2A40S5U4gSyVzZ11j3ieuxtBPsgvOCxgguW0MgyPyKIV86mXifiJh7YEbFtc/Ms98CSIs0MlutUUHrW7lpq87rkpAj/8i3AS90ESSKUPl++8J9XpDyczOyHWvCBmJe4x/WvMi2PfxImLe7tN8vv+84+8eqhAwhfpg/gyzRD3v5Vi7Sc+BXuRIqSedL8zIDYYrGDWiGpWtg/3audE1mBtpVhiGjWWFKw3h1OSsZd20pUvhnjAdxPdlP3/RNDFTA4sxVH14bNX7XAqHxgHpiF0u9X6cTG8S0ZAnDZ2TeVV1GmRxl15/w60cFvaD1D+o+06lC1KFdmtW7/ykxRB+hXr4R29bi8jIrkTLdCUVjEKNWr4cz1KOU/k1EAIqXqg7H3VjwGSslCMrKh9ssBRwdJsrxOvqJNKe58WirKBX67E1uLeeNRKkekaPO7PYxKnVzq65WseoLGSq2D1UEhIJ5hAleYnx6VKJT844cWoKWDP/qwnwoUgO7ZjD+u030RpUW7uxZNGMUDeX44zNOn9a0/Guh1Cna/PEejdcNqayOhAhbHxXdtRLsoMX3nOEFElawqWoosHRzcYCM/YtX3Jy71ipCv3fkJs8rUu87k4yhvjwVE87F7SkDk6xGR+cTzRvEfLXCZE6V2fHSCei9z/NeF+ohue68ylVMowXlCOQWvpzAD0zR6UXcZEiHhKi0N23OngcWGxVpgllG+Q38hFkBSyZp5M5lv4JD+eJhZz7ikq76EJmGO/KTgWVIE9Van+mLRDM6Jr651XwNG8Adj7NPeCOq3qNv6pfzyijRnPWCDOGkUwZphdfOSllWW5s3rmnPTdCbl1HuRvO1/GEAkwEjfooUiiCtq3iZflWhcQBW/ms8/YjvwA9yM/l1Y1HL/3n6b5rh9y6XIUE+i6S0ut+geG0+OkzqUQswAAAABJRU5ErkJggg==");
  background-size: 64px 64px;
  opacity: 0.03;
  mix-blend-mode: overlay;
  pointer-events: none;
  z-index: 1;
}

/* Marcas de registro (crosshairs de plano técnico) */
.ticks { position: relative; }
/* Los crosshairs se pintan con currentColor del PROPIO pseudo-elemento (no del
   card, para no arrastrar el color del texto): así el hover de §5.3 anima `color`
   —permitido por §4.4— y no `background-image`, que §4.5 prohíbe. */
.ticks::before, .ticks::after {
  content: "";
  position: absolute;
  width: 9px; height: 9px;
  pointer-events: none;
  color: var(--ink-5);
  background:
    linear-gradient(currentColor, currentColor) center / 100% 1px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1px 100% no-repeat;
  transition: color var(--dur-hover) ease;
}
.ticks::before { top: 8px; left: 8px; }
.ticks::after { bottom: 8px; right: 8px; }

/* ---------- 4.1 SUPERFICIE ELEVADA E1 (§P2) -----------------------------
   Composición ESTÁTICA de fondo (no animada): fill --ink-3 + canto superior de
   luz de 1px + radial suave sup-izq + borde hairline + radio. Es "lo que opera"
   captando luz de la nave. La sombra --shadow-card se aplica SOLO en 2 usos
   (fila destacada de servicios y panel de contacto). */
.surf-e1 {
  position: relative;
  background-color: var(--ink-3);
  background-image:
    linear-gradient(var(--lum-edge), var(--lum-edge)),
    radial-gradient(420px 260px at 18% 0%, var(--lum-soft), transparent 70%);
  background-repeat: no-repeat, no-repeat;
  background-size: 100% 1px, 100% 100%;
  background-position: top left, top left;
  border: var(--border-1);
  border-radius: var(--r-md);
}

/* ---------- 4.2 CAPA DE ATMÓSFERA POR SECCIÓN (§P2) ---------------------
   Un radial navy por sección numerada, alternando izq/der para bajar en zigzag.
   Deriva sutil con el scroll la escribe motion.js (translateY, solo esta capa).
   z-index:0 → detrás del contenido, que sube a z-index:1 (regla de abajo). */
.section__light {
  position: absolute;
  z-index: 0;
  top: 6%;
  left: -8%;
  width: min(900px, 78%);
  height: min(600px, 72%);
  background: radial-gradient(50% 50% at 50% 50%, var(--navy-spot), transparent 70%);
  pointer-events: none;
}
.section__light--right { left: auto; right: -8%; }
/* El contenido real de cada sección se apoya sobre la capa de luz. */
.section > .container { position: relative; z-index: 1; }

/* ---------- 5. TIPOGRAFÍA ------------------------------------------------ */
.display {
  font-size: var(--fs-display);
  font-weight: 800;
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  text-wrap: balance;
}
.h2 {
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: var(--lh-h2);
  letter-spacing: var(--track-h2);
  text-wrap: balance;
  max-width: 22ch;
}
.h3 {
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: var(--lh-h3);
  letter-spacing: -0.01em;
}
.lead {
  font-size: var(--fs-lead);
  color: var(--text-mid);
  max-width: 62ch;
}
.body { color: var(--text-mid); max-width: 62ch; }

/* ---------- 6. BOTONES --------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 0.875rem 1.375rem;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  /* §4.5: cero animación de box-shadow. El --glow-cta de §2.2 sigue siendo un
     estado legítimo del hover — se aplica, no se interpola. */
  transition: transform var(--dur-micro) ease,
              background-color var(--dur-hover) ease,
              border-color var(--dur-hover) ease,
              color var(--dur-hover) ease;
}
.btn__arrow {
  display: inline-block;
  transition: transform 150ms ease;
}
.btn--primary {
  background: var(--green-bright);
  color: var(--text-on-green);
}
.btn--ghost {
  border-color: var(--ink-5);
  color: var(--text-hi);
}
.btn--sm { padding: 0.5rem 0.875rem; font-size: var(--fs-mono); }
.btn:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover { box-shadow: var(--glow-cta); }
  .btn--ghost:hover { border-color: var(--green-line); color: var(--green-bright); }
  .btn:hover .btn__arrow { transform: translateX(4px); }
}

/* Link con subrayado que se dibuja */
.ulink {
  position: relative;
  color: var(--green-bright);
  font-weight: 500;
}
.ulink::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) {
  .ulink:hover::after { transform: scaleX(1); }
}
.ulink:focus-visible::after { transform: scaleX(1); }

/* Chips y tags */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.chip {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: var(--text-mid);
  background: rgba(255, 255, 255, 0.03);
  border: var(--border-1);
  border-radius: var(--r-xs);
  padding: 0.3rem 0.5rem;
  line-height: 1.35;
}
.tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: var(--green-bright);
  background: var(--green-tint);
  border: 1px solid var(--green-line);
  border-radius: var(--r-xs);
  padding: 0.25rem 0.5rem;
}

/* ---------- 7. REVEAL ---------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.reveal.is-in { opacity: 1; transform: none; }
.no-js .reveal { opacity: 1; transform: none; }

/* ---------- 8. BARRA DE PROGRESO ---------------------------------------- */
.progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: calc(var(--z-nav) + 1);
  pointer-events: none;
}
.progress__bar {
  display: block;
  height: 100%;
  background: var(--green-bright);
  transform: scaleX(0);
  transform-origin: left;
}

/* ---------- 9. NAV ------------------------------------------------------- */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-nav);
  background: transparent;
  border-bottom: 1px solid transparent;
  /* §4.5 prohíbe el blur animado: `backdrop-filter` NO se transiciona (además
     `none -> blur(12px)` no es interpolable, así que era código muerto). §4.3 se
     cumple con el fade de fondo/borde a 200ms, que es lo único que se percibe. */
  transition: background-color 200ms ease, border-color 200ms ease;
}
.nav.is-solid {
  background: rgba(10, 12, 14, 0.82);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: var(--border-1);
}
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  min-height: 72px;
}
/* §5.1 — LA LÍNEA DEL LOGO. `line-height: 0` + width fija hacen que la caja del
   enlace mida exactamente lo que el PNG (132x66): sin eso los % del ::after se
   calculan sobre una caja mayor y la línea cae desalineada.
   Geometría MEDIDA sobre logo_blanco.png (400x200, RGBA): la línea verde ocupa
   las filas 115-123 (top 57.5%, alto 4.5%) y las columnas 1-397 (left 0.25%,
   ancho 99.25%). El mask recorta esa banda del PROPIO PNG y el ::after la
   redibuja en su sitio: una sola línea, nunca dos. Se recorta el pixel en vez de
   taparlo con un bloque del color del fondo — así el gesto es independiente del
   fondo (nav transparente sobre el hero, nav sólida tras 60px, footer).
   Todo bajo `.js`: sin JS el PNG se sirve intacto y no falta nada. */
.nav__logo {
  position: relative;
  display: block;
  flex: none;
  width: 132px;
  line-height: 0;
  /* Capa de composición propia: sin esto, el `backdrop-filter: blur` de .nav.is-solid
     corrompe el mask-image del logo al scrollear y reaparece la línea cruda del PNG
     (más larga, con cápsulas). translateZ(0) lo aísla del filtro del ancestro. */
  transform: translateZ(0);
  isolation: isolate;
}
.nav__logo img { width: 132px; height: auto; }
.js .nav__logo img {
  -webkit-mask-image: linear-gradient(to bottom,
    #000 0 57.5%, transparent 57.5% 62%, #000 62% 100%);
  mask-image: linear-gradient(to bottom,
    #000 0 57.5%, transparent 57.5% 62%, #000 62% 100%);
}
.js .nav__logo::after {
  content: "";
  position: absolute;
  left: 0.25%;
  width: 99.25%;
  top: 57.5%;
  height: 4.5%;
  background: var(--green-core);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 600ms var(--ease-out-strong) 200ms;
}
.js .nav__logo.is-drawn::after { transform: scaleX(1); }

.nav__links { display: flex; align-items: center; gap: var(--sp-6); }
.nav__link {
  position: relative;
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--text-mid);
  padding: var(--sp-2) 0;
  transition: color var(--dur-hover) ease;
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--green-bright);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) {
  .nav__link:hover { color: var(--text-hi); }
  .nav__link:hover::after { transform: scaleX(1); }
}
.nav__link[aria-current="true"] { color: var(--text-hi); }
.nav__link[aria-current="true"]::after { transform: scaleX(1); }

.nav__end { display: flex; align-items: center; gap: var(--sp-4); }
.lang {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  border: var(--border-1);
  border-radius: var(--r-pill);
  padding: 0 var(--sp-1);
}
/* §6.8: `--text-low` nunca bajo 13px -> el switcher usa --fs-mono (13px), no
   --fs-mono-xs (11px, reservado a índices y ticks en §2.2). De paso el enlace
   toma caja propia (36px de alto) en vez de heredar el padding del contenedor:
   la píldora conserva su métrica visual y crece la zona golpeable. */
.lang a {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding-inline: var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--track-mono-label);
  color: var(--text-low);
  transition: color var(--dur-hover) ease;
}
/* §2.1/§6.2 — el idioma activo se marca con TEXTO y superficie sutil, nunca con
   un bloque verde: el verde es "lo que está operando", no cromo de navegación.
   Un switcher relleno de --green-bright gastaba el acento en el mueble y empujaba
   el verde hacia el 5% de viewport que §6.2 fija como techo. El contraste activo
   /inactivo lo da el par --text-hi (18:1) vs --text-low (5:1), que es suficiente
   por sí solo; la superficie al 6% solo lo refuerza. */
.lang a[aria-current="true"] {
  color: var(--text-hi);
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--r-pill);
}
@media (hover: hover) and (pointer: fine) { .lang a:hover { color: var(--text-hi); } }
.lang__sep { color: var(--ink-5); font-size: var(--fs-mono); }

.nav__toggle {
  display: none;
  width: 44px; height: 44px;
  align-items: center;
  justify-content: center;
  border: var(--border-1);
  border-radius: var(--r-sm);
}
.nav__toggle span {
  position: relative;
  display: block;
  width: 18px; height: 1.5px;
  background: var(--text-hi);
  transition: transform var(--dur-ui) var(--ease-out), opacity var(--dur-ui) var(--ease-out);
}
.nav__toggle span::before, .nav__toggle span::after {
  content: "";
  position: absolute;
  left: 0;
  width: 18px; height: 1.5px;
  background: var(--text-hi);
  transition: transform var(--dur-ui) var(--ease-out);
}
.nav__toggle span::before { transform: translateY(-6px); }
.nav__toggle span::after { transform: translateY(6px); }
.nav__toggle[aria-expanded="true"] span { transform: rotate(45deg); }
.nav__toggle[aria-expanded="true"] span::before { transform: rotate(-90deg); }
.nav__toggle[aria-expanded="true"] span::after { transform: translateY(0) rotate(-90deg); opacity: 0; }

@media (max-width: 959px) {
  .nav__links { display: none; }
  .nav__end .btn--primary { display: none; }
  .nav__toggle { display: inline-flex; }
}

/* Menú móvil full-screen */
.mobilemenu {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-nav) - 1);
  background: var(--ink-0);
  padding: calc(72px + var(--sp-8)) var(--gutter) var(--sp-8);
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity var(--dur-ui) var(--ease-out), transform var(--dur-ui) var(--ease-out);
  overflow-y: auto;
}
.mobilemenu[hidden] { display: none; }
.mobilemenu.is-open { opacity: 1; transform: none; }
.mobilemenu a.mobilemenu__link {
  font-size: var(--fs-h3);
  font-weight: 600;
  color: var(--text-hi);
  padding: var(--sp-2) 0;
  border-bottom: var(--border-1);
}
.mobilemenu .btn { justify-content: center; }

/* ---------- 10. SECCIONES ------------------------------------------------ */
.section, .hero, main[id] { scroll-margin-top: 88px; }
.section { padding-block: var(--sp-section); position: relative; border-top: var(--border-1); }
/* §P1 — se elimina la alternancia ink-1/ink-2 (imperceptible): todo el cuerpo
   corre sobre --ink-1 y la separación la hace el filete full-width de arriba.
   Fondos REALES que se conservan: hero, statement, Seguridad (navy), banda,
   footer. .section--alt queda neutra para no romper el marcado bilingüe. */
.section--alt { background: transparent; }
.section--navy {
  background: var(--ink-2);
  background-image: radial-gradient(900px 600px at 20% 0%, rgba(0, 90, 130, 0.16), transparent 70%);
  isolation: isolate;
}
/* Viñeta (§P2): hunde los bordes superiores de Seguridad. Va en ::before para no
   chocar con el grano (que ya vive en ::after vía la clase .grain). */
.section--navy::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--vignette);
}
.section__head { margin-bottom: var(--sp-12); }
.section__index {
  color: var(--text-low);
  margin-bottom: var(--sp-3);
}
/* §4.3.2 — jerarquía dato-vs-rótulo de HMI real: el índice es el dato (verde,
   500) y el nombre de la sección el rótulo (--text-low, 400). El texto va en caja
   real en el HTML y sube a versales por CSS: hornear "01 — SERVICIOS" en el
   marcado hace que los lectores de pantalla deletreen las versales. */
.sec-index__num { color: var(--green-bright); font-weight: 500; }
/* El tinte navy de §3.6 aclara el fondo: --text-low caería a 4.30:1 (FALLA AA
   para 13px). El rol "low" de superficie teñida da 5.04:1 y conserva el matiz. */
.section--navy .section__index { color: var(--text-low-card); }
.section__rule {
  height: 1px;
  background: rgba(143, 191, 77, 0.6);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-draw) var(--ease-move);
  margin-bottom: var(--sp-8);
}
.section__rule.is-in { transform: scaleX(1); }
.no-js .section__rule { transform: scaleX(1); }
.section__lead { margin-top: var(--sp-6); }

/* ---------- 11. HERO ----------------------------------------------------- */
.hero {
  position: relative;
  background: var(--ink-0);
  background-image: radial-gradient(1200px 800px at 70% 20%, var(--navy-glow), transparent 70%);
  padding-top: calc(72px + var(--sp-16));
  padding-bottom: var(--sp-24);
  min-height: min(100svh, 900px);
  display: flex;
  align-items: center;
  overflow: hidden;
}
.hero__inner {
  position: relative;
  z-index: 2;
  width: 100%;
}
/* El bloque de texto corre hasta 62rem (992px) por encima de la zona del canvas
   que solo tiene retícula: el brazo vive a la DERECHA de su base (0.62w) y la
   máscara del lienzo desvanece la retícula antes de llegar al H1. Es lo que
   permite sostener el techo real de --fs-display (92px) en 2 líneas: el salto
   equilibrado de "Robótica industrial / que no se detiene." mide 9.664em -> 889px
   a 92px, y caben en 992px. §2.2 queda intacto. */
.hero__intro { max-width: min(100%, 62rem); }
/* Lienzo full-bleed (§4.2): la envolvente de alcance solo cierra entera en un
   lienzo apaisado; encajonarla al 55% derecho la recortaba. La máscara devuelve
   el peso visual a la derecha que pide §3.1 desvaneciendo la retícula antes del
   texto, y el arco barre todo el hero por detrás del H1. */
.hero__canvas-wrap {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* DELANTE del texto para el efecto "robot sobre las letras": el canvas limpia a
     transparente, así que solo el brazo/pieza-en-vuelo cruzan por delante del H1;
     lo denso (retícula, casas, sombra, partículas) se desvanece sobre la columna
     del texto por un gate por-fragmento en los shaders (uRes + gl_FragCoord.x).
     pointer-events:none → el texto/CTAs siguen seleccionables. */
  z-index: 3;
  pointer-events: none;
  contain: strict;
}
.hero__canvas {
  width: 100%;
  height: 100%;
  /* El canvas trae aspect-ratio intrínseco de sus atributos width/height: hay
     que anularlo o no llena el hero. */
  aspect-ratio: auto;
  /* Sin máscara CSS: el gate por-fragmento (shaders) hace el trabajo y así el brazo
     —el único que debe cruzar el texto— no se desvanece en el lado izquierdo. */
  -webkit-mask-image: none;
  mask-image: none;
}
.hero__kicker { color: var(--green-bright); margin-bottom: var(--sp-4); }
/* §2.2 intacto: el H1 consume --fs-display sin techo (92px en el tope del clamp).
   `text-wrap: balance` (heredado de .display) reparte el título en 2 líneas
   equilibradas dentro de los 992px de .hero__intro. */
.hero h1 {
  font-size: var(--fs-display);
  max-width: 20ch;
  margin-bottom: var(--sp-6);
  /* Escudo de contraste: el canvas va delante y un canto claro del brazo puede
     cruzar una letra; el halo oscuro sostiene la legibilidad sin assets. */
  text-shadow: 0 0 2px rgba(4, 10, 14, 0.6), 0 1px 10px rgba(4, 10, 14, 0.45);
}
.hero__lead {
  max-width: 52ch;
  margin-bottom: var(--sp-8);
  text-shadow: 0 0 2px rgba(4, 10, 14, 0.55), 0 1px 8px rgba(4, 10, 14, 0.4);
}
.hero__ctas { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-8); }
.hero__micro { color: var(--text-low); font-size: 0.75rem; }

/* Intro escalonada del texto del hero */
.js .hero__intro > * {
  opacity: 0;
  transform: translateY(24px);
}
.js .hero__intro.is-in > * {
  opacity: 1;
  transform: none;
  transition: opacity 600ms var(--ease-out-strong), transform 600ms var(--ease-out-strong);
  transition-delay: calc(var(--i, 0) * 80ms);
}
.js .hero__intro .hero__kicker { transform: none; }
.js .hero__intro.is-in .hero__kicker { transition: opacity 300ms var(--ease-out); }

@media (max-width: 959px) {
  /* §3.1 móvil: el canvas pasa a fondo tras el texto. Sin máscara — a ancho
     completo desvanecería el brazo, no la retícula sobrante. */
  .hero__canvas-wrap { width: 100%; opacity: 0.35; z-index: 1; }
  .hero__canvas { -webkit-mask-image: none; mask-image: none; }
  .hero__intro { max-width: 100%; }
  .hero { min-height: auto; padding-bottom: var(--sp-16); }
}

/* ---------- 12. FICHA DE CAPACIDAD -------------------------------------- */
.spec {
  border-top: var(--border-1);
  border-bottom: var(--border-1);
  background: var(--ink-1);
}
.spec__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.spec__cell {
  padding: var(--sp-8) var(--sp-6);
  border-left: var(--border-1);
}
.spec__cell:first-child { border-left: 0; padding-left: 0; }
.spec__label { color: var(--text-low); margin-bottom: var(--sp-3); }
.spec__value { font-weight: 600; font-size: var(--fs-body); color: var(--text-hi); line-height: 1.5; }
@media (max-width: 959px) {
  .spec__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .spec__cell { padding: var(--sp-6) var(--sp-4); }
  .spec__cell:nth-child(odd) { border-left: 0; padding-left: 0; }
  .spec__cell:nth-child(n + 3) { border-top: var(--border-1); }
}
/* §3.2 pide 2x2 en móvil: solo por debajo de 360px se apila en una columna. */
@media (max-width: 359px) {
  .spec__grid { grid-template-columns: 1fr; }
  .spec__cell { border-left: 0; padding-left: 0; }
  .spec__cell + .spec__cell { border-top: var(--border-1); }
}

/* ---------- 13. SERVICIOS ------------------------------------------------ */
.pillar + .pillar { margin-top: var(--sp-16); }
.pillar__label {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  color: var(--text-low);
  margin-bottom: var(--sp-6);
}
.pillar__label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--ink-4);
}
.pillar__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--grid-gap);
}
@media (max-width: 767px) { .pillar__grid { grid-template-columns: 1fr; } }

.card {
  position: relative;
  background: var(--ink-3);
  border: var(--border-1);
  border-radius: var(--r-md);
  padding: var(--sp-8);
  transition: transform var(--dur-hover) ease, border-color var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) {
  .svc:hover { transform: translateY(-4px); border-color: var(--green-line); }
  .svc:hover.ticks::before, .svc:hover.ticks::after { color: var(--green-bright); }
}
.svc:focus-within { border-color: var(--green-line); }
.svc--wide { grid-column: 1 / -1; }
/* Tarjeta destacada (LÍNEA PRINCIPAL) a dos columnas: estructura a la izquierda
   —icono, tag, título y el enlace al detalle— y el dato técnico a la derecha. A
   ancho completo con el título en una sola fila, la mitad derecha quedaba muerta.
   `display: contents` en .svc__foot promueve chips y "ver detalle" a items del
   grid para poder repartirlos entre ambas columnas. */
@media (min-width: 768px) {
  .svc--wide {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    grid-template-rows: auto auto 1fr auto;
    column-gap: var(--sp-16);
    row-gap: var(--sp-4);
    align-items: start;
  }
  .svc--wide .svc__head { grid-column: 1; grid-row: 1; margin-bottom: 0; }
  .svc--wide .svc__title { grid-column: 1; grid-row: 2; margin-bottom: 0; }
  .svc--wide .svc__desc {
    grid-column: 2;
    grid-row: 1 / 4;
    margin-bottom: 0;
    max-width: none;
  }
  .svc--wide .svc__foot { display: contents; }
  .svc--wide .svc__foot .chips { grid-column: 2; grid-row: 4; margin-top: var(--sp-6); }
  .svc--wide .svc__foot .svc__more {
    grid-column: 1;
    grid-row: 4;
    align-self: end;
    padding-top: var(--sp-6);
  }
  .svc--wide .svc__detail { grid-column: 1 / -1; }
}
.svc__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}
.svc__icon { flex: none; color: var(--ink-6); transition: color var(--dur-hover) ease; }
@media (hover: hover) and (pointer: fine) { .svc:hover .svc__icon { color: var(--green-bright); } }
.svc__title { margin-bottom: var(--sp-3); }
.svc__trigger {
  text-align: left;
  font: inherit;
  color: var(--text-hi);
  transition: color var(--dur-hover) ease;
}
.svc__trigger::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-md);
}
@media (hover: hover) and (pointer: fine) { .svc__trigger:hover { color: var(--green-bright); } }
.svc__desc { color: var(--text-mid); font-size: var(--fs-small); margin-bottom: var(--sp-6); max-width: 62ch; }
.svc__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-top: var(--sp-6);
}
.svc__more {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: var(--green-bright);
  white-space: nowrap;
}
.svc__detail[hidden] { display: none; }

/* --- Índice de servicios (rediseño): fila escaneable, detalle en el drawer ---
   Cada .svc conserva su .svc__trigger + .svc__detail (el drawer no cambia). La
   fila entera es el objetivo de clic: .svc__trigger::after {inset:0} se resuelve
   contra .svc--row (position:relative). Se neutraliza el lift de tarjeta heredado
   de `.svc:hover` porque en una lista una fila no "levanta". */
.svc-list { border-top: var(--border-1); }
.svc--row {
  position: relative;
  display: grid;
  grid-template-columns: var(--sp-8) minmax(0, 1fr) auto;
  column-gap: var(--sp-6);
  align-items: start;
  padding: var(--sp-8) var(--sp-3);
  border-bottom: var(--border-1);
  background: none;
  border-radius: 0;
  transition: background-color var(--dur-hover) ease, border-color var(--dur-hover) ease;
}
.svc__idx {
  grid-column: 1; grid-row: 1;
  color: var(--text-low);
  padding-top: 0.15em;
  transition: color var(--dur-hover) ease;
}
.svc--row .svc__title { grid-column: 2; grid-row: 1; margin: 0; }
.svc__label {
  display: inline-block;
  font-size: var(--fs-index);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.1;
  color: var(--text-hi);
  transition: transform var(--dur-hover) var(--ease-out), color var(--dur-hover) ease;
}
.svc__result {
  grid-column: 2; grid-row: 2;
  margin-top: var(--sp-2);
  color: var(--text-mid);
  font-size: var(--fs-small);
  line-height: 1.5;
  max-width: 64ch;
}
.svc--row .svc__more {
  grid-column: 3; grid-row: 1 / span 2;
  align-self: center;
  color: var(--text-low);
  transition: color var(--dur-hover) ease;
}
/* Fila destacada (LÍNEA PRINCIPAL) — superficie elevada E1 real (§P2). La clase
   .surf-e1 aporta fill/canto de luz/radial/borde/radio; aquí solo la sombra
   estática (1 de los 2 únicos usos de --shadow-card) y el respiro lateral. */
.svc--lead {
  /* Re-afirma la receta E1 aquí: la regla .svc--row (posterior a .surf-e1 en el
     archivo) resetea background/border-radius, y sin esto ganaría por orden. */
  background-color: var(--ink-3);
  background-image:
    linear-gradient(var(--lum-edge), var(--lum-edge)),
    radial-gradient(420px 260px at 18% 0%, var(--lum-soft), transparent 70%);
  background-repeat: no-repeat, no-repeat;
  background-size: 100% 1px, 100% 100%;
  background-position: top left, top left;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-card);
  padding-left: var(--sp-6);
  padding-right: var(--sp-6);
}
/* La fila destacada no debe empujar el borde superior de la lista */
.svc-list > .svc--lead:first-child { margin-top: var(--sp-3); }
/* El tag "Línea principal" vive AHORA en la fila (antes solo en el detalle):
   se apila sobre el título grande sin salir de la celda de la rejilla. */
.svc--lead .svc__title { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-3); }

@media (hover: hover) and (pointer: fine) {
  .svc--row:hover { transform: none; background: var(--ink-2); }
  .svc--row:hover .svc__label { transform: translateX(6px); }
  .svc--row:hover .svc__more,
  .svc--row:hover .svc__idx { color: var(--green-bright); }
}
.svc--row:focus-within { border-bottom-color: var(--green-line); }

@media (max-width: 767px) {
  .svc--row {
    grid-template-columns: 1.5rem minmax(0, 1fr) auto;
    column-gap: var(--sp-4);
    padding: var(--sp-4) var(--sp-2);
    min-height: 56px;
  }
  .svc__label { font-size: var(--fs-body); }
  .svc--lead { padding-left: var(--sp-3); padding-right: var(--sp-3); }
}

/* --- Statement "Más allá de la planta" (declaración, no sección numerada) --- */
.statement {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ink-0);
  background-image: radial-gradient(1200px 720px at 72% 18%, var(--navy-glow), transparent 70%);
  padding: var(--sp-section) 0;
  text-align: center;
}
/* §P3 — fondo cinematográfico: foto de planta velada + viñeta centrada que crea
   un pozo casi negro bajo el texto. AA medido en el peor píxel del bloque de
   texto: --text-hi ~18:1, --text-mid ~9:1 (el centro queda ~rgb(7,8,9)). */
.statement__photo {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center;
  --photo-o: 0.42;
  opacity: var(--photo-o);
}
.statement::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(78% 68% at 50% 50%,
      rgba(5, 6, 7, 0.9) 0%, rgba(5, 6, 7, 0.55) 60%, rgba(5, 6, 7, 0.4) 100%),
    linear-gradient(to bottom, rgba(5, 6, 7, 0.55) 0%, rgba(5, 6, 7, 0.8) 100%);
}
@media (max-width: 640px) {
  .statement__photo { --photo-o: 0.32; }
  .statement::before {
    background:
      radial-gradient(66% 56% at 50% 50%,
        rgba(5, 6, 7, 0.92) 0%, rgba(5, 6, 7, 0.5) 100%),
      linear-gradient(to bottom, rgba(5, 6, 7, 0.6) 0%, rgba(5, 6, 7, 0.85) 100%);
  }
}
.statement__inner { position: relative; z-index: 1; max-width: 46rem; }
.statement__kicker { color: var(--text-low); margin-bottom: var(--sp-6); }
.statement__title {
  font-size: var(--fs-display);
  font-weight: 800;
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  margin: 0 auto;
  max-width: 14ch;
}
.statement__rule {
  width: 72px;
  margin: var(--sp-8) auto;
  transform-origin: center;
}
.statement__body { color: var(--text-mid); max-width: 60ch; margin: var(--sp-8) auto 0; }
.statement__note {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1.65;
  color: var(--text-low);
  max-width: 58ch;
  margin: var(--sp-6) auto 0;
}
.statement__cta { margin-top: var(--sp-8); }

/* ---------- 14. DRAWER --------------------------------------------------- */
.backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-backdrop);
  background: rgba(5, 6, 7, 0.6);
  opacity: 0;
  transition: opacity var(--dur-panel) var(--ease-out-strong);
}
.backdrop[hidden] { display: none; }
.backdrop.is-open { opacity: 1; }
.backdrop.is-closing { opacity: 0; transition-duration: var(--dur-ui); }

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  z-index: var(--z-drawer);
  width: min(480px, 100vw);
  background: var(--ink-3);
  border-left: var(--border-1);
  box-shadow: var(--shadow-drawer);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--dur-panel) var(--ease-out-strong);
  /* §4.5: `will-change: transform` se pone en openDrawer() y se RETIRA al
     terminar la animación de cierre (main.js §6). No vive en CSS. */
  overscroll-behavior: contain;
}
.drawer[hidden] { display: none; }
.drawer.is-open { transform: none; }
.drawer.is-closing { transform: translateX(100%); transition-duration: var(--dur-ui); }
.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-6);
  border-bottom: var(--border-1);
}
.drawer__index { color: var(--text-low-card); }
.drawer__close {
  width: 40px; height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--border-1);
  border-radius: var(--r-sm);
  color: var(--text-mid);
  transition: color var(--dur-hover) ease, border-color var(--dur-hover) ease, transform var(--dur-micro) ease;
}
.drawer__close:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) {
  .drawer__close:hover { color: var(--green-bright); border-color: var(--green-line); }
}
.drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--sp-8) var(--sp-6);
}
.drawer__body h2 { margin-bottom: var(--sp-6); }
.drawer__body p { color: var(--text-mid); font-size: var(--fs-small); }
.drawer__body p + p { margin-top: var(--sp-4); }
/* Los subtítulos de `.svc__detail` son h3, no h4: en página el bloque está
   `hidden` y su único contexto real es el drawer, cuyo título es el h2
   #drawer-title. h3 evita el salto de nivel h2 -> h4 (WCAG 1.3.1). */
.drawer__body h3 {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: var(--text-low-card);
  margin-top: var(--sp-8);
  margin-bottom: var(--sp-3);
}
.drawer__body ul.dlist { display: grid; gap: var(--sp-2); }
.drawer__body ul.dlist li {
  position: relative;
  padding-left: var(--sp-6);
  color: var(--text-mid);
  font-size: var(--fs-small);
}
.drawer__body ul.dlist li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.7em;
  width: 8px; height: 1px;
  background: var(--green-bright);
}
.drawer__foot { padding: var(--sp-6); border-top: var(--border-1); }
.drawer__foot .btn { width: 100%; justify-content: center; }

@media (max-width: 640px) {
  .drawer {
    top: auto;
    left: 0;
    height: 92vh; height: 92svh;
    width: 100%;
    border-left: 0;
    border-top: var(--border-1);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    transform: translateY(100%);
  }
  .drawer.is-open { transform: none; }
  .drawer.is-closing { transform: translateY(100%); }
}

/* ---------- 15. EQUIPOS — BENTO DE SUPERFICIES E1 (§P2/§P3) --------------
   Antes: filas de tabla sobre --ink-2. Ahora: celdas elevadas E1 en un bento;
   la celda grande (Robots de 6 ejes) lleva una textura velada de tablero al pie.
   Se conservan las clases (.plat__row/.plat__idx/.plat__name/.plat__detail) para
   no tocar la coreografía de motion.js (reveal + pulso de relé del índice). */
.plat {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--grid-gap);
}
.plat__row {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-8);
  min-height: 14rem;
  transition: transform var(--dur-hover) var(--servo-settle),
              border-color var(--dur-hover) ease;
}
.plat__row--lead { grid-column: 1 / -1; min-height: 18rem; }
/* --text-low sobre superficie E1 (ink-3) cae a 4.41:1 -> se usa el rol de tarjeta. */
.plat__idx { color: var(--text-low-card); transition: color var(--dur-hover) ease; }
.plat__name {
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.25rem);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.1;
  margin-top: auto;
}
.plat__detail { color: var(--text-mid); }
/* Textura velada (tablero eléctrico) al pie de la celda grande: es textura, no
   foto protagonista — opacidad 0.4, desvanecido a transparente hacia arriba. */
.plat__texture {
  position: absolute;
  z-index: -1;
  inset: auto 0 0 0;
  width: 100%; height: 72%;
  object-fit: cover;
  object-position: center 40%;
  opacity: 0.4;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to top, #000 0%, transparent 100%);
  mask-image: linear-gradient(to top, #000 0%, transparent 100%);
}
@media (hover: hover) and (pointer: fine) {
  .plat__row:hover { transform: translateY(-4px) scale(1.01); border-color: var(--green-line); }
  .plat__row:hover .plat__idx { color: var(--green-bright); }
}
@media (max-width: 640px) {
  .plat { grid-template-columns: 1fr; }
  .plat__row--lead { grid-column: auto; }
}

/* ---------- 16. SECTORES ------------------------------------------------- */
.sectors {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--grid-gap);
}
@media (max-width: 959px) { .sectors { grid-template-columns: 1fr; } }
/* ---- FOTOS FRANCAS (§P3) ------------------------------------------------
   Se retira el desvanecido de 4 bordes y el velo+texto superpuesto: la foto es
   un rectángulo FRANCO (aspect 4:5 desktop / 3:2 móvil, borde hairline --ink-4,
   sin radio, duotono, opacidad plena) y el texto vive DEBAJO. Es fotografía,
   no marca de agua. Crosshairs .ticks en 2 esquinas del marco. */
.sector { display: flex; flex-direction: column; gap: var(--sp-2); }
.sector__frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  border: 1px solid var(--ink-4);
  margin-bottom: var(--sp-4);
}
.sector__photo {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center;
  /* Franca: descansa a 0.9 y sube a 1 en hover. motion.js lee este destino. */
  --photo-o: 0.9;
  opacity: var(--photo-o);
}
.sector__idx { color: var(--text-low); }
.sector__title { margin-bottom: var(--sp-1); }
.sector__desc { color: var(--text-mid); font-size: var(--fs-small); }
/* Hover: la foto respira (opacity 0.9→1) y avanza un punto (scale 1.03).
   Solo opacity/transform (§4.4/§4.5), nunca layout. */
@media (hover: hover) and (pointer: fine) {
  .sector__photo {
    transition: opacity var(--dur-hover) ease,
                transform var(--dur-hover) var(--servo-settle);
  }
  .sector:hover .sector__photo { opacity: 1; transform: scale(1.03); }
}
@media (max-width: 959px) {
  .sector__frame { aspect-ratio: 3 / 2; }
}

/* ---------- 17. PORTADA FULL-BLEED (§P3) --------------------------------
   Antes "banda" desvanecida; ahora una PORTADA franca entre Seguridad y Proceso:
   nave industrial a ~55vh, cantos hairline arriba/abajo, opacidad 0.9, duotono,
   grano encima (clase .grain en el marcado) y una sola línea mono abajo-izq que
   ecoa el kicker del hero (cero datos nuevos). Sin desvanecido de bordes. */
.band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  height: clamp(24rem, 55vh, 40rem);
  background: var(--ink-0);
  border-top: 1px solid var(--ink-4);
  border-bottom: 1px solid var(--ink-4);
}
.band__photo {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 55%;
  --photo-o: 0.9;
  opacity: var(--photo-o);
}
/* Velo inferior mínimo para sostener el AA de la línea mono sobre la foto. */
.band__caption {
  position: absolute;
  left: var(--gutter);
  bottom: var(--sp-6);
  z-index: 1;
  color: var(--text-hi);
  font-size: var(--fs-mono-xs);
  text-shadow: 0 1px 8px rgba(4, 10, 14, 0.85), 0 0 2px rgba(4, 10, 14, 0.8);
}

/* ---------- 18. SEGURIDAD ------------------------------------------------ */
.safety__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--grid-gap);
  margin-top: var(--sp-12);
}
@media (max-width: 959px) { .safety__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 479px) { .safety__grid { grid-template-columns: 1fr; } }
/* Ficha de seguridad = superficie E1 (§P2). El fill/borde/radio los da .surf-e1;
   aquí solo el padding y el hover servo (§P4). */
.safety__card {
  padding: var(--sp-8) var(--sp-6);
  transition: transform var(--dur-hover) var(--servo-settle),
              border-color var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) {
  .safety__card:hover { transform: translateY(-4px) scale(1.01); border-color: var(--green-line); }
}
/* Acrónimo grande en BLANCO, no verde: el verde grande sería "verde gamer"
   y rompería el techo del 5% de acento (§P1). */
.safety__acr {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-acr);
  letter-spacing: 0.02em;
  color: var(--text-hi);
  margin-bottom: var(--sp-3);
  line-height: 1;
}
.safety__def { color: var(--text-mid); font-size: var(--fs-small); }

/* ---------- 18. PROCESO -------------------------------------------------- */
.process__track { position: relative; margin-top: var(--sp-16); }
.process__rail {
  position: absolute;
  top: 5px; left: 0; right: 0;
  height: 1px;
  background: var(--ink-4);
}
.process__carriage {
  position: absolute;
  top: 1px; left: 0;
  width: 8px; height: 8px;
  background: var(--green-bright);
  border-radius: 1px;
  transform: translateX(0);
  /* §4.5: `will-change` lo pone/retira el IntersectionObserver de la sección
     (main.js §1) — solo mientras el riel está en viewport y hay motion. */
}
.process__stations {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--grid-gap);
}
.station { padding-top: var(--sp-8); position: relative; }
.station::before {
  content: "";
  position: absolute;
  top: 2px; left: 0;
  width: 1px; height: 8px;
  background: var(--ink-5);
  transition: background-color var(--dur-ui) ease;
}
.station.is-lit::before { background: var(--green-bright); }
.station__num {
  font-size: clamp(1.75rem, 1.2rem + 1.8vw, 2.5rem);
  line-height: 1;
  color: var(--text-low);
  margin-bottom: var(--sp-4);
  transition: color var(--dur-ui) ease;
}
.station.is-lit .station__num { color: var(--green-bright); }
.station__title { font-size: var(--fs-h3); font-weight: 600; letter-spacing: -0.01em; margin-bottom: var(--sp-2); }
.station__desc { color: var(--text-mid); font-size: var(--fs-small); margin-bottom: var(--sp-4); }

@media (max-width: 959px) {
  .process__track { margin-left: var(--sp-6); }
  .process__rail { top: 0; bottom: 0; left: -1.5rem; right: auto; width: 1px; height: auto; }
  .process__carriage { top: 0; left: calc(-1.5rem - 3.5px); }
  .process__stations { grid-template-columns: 1fr; gap: var(--sp-8); }
  .station { padding-top: 0; }
  .station::before { top: 0.5rem; left: calc(-1.5rem - 3.5px); width: 8px; height: 1px; }
}

/* ---------- 19. I+D ------------------------------------------------------ */
.rnd__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-12);
  align-items: center;
}
@media (max-width: 959px) { .rnd__inner { grid-template-columns: 1fr; } }
/* La columna de arte ahora lleva una FOTO tratada (visión artificial), no el
   motivo SVG (HERO-V2 §3). Mismo lenguaje que Sectores: duotono oscuro, máscara
   de desvanecido a negro por los cuatro bordes (sin marco ni radio grande),
   overlay --ink-0 para hundirla en la sección, grano encima (clase `grain`).
   No hay texto sobre la foto: el texto vive en la columna hermana. */
.rnd__art {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  min-height: 18rem;
  color: var(--ink-5);        /* por si el motivo de línea volviera a convivir */
}
@media (max-width: 959px) { .rnd__art { aspect-ratio: 16 / 9; min-height: 14rem; } }
.rnd__photo {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%; height: 100%;
  object-fit: cover;
  --photo-o: 0.88;            /* DESTINO de opacidad (lo lee motion.js) */
  opacity: var(--photo-o);
  /* Emerge de la oscuridad por los cuatro bordes: sin borde recto de "caja". */
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 18%, #000 66%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 14%, #000 86%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 18%, #000 66%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 14%, #000 86%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  background-size: cover;
  background-position: center;
}
/* Velo --ink-0 muy sutil: hunde la foto en la sección y sostiene el AA del texto
   de la columna vecina cuando el layout se apila en móvil. */
.rnd__art::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(5, 6, 7, 0.28) 0%, rgba(5, 6, 7, 0.10) 45%, rgba(5, 6, 7, 0.45) 100%);
}
.rnd h2 { margin-block: var(--sp-4) var(--sp-6); }

/* ---------- 20. CONTACTO ------------------------------------------------- */
.contact__inner {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: var(--sp-16);
  align-items: start;
}
@media (max-width: 959px) { .contact__inner { grid-template-columns: 1fr; gap: var(--sp-12); } }
.cinfo { display: grid; gap: var(--sp-6); margin-top: var(--sp-8); }
.cinfo__item { border-top: var(--border-1); padding-top: var(--sp-4); }
.cinfo__label { color: var(--text-low); margin-bottom: var(--sp-2); }
.cinfo__value { font-size: var(--fs-h3); font-weight: 600; letter-spacing: -0.01em; }

.form { display: grid; gap: var(--sp-4); }
/* Panel del formulario = superficie E1 (§P2). El fill/canto/borde/radio los da
   .surf-e1; aquí el padding y la sombra estática (2º y último uso de --shadow-card). */
.form.surf-e1 { padding: var(--sp-8); box-shadow: var(--shadow-card); }
@media (max-width: 640px) { .form.surf-e1 { padding: var(--sp-6); } }
.field { display: grid; gap: var(--sp-2); }
.field--half { grid-column: span 1; }
.form__row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }
@media (max-width: 640px) { .form__row { grid-template-columns: 1fr; } }
.label {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: var(--text-low);
}
.label .req { color: var(--green-bright); }
.input, .select, .textarea {
  width: 100%;
  background: var(--ink-3);
  border: var(--border-1);
  border-radius: var(--r-sm);
  padding: 0.75rem var(--sp-4);
  font-size: var(--fs-small);
  color: var(--text-hi);
  /* §4.5: el anillo de focus (box-shadow) se aplica sin interpolar. */
  transition: border-color var(--dur-hover) ease;
  min-height: 44px;
}
.textarea { min-height: 8.5rem; resize: vertical; line-height: 1.6; }
.select {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-low-card) 50%),
    linear-gradient(135deg, var(--text-low-card) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: var(--sp-12);
}
.input::placeholder, .textarea::placeholder { color: var(--text-low-card); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--green-line);
  box-shadow: 0 0 0 1px var(--green-line);
}
.input:focus-visible, .select:focus-visible, .textarea:focus-visible {
  outline: 2px solid var(--green-bright);
  outline-offset: 2px;
}
.field.is-invalid .input,
.field.is-invalid .select,
.field.is-invalid .textarea { border-color: #E2725B; }
.error {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: #F09A88;
  min-height: 1em;
}
.form__note { color: var(--text-low); font-size: var(--fs-small); }
.hp { position: absolute; left: -9999px; opacity: 0; }
.form__subject {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  border: 1px dashed var(--green-line);
  background: var(--green-tint);
  border-radius: var(--r-sm);
  padding: var(--sp-3) var(--sp-4);
}
.form__subject[hidden] { display: none; }
.form__subject-text {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: var(--green-bright);
}
.form__subject button { margin-left: auto; color: var(--text-mid); }

/* ---------- 21. FOOTER --------------------------------------------------- */
.footer {
  position: relative;
  background: var(--ink-0);
  border-top: var(--border-1);
  padding-block: var(--sp-16) var(--sp-8);
}
.footer__inner {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr)) minmax(0, 1fr);
  gap: var(--sp-8);
}
@media (max-width: 959px) { .footer__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 479px) { .footer__inner { grid-template-columns: 1fr; } }
/* Mismo tratamiento que .nav__logo/.aux__logo: la línea verde del PNG crudo trae
   cápsulas redondeadas que sobresalen del wordmark; se enmascara y se redibuja con
   extremos rectos al ancho de ROBRICKS. El <img> va envuelto en <span class="footer__logomark">. */
.footer__logomark {
  display: block;
  position: relative;
  width: 148px;
  line-height: 0;
  margin-bottom: var(--sp-6);
}
.footer__logomark img {
  display: block;
  width: 148px;
  height: auto;
  margin-bottom: 0;
  -webkit-mask-image: linear-gradient(to bottom,
    #000 0 57.5%, transparent 57.5% 62%, #000 62% 100%);
  mask-image: linear-gradient(to bottom,
    #000 0 57.5%, transparent 57.5% 62%, #000 62% 100%);
}
.footer__logomark::after {
  content: "";
  position: absolute;
  left: 0.25%;
  width: 99.25%;
  top: 57.5%;
  height: 4.5%;
  background: var(--green-core);
}
.footer__about { color: var(--text-mid); font-size: var(--fs-small); max-width: 34ch; }
.footer__title { color: var(--text-low); margin-bottom: var(--sp-4); }
.footer__list { display: grid; gap: var(--sp-3); }
.footer__list a, .footer__list span {
  color: var(--text-mid);
  font-size: var(--fs-small);
  transition: color var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) { .footer__list a:hover { color: var(--green-bright); } }
.footer__bottom {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-16);
  padding-top: var(--sp-6);
  border-top: var(--border-1);
  color: var(--text-low);
}

/* ---------- 22. WHATSAPP FLOAT ------------------------------------------ */
.whatsapp-float {
  position: fixed;
  right: calc(2rem + env(safe-area-inset-right, 0px));
  bottom: calc(2rem + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-whatsapp);
  width: 56px; height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--whatsapp);
  color: #052B15;
  border-radius: var(--r-pill);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  transition: transform var(--dur-hover) ease;
}
/* Con el menú móvil o el drawer abiertos el float queda fuera de la trampa de
   foco: si además se pintara encima (z 150 > z-nav-1 = 99) sería un control
   alcanzable con el ratón pero no con Tab. Se retira mientras haya modal. */
body.is-locked .whatsapp-float { display: none; }
@media (hover: hover) and (pointer: fine) { .whatsapp-float:hover { transform: scale(1.06); } }
.whatsapp-float:active { transform: scale(0.97); }
@media (max-width: 640px) {
  .whatsapp-float {
    right: calc(1.25rem + env(safe-area-inset-right, 0px));
    bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
    width: 52px; height: 52px;
  }
}

/* ---------- 23. PÁGINAS AUXILIARES -------------------------------------- */
.aux {
  min-height: 100vh; min-height: 100svh;
  display: grid;
  place-items: center;
  text-align: center;
  background: var(--ink-0);
  background-image: radial-gradient(1200px 800px at 70% 20%, var(--navy-glow), transparent 70%);
  padding: var(--sp-16) var(--gutter);
  position: relative;
}
.aux__inner { position: relative; z-index: 2; max-width: 44rem; }
/* El logo auxiliar recibe el MISMO tratamiento que .nav__logo: se enmascara la
   línea verde propia del PNG (que trae cápsulas redondeadas y "sobresale" del
   wordmark) y se redibuja con extremos rectos al ancho exacto de ROBRICKS.
   Pura CSS: las auxiliares no cargan un byte de JS (ver §23). El <img> va envuelto
   en un <span class="aux__logo"> porque los elementos reemplazados no pintan ::after. */
.aux__logo {
  display: block;
  width: 160px;
  margin: 0 auto var(--sp-8);
  position: relative;
  line-height: 0;
}
.aux__logo img {
  display: block;
  width: 160px;
  height: auto;
  -webkit-mask-image: linear-gradient(to bottom,
    #000 0 57.5%, transparent 57.5% 62%, #000 62% 100%);
  mask-image: linear-gradient(to bottom,
    #000 0 57.5%, transparent 57.5% 62%, #000 62% 100%);
}
.aux__logo::after {
  content: "";
  position: absolute;
  left: 0.25%;
  width: 99.25%;
  top: 57.5%;
  height: 4.5%;
  background: var(--green-core);
}
.aux h1 { margin-bottom: var(--sp-4); }
.aux .lead { margin-inline: auto; margin-bottom: var(--sp-8); }
.aux__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; }
.aux__contact { margin-top: var(--sp-12); color: var(--text-low); display: grid; gap: var(--sp-2); }

/* --- Entrada de las auxiliares (auditoría 16-07-2026) ---
   404 y success eran las dos únicas páginas del sitio que entraban en seco.
   Se resuelve en CSS PURO, no en motion.js, y es una decisión, no una comodidad:
   estas páginas no cargan un solo byte de JS —ni main.js, ni el vendor— y no van
   a empezar a hacerlo por una animación de entrada. Un @keyframes con `both` no
   necesita a nadie que lo dispare y no puede dejar el contenido invisible.
   Mismo gesto que el cuerpo: el elemento LLEGA (--servo-settle = power4.out),
   70 ms entre elementos, 40 ms entre los datos de contacto (son piezas del mismo
   bloque). La timeline cierra en ~1.0 s, bajo el techo de 1.4 s de MOTION §4.2. */
@keyframes rb-aux-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.aux__inner > *:not(.aux__contact) {
  animation: rb-aux-in var(--dur-reveal) var(--servo-settle) both;
  animation-delay: calc(var(--i, 0) * var(--stagger-el));
}
.aux__contact p {
  animation: rb-aux-in 420ms var(--servo-settle) both;
  animation-delay: calc(480ms + var(--i, 0) * var(--stagger-chip));
}

/* ---------- 24. REDUCED MOTION (matriz §4.6) ---------------------------- */
/* ==========================================================================
   MOTION v2 — enganches de la coreografía (MOTION §4)

   DOBLE SISTEMA, CERO REGRESIÓN. Todo lo de abajo cuelga de `.has-motion`, que
   solo pone motion.js cuando el vendor cargó de verdad. Sin esa clase —vendor
   caído, reduced-motion, saveData, sin JS— NADA de esto aplica y el sitio es,
   literalmente, el de hoy. `.no-vendor` no necesita reglas propias: su
   definición es la ausencia de estas.
   ========================================================================== */

/* El CSS y GSAP no pueden animar la misma propiedad a la vez: donde manda la
   timeline, la transición se retira. GSAP escribe estilos inline; sin esto cada
   `set()` suyo dispararía además la transición del CSS. */
.has-motion .reveal,
.has-motion .section__rule,
.has-motion .hero__intro.is-in > * {
  transition: none;
  transition-delay: 0s;
}
.has-motion .reveal { opacity: 1; transform: none; }

/* --- Ficha de capacidad: el borde izq se dibuja de arriba abajo (§4.5) ---
   El borde real se retira y lo reemplaza un ::before escalable: `border-left`
   no es animable sin disparar layout. --rb-sy lo mueve GSAP. */
@media (min-width: 360px) {
  .has-motion .spec__cell { position: relative; border-left-color: transparent; }
  .has-motion .spec__cell::before {
    content: "";
    position: absolute;
    top: 0; bottom: 0; left: -1px;
    width: 1px;
    background: var(--ink-4);
    transform: scaleY(var(--rb-sy, 1));
    transform-origin: top center;
  }
  .has-motion .spec__cell:first-child::before { display: none; }
}
@media (max-width: 959px) and (min-width: 360px) {
  .has-motion .spec__cell:nth-child(odd)::before { display: none; }
}

/* --- Cabecera de pilar: la línea se dibuja DESDE el rótulo (§4.5) ---
   Es un ::after, así que GSAP no puede tocarlo: mueve --rb-px y el pseudo lo lee.
   Sin `.has-motion` el ::after queda en scaleX(1) por el valor de reserva. */
.has-motion .pillar__label::after {
  transform: scaleX(var(--rb-px, 1));
  transform-origin: left center;
}

/* --- Estación del proceso: el tick es un contactor que cierra (§4.8) --- */
.has-motion .station::before {
  transform: scaleY(var(--rb-tick, 1));
  transform-origin: top center;
}
@media (max-width: 959px) {
  .has-motion .station::before {
    transform: scaleX(var(--rb-tick, 1));
    transform-origin: left center;
  }
}

/* --- Drawer: panel y backdrop siguen PAREADOS, ahora a 380ms (§4.7) --- */
.has-motion .drawer,
.has-motion .backdrop { transition-duration: var(--dur-drawer-in); }
.has-motion .drawer.is-closing,
.has-motion .backdrop.is-closing { transition-duration: var(--dur-drawer-out); }
/* El borde superior del footer se dibuja (scaleX 0→1, origin left). */
.has-motion .drawer__foot { position: relative; border-top-color: transparent; }
.has-motion .drawer__foot::before {
  content: "";
  position: absolute;
  top: -1px; left: 0; right: 0;
  height: 1px;
  background: var(--ink-4);
  transform: scaleX(var(--rb-sx, 1));
  transform-origin: left center;
}

/* --- M7: el campo se energiza al recibir el foco (§4.10) ---
   Vive en CSS a propósito: cuesta 0 KB, no depende del vendor y funciona en
   todos los tiers. */
.field .label {
  transition: color var(--dur-hover) var(--servo-brake),
              transform var(--dur-hover) var(--servo-brake);
}
.field:focus-within .label {
  color: var(--green-bright);
  transform: translateY(-1px);
}

/* --- M7: indicador de falla. El ÚNICO movimiento expresivo del sitio, y el
   único @keyframes: se justifica porque COMUNICA ESTADO, no estilo — es la
   sacudida de un campo rechazado en un HMI. Un ciclo, 200 ms, y se acabó.
   Se dispara solo en la transición válido→inválido (classList.toggle no
   reinicia la animación si la clase ya estaba). --- */
@keyframes rb-fault {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-3px); }
  75%      { transform: translateX(3px); }
}
.field.is-invalid .input,
.field.is-invalid .select,
.field.is-invalid .textarea {
  animation: rb-fault 100ms var(--servo-brake) 2;   /* 2 ciclos = 200 ms en total */
}

@media (prefers-reduced-motion: reduce) {
  /* Páginas auxiliares: el contenido ya está en su sitio, sin entrada. */
  .aux__inner > *:not(.aux__contact), .aux__contact p {
    animation: none !important;
    opacity: 1;
    transform: none;
  }
  /* La línea de la cabecera de pilar queda dibujada. */
  .has-motion .pillar__label::after { transform: scaleX(1) !important; }

  /* El indicador de falla se queda: el borde rojo ya comunica el estado. */
  .field.is-invalid .input,
  .field.is-invalid .select,
  .field.is-invalid .textarea { animation: none; }
  .field:focus-within .label { transform: none; }

  /* Reveals, líneas y stagger: visibles sin transición */
  .reveal, .js .hero__intro > *, .js .hero__intro.is-in > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0ms !important;
  }
  /* La línea del logo queda ya dibujada (el PNG sigue recortado: una sola línea). */
  .section__rule, .js .nav__logo::after,
  .js .nav__logo.is-drawn::after { transform: scaleX(1) !important; transition: none !important; }

  /* Drawer: fade 150ms, sin desplazamiento */
  .drawer, .drawer.is-open, .drawer.is-closing { transform: none !important; }
  .drawer { opacity: 0; transition: opacity 150ms linear !important; }
  .drawer.is-open { opacity: 1; }
  .drawer.is-closing { opacity: 0; }
  .backdrop { transition: opacity 150ms linear !important; }

  /* Hovers y active: solo color, sin transform */
  .btn, .btn__arrow, .card, .svc, .whatsapp-float, .mobilemenu,
  .nav__toggle span, .nav__toggle span::before, .nav__toggle span::after,
  .station::before, .station__num, .skip-link {
    transition-property: color, background-color, border-color, opacity;
    transition-duration: var(--dur-hover);
  }
  .btn:active, .whatsapp-float:active, .drawer__close:active { transform: none; }
  .skip-link { transition: none; }
  .mobilemenu { transform: none; }
  .ulink::after, .nav__link::after { transition: none; }
  /* Superficies E1 y foto de sector: sin transform en reduced-motion (§P4). */
  .plat__row, .safety__card, .sector__photo {
    transition-property: color, background-color, border-color, opacity;
    transition-duration: var(--dur-hover);
  }
  @media (hover: hover) and (pointer: fine) {
    .svc:hover, .whatsapp-float:hover { transform: none; }
    .svc--row:hover .svc__label { transform: none; }
    .btn:hover .btn__arrow { transform: none; }
    .plat__row:hover, .safety__card:hover { transform: none; }
    .sector:hover .sector__photo { transform: none; }
    .ulink:hover::after, .nav__link:hover::after { transform: scaleX(1); }
  }
}
