/* ==========================================================================
   SIICOMDT · Sistema de diseno v4 "Editorial tecnico"
   Vanilla CSS. Sin build, sin framework, sin dependencias.
   Mobile-first desde 390px.

   Paleta tomada del logotipo real (SIICOMDT-Logo.png):
     amarillo #ffcb00 · azul marino #00204c · verde #7dc045

   Reglas fijas de este sistema (no romper sin documentar):
   - Un solo tema: claro. Ninguna seccion invierte a fondo oscuro.
   - Un solo acento: amarillo de marca. El marino es tinta, no acento.
   - El verde vive solo en el logotipo. No es color de interfaz.
   - Una sola forma: radio 0. Todo es recto.
   ========================================================================== */

/* ---------- 1.0 Fuentes propias ----------
   Alojadas aqui y no en Google Fonts. Dos motivos, los dos medidos: la hoja de
   Google bloqueaba el pintado 750ms por 1,6 KB (el coste es ir a otro dominio,
   no el peso), y cada visita mandaba su IP a Google antes de que la persona
   aceptara nada, en un sitio que cuida justamente eso con Analytics.
   Archivo e IBM Plex son OFL: alojarlas esta permitido.
   Solo latin y latin-ext: los demas subconjuntos son cientos de KB que este
   sitio nunca usa. font-display: swap, igual que antes. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------- 1. Tokens ---------- */
:root {
  /* 1.1 Color · superficies (neutros teñidos de marino) */
  --c-paper:      #ffffff;
  --c-panel:      #f2f4f7;
  --c-panel-2:    #e5e9ef;

  /* 1.2 Color · tinta (familia marino, una sola) */
  --c-ink:        #00204c;   /* marino de marca              15.8:1 */
  --c-ink-2:      #33455f;   /* texto secundario              9.0:1 */
  --c-ink-3:      #5a6a80;   /* atenuado, etiquetas           5.3:1 */

  /* 1.3 Color · lineas */
  --c-line:       #dbe0e8;
  --c-line-2:     #b9c2d0;

  /* 1.4 Color · acento unico (amarillo de marca) */
  --c-yellow:      #ffcb00;
  --c-yellow-lt:   #ffd84d;  /* brillo superior del tubo */
  --c-yellow-dk:   #e0b200;  /* sombra inferior del tubo, hover */
  --c-yellow-wash: #fff8e0;

  /* 1.5 Verde de marca. SOLO para el logotipo, nunca para interfaz. */
  --c-brand-green: #7dc045;

  /* 1.6 Color · estados de formulario */
  --c-error:      #a02419;
  --c-ok:         #1f6b3f;

  /* 1.7 Tipografia */
  --f-display: "Archivo", system-ui, -apple-system, sans-serif;
  --f-body:    "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --f-mono:    "IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace;

  --fs-label:  .6875rem;
  --fs-sm:     .8125rem;
  --fs-body:   1.0625rem;
  --fs-lead:   1.1875rem;
  --fs-h3:     1.25rem;
  /* En escritorio (1100px o mas) suben label, sm, body y lead: ver 1.7.b. */
  --fs-h2:     clamp(1.75rem, 3.6vw, 2.75rem);
  --fs-h1:     clamp(2rem, 4vw, 3rem);          /* escala planeada junto a la foto: 2 lineas */
  --fs-figure: clamp(1.75rem, 3vw, 2.5rem);

  --lh-display: 1.03;
  --lh-body:    1.6;
  --tr-display: -.03em;
  --tr-label:   .14em;

  /* 1.8 Espacio */
  --s-1: .25rem; --s-2: .5rem;  --s-3: .75rem; --s-4: 1rem;
  --s-5: 1.5rem; --s-6: 2rem;   --s-7: 3rem;   --s-8: 4rem;
  --s-9: 6rem;

  --section-y: clamp(4rem, 8vw, 7rem);
  --gutter:    clamp(1.25rem, 5vw, 2.5rem);
  --maxw:      1280px;
  --measure:   65ch;

  /* 1.9 Forma y capas */
  --radius:    0;
  --header-h:  72px;
  --z-header:  100;
  --z-nav:     110;
  --z-skip:    120;

  /* 1.10 Movimiento (MOTION_INTENSITY 4) */
  --ease:      cubic-bezier(.22, .61, .36, 1);
  --dur-fast:  140ms;
  --dur:       240ms;
  --dur-slow:  520ms;
  --dur-reveal: 400ms;                           /* entrada al desplazar */
  --dur-flow: 1400ms;                            /* llenado del ducto */
}

/* 1.7.b Texto mas grande en escritorio (15/09/2026). A 11 y 13px las etiquetas,
   descripciones y el pie costaban trabajo de leer en monitores. Solo cambian los
   tamanos de texto; el espaciado (rem) queda igual. Telefono y tableta sin cambio. */
@media (min-width: 1100px) {
  :root {
    --fs-label: .75rem;       /* 11 -> 12px */
    --fs-sm:    .9375rem;     /* 13 -> 15px */
    --fs-body:  1.125rem;     /* 17 -> 18px */
    --fs-lead:  1.3125rem;    /* 19 -> 21px */
  }
}

/* ---------- 2. Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
  /* Que el foco por teclado no quede escondido tras el boton flotante. */
  scroll-padding-bottom: calc(var(--cookies-alto, 0px) + 48px + var(--s-5) * 2);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: var(--f-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--c-ink);
  background: var(--c-paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, picture, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
ul, ol { list-style: none; padding: 0; }

.spec__value, time, .mono { font-variant-numeric: tabular-nums; }

/* ---------- 3. Tipografia base ---------- */
h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 700;
  line-height: var(--lh-display);
  letter-spacing: var(--tr-display);
  text-wrap: balance;
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); letter-spacing: -.015em; line-height: 1.25; }

p { text-wrap: pretty; max-width: var(--measure); }
strong { font-weight: 600; }

.label {
  font-family: var(--f-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--c-ink-3);
}

.lead { font-size: var(--fs-lead); line-height: 1.55; color: var(--c-ink-2); }

/* ---------- 4. Primitivas de layout ---------- */
.wrap {
  width: 100%; max-width: var(--maxw);
  margin-inline: auto; padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }
.section--panel { background: var(--c-panel); }
.section--tight { padding-block: clamp(2.5rem, 5vw, 4rem); }

.rule { border: 0; border-top: 1px solid var(--c-line); }

.section__head { margin-bottom: var(--s-8); }
.section__head h2 { max-width: 18ch; }
.section__head p { margin-top: var(--s-4); color: var(--c-ink-2); }

/* ---------- 5. Accesibilidad ---------- */
.skip-link {
  position: absolute; left: var(--s-4); top: -100%;
  z-index: var(--z-skip);
  background: var(--c-ink); color: var(--c-paper);
  padding: var(--s-3) var(--s-5);
  font-family: var(--f-mono); font-size: var(--fs-sm);
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: var(--s-4); }

/* Marino, no amarillo: el amarillo sobre blanco da 1.7:1 y no se ve. */
:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 3px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- 6. Header y navegacion ---------- */
.header {
  position: sticky; top: 0; z-index: var(--z-header);
  height: var(--header-h);
  display: flex; align-items: center;
  background: color-mix(in srgb, var(--c-paper) 90%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--c-line);
}
@supports not (backdrop-filter: blur(12px)) { .header { background: var(--c-paper); } }

.header__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-5);
}

/* Logotipo real, recortado a su contenido (logo-siicom-2026.png, 343x112 desde
   el original de 1282px): 56px de alto a 2x como maximo. El logo.png anterior
   traia margen transparente y a 40px las letras median 24px. */
.logo { display: inline-flex; align-items: center; min-height: 44px; }
.logo img { height: 38px; width: auto; }
.footer .logo img { height: 48px; }
@media (min-width: 900px) { .logo img { height: 50px; } }

.nav { display: flex; align-items: center; gap: var(--s-6); }
.nav a {
  font-size: var(--fs-sm); font-weight: 500;
  color: var(--c-ink-2);
  padding-block: var(--s-2);
  position: relative;
  transition: color var(--dur-fast) var(--ease);
}
.nav a:hover { color: var(--c-ink); }
.nav a[aria-current="page"] { color: var(--c-ink); }
/* Subrayado amarillo: el mismo gesto que el logotipo. */
.nav a[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px;
  height: 3px; background: var(--c-yellow);
}
/* Colores forzados: el amarillo se aplana y la pagina actual quedaria igual
   que las demas. */
@media (forced-colors: active) {
  .nav a[aria-current="page"]::after { forced-color-adjust: none; background: Highlight; }
}

.nav__toggle { display: none; }

@media (max-width: 899px) {
  .nav__toggle {
    display: grid; place-items: center;
    width: 48px; height: 48px;
    margin-right: calc(var(--s-3) * -1);
  }
  .nav__toggle span,
  .nav__toggle span::before,
  .nav__toggle span::after {
    content: ""; display: block;
    width: 22px; height: 2px; background: var(--c-ink);
    transition: transform var(--dur) var(--ease), opacity var(--dur-fast) var(--ease);
  }
  .nav__toggle span::before { transform: translateY(-7px); }
  .nav__toggle span::after  { transform: translateY(5px); }
  .nav__toggle[aria-expanded="true"] span { background: transparent; }
  .nav__toggle[aria-expanded="true"] span::before { transform: rotate(45deg); }
  .nav__toggle[aria-expanded="true"] span::after  { transform: rotate(-45deg) translateY(0); }

  .nav {
    position: fixed; inset: var(--header-h) 0 auto 0;
    z-index: var(--z-nav);
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--c-paper);
    border-bottom: 3px solid var(--c-yellow);
    padding: var(--s-2) var(--gutter) var(--s-6);
    transform: translateY(-12px); opacity: 0; pointer-events: none;
    /* visibility saca los enlaces del orden de tabulacion con el menu cerrado.
       Al cerrar se aplica al terminar el fundido; al abrir, de inmediato.
       Antes habia 4 enlaces invisibles pero enfocables. */
    visibility: hidden;
    transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease),
                visibility 0s linear var(--dur);
  }
  .nav[data-open="true"] {
    transform: none; opacity: 1; pointer-events: auto; visibility: visible;
    transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease),
                visibility 0s linear 0s;
  }
  .nav a {
    padding-block: var(--s-4);
    border-bottom: 1px solid var(--c-line);
    font-size: 1.0625rem;
  }
  .nav a[aria-current="page"]::after { display: none; }
  .nav a[aria-current="page"] { color: var(--c-ink); font-weight: 600; }
}

/* ---------- 7. Botones ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-2);
  min-height: 48px; padding: 0 var(--s-6);
  border-radius: var(--radius);
  font-family: var(--f-body);
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: .01em;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:active { transform: translateY(1px); }

/* Primario: senalizacion industrial. Amarillo de marca con texto marino.
   Texto sobre fondo 9.6:1. El borde marino da el contraste de contorno
   que el amarillo sobre blanco (1.7:1) no alcanza por si solo. */
.btn--primary {
  background: var(--c-yellow);
  color: var(--c-ink);
  border: 2px solid var(--c-ink);
}
.btn--primary:hover { background: var(--c-yellow-dk); }

.btn--ghost {
  border: 1px solid var(--c-line-2);
  color: var(--c-ink);
  background: var(--c-paper);
}
.btn--ghost:hover { border-color: var(--c-ink); background: var(--c-panel); }

.link {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-sm); font-weight: 600;
  color: var(--c-ink);
  padding-block: var(--s-2);
  position: relative;
}
.link::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 3px; background: var(--c-yellow);
  transform-origin: left; transform: scaleX(0);
  transition: transform var(--dur) var(--ease);
}
/* Area tactil de 44px sin mover la maqueta: el enlace mide 37px de alto. */
.link::before {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; height: 44px; transform: translateY(-50%);
}
.link:hover::after { transform: scaleX(1); }
.link .arrow { transition: transform var(--dur) var(--ease); }
.link:hover .arrow { transform: translateX(3px); }

/* ---------- 8. EL DUCTO ----------
   El motivo grafico del sitio. Los ductos de gas se pintan de amarillo por
   norma, y de ahi sale el amarillo del logotipo. No es decoracion: es la
   infraestructura de la empresa usada como elemento estructural.

   El gradiente vertical simula el cilindro. Las marcas marinas son bridas.
   Al entrar en pantalla el tubo se llena de izquierda a derecha: el gas
   corriendo por la linea. Es decorativo, va con aria-hidden.
*/
.pipe {
  position: relative;
  height: 14px;
  background: var(--c-panel-2);
  overflow: hidden;
  /* Va suelto dentro de <main>, fuera de .wrap, asi que ya es de borde a borde.
     Nada de 100vw: incluiria el ancho de la barra de scroll. */
  width: 100%;
}
.pipe__flow {
  position: absolute; inset: 0;
  background: linear-gradient(
    180deg,
    var(--c-yellow-lt) 0%,
    var(--c-yellow)    38%,
    var(--c-yellow)    62%,
    var(--c-yellow-dk) 100%
  );
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-flow) var(--ease);
}
.pipe.is-visible .pipe__flow { transform: scaleX(1); }

/* Bridas: marcas verticales marinas cada 120px. */
.pipe::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(
    90deg,
    transparent 0 117px,
    rgba(0, 32, 76, .55) 117px 120px
  );
  pointer-events: none;
}

/* Variante fina, para separar secciones sin tanto peso. */
.pipe--thin { height: 8px; }
.pipe--thin::after {
  background: repeating-linear-gradient(
    90deg, transparent 0 78px, rgba(0, 32, 76, .5) 78px 80px
  );
}

/* ---------- 9. Hero (familia: split asimetrico) ---------- */
.hero {
  /* Sin padding arriba: la foto arranca pegada al header y el hero se lee como
     banner. El aire que necesita el texto va en .hero__copy, no aqui, para que
     la imagen no herede ese margen. */
  padding-top: 0;
  padding-bottom: 0;
}
/* Era align-items: end. Como la columna de texto (421px) es mas baja que la
   foto (500px), alinear al pie empujaba el texto 79px hacia abajo y abria un
   hueco arriba. Con stretch ambas columnas llenan la fila: el texto arranca
   pegado al borde superior y los dos pies siguen coincidiendo, que era lo que
   se buscaba para que el ducto corra por debajo de ambos. */
.hero__grid { display: grid; gap: clamp(2.5rem, 5vw, 4rem); align-items: stretch; }
/* El aire superior vive aqui, no en .hero: asi el texto respira y la foto
   sigue llegando hasta el borde del header. */
.hero__copy {
  padding-top: clamp(1.5rem, 3vw, 3rem);
  padding-bottom: clamp(2.5rem, 5vw, 4.5rem);
}
.hero__copy .label { display: block; margin-bottom: var(--s-5); }
/* 22ch mantiene el titular en 2 lineas. Medido: con 18ch el titular actual
   (45 caracteres) no baja de 3 lineas ni reduciendo la fuente, porque lo que
   ata es el ancho maximo, no el tamano. */
.hero__copy h1 { max-width: 22ch; }
.hero__copy .lead { margin-top: var(--s-5); max-width: 46ch; }
.hero__actions {
  display: flex; align-items: center; gap: var(--s-6);
  margin-top: var(--s-7); flex-wrap: wrap;
}

.hero__media { position: relative; }

/* Entrada de la portada. Unica animacion de arranque del sitio: la foto se
   descubre con un corte diagonal y el texto sube escalonado. Al texto se le
   anima SOLO la posicion: la foto es el elemento LCP y animar la opacidad del
   titular retrasaba la medicion. Fuera con movimiento reducido. */
@media (prefers-reduced-motion: no-preference) {
  /* La cortina es un pseudo-elemento que se desliza, NO un clip-path sobre la
     foto. Con clip-path la foto se pintaba recortada a cero y el navegador
     dejaba de contarla como elemento LCP (medido: pasaba a medir el titular en
     10 de 10 corridas), ademas de costar 10 fotogramas y un pico de 183ms en el
     primer desplazamiento. Con transform la cortina corre en el compositor:
     1,5 fotogramas, pico de 42ms, y la foto vuelve a ser el LCP.
     Acotada a .hero: nosotros.html tambien usa .hero__media y ahi no se diseno. */
  .hero .hero__media { overflow: hidden; }
  .hero .hero__media::after {
    content: "";
    position: absolute; top: 0; bottom: 0; left: -12%; width: 124%;
    background: var(--c-paper);
    animation: hero-cortina 1100ms var(--ease) forwards;
  }
  @keyframes hero-cortina {
    from { transform: skewX(-9.3deg) translateX(0); }
    to   { transform: skewX(-9.3deg) translateX(124%); }
  }
  /* backwards, no both: al texto no le hace falta estado final. */
  .hero__copy > * { animation: hero-sube 520ms var(--ease) backwards; }
  @keyframes hero-sube {
    from { transform: translateY(20px); }
    to   { transform: none; }
  }
  .hero__copy > :nth-child(1) { animation-delay:  40ms; }
  .hero__copy > :nth-child(2) { animation-delay: 120ms; }
  .hero__copy > :nth-child(3) { animation-delay: 200ms; }
  .hero__copy > :nth-child(4) { animation-delay: 280ms; }
}
.hero__media img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4 / 3; }

@media (min-width: 900px) {
  .hero__grid { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); }
  .hero__media {
    margin-right: calc((100vw - min(100vw, var(--maxw))) / -2 - var(--gutter));
  }
  .hero__media img { aspect-ratio: auto; min-height: 28rem; max-height: 34rem; }
}

/* ---------- 10. Riel de credenciales ---------- */
.spec {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: 1px solid var(--c-line);
}
/* Columna flex + margin-top auto en la etiqueta: todas las etiquetas quedan
   alineadas al pie aunque el valor de arriba ocupe una o dos lineas. */
.spec__item {
  padding: var(--s-6) var(--s-5) var(--s-6) 0;
  border-bottom: 1px solid var(--c-line);
  display: flex; flex-direction: column;
}
/* A 2 columnas solo la columna derecha lleva hairline. Con "+" el tercer
   elemento (columna izquierda, segunda fila) arrastraba un borde suelto. */
.spec__item:nth-child(2n) { border-left: 1px solid var(--c-line); padding-left: var(--s-5); }
.spec__value {
  display: block;
  font-family: var(--f-display);
  font-size: var(--fs-figure);
  font-weight: 700; letter-spacing: -.03em; line-height: 1;
  color: var(--c-ink);
  /* Un token sin llenar es mas largo que el valor real. */
  overflow-wrap: anywhere;
}
.spec__key {
  display: block;
  margin-top: auto; padding-top: var(--s-3);
  font-size: var(--fs-sm); color: var(--c-ink-3);
}

/* 900px, no 700px: a 4 columnas por debajo de eso la celda queda en 128px de
   contenido y "ISO 14001" se parte en dos lineas. */
@media (min-width: 900px) {
  .spec { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .spec__item { border-bottom: 0; }
  .spec__item + .spec__item { border-left: 1px solid var(--c-line); padding-left: var(--s-5); }
  .spec__item:first-child { padding-left: 0; }
}

/* Por debajo de 400px "ISO 9001" e "ISO 14001" partian en dos lineas. */
@media (max-width: 399.98px) {
  .spec__value { font-size: 1.375rem; }
  .spec__item { padding-right: var(--s-3); }
  .spec__item:nth-child(2n) { padding-left: var(--s-4); }
}

/* ---------- 11. Servicios (familia: lista de filas) ---------- */
.rows { border-top: 1px solid var(--c-line); }
.row {
  display: block;
  border-bottom: 1px solid var(--c-line);
  padding-block: var(--s-6);
  position: relative;
  transition: background var(--dur) var(--ease);
}
.row:hover, .row:focus-visible { background: var(--c-panel); }
/* Al pasar el cursor entra una brida amarilla por la izquierda. */
.row::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 4px; background: var(--c-yellow);
  transform: scaleY(0); transform-origin: top;
  transition: transform var(--dur) var(--ease);
}
.row:hover::before, .row:focus-visible::before { transform: scaleY(1); }

.row__inner { display: grid; gap: var(--s-3); align-items: baseline; padding-inline: var(--s-4); }
.row__num {
  font-family: var(--f-mono);
  font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label);
  color: var(--c-ink-3);
}
.row__title {
  font-family: var(--f-display);
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  font-weight: 700; letter-spacing: -.025em; line-height: 1.15;
}
.row__desc { color: var(--c-ink-2); font-size: var(--fs-body); max-width: 52ch; }
.row__arrow {
  justify-self: start; color: var(--c-ink-3);
  transition: transform var(--dur) var(--ease), color var(--dur-fast) var(--ease);
}
.row:hover .row__arrow { transform: translateX(4px); color: var(--c-ink); }

@media (min-width: 800px) {
  .row__inner {
    grid-template-columns: 4rem minmax(0, 19rem) minmax(0, 1fr) 2.5rem;
    gap: var(--s-6); align-items: start;
  }
  .row__arrow { justify-self: end; }
}

/* Servicios con alcance (servicios.html): cada linea es un bloque completo.
   La cabecera (.row) es el enlace a la pagina y, en escritorio, el boton de la
   escena; debajo cuelgan el alcance y el enlace con nombre propio. El filete
   que separaba filas pasa al bloque para que no corte el bloque en dos. */
.servicio { border-bottom: 1px solid var(--c-line); position: relative; }
/* El realce de la cabecera (fondo y brida amarilla) se extiende al bloque
   entero: cortado a media ficha parecia un error de maqueta. Donde no haya
   :has() se queda en la cabecera, que es la parte pulsable. */
.servicio::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 4px; background: var(--c-yellow);
  transform: scaleY(0); transform-origin: top;
  transition: transform var(--dur) var(--ease);
}
.servicio:has(.row:hover),
.servicio:has(.row:focus-visible) { background: var(--c-panel); }
.servicio:has(.row:hover)::before,
.servicio:has(.row:focus-visible)::before { transform: scaleY(1); }
.servicio .row { border-bottom: 0; padding-bottom: var(--s-3); }
.servicio__detalle { padding-inline: var(--s-4); padding-bottom: var(--s-6); }
.servicio__detalle .scope { margin-top: var(--s-3); }
.servicio__enlace { margin-top: var(--s-3); }
/* En una sola columna la flecha de la cabecera caia en un renglon suelto,
   encima del alcance. El enlace con nombre propio queda tres renglones abajo. */
@media (max-width: 799.98px) { .servicio .row__arrow { display: none; } }

@media (min-width: 800px) {
  /* El bloque trae mas que un renglon, asi que se apila como en escritorio:
     numero a un lado y titulo, descripcion, alcance y enlace alineados en una
     sola columna. Con las cuatro columnas de la fila corta la descripcion
     quedaba en una tira angosta (184px a 800px de ancho) y el alcance arrancaba
     en otro margen. */
  .servicio .row__inner {
    grid-template-columns: 4rem minmax(0, 1fr) 2.5rem;
    gap: var(--s-2) var(--s-6);
  }
  .servicio .row__num   { grid-column: 1; grid-row: 1 / span 2; padding-top: .35rem; }
  .servicio .row__title { grid-column: 2; grid-row: 1; }
  .servicio .row__desc  { grid-column: 2; grid-row: 2; }
  .servicio .row__arrow { grid-column: 3; grid-row: 1; justify-self: end; }
  .servicio__detalle { padding-left: calc(var(--s-4) + 4rem + var(--s-6)); }
  /* Cuatro renglones cortos en dos columnas: la caja queda llena y el bloque
     no se estira de mas. Sin partir un renglon entre columnas. */
  .servicio__detalle .scope { columns: 2; column-gap: var(--s-6); }
  .servicio__detalle .scope li { break-inside: avoid; }
}

/* ---------- 12. Bloque de texto ---------- */
.prose h2 { max-width: 20ch; }
/* Paginas de servicio con varios apartados: cada h2 abre bloque nuevo y no se
   pega al parrafo o la lista de arriba. */
.prose * + h2 { margin-top: var(--s-8); }
.prose p { margin-top: var(--s-5); color: var(--c-ink-2); }
/* Dentro de un texto corrido la lista va al tamano del parrafo; a 13px quedaba
   mas chica que el texto que la presenta. */
.prose .scope li { font-size: var(--fs-body); }
.prose .link { margin-top: var(--s-6); }

.pull { border-left: 4px solid var(--c-yellow); padding-left: var(--s-6); margin-top: var(--s-7); }
.pull p {
  font-family: var(--f-display);
  font-size: clamp(1.25rem, 2.2vw, 1.625rem);
  font-weight: 600; letter-spacing: -.02em; line-height: 1.3;
  color: var(--c-ink);
  max-width: 34ch;
}

/* ---------- 13. Galeria de campo ---------- */
/* 3 columnas en escritorio: las fotos originales son pequenas (453px el lado
   mayor) y a mas ancho se ven blandas. Ver DESIGN-SYSTEM.md. */
.gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }
.gallery figure { position: relative; overflow: hidden; background: var(--c-panel-2); }
.gallery img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--dur-slow) var(--ease);
}
.gallery figure:hover img { transform: scale(1.03); }

.gallery figure:nth-child(1) { grid-column: span 2; aspect-ratio: 16 / 9; }
.gallery figure:nth-child(2) { aspect-ratio: 3 / 4; }
.gallery figure:nth-child(3) { aspect-ratio: 3 / 4; }
.gallery figure:nth-child(4) { aspect-ratio: 4 / 3; }
.gallery figure:nth-child(5) { aspect-ratio: 4 / 3; }
.gallery figure:nth-child(6) { grid-column: span 2; aspect-ratio: 16 / 9; }

/* Se abandono la rejilla asimetrica. Medido en el navegador: las celdas grandes
   ampliaban las fotos de campo hasta 1.71x sobre su tamano nativo (la mayor mide
   453px de ancho) y se veian blandas. Con 4 columnas uniformes ninguna celda
   pasa de ~280px, asi que casi ninguna foto se amplia. Se pierde la asimetria;
   se gana nitidez. Si llegan los originales en alta, se puede revertir. */
@media (min-width: 800px) {
  .gallery { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4); }
  .gallery figure:nth-child(n) { grid-column: auto; grid-row: auto; aspect-ratio: 4 / 3; }
}

/* ---------- 14. Panel de cierre ---------- */
.close { background: var(--c-panel); }
/* El flotante de WhatsApp puede caer encima del boton de la seccion de cierre:
   el CTA gana el apilado para que se lea entero y siga siendo clicable. */
.close .btn--primary { position: relative; z-index: calc(var(--z-header) + 1); }
.close__grid { display: grid; gap: var(--s-6); align-items: end; }
.close h2 { max-width: 16ch; }
.close p { color: var(--c-ink-2); max-width: 44ch; margin-top: var(--s-4); }

@media (min-width: 800px) {
  .close__grid { grid-template-columns: minmax(0, 1fr) auto; gap: var(--s-8); }
}

/* ---------- 15. Footer ---------- */
.footer {
  background: var(--c-paper);
  border-top: 1px solid var(--c-line);
  /* El pie reserva la franja del boton flotante (24px + 48px + aire): antes
     tapaba "Aviso de privacidad" al llegar al final de la pagina. */
  padding-block: var(--s-8) calc(48px + var(--s-5) * 2 + env(safe-area-inset-bottom, 0px));
  font-size: var(--fs-sm);
}
.footer__grid { display: grid; gap: var(--s-7); }
.footer__brand p { color: var(--c-ink-3); margin-top: var(--s-4); max-width: 34ch; }
.footer h3 {
  font-family: var(--f-mono);
  font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); text-transform: uppercase;
  color: var(--c-ink-3);
  margin-bottom: var(--s-4);
}
/* Cada renglon del pie mide 44px: antes los enlaces median 17px de alto. */
.footer li { display: flex; align-items: center; min-height: 44px; }
.footer li > a { display: flex; align-items: center; min-height: 44px; }
.footer a { color: var(--c-ink-2); transition: color var(--dur-fast) var(--ease); }
.footer a:hover { color: var(--c-ink); }
.footer__bottom {
  margin-top: var(--s-8); padding-top: var(--s-5);
  border-top: 1px solid var(--c-line);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-6);
  justify-content: space-between;
  color: var(--c-ink-3);
}
.footer__bottom a { display: inline-flex; align-items: center; min-height: 44px; }
/* Con la sigla al final, a 390px la linea medía 356px en 350 disponibles y
   "(SIICOMDT)" caia sola en un segundo renglon. Balanceado, el corte queda en
   "...Terminales / S.A. de C.V. (SIICOMDT)". */
.footer__bottom > span { text-wrap: balance; }

@media (min-width: 700px) { .footer__grid { grid-template-columns: 2fr 1fr 1.4fr; gap: var(--s-8); } }

/* ---------- 16. Boton flotante de WhatsApp ---------- */
/* Bloqueo de acento: marino y amarillo, no el verde de marca de WhatsApp. */
.wa {
  position: fixed; right: var(--s-5);
  /* --cookies-alto lo fija consent.js mientras la banda esta puesta (0 si no). */
  bottom: calc(var(--cookies-alto, 0px) + var(--s-5) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-header);
  display: inline-flex; align-items: center; gap: var(--s-3);
  min-height: 48px; padding: 0 var(--s-5);
  background: var(--c-ink); color: var(--c-paper);
  font-size: var(--fs-sm); font-weight: 600;
  border-bottom: 3px solid var(--c-yellow);
  box-shadow: 0 2px 8px rgba(0, 32, 76, .18), 0 8px 24px rgba(0, 32, 76, .14);
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.wa:hover { background: #001736; }
.wa:active { transform: translateY(1px); }

/* ==========================================================================
   PAGINAS INTERIORES
   ========================================================================== */

/* ---------- 17. Inicio de pagina interior ----------
   Sin banda de titulo ni migas (15/09/2026): la pagina abre directo en su
   contenido. El h1 va oculto (lo leen lectores de pantalla y buscadores) donde
   la primera seccion ya trae su propio titulo, y visible al inicio del texto
   en las fichas de servicio y el aviso. La navegacion marca la pagina actual. */
.section--inicio { padding-top: clamp(2.5rem, 5vw, 4rem); }
.prose > h1 { max-width: 20ch; }
.doc > h1 { max-width: 16ch; margin-bottom: var(--s-6); }

/* ---------- 18. Servicios a detalle (familia: ficha tecnica) ---------- */
/* Se distingue de las filas del inicio porque cada bloque trae lista de
   alcances y respira mucho mas. */
.sheet { border-top: 1px solid var(--c-line); }
.sheet__item { border-bottom: 1px solid var(--c-line); padding-block: var(--s-7); }
.sheet__grid { display: grid; gap: var(--s-5); }
.sheet__num {
  font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); color: var(--c-ink-3);
}
.sheet__item h2,
.sheet__item h3 { font-size: clamp(1.5rem, 2.6vw, 2rem); margin-top: var(--s-3); letter-spacing: var(--tr-display); line-height: var(--lh-display); }
.sheet__item > .sheet__grid > div > p { margin-top: var(--s-4); color: var(--c-ink-2); }

/* Lista de alcances: brida amarilla en lugar de vineta. */
.scope { margin-top: var(--s-5); }
.scope li {
  position: relative;
  padding-left: var(--s-5);
  padding-block: var(--s-2);
  color: var(--c-ink-2);
  font-size: var(--fs-sm);
}
.scope li::before {
  content: ""; position: absolute; left: 0; top: .95em;
  width: 12px; height: 3px; background: var(--c-yellow);
}
/* Lista de descargas: cada enlace es un objetivo de 44px y la brida se centra
   en la fila. Antes las dos areas quedaban casi pegadas. */
.scope--enlaces li { padding-block: 0; }
.scope--enlaces li::before { top: 50%; transform: translateY(-50%); }
.scope--enlaces a { display: inline-flex; align-items: center; min-height: 44px; }

@media (min-width: 800px) {
  .sheet__grid { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); gap: var(--s-8); }
  .sheet__item h2 { margin-top: var(--s-3); }
}

/* ---------- 19. Etapas del proceso (familia: ducto con derivaciones) ---------- */
/* Las cuatro etapas cuelgan de un mismo ducto: el .pipe de siempre (tubo de
   14px con bridas que se llena al entrar en pantalla), ahora dentro de .wrap y
   encima de la rejilla. En telefono las etapas van apiladas bajo el ducto,
   separadas por una linea fina. */
.stages { display: grid; gap: 0; }
.stage {
  position: relative;
  padding-top: var(--s-6);
  padding-bottom: var(--s-6);
  padding-right: var(--s-5);
}
.stage + .stage { border-top: 1px solid var(--c-line); }
.stage__num {
  font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); color: var(--c-ink-3);
}
.stage h3 { margin-top: var(--s-2); font-size: 1.125rem; }
@media (min-width: 1100px) { .stage h3 { font-size: 1.25rem; } }
.stage p { margin-top: var(--s-3); font-size: var(--fs-sm); color: var(--c-ink-2); }

@media (min-width: 700px) {
  .stages { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .stage + .stage { border-top: 0; }
}

/* ---------- 20. Valores y ventajas (familia: rejilla con hairlines) ---------- */
.grid-lines {
  display: grid;
  gap: 0;
  border-top: 1px solid var(--c-line);
}
.grid-lines > * {
  padding: var(--s-6) var(--s-5) var(--s-6) 0;
  border-bottom: 1px solid var(--c-line);
}
.grid-lines h3 { margin-bottom: var(--s-3); }
.grid-lines p { color: var(--c-ink-2); font-size: var(--fs-sm); }
/* Enlace al pie de cada bloque (las otras lineas de servicio en las fichas). */
.grid-lines .link { margin-top: var(--s-3); }

@media (min-width: 640px) {
  .grid-lines { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s-7); }
}
@media (min-width: 1000px) {
  .grid-lines--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Entre 640 y 999px la rejilla va a dos columnas: con tres bloques, el tercero
   quedaba solo, con la celda de al lado vacia y su filete cortado a media
   anchura. Si el ultimo cae en posicion impar, ocupa la fila entera. La rejilla
   de seis de nosotros.html no se entera: su ultimo hijo es par. */
@media (min-width: 640px) and (max-width: 999.98px) {
  .grid-lines--3 > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* Foto del contrato que describe la ficha. Las fotos son 4:3; en escritorio se
   recorta la banda central, porque a 4:3 y ancho completo la imagen se come la
   pantalla y empuja el texto fuera del primer golpe de vista. */
.foto-caso { margin-top: var(--s-6); background: var(--c-panel-2); overflow: hidden; aspect-ratio: 4 / 3; }
.foto-caso img { width: 100%; height: 100%; object-fit: cover; }
@media (min-width: 700px) { .foto-caso { aspect-ratio: 16 / 7; } }

/* ---------- 21. Contacto: datos + formulario ---------- */
.contact { display: grid; gap: var(--s-8); align-items: start; }

@media (min-width: 900px) {
  .contact { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); gap: var(--s-9); }
}

.contact__aside h2 { max-width: 14ch; }
.contact__aside > p { margin-top: var(--s-4); color: var(--c-ink-2); }

/* Sin iconos: los datos van como lista de definiciones con hairlines,
   igual que el resto del sitio. */
.contact__list { margin-top: var(--s-7); border-top: 1px solid var(--c-line); }
.contact__list > div {
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--c-line);
}
.contact__list dt {
  font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--c-ink-3);
}
.contact__list dd { margin: var(--s-2) 0 0; font-weight: 500; }
.contact__list dd a { display: inline-flex; align-items: center; min-height: 44px; }

/* ---------- 22. Formulario ---------- */
.form { display: grid; gap: var(--s-5); }
@media (min-width: 640px) { .form { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* align-content: start es obligatorio. Sin el, un campo sin texto de error
   reparte el espacio sobrante entre sus filas y su input baja respecto al
   campo de al lado, que si tiene error. */
.field { display: grid; gap: var(--s-2); align-content: start; }
.field--full { grid-column: 1 / -1; }

.field label {
  font-size: var(--fs-sm); font-weight: 600; color: var(--c-ink);
}
.req { color: var(--c-error); }

.input, .textarea {
  width: 100%;
  padding: var(--s-3) var(--s-4);
  min-height: 48px;
  background: var(--c-paper);
  /* --c-ink-3 (5.3:1). Con --c-line-2 el borde, unico limite del campo,
     quedaba en 1.80:1 y no cumplia el 3:1 de WCAG 1.4.11. */
  border: 1px solid var(--c-ink-3);
  border-radius: var(--radius);
  color: var(--c-ink);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.textarea { min-height: 9rem; resize: vertical; padding-top: var(--s-3); }

/* Placeholder a 5.3:1: pasa AA, no es un gris fantasma. */
.input::placeholder, .textarea::placeholder { color: var(--c-ink-3); opacity: 1; }

.input:hover, .textarea:hover { border-color: var(--c-ink-2); }
.input:focus-visible, .textarea:focus-visible {
  border-color: var(--c-ink);
  box-shadow: 0 0 0 3px var(--c-yellow);
}
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--c-error); }

.field__error {
  font-size: var(--fs-sm);
  color: var(--c-error);
  min-height: 0;
}
.field__error:empty { display: none; }

/* Consentimiento: objetivo tactil completo, no solo la casilla. */
/* .field .consent le gana a .field label sin !important. Antes el texto tomaba
   el marino de las etiquetas y el enlace del aviso no se distinguia de el. */
.field .consent {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-3); align-items: start;
  font-weight: 400;
  font-size: var(--fs-sm); color: var(--c-ink-2);
  cursor: pointer;
  padding-block: var(--s-2);
}
.consent input {
  width: 20px; height: 20px; margin-top: 2px;
  accent-color: var(--c-ink);
}
.consent a { color: var(--c-ink); }

/* Trampa antispam. Fuera de pantalla, no display:none (los bots lo detectan). */
.hp {
  position: absolute; left: -9999px;
  width: 1px; height: 1px; overflow: hidden;
}

.btn--block { width: 100%; }
.btn[disabled] { opacity: .6; cursor: not-allowed; }

.form__status {
  margin-top: var(--s-3);
  font-size: var(--fs-sm);
  color: var(--c-ink-2);
}
.form__status:empty { display: none; }
.form__status[data-state="ok"] {
  color: var(--c-ok);
  border-left: 3px solid var(--c-ok);
  padding-left: var(--s-3);
}
.form__status[data-state="error"] {
  color: var(--c-error);
  border-left: 3px solid var(--c-error);
  padding-left: var(--s-3);
}
/* Aviso neutro: el envio no ocurrio, pero no es un error del visitante. */
.form__status[data-state="warn"] {
  color: var(--c-ink-2);
  border-left: 3px solid var(--c-ink-3);
  padding-left: var(--s-3);
}
/* Respaldo del formulario. Junto al boton si form.js no carga; form.js lo sube
   al inicio del formulario si el envio en linea no esta activo, para que se lea
   antes de llenar los campos y no despues. */
.form__respaldo[hidden] { display: none; }
.form > .form__respaldo { grid-column: 1 / -1; margin-top: 0; }
.form__canales { display: flex; flex-wrap: wrap; gap: 0 var(--s-5); margin-top: var(--s-1); }
.form__canales a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }

/* ---------- 23. Mapa ---------- */
.map { border: 1px solid var(--c-line); background: var(--c-panel); }
.map iframe { display: block; width: 100%; height: clamp(20rem, 45vw, 28rem); border: 0; }
/* Estado vacio: dice que falta y por que, no solo un hueco gris. */
.map__empty {
  display: grid; place-items: center; text-align: center;
  min-height: clamp(14rem, 30vw, 20rem);
  padding: var(--s-7) var(--s-5);
}
.map__empty p { color: var(--c-ink-3); font-size: var(--fs-sm); max-width: 34ch; }
.map__empty .label { display: block; margin-bottom: var(--s-3); }

/* ==========================================================================
   BLOQUES DE CONFIANZA
   Todo esto existe por una razon: el comprador industrial (operador con
   permiso, planta con red propia) filtra proveedores por obra comprobable,
   record de seguridad y existencia legal. Sin eso no pasa del primer minuto.
   ========================================================================== */

/* ---------- 24. Hueco pendiente ----------
   Un hueco tiene que verse. Un espacio en blanco se lee como descuido; un
   bloque que dice que falta se lee como trabajo en curso. NO debe publicarse:
   main.js avisa en local y fill.js bloquea la publicacion. */
.ph {
  border: 2px dashed var(--c-line-2);
  background: var(--c-panel);
  display: grid; place-items: center;
  text-align: center;
  padding: var(--s-7) var(--s-5);
  min-height: 12rem;
}
.ph .label { display: block; margin-bottom: var(--s-3); color: var(--c-ink-3); }
.ph p { color: var(--c-ink-3); font-size: var(--fs-sm); max-width: 32ch; }
.ph--media { min-height: 100%; aspect-ratio: 4 / 3; }

/* Marca de fill.js --preview para los {{TOKEN}} sin llenar. */
.pending {
  background: var(--c-error); color: #fff;
  padding: 0 .35em; font-family: var(--f-mono); font-size: .9em;
}

/* ---------- 25. Casos de exito (familia: expediente) ---------- */
.cases { display: grid; gap: var(--s-6); }
@media (min-width: 900px) { .cases { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.case { display: flex; flex-direction: column; border-top: 3px solid var(--c-yellow); }
.case__media { background: var(--c-panel-2); aspect-ratio: 4 / 3; overflow: hidden; }
.case__media img { width: 100%; height: 100%; object-fit: cover; }
.case__body { padding-top: var(--s-5); display: flex; flex-direction: column; flex: 1; }
.case__meta {
  font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--c-ink-3);
}
.case h3 { margin-top: var(--s-3); font-size: 1.1875rem; }
@media (min-width: 1100px) { .case h3 { font-size: 1.3125rem; } }
.case p { margin-top: var(--s-3); font-size: var(--fs-sm); color: var(--c-ink-2); }
.case p.case__meta { margin-top: 0; }
.case .scope { margin-top: var(--s-3); }
/* Cuatro proyectos: 2x2. En 3 columnas quedaba una tarjeta sola en la segunda fila. */
@media (min-width: 800px) { .cases--dos { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-7) var(--s-6); } }
/* El resultado se ancla al pie para que las tres tarjetas cierren parejas. */
.case__result {
  margin-top: auto; padding-top: var(--s-5);
  font-size: var(--fs-sm); font-weight: 600; color: var(--c-ink);
  border-top: 1px solid var(--c-line);
}

/* ---------- 26. Seguridad (familia: tablero de cifras) ---------- */
.hse { display: grid; gap: 0; border-top: 1px solid var(--c-line); }
.hse__item { padding: var(--s-6) var(--s-5) var(--s-6) 0; border-bottom: 1px solid var(--c-line); }
.hse__value {
  display: block; font-family: var(--f-display);
  font-size: clamp(2rem, 4vw, 3rem); font-weight: 700;
  letter-spacing: -.03em; line-height: 1; color: var(--c-ink);
  overflow-wrap: anywhere;
}
.hse__key { display: block; margin-top: var(--s-3); font-size: var(--fs-sm); color: var(--c-ink-2); }
.hse__note { margin-top: var(--s-5); font-size: var(--fs-sm); color: var(--c-ink-3); }

@media (min-width: 700px) {
  .hse { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--s-6); }
  .hse__item { border-bottom: 0; }
}

/* ---------- 27. Muro de clientes ---------- */
.clients {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: var(--c-line);
  border: 1px solid var(--c-line);
}
.clients > * {
  background: var(--c-paper);
  display: grid; place-items: center;
  min-height: 6.5rem; padding: var(--s-5);
}
/* En reposo en gris, para que ningun logotipo pese mas que otro; al pasar el
   cursor por su celda recupera sus colores. STETRU es blanco y negro de origen. */
.clients img {
  max-height: 3rem; max-width: 100%; width: auto; height: auto;
  filter: grayscale(1); opacity: .8;
  transition: filter var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
@media (hover: hover) {
  .clients li:hover img { filter: none; opacity: 1; }
}
/* Igualar la altura iguala la caja, no el peso optico: con 3rem de alto STETRU
   salia 216px de ancho y CAP 42. Se topa el ancho de las marcas apaisadas y se
   deja crecer a los emblemas compactos. */
.clients img { max-width: 170px; }
.clients img.is-emblema { max-height: 4.5rem; }
.clients__nota { margin-top: var(--s-5); font-size: var(--fs-sm); color: var(--c-ink-3); max-width: 70ch; }
.clients .ph {
  border: 0; background: var(--c-panel); min-height: 6.5rem; padding: var(--s-4);
}
.clients .ph p { font-size: var(--fs-label); font-family: var(--f-mono); letter-spacing: .06em; }

@media (min-width: 700px) { .clients { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* ---------- 28. Cobertura ---------- */
.coverage { display: grid; gap: var(--s-6); align-items: start; }
/* Doce estados en dos columnas: en una sola, la lista duplicaba la altura
   de la columna de texto de al lado. */
/* Rejilla por filas y no columns:2. Con columnas de texto, un estado que parte
   en dos lineas a 320px ("Baja California Sur") alargaba solo su columna y las
   rayas finales quedaban desfasadas. En rejilla cada fila toma la altura de su
   celda mas alta, sin depender de la fuente ni de cuantos estados haya. */
.coverage__list--2col {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--s-6);
}
@media (min-width: 800px) { .coverage { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: var(--s-8); } }

/* Cobertura con mapa (cobertura.html). En telefono: texto, mapa, lista. En
   escritorio el mapa ocupa la columna derecha y se queda a la vista mientras
   se recorre el texto y la lista de la izquierda. */
@media (min-width: 800px) {
  .coverage--mapa {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
    grid-template-areas: "texto mapa" "estados mapa";
    row-gap: var(--s-7);
  }
  .coverage--mapa .coverage__texto { grid-area: texto; }
  .coverage--mapa .coverage__estados { grid-area: estados; }
  .coverage--mapa .coverage__mapa {
    grid-area: mapa;
    position: sticky; top: calc(var(--header-h) + var(--s-5));
  }
}

.mapa svg { display: block; width: 100%; height: auto; overflow: visible; }
.mapa path {
  fill: var(--c-panel-2);
  stroke: var(--c-paper); stroke-width: 1; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: fill var(--dur-fast) var(--ease);
}
.mapa path.servido { fill: var(--c-yellow); }
@media (hover: hover) and (forced-colors: none) { .mapa path.servido:hover { fill: var(--c-yellow-dk); } }
/* Oficina: cuadro marino, sin radio, con filo blanco para separarlo del amarillo. */
.mapa__oficina { fill: var(--c-ink); stroke: var(--c-paper); stroke-width: 2; vector-effect: non-scaling-stroke; }
/* En telefono el mapa mide ~350px y el cuadro quedaba de 6px: se agranda desde su centro. */
@media (max-width: 599px) { .mapa__oficina { transform: scale(1.7); transform-box: fill-box; transform-origin: center; } }
.mapa figcaption {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-5);
  margin-top: var(--s-4);
  font-size: var(--fs-sm); color: var(--c-ink-2);
}
.mapa__clave { display: inline-flex; align-items: center; gap: var(--s-2); }
.mapa__muestra { width: 14px; height: 14px; background: var(--c-yellow); flex: 0 0 auto; }
.mapa__muestra--oficina { width: 10px; height: 10px; margin-inline: 2px; background: var(--c-ink); }
.mapa__credito { flex-basis: 100%; font-size: .75rem; color: var(--c-ink-3); }
@media (min-width: 1100px) { .mapa__credito { font-size: .8125rem; } }
.mapa__credito a { text-decoration: underline; text-underline-offset: 2px; }
.mapa__credito a:hover { color: var(--c-ink); }
@media (forced-colors: active) {
  .mapa path { fill: Canvas; stroke: CanvasText; }
  .mapa path.servido, .mapa__muestra { fill: Highlight; background: Highlight; forced-color-adjust: none; }
  /* Filo en Canvas: con relleno y filo del mismo color el cuadro se perdia sobre Highlight. */
  .mapa__oficina { fill: CanvasText; stroke: Canvas; forced-color-adjust: none; }
  .mapa__muestra--oficina { background: CanvasText; forced-color-adjust: none; }
}

.coverage__list { border-top: 1px solid var(--c-line); }
.coverage__list li {
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--c-line);
  display: flex; align-items: baseline; gap: var(--s-4);
  font-size: var(--fs-sm);
}
.coverage__list li::before {
  content: ""; flex: 0 0 auto;
  width: 14px; height: 3px; background: var(--c-yellow);
  transform: translateY(-3px);
}

/* ---------- 29. Ficha legal ---------- */
.legal { border-top: 1px solid var(--c-line); }
.legal > div {
  display: grid; gap: var(--s-1);
  padding-block: var(--s-4); border-bottom: 1px solid var(--c-line);
}
.legal dt {
  font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--c-ink-3);
}
.legal dd { margin: 0; font-weight: 500; }

@media (min-width: 640px) {
  .legal > div { grid-template-columns: minmax(0, 16rem) minmax(0, 1fr); gap: var(--s-5); align-items: baseline; }
}

/* ---------- 29.b Galeria de retratos ----------
   Las fotos del proyecto de PEAD son verticales (520x693). La rejilla asimetrica
   de arriba esta pensada para horizontales: ampliaba la primera foto a 785px
   desde un original de 520 y la recortaba a formato apaisado. Esta variante
   respeta el formato vertical y nunca pasa del tamano nativo. */
/* :nth-child(n) iguala la especificidad de las reglas nth-child de la rejilla
   base; si no, aquella gana y la primera foto vuelve a ampliarse. */
.gallery--retrato { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.gallery--retrato figure:nth-child(n) { grid-column: auto; grid-row: auto; aspect-ratio: 3 / 4; }

@media (min-width: 800px) {
  .gallery--retrato { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* 4 columnas dentro de 1280px dan ~280px por celda: por debajo de los 520px
     nativos, asi que no hay ampliacion. */
  .gallery--retrato figure:nth-child(n) { grid-column: auto; grid-row: auto; aspect-ratio: 3 / 4; }
}
/* Ocho retratos del perfil 2026: 2 columnas en telefono, 3 en tableta y 4 en
   escritorio (dos filas llenas). Ninguna celda pasa del ancho nativo de la foto
   mas chica (314px), asi que nunca se amplian. */
@media (min-width: 700px) { .gallery--campo { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .gallery--campo { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* ---------- 29.c Pasos numerados (rutas y metodologias) ----------
   Numero en Archivo, no en mono: no gasta el presupuesto de etiquetas.
   Sangria con posicion absoluta, no rejilla: con <strong> y texto dentro del
   mismo <li>, la rejilla volvia cada nodo una celda y el texto quedaba en una
   columna de una palabra por renglon. */
.pasos { margin-top: var(--s-5); max-width: 56rem; border-top: 1px solid var(--c-line); counter-reset: paso; }
.pasos li {
  counter-increment: paso;
  position: relative;
  padding: var(--s-3) 0 var(--s-3) 3rem; border-bottom: 1px solid var(--c-line);
  color: var(--c-ink-2);
}
.pasos li::before {
  content: counter(paso, decimal-leading-zero);
  position: absolute; left: 0; top: var(--s-3);
  font-family: var(--f-display); font-weight: 700; color: var(--c-ink);
  font-variant-numeric: tabular-nums;
}
.pasos strong { color: var(--c-ink); font-weight: 600; }
.prose .legal { margin-top: var(--s-5); }

/* Riel de dos cifras (caso de O&M). */
@media (min-width: 900px) { .spec--dos { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---------- 30. Documento legal (aviso de privacidad) ---------- */
/* Texto corrido largo. Se lee, no se escanea: medida angosta y numeracion
   visible para poder citar "seccion 4" en un correo. */
.doc { max-width: 68ch; }
.doc h2 {
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  margin-top: var(--s-8);
  padding-top: var(--s-5);
  border-top: 1px solid var(--c-line);
}
.doc h2:first-of-type { margin-top: var(--s-6); }
.doc h3 { font-size: 1.0625rem; margin-top: var(--s-6); }
@media (min-width: 1100px) { .doc h3 { font-size: 1.125rem; } }
.doc p { margin-top: var(--s-4); color: var(--c-ink-2); }
/* Estado del documento, visible mientras no haya visto bueno legal.
   Vacio en site.config.json = no se muestra. */
.doc__estado {
  display: inline-block;
  margin-bottom: var(--s-5);
  padding: var(--s-2) var(--s-3);
  border-left: 3px solid var(--c-ink-3);
  background: var(--c-panel);
  font-size: var(--fs-sm); color: var(--c-ink-2);
}
.doc__estado:empty { display: none; }

/* Listas dentro del documento: aqui si llevan vineta, es texto legal. */
.doc ul { margin-top: var(--s-4); }
.doc ul li {
  position: relative;
  padding-left: var(--s-5);
  padding-block: var(--s-2);
  color: var(--c-ink-2);
}
.doc ul li::before {
  content: ""; position: absolute; left: 0; top: 1.05em;
  width: 10px; height: 2px; background: var(--c-yellow);
}
.doc__meta {
  margin-top: var(--s-6); padding-top: var(--s-4);
  border-top: 1px solid var(--c-line);
  font-size: var(--fs-sm); color: var(--c-ink-3);
}

/* ---------- 31. Página 404 ---------- */
.notfound {
  min-height: 60vh;
  display: grid; align-content: center;
  padding-block: var(--s-9);
}
.notfound .label { display: block; margin-bottom: var(--s-4); }
.notfound h1 { max-width: 16ch; }
.notfound p { margin-top: var(--s-5); }
.notfound__links {
  display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-6);
  margin-top: var(--s-7);
  padding-top: var(--s-5);
  border-top: 1px solid var(--c-line);
}

/* ---------- 31.b Enlaces dentro de texto ----------
   Un solo idioma de enlace en todo el sitio: subrayado en --c-ink-3 (5.3:1).
   Antes era un borde amarillo de 1.52:1 sobre blanco, casi invisible, y en
   varios casos la UNICA senal de que habia un enlace (WCAG 1.4.1 y 1.4.11).
   Con text-decoration el subrayado sigue pegado al texto aunque el enlace crezca
   a 44px de alto para el dedo. El amarillo se queda en el hover de .link y en
   la navegacion activa. */
.contact__list dd a, .consent a, .doc a, .scope a,
.footer__bottom a, .form__status a {
  text-decoration: underline;
  text-decoration-color: var(--c-ink-3);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease), text-decoration-color var(--dur-fast) var(--ease);
}
.contact__list dd a:hover, .consent a:hover, .doc a:hover,
.scope a:hover, .footer__bottom a:hover, .form__status a:hover {
  color: var(--c-ink);
  text-decoration-color: var(--c-ink);
}

/* ---------- 32. Reveal al scroll ----------
   El contenido es visible por defecto. Solo se oculta cuando main.js confirma que
   puede revelarlo (clase js-reveal en <html>). Antes, sin JavaScript o si main.js
   no cargaba, la pagina quedaba practicamente en blanco. */
/* Entrada al desplazar. El recorrido tiene que competir con el del propio
   scroll: a 14px el bloque se movia el 3% de lo que ya lo movia la rueda y la
   entrada se leia como un fundido. Duracion propia y no --dur-slow, que la
   comparten el lienzo 3D y la galeria y ahi 520ms esta bien. */
.reveal {
  transition: opacity var(--dur-reveal) var(--ease), transform var(--dur-reveal) var(--ease);
}
.js-reveal .reveal { opacity: 0; transform: translateY(26px); }
.js-reveal .reveal.is-visible { opacity: 1; transform: none; }
/* Los bloques altos recorren menos: a 26px una ficha entera parece rebotar.
   El :not() no es adorno: sin el, estas reglas empatan en especificidad con
   .is-visible y, al ir despues, ganaban tambien con el bloque ya visible. El
   resultado era que no recorrian nada y se quedaban 16px fuera de sitio. */
.js-reveal .case.reveal:not(.is-visible),
.js-reveal .prose.reveal:not(.is-visible),
.js-reveal .foto-caso.reveal:not(.is-visible) { transform: translateY(16px); }
/* En telefono la pantalla es mas corta y el mismo recorrido se nota mas. */
@media (max-width: 699.98px) {
  .js-reveal .reveal:not(.is-visible) { transform: translateY(18px); }
}

/* Las filas del menu 3D estan en display:none por debajo de 1100px, asi que su
   reveal nunca dispara. Hoy no se ve porque miden 0px, pero si algun dia se
   vuelven a mostrar a ese ancho sin recargar, apareceran en blanco. */
@media (max-width: 1099.98px) {
  /* js-reveal y estacion-3d viven las dos en <html>: sin espacio entre ellas. */
  .js-reveal.estacion-3d .servicios-3d .rows .reveal { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .reveal, .js-reveal .reveal { opacity: 1; transform: none; transition: none; }
  .gallery img, .row, .row::before, .btn, .link::after, .link .arrow, .clients img { transition: none; }
  .gallery figure:hover img { transform: none; }
  /* El ducto se muestra lleno, sin animar el llenado. */
  .pipe__flow { transform: scaleX(1); transition: none; }
}

/* Se reserva el alto real del widget (84,2-84,8px medidos) para que el boton de
   enviar no baje cuando llega, sobre todo en redes lentas. */
.h-captcha { min-height: 85px; margin-bottom: var(--s-2); }
.h-captcha iframe { max-width: 100%; }

/* ---------- 32.b Banda de cookies (consent.js) ----------
   Aparece solo si hay analitica configurada y la persona no ha elegido todavia.
   No tapa el boton de WhatsApp: lo sube mientras esta puesta. */
.cookies {
  position: fixed; left: 0; right: 0; bottom: 0;
  z-index: var(--z-nav);
  background: var(--c-paper);
  border-top: 3px solid var(--c-yellow);
  box-shadow: 0 -8px 24px rgba(0, 32, 76, .10);
  padding-block: var(--s-4);
  padding-bottom: calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
}
.cookies__inner { display: grid; gap: var(--s-4); align-items: center; }
.cookies__texto { font-size: var(--fs-sm); color: var(--c-ink-2); max-width: 70ch; }
.cookies__texto a { text-decoration: underline; text-decoration-color: var(--c-ink-3); text-underline-offset: 3px; }
.cookies__texto a:hover { color: var(--c-ink); text-decoration-color: var(--c-ink); }
.cookies__acciones { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.cookies__acciones .btn { min-height: 44px; }
.cookies__no { border: 1px solid var(--c-ink-3); }
.cookies__no:hover { border-color: var(--c-ink); }
@media (min-width: 700px) {
  .cookies__inner { grid-template-columns: minmax(0, 1fr) auto; gap: var(--s-6); }
}
/* Cambiar de opinion, en el pie: mismo tamano y color que el enlace vecino. */
.cookies-cambiar {
  font-size: var(--fs-sm); color: var(--c-ink-2);
  text-decoration: underline; text-decoration-color: var(--c-ink-3);
  text-underline-offset: 3px;
  padding: 0; min-height: 44px;
}
.cookies-cambiar:hover { color: var(--c-ink); text-decoration-color: var(--c-ink); }
.cookies-cambiar[hidden] { display: none; }

/* ---------- 33. Impresion ---------- */
@media print {
  .header, .wa, .skip-link, .pipe, .cookies, .cookies-cambiar,
  .hero__media, .gallery,
  .estacion, .estacion-movil, .case__media, .h-captcha { display: none; }
  /* Sin esto el contenido con reveal se imprime en opacity:0 y salen hojas en blanco. */
  .js-reveal .reveal { opacity: 1; transform: none; }
  /* Por debajo de 1100px las pestanas sustituyen a la lista: al apagarlas, vuelve. */
  .estacion-3d .servicios-3d .rows { display: block; }
  .clients img { filter: none; opacity: 1; }
  body { color: #000; background: #fff; }
}


/* ==========================================================================
   34. VARIANTE 3D  (antes assets/css/estacion-3d.css)
   Se fusiono aqui el 02/10/2026: como hoja aparte bloqueaba el pintado 450ms
   por 5,8 KB, y el coste era la ida y vuelta, no el peso. Va al final a
   proposito: antes se cargaba DESPUES de styles.css y sus reglas ganan los
   empates de especificidad. No mover de sitio.
   ========================================================================== */
/* ==========================================================================
   SIICOMDT — variante 3D: estacion de regulacion con las lineas de servicio
   Solo aplica con <html class="estacion-3d">, que el <head> pone sin movimiento
   reducido, sin ahorro de datos, con memoria suficiente y con WebGL 2. En
   escritorio (>= 1100px) el modelo va junto a la lista; por debajo, arriba de
   cuatro pestanas. Sin la clase, la seccion queda identica a la original.
   ========================================================================== */

.estacion { display: none; }
.estacion-movil { display: none; }

/* ---------- Movil y tableta: modelo arriba, pestanas debajo ---------- */
@media (max-width: 1099.98px) {
  /* La lista se reemplaza por las pestanas; su texto vive en .estacion-info. */
  .estacion-3d .servicios-3d .rows { display: none; }
  /* Salvo en servicios.html, donde cada bloque trae ademas el alcance de la
     linea: ahi la lista no es el menu (lo son las pestanas), se queda a la
     vista debajo del modelo y cada cabecera vuelve a ser un enlace. */
  .estacion-3d .servicios-3d .rows--detalle {
    display: block;
    margin-top: clamp(2.5rem, 6vw, 4rem);
  }

  .estacion-3d .estacion {
    display: block;
    position: relative;
    aspect-ratio: 4 / 3;
    /* Telefono en horizontal: sin tope, el panel media mas que la pantalla y la
       escena elegida quedaba fuera de la vista. El panel + pestanas caben bajo el
       header. width:100% evita que aspect-ratio angoste el panel al topar. */
    width: 100%;
    max-height: max(12rem, calc(100svh - var(--header-h) - 56px - var(--s-5)));
    background: var(--c-panel);
    border: 1px solid var(--c-line);
    overflow: hidden;
  }
  /* En el panel angosto solo cabe el rotulo: leyenda, nota y enlace pasan a
     .estacion-info, debajo de las pestanas. */
  .estacion-3d .estacion .estacion__leyenda,
  .estacion-3d .estacion .estacion__nota,
  .estacion-3d .estacion .estacion__enlace { display: none; }
  .estacion-3d .estacion .estacion__cabeza {
    left: var(--s-4); top: var(--s-4);
    max-width: calc(100% - 2 * var(--s-4));
  }

  .estacion-3d .estacion-movil { display: block; }

  /* Cuatro columnas; si el texto se agranda (125-200%) y un nombre ya no cabe,
     salta directo a 2x2 en vez de encimar nombres. El clamp elige 25% o 50%
     segun si el ancho minimo del nombre (en rem, crece con el texto) supera el
     cuarto del contenedor. Los filetes son el gap sobre fondo de linea, asi
     tambien separan filas. */
  .estacion-tabs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(clamp(calc(25% - 1px), (3.9rem + 25px - 25%) * 999, calc(50% - 1px)), 1fr));
    gap: 1px;
    background: var(--c-line);
    border: 1px solid var(--c-line);
    border-top: 0;
  }
  .estacion-tab {
    position: relative;
    display: flex; flex-direction: column; justify-content: center; gap: 2px;
    min-height: 56px;
    padding: var(--s-2) var(--s-3);
    color: var(--c-ink);
    background: var(--c-paper);
    text-decoration: none;
    transition: background var(--dur-fast) var(--ease);
  }
  /* Brida arriba, pegada al modelo: marino en la elegida (14.5:1). */
  .estacion-tab::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0;
    height: 3px; background: var(--c-ink);
    transform: scaleX(0); transform-origin: left;
    transition: transform var(--dur) var(--ease);
  }
  /* Solo con puntero real: en tactil el :hover se queda pegado tras tocar y la
     pestana recien deseleccionada parecia seguir elegida. */
  @media (hover: hover) { .estacion-tab:hover { background: var(--c-panel); } }
  .estacion-tab:focus-visible { outline-offset: -3px; }
  .estacion-tab[aria-pressed="true"] { background: var(--c-panel-2); }
  .estacion-tab[aria-pressed="true"]::before { transform: scaleX(1); }
  .estacion-tab__num {
    font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--tr-label);
    color: var(--c-ink-3);
  }
  .estacion-tab[aria-pressed="true"] .estacion-tab__num { color: var(--c-ink); }
  .estacion-tab__nombre {
    font-size: var(--fs-sm); font-weight: 600; line-height: 1.2;
    /* "Construcción" y "Cumplimiento" no caben en una linea a 320-390px. El HTML
       lleva guiones blandos (&shy;): si no cabe, parte por silaba con guion
       ("Cumpli- / miento"), nunca a media letra ni encimada sobre la vecina. */
    hyphens: manual;
    overflow-wrap: normal;
  }

  .estacion-info { padding-top: var(--s-5); }
  /* Hasta que la estacion monta, las pestanas son enlaces y no hay escena que
     elegir: la invitacion se oculta para no prometer algo que aun no pasa. */
  /* visibility y no display: el bloque reserva su alto desde el principio. Con
     display:none aparecia de golpe al montar y empujaba la seccion siguiente
     unos 116px (CLS). Oculto asi tampoco recibe foco ni se lee. */
  .servicios-3d:not(.es-interactiva) .estacion-info { visibility: hidden; }
  .estacion-info__titulo {
    font-family: var(--f-display);
    font-size: clamp(1.25rem, 4vw, 1.5rem);
    font-weight: 700; letter-spacing: -.025em; line-height: 1.15;
    color: var(--c-ink);
  }
  .estacion-info__desc { margin-top: var(--s-2); color: var(--c-ink-2); max-width: 52ch; }
  .estacion-info__escena { margin-top: var(--s-2); font-size: var(--fs-sm); color: var(--c-ink-3); }
  .estacion-info__escena[hidden],
  .estacion-info__enlace[hidden] { display: none; }
  .estacion-info__enlace { margin-top: var(--s-3); min-height: 44px; }
  .estacion-info__nota {
    margin-top: var(--s-4);
    font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--tr-label); text-transform: uppercase;
    color: var(--c-ink-3);
  }
}
@media (min-width: 700px) and (max-width: 1099.98px) {
  /* Tableta: un panel 4:3 a todo lo ancho seria mas alto que la pantalla. */
  .estacion-3d .estacion { aspect-ratio: 16 / 10; }
}
/* Telefonos: cada pestana mide unos 70-90px. Los nombres largos parten por
   silaba gracias a los guiones blandos del HTML. */
@media (max-width: 419.98px) {
  .estacion-3d .estacion-tabs { grid-template-columns: repeat(auto-fit, minmax(clamp(calc(25% - 1px), (3.6rem + 14px - 25%) * 999, calc(50% - 1px)), 1fr)); }
  .estacion-tab { padding-inline: 6px; }
  .estacion-tab__nombre { font-size: .75rem; }
}
@media (max-width: 359.98px) {
  .estacion-3d .estacion-tabs { grid-template-columns: repeat(auto-fit, minmax(clamp(calc(25% - 1px), (3.3rem + 10px - 25%) * 999, calc(50% - 1px)), 1fr)); }
  .estacion-tab { padding-inline: 4px; }
  .estacion-tab__nombre { font-size: .6875rem; letter-spacing: -.01em; }
}

@media (min-width: 1100px) {
  .estacion-3d .servicios-3d {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr);
    gap: var(--s-7);
    align-items: start;
  }
  /* El modelo va primero en el HTML porque en movil se lee primero. Aqui la
     lista vuelve a la columna izquierda. */
  .estacion-3d .servicios-3d .rows { order: -1; }

  /* Filas mas angostas: numero, titulo y descripcion apilados, flecha a la
     derecha. Con las cuatro columnas originales la descripcion quedaba en
     una tira de 200px. */
  .estacion-3d .servicios-3d .row__inner {
    grid-template-columns: 3rem minmax(0, 1fr) 2rem;
    gap: var(--s-2) var(--s-5);
  }
  .estacion-3d .servicios-3d .row__num   { grid-column: 1; grid-row: 1 / span 2; padding-top: .35rem; }
  .estacion-3d .servicios-3d .row__title { grid-column: 2; grid-row: 1; }
  .estacion-3d .servicios-3d .row__desc  { grid-column: 2; grid-row: 2; }
  .estacion-3d .servicios-3d .row__arrow { grid-column: 3; grid-row: 1; justify-self: end; }
  /* El alcance, alineado con el titulo. La columna es angosta: una sola
     columna de renglones, no dos. */
  .estacion-3d .servicios-3d .servicio__detalle {
    padding-left: calc(var(--s-4) + 3rem + var(--s-5));
  }
  .estacion-3d .servicios-3d .servicio__detalle .scope { columns: auto; }

  /* Con la estacion montada las filas son el menu de escenas: la flecha de
     "ir a otra pagina" sobra, y la fila elegida queda marcada con la brida y
     el fondo del hover. La pagina del servicio se abre desde el panel. */
  .estacion-3d .servicios-3d.es-interactiva .row__arrow { visibility: hidden; }
  /* Elegida no es lo mismo que hover: brida MARINO (14.5:1 sobre el panel) y
     fondo un tono mas oscuro. El hover conserva la brida amarilla, asi que al
     pasar el cursor por otra fila no parecen elegidas las dos. */
  .estacion-3d .servicios-3d.es-interactiva .row[aria-pressed="true"] { background: var(--c-panel-2); }
  .estacion-3d .servicios-3d.es-interactiva .row[aria-pressed="true"]::before { transform: scaleY(1); background: var(--c-ink); }
  .estacion-3d .servicios-3d.es-interactiva .row[aria-pressed="true"] .row__num { color: var(--c-ink); }
  /* Igual que el hover: la marca cubre el bloque entero, no solo la cabecera. */
  .estacion-3d .servicios-3d.es-interactiva .servicio:has(.row[aria-pressed="true"]) { background: var(--c-panel-2); }
  .estacion-3d .servicios-3d.es-interactiva .servicio:has(.row[aria-pressed="true"])::before {
    transform: scaleY(1); background: var(--c-ink);
  }

  /* Donde cada bloque trae su propio enlace con nombre (servicios.html), el del
     panel repetia destino y se tabulaba antes que la lista: desde la cabecera
     elegida habia que retroceder seis veces para alcanzarlo. En la portada,
     donde las filas no llevan enlace propio, se queda. */
  .estacion-3d .servicios-3d--detalle .estacion__enlace { display: none; }

  .estacion-3d .estacion {
    display: block;
    position: sticky;
    top: calc(var(--header-h) + var(--s-5));
    aspect-ratio: 5 / 4;
    background: var(--c-panel);
    border: 1px solid var(--c-line);
    overflow: hidden;
  }
}

.estacion__lienzo {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease);
}
.estacion.is-lista .estacion__lienzo { opacity: 1; }
.estacion__lienzo canvas { display: block; width: 100%; height: 100%; }

.estacion__rotulo,
.estacion__nota {
  position: absolute;
  font-family: var(--f-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  pointer-events: none;
}
.estacion__cabeza {
  position: absolute; left: var(--s-5); top: var(--s-5);
  /* Fondo del panel detras del texto: las escenas (hojas, plano de corte) pueden
     pasar por esa zona y el texto debe seguir legible. */
  background: var(--c-panel);
  /* Deja libre la esquina de "Modelo ilustrativo" (unos 9.5rem) aun en el
     panel mas angosto, a 1100px. */
  max-width: min(calc(100% - 3 * var(--s-5) - 10rem), 30rem);
  pointer-events: none;
}
.estacion__rotulo {
  position: static;
  display: flex; gap: var(--s-3); align-items: baseline;
  color: var(--c-ink);
}
/* Que muestra la escena. Sans, no mono: es una frase, no una etiqueta. */
.estacion__leyenda {
  margin-top: var(--s-2);
  font-size: var(--fs-sm); line-height: 1.45;
  color: var(--c-ink-2);
}
.estacion__num { color: var(--c-ink-3); }
.estacion__modo { border-bottom: 2px solid var(--c-yellow); padding-bottom: 2px; }
.estacion__nota { right: var(--s-5); top: var(--s-5); color: var(--c-ink-3); }

/* Enlace a la pagina del servicio elegido, abajo a la derecha. */
.estacion__enlace {
  position: absolute; right: var(--s-5); bottom: var(--s-3);
  min-height: 44px;
}
.estacion__enlace[hidden] { display: none; }

/* Nombres de los permisos sobre cada hoja. El JS fija la posicion con transform. */
.estacion__fichas { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.estacion__ficha {
  position: absolute; left: 0; top: 0;
  padding: 2px var(--s-2);
  font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); text-transform: uppercase;
  color: var(--c-ink); background: var(--c-paper);
  border: 1px solid var(--c-line-2);
  white-space: nowrap;
}
.estacion__ficha[hidden] { display: none; }
.estacion__ficha.es-sellada { border-color: var(--c-ink); box-shadow: inset 0 -2px 0 var(--c-yellow); }

/* Boton de pausa (WCAG 2.2.2). Mismo lenguaje que los rotulos: mono, recto,
   filete fino. 44px de alto como todo objetivo tactil del sitio. */
.estacion__pausa {
  position: absolute; left: var(--s-4); bottom: var(--s-3);
  min-height: 44px; padding-inline: var(--s-3);
  display: inline-flex; align-items: center;
  font-family: var(--f-mono); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--tr-label); text-transform: uppercase;
  color: var(--c-ink);
  background: var(--c-paper);
  border: 1px solid var(--c-ink-3);
  border-radius: 0;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease);
}
.estacion__pausa:hover { border-color: var(--c-ink); }
.estacion__pausa[hidden] { display: none; }

/* Si three.js no carga con el panel a la vista, el panel se queda quieto con
   sus rotulos (sin saltos de la rejilla) hasta que sale de pantalla. Excepcion:
   con el bloque al inicio de la pagina (data-estacion="inicio", servicios.html)
   se vuelve a la lista en el acto, porque ahi el panel nunca sale por abajo. */
.estacion.es-fallida .estacion__lienzo,
.estacion.es-fallida .estacion__pausa { display: none; }

/* Mientras three.js carga (o si fallo) no hay escena que elegir: la invitacion
   a elegir una linea confundiria, porque ahi las filas todavia llevan a otra pagina. */
.estacion:not(.is-lista) .estacion__leyenda,
.estacion.es-fallida .estacion__leyenda { visibility: hidden; }

/* ---------- Fichas de servicio (servicios-*.html) ----------
   Solo escritorio: texto a la izquierda y la escena de la linea a la derecha,
   fija bajo el encabezado mientras se lee la primera seccion. Sin la clase
   estacion-3d (o por debajo de 1100px) la pagina queda como siempre. */
@media (max-width: 1099.98px) {
  .estacion-3d .ficha-3d .estacion { display: none; }
}
@media (min-width: 1100px) {
  .estacion-3d .ficha-3d {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--s-8);
    align-items: start;
  }
  /* Autoridades (cumplimiento): en media columna, 16rem de etiqueta dejaba la
     descripcion en tres renglones. "Estatal y municipal" cabe en 11rem. */
  .estacion-3d .ficha-3d .legal > div { grid-template-columns: minmax(0, 11rem) minmax(0, 1fr); }
}

/* Colores forzados (Windows Contraste alto): el fondo del pseudo-elemento se
   anula y la fila elegida no se distinguiria en nada. */
@media (forced-colors: active) {
  .estacion-tab[aria-pressed="true"]::before,
  .estacion-3d .servicios-3d.es-interactiva .row[aria-pressed="true"]::before {
    forced-color-adjust: none;
    background: Highlight;
  }
  /* Aparte, y no en la lista de arriba: un selector que el navegador no entiende
     tira la regla COMPLETA (las listas de selectores no perdonan, al reves que
     :is()). Sin :has() se pierde el realce del bloque, no el de la cabecera. */
  .estacion-3d .servicios-3d.es-interactiva .servicio:has(.row[aria-pressed="true"])::before {
    forced-color-adjust: none;
    background: Highlight;
  }
}

/* Impresion: el modelo sale como un recuadro gris de media hoja. Aqui, no en
   styles.css: la regla de escritorio de este archivo (.estacion-3d .estacion)
   gana por especificidad y volvia a mostrarlo. */
@media print {
  .estacion-3d .estacion,
  .estacion-3d .estacion-movil { display: none; }
  .estacion-3d .servicios-3d .rows { display: block; }
}
