/* ============================================================
   DYRECO · Dirección C — NAVE
   estilos.css
   ------------------------------------------------------------
   00 Base y reset            13 Trabajos recientes
   01 Utilidades y retícula   14 Sectores
   02 Tipografía              15 Equipo (rejilla) · 16 Reseñas (rejilla)
   03 Botones y enlaces       17 Tiendas · 18 FAQ · 19 Cierre · 20 Pie
   04 Tratamiento de imagen   ------------------------------------------
   05 Cabecera + menú         MECANISMOS COMPARTIDOS (los usan las 8 págs)
   06 Hero + PERSIANA         21 Fichas arrastrables   [data-fichas]
   07 Barra de confianza      22 Galería horizontal    [data-galh]
   08 Las 4 divisiones        23 Panel vertical        [data-panelv]
   09 Proveedor integral      24 Pestañas clip-path    [data-pest]
   10 Cómo trabajamos         25 Transiciones de sección
   11 Marcas (marquee)        26 Microinteracciones
   12 Personalización         27 prefers-reduced-motion · 28 Impresión
                              29 NOMBRES RESERVADOS ← léelo antes de
                                 bautizar nada en una hoja de página
   ------------------------------------------------------------
   Cada pieza deslizante del sitio usa un mecanismo DISTINTO:
   persiana (hero) · fichas (equipo) · galería horizontal (trabajos)
   marquee (marcas) · panel vertical (reseñas) · pestañas (técnicas).
   Si dos secciones se mueven igual, el trabajo está a medias.
   ============================================================ */


/* ============================================================
   00 · BASE Y RESET
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  overflow-x: clip;              /* clip, nunca hidden: hidden rompe sticky */
  background: var(--fondo);
  color: var(--texto);
  font-family: var(--lectura);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, p, ul, ol, figure, blockquote, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { max-width: 100%; }
img { border-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
address { font-style: normal; }
summary::-webkit-details-marker { display: none; }

:focus-visible {
  outline: 2px solid var(--amarillo);
  outline-offset: 3px;
  border-radius: 1px;
}

::selection { background: var(--amarillo); color: var(--sobre-amarillo); }

.saltar {
  position: fixed; top: 8px; left: 8px; z-index: 200;
  transform: translateY(-160%);
  background: var(--amarillo); color: var(--sobre-amarillo);
  padding: 10px 18px; font-family: var(--display); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--ls-nav);
  transition: transform var(--dur-ui) var(--ease);
}
.saltar:focus { transform: translateY(0); }

.visualmente-oculto {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}


/* ============================================================
   01 · RETÍCULA Y UTILIDADES
   ============================================================ */

.cont {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Reveals — estado oculto SOLO si hay JS **y** solo mientras no esté sellado.
   `.reveals-sellados` la pone el script EN LÍNEA del <head> a los 2.500 ms
   (el mismo que pone `.js`), así que el rescate no depende de que llegue a
   ejecutarse ningún fichero externo: si js/main.js no carga, el estado oculto
   caduca solo y se ve todo. */
html.js:not(.reveals-sellados) [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 38px, 0);
  transition:
    opacity var(--dur-reveal) var(--ease),
    transform var(--dur-reveal) var(--ease);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
html.js [data-reveal].is-in { opacity: 1; transform: none; }
/* Al caducar el estado oculto, que aparezca con un fundido y no de golpe. */
html.js.reveals-sellados [data-reveal]:not(.is-in) {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-reveal) var(--ease),
    transform var(--dur-reveal) var(--ease);
}

/* --- Utilidades de composición que estaban RECOPIADAS en cada hoja ---------
   Medido en agosto-2026 sobre las 11 hojas: la misma receta se reescribía una
   y otra vez en vez de vivir una sola vez.
     · el fondo de banda a sangre  → 11 sitios en 6 hojas (`.integral__fondo`,
       `.cierre__fondo`, `.pnl__fondo`, `.franja__fondo`, `.insta__fondo`,
       `.rep__fondo`, `.ccta__fondo`, `.ent__fondo`, `.aseso__fondo`,
       `.uepi__fondo`, `.ucta__fondo`);
     · la fila de botones          → 10 sitios en 5 hojas;
     · la numeración amarilla      → 10 sitios en 6 hojas, teniendo `.div__num`;
     · la banda gris de sección    → 6 sitios en 4 hojas.
   El coste no es el peso: es que el día que el fondo de banda tenga que pasar
   de `overflow:hidden` a `overflow:clip` hay que tocarlo 11 veces en 6
   ficheros. Aquí están las cuatro, con nombre del sistema.

   ⚠️ PARA QUIEN TOQUE UNA HOJA DE PÁGINA: al pasar por una de esas reglas,
   cámbiala por la clase de aquí en el HTML y borra el bloque de tu hoja. Lo
   que sea propio de la pieza (un `margin-bottom`, un `z-index`) se queda en tu
   hoja como ajuste sobre la utilidad; la receta común, no. */
.banda__fondo {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}
.fila-botones {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}
/* Numeración amarilla de kicker. Es la misma de `.div__num` (§08), que se
   queda como está por no romper la Home; en una pieza nueva se usa esta. */
.num {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
}
.sec--gris {
  background: var(--sup);
  border-block: 1px solid var(--linea);
  padding-block: var(--sec-y);
}

/* Los saltos por ancla no se meten debajo de la cabecera fija */
:target,
main > section[id],
footer[id] { scroll-margin-top: calc(var(--alto-cab) + 24px); }

/* …y TAMPOCO el elemento que acaba de recibir el foco. Con la cabecera fija y
   una ventana baja (1440×600, 1280×620) el navegador sube el campo enfocado
   hasta el borde del viewport, no hasta el borde de la cabecera: medido, a
   1440×600 los inputs #f-nombre y #f-empresa de contacto aterrizaban en y=73
   con la cabecera llegando a y=77, o sea 4 px del campo y su anillo de foco
   por debajo (13 px a 1280×620). Misma cuenta que la regla de arriba.
   Va sobre :focus-visible y no sobre :focus para no reposicionar la página
   cuando el foco lo da un clic del ratón, que ya sabe dónde ha pulsado. */
:is(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  scroll-margin-top: calc(var(--alto-cab) + 16px);
  scroll-margin-bottom: 16px;
}


/* ============================================================
   02 · TIPOGRAFÍA
   ============================================================ */

.kicker {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  line-height: 1;
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--amarillo);
  margin-bottom: var(--pieza);
}
/* La diagonal — único activo gráfico de la marca junto al amarillo */
.kicker::before {
  content: "";
  width: 22px; height: 2px;
  background: var(--amarillo);
  transform: skewX(-30deg);
  flex: none;
}

.h2 {
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--fs-h2);
  line-height: var(--lh-titulo);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--texto);
  text-wrap: balance;
  max-width: 20ch;
}

/* Los trece H2 arrancan en el mismo borde izquierdo: no hay variante centrada.
   (La había, y la usaban `marcas` y `cierre`; dos excepciones en una página
   cuyo orden se basa precisamente en ese borde.) */
.cabecera-sec { margin-bottom: var(--bloque); }

.cabecera-sec--split {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--gap) clamp(32px, 6vw, 120px);
  align-items: end;
}
.cabecera-sec__entrada {
  color: var(--apagado);
  /* Anclada al borde derecho de su columna (5 de 12, 517 px a 1440): así la
     cabecera se lee como lo que es —titular a la izquierda, entrada a la
     derecha— y el bloque cierra por los dos lados.
     El tope era `52ch` = 509 px y renderizaba 71 caracteres reales; con
     `--medida` son 63 (medido con Range, 8-sep-2026). La columna sigue
     llena por la derecha porque el bloque va `justify-self: end`. */
  max-width: var(--medida);
  justify-self: end;
  padding-bottom: .35em;
}

@media (max-width: 900px) {
  .cabecera-sec--split { grid-template-columns: 1fr; align-items: start; }
  /* Apilada, la entrada vuelve al margen izquierdo: el `justify-self: end` de
     escritorio la dejaba descolgada 277 px a la derecha bajo el titular. */
  .cabecera-sec__entrada { justify-self: start; padding-bottom: 0; }
}


/* --- 2.x · VIUDAS ------------------------------------------
   `text-wrap: pretty` le pide al navegador que no deje la última línea de un
   párrafo en un muñón de una o dos palabras. Se medían ocho en las cuatro
   páginas de división —rótulos de dos palabras colgando bajo tres líneas
   llenas— y perseguirlas reescribiendo frases es trabajo que se deshace al
   primer cambio de copy: esto lo resuelve el motor y aguanta.
   Donde no está soportado, el texto se parte como siempre: no rompe nada. */
main p,
.faq__r p,
.destacado__p, .rail__p, .ccta__p, .cat__p, .punto__p,
.asesor__entrada, .hero-div__entrada, .cabecera-sec__entrada {
  text-wrap: pretty;
}


/* ============================================================
   03 · BOTONES Y ENLACES
   ============================================================ */

.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 14px clamp(24px, 2.4vw, 36px);
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(.875rem, 1vw, 1rem);
  letter-spacing: .09em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
  transition: color var(--dur-ui) var(--ease), transform var(--dur-ui) var(--ease);
}
/* corte diagonal, eco del wordmark */
.btn::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  clip-path: polygon(11px 0, 100% 0, calc(100% - 11px) 100%, 0 100%);
  /* ⚠️ La `transition` de este pseudoelemento NO se declara aquí: vive con el
     barrido, en §26 («Botones: el fondo se desplaza en diagonal»), porque es
     la que anima `background-position`. Estuvo declarada en los dos sitios y
     ganaba la de §26 entera, así que tocar la de aquí no hacía nada. */
}

.btn--luz { color: var(--sobre-amarillo); }        /* sobre amarillo → casi negro */
/* El color se repite en los tres estados porque cualquier host que declare
   un `.btn:hover{color:…}` (Bootstrap y Odoo lo hacen) gana a `.btn--luz`
   por especificidad: 0-1-1 contra 0-1-0. Con dos clases mandamos nosotros y
   el texto sobre amarillo nunca se aclara. 9-sep-2026. */
.btn--luz:hover,
.btn--luz:focus,
.btn--luz:active { color: var(--sobre-amarillo); }
.btn--luz::before { background: var(--amarillo); }
.btn--luz:hover::before { box-shadow: 0 0 0 1px var(--amarillo), 0 14px 44px rgb(var(--am-rgb) / .22); }

/* El secundario no lleva filete: un borde de 1 px se parte contra el corte
   diagonal del clip-path. Se resuelve como superficie de vidrio, misma
   silueta y menos peso que el amarillo. */
.btn--linea { color: var(--texto); }
.btn--linea::before {
  background: rgb(var(--texto-rgb) / .10);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.btn--linea:hover { color: var(--amarillo); }
.btn--linea:hover::before { background: rgb(var(--am-rgb) / .14); }

.enlace-flecha {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  gap: 12px;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--amarillo);
  padding-block: 12px;
  transition: gap var(--dur-ui) var(--ease);
}
.enlace-flecha span {
  width: 30px; height: 1px; background: var(--amarillo);
  transition: width var(--dur-ui) var(--ease);
}
.enlace-flecha:hover { gap: 18px; }
.enlace-flecha:hover span { width: 46px; }

/* --- Destino PREVISTO que todavía no tiene página en la maqueta ---
   /aviso-legal, /privacidad, /cookies, /blog, /tienda y
   /trabaja-con-nosotros son rutas FINALES de Odoo y allí existirán, pero
   esta maqueta son 8 páginas y detrás de ellas no hay nada: con el href
   puesto, un clic delante del cliente daba un 404.
   El trabajo lo hace el HTML — el ancla va SIN href, con
   aria-disabled="true", un title que lo explica y la ruta real guardada
   en data-ruta (ver el comentario del pie en cada página). Sin href el
   ancla ya no navega, sale del orden de tabulación y el lector de
   pantalla la anuncia como texto, no como enlace roto.
   Aquí solo queda vestirla: se APAGA, no se tacha — tiene que leerse
   "prevista", no "anulada". El punteado amarillo al 26 % es el mismo
   amarillo-luz de la dirección, en su intensidad más baja.
   Los selectores repiten .pie__col / .pie__legales porque el pie tiñe
   por selector de elemento (`.pie__col a:hover`) y ganaría por
   especificidad.

   ⚠️ EL COLOR NO SE PUEDE APAGAR MÁS. Estuvo en `--apagado` al 55 %, que
   sobre carbón da rgb(83,83,89) y 2,51:1 — la mitad del 4,5:1 que le toca a
   un texto de 12-14 px, y eran los seis enlaces del pie en las ocho páginas
   (49 nodos). La excepción de WCAG 1.4.3 para componentes deshabilitados NO
   los cubre: son `<a>` SIN href, o sea texto corriente, no un widget.
   La señal de "todavía no está" la llevan el SUBRAYADO PUNTEADO AMARILLO y
   el cursor, que no dependen del contraste; el color se queda en `--apagado`
   opaco (7,61:1 desde que se aclaró el token el 6-sep-2026; eran 5,79), el
   mismo de los enlaces vivos del pie. */
.enlace-pendiente,
.pie__col a.enlace-pendiente,
.pie__legales a.enlace-pendiente {
  color: var(--apagado);
  cursor: default;
  text-decoration: underline dotted rgb(var(--am-rgb) / .55);
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}
/* El pie desplaza y aclara sus enlaces en hover: aquí no pasa nada. */
.enlace-pendiente:hover,
.pie__col a.enlace-pendiente:hover,
.pie__legales a.enlace-pendiente:hover {
  color: var(--apagado);
  padding-left: 0;
  /* Se repite el subrayado porque en Odoo `a:hover` lo apaga (ver el
     puente dyreco-odoo.css): con dos clases gana esta regla. */
  text-decoration: underline dotted rgb(var(--am-rgb) / .55);
}


/* ============================================================
   04 · TRATAMIENTO DE IMAGEN
   Foto oscurecida + viñeta + barrido de luz amarilla rasante
   ============================================================ */

.media {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  border-radius: var(--radio);
  background: var(--sup-2);
}
/* <picture> solo sirve para elegir el recorte; no debe crear caja, o el
   height:100% del <img> perdería su referencia y se rompería el cover. */
.media picture { display: contents; }
.media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--foto);
  transition: transform var(--dur-hover) var(--ease);
}
/* viñeta */
.media::before {
  content: "";
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(122% 84% at 50% 46%, transparent 38%, rgb(var(--negro-rgb) / .48) 100%);
}
/* barrido de luz amarilla */
.media::after {
  content: "";
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(105deg, rgb(var(--am-rgb) / .16) 0%, transparent 38%);
  mix-blend-mode: screen;
}
/* fotos flojas (ferretería / maquinaria): más contraste, menos oscuridad */
.media--plana img { filter: var(--foto-plana); }
.media--plana::before {
  background: radial-gradient(122% 84% at 50% 46%, transparent 46%, rgb(var(--negro-rgb) / .36) 100%);
}


/* ============================================================
   05 · CABECERA + MENÚ MÓVIL
   ============================================================ */

.cab {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 90;
  background: rgb(var(--fondo-rgb) / 0);
  transition: background 420ms var(--ease), backdrop-filter 420ms var(--ease);
}
/* Scrim permanente bajo la cabecera transparente.
   La cabecera sigue siendo transparente sobre el hero (contrato de la
   dirección: sólida solo al superar el pin), pero el hero desfila por debajo
   y el CTA amarillo del propio hero acababa detrás del wordmark blanco
   (1,21:1). Este degradado rasante mantiene el logo y la hamburguesa sobre
   carbón pase lo que pase por debajo, sin volver opaca la cabecera. */
.cab::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: calc(var(--alto-cab) + 46px);
  pointer-events: none;
  background: linear-gradient(180deg,
    rgb(var(--fondo-rgb) / .94) 0%,
    rgb(var(--fondo-rgb) / .88) 46%,
    rgb(var(--fondo-rgb) / .52) 74%,
    rgb(var(--fondo-rgb) / 0) 100%);
  opacity: 1;
  transition: opacity 420ms var(--ease);
}
.cab.is-solida::before { opacity: 0; }
.cab__int {
  position: relative;
  z-index: 1;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
  height: var(--alto-cab);
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 40px);
}
.cab__filete {
  position: relative;
  z-index: 1;
  height: 1px;
  background: var(--linea);
  opacity: 0;
  transition: opacity 420ms var(--ease);
}
.cab.is-solida {
  background: rgb(var(--fondo-rgb) / .94);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
}
.cab.is-solida .cab__filete { opacity: 1; }

.cab__logo {
  flex: none;
  display: flex;
  align-items: center;
  min-height: 44px;                  /* área táctil: el wordmark mide 12 px de alto */
  width: clamp(132px, 12vw, 172px);
  color: #fff;                       /* logo blanco siempre */
}
/* El wordmark va con fill:currentColor, pero servido como <img> no hereda
   el color del documento: se blanquea con filtro. Blanco sobre el hero y
   blanco sobre la cabecera sólida. */
.cab__logo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 24145 / 2219;        /* nunca deformarlo */
  filter: brightness(0) invert(1);
}

.cab__nav { margin-left: auto; }
.cab__lista {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.5vw, 28px);
}
.cab__lista a {
  display: block;
  position: relative;
  padding: 12px 2px;
  font-family: var(--display);
  font-weight: 600;
  font-size: .8125rem;
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  color: var(--texto);
  transition: color var(--dur-ui) var(--ease);
}
.cab__lista a::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 6px;
  height: 2px; background: var(--amarillo);
  transform: scaleX(0) skewX(-30deg);
  transform-origin: left;
  transition: transform 420ms var(--ease);
}
.cab__lista a:hover { color: var(--amarillo); }
.cab__lista a:hover::after { transform: scaleX(1) skewX(-30deg); }

.cab__acciones { display: flex; align-items: center; gap: clamp(12px, 1.4vw, 24px); }
.cab__tel {
  font-family: var(--display);
  font-weight: 600;
  font-size: .875rem;
  letter-spacing: .08em;
  color: var(--apagado);
  padding: 12px 0;
  transition: color var(--dur-ui) var(--ease);
}
.cab__tel:hover { color: var(--texto); }
.cab__cta { min-height: 44px; padding-block: 11px; }

.cab__burger {
  display: none;
  width: 46px; height: 46px;
  align-items: center; justify-content: center;
}
.cab__burger-lineas { display: grid; gap: 6px; width: 24px; }
.cab__burger-lineas i {
  display: block; height: 2px; background: var(--texto);
  transition: background var(--dur-ui) var(--ease);
}
.cab__burger-lineas i:last-child { width: 16px; margin-left: auto; }
.cab__burger:hover .cab__burger-lineas i { background: var(--amarillo); }

/* ⚠️ EL CORTE SUBIÓ DE 1180 A 1259 EL 8-SEP-2026, AL ENTRAR «BLOG».
   La navegación pasó de siete enlaces a ocho y la cabecera se quedó sin
   sitio por abajo. Medido en blog.html con el tipo real, ventana a ventana:
     1280 …… teléfono en 1 línea · 63 px de holgura entre el logo y la nav
     1260 …… 1 línea ·  49 px
     1240 …… 1 línea ·  35 px
     1220 …… teléfono partido en DOS líneas
     1181 …… teléfono partido en TRES: «971 / 270 / 687»
   El teléfono es texto y encoge antes que nadie, así que el síntoma no era
   un desborde —el QA de los siete anchos salía limpio— sino el número de la
   tienda roto en tres renglones dentro de un `tel:`. Con ocho enlaces la
   cabecera de escritorio necesita 1.240 px; el corte se pone en 1.259 para
   dejar 49 px de margen a una cara de respaldo un pelo más ancha.
   NO SE PIERDE NADA entre 1.180 y 1.259: ahí sale la hamburguesa, y el panel
   lleva los ocho enlaces, el teléfono y el CTA (comprobado a 390×844: los
   ocho caben sin scroll). Si algún día se quita un enlace de la nav, esta
   cifra puede volver a bajar — pero se vuelve a medir, no se supone. */
@media (max-width: 1259px) {
  .cab__nav { display: none; }
  .cab__acciones { margin-left: auto; }
  .cab__burger { display: inline-flex; }

  /* --- RED DE SEGURIDAD: la hamburguesa promete algo que vive en main.js ---
     Con `js/main.js` bloqueado (404, corte de red, bloqueador) la cabecera se
     quedaba MUDA por debajo de 1259 px: `.cab__nav` oculta por la media query
     de arriba y un burger que se ve y no hace nada, que es peor que no estar.
     Medido a 390×844: 0 enlaces de navegación visibles en la cabecera.
     No basta con `html:not(.js)`: la clase `js` la pone el script EN LÍNEA del
     <head>, que sí se ejecuta siempre. Hace falta saber si main.js LLEGÓ, y eso
     lo dice `js-ok`, que main.js escribe al final de arranca().
     La condición lleva además `.reveals-sellados` —el sello de 2.500 ms del
     mismo script en línea— para no encender la navegación completa en el medio
     segundo que main.js tarda en ejecutarse en una carga normal: sin eso, cada
     carga en móvil enseñaría los 7 enlaces y los recogería después.

     ⚠️ Y LA CABECERA SIGUE SIENDO `fixed`. Esta red se probó primero con
     `position: static` para que pudiera crecer sin tapar nada, y en una red
     lenta salía carísima: en Slow 4G main.js no llega hasta los ~9,5 s, o sea
     MUY después del sello de 2.500 ms, así que la cabecera se volvía estática,
     empujaba el hero 154 px hacia abajo y lo devolvía al llegar main.js. CLS
     medido a 390×844@3: 0,3649, contra los 0,0005 de la carga normal. Fuera
     del flujo no hay salto posible: la cabecera crece POR ENCIMA del hero, que
     es a sangre y tiene el contenido pegado abajo, y se le pone fondo opaco
     porque el degradado de `.cab::before` solo cubre `--alto-cab + 46px`. */
  html.reveals-sellados:not(.js-ok) .cab {
    background: rgb(var(--fondo-rgb) / .96);
  }
  html.reveals-sellados:not(.js-ok) .cab__filete { opacity: 1; }
  html.reveals-sellados:not(.js-ok) .cab__int {
    height: auto;
    min-height: var(--alto-cab);
    flex-wrap: wrap;
    padding-block: 8px;
    row-gap: 2px;
  }
  html.reveals-sellados:not(.js-ok) .cab__nav {
    display: block;
    order: 3;
    width: 100%;
    margin-left: 0;
  }
  html.reveals-sellados:not(.js-ok) .cab__lista { flex-wrap: wrap; gap: 0 18px; }
  html.reveals-sellados:not(.js-ok) .cab__lista a { padding-block: 10px; }
  html.reveals-sellados:not(.js-ok) .cab__burger { display: none; }
}
@media (max-width: 720px) {
  .cab__tel, .cab__cta { display: none; }
}

/* --- Panel a pantalla completa --- */
.panel {
  position: fixed; inset: 0; z-index: 120;
  background: var(--fondo);
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 0 var(--gutter) calc(env(safe-area-inset-bottom, 0px) + 28px);
  opacity: 0;
  transform: translate3d(0, -14px, 0);
  transition: opacity 340ms var(--ease), transform 340ms var(--ease);
  overflow-y: auto;
}
.panel[hidden] { display: none; }
.panel.is-abierto { opacity: 1; transform: none; }

/* La ✕ NO se va con el scroll. El panel es `position:fixed; inset:0` con
   `overflow-y:auto`, y en cuanto el menú no cabe —apaisado en los cinco
   móviles de la matriz (864/430, 860/393, 860/390, 854/375, 860/360) y también
   a 375×667 y 320×568 (779 px de contenido)— al bajar a ver el CTA y el
   teléfono desaparecían de pantalla el wordmark y el único botón de cerrar.
   Escape seguía funcionando, pero eso es teclado: en un móvil no cerraba nadie.
   Con la cabecera pegajosa la ✕ queda siempre a la vista, y el fondo opaco es
   obligatorio o el texto del menú desfila por debajo del wordmark. */
.panel__cab {
  position: sticky;
  top: 0;
  z-index: 2;
  height: var(--alto-cab);
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--linea);
  background: var(--fondo);
}
/* la marca del panel es el wordmark, no un texto: en la pantalla más visible
   del móvil la marca tiene que ser la marca */
.panel__marca {
  display: flex;
  align-items: center;
  width: 150px;
  color: #fff;
}
.panel__marca img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 24145 / 2219;
  filter: brightness(0) invert(1);
}
.panel__cerrar {
  width: 46px; height: 46px; display: grid; place-items: center;
  font-size: 1.125rem; color: var(--texto);
}
.panel__cerrar:hover { color: var(--amarillo); }

.panel__nav { align-self: center; padding-block: var(--bloque); }
.panel__nav li {
  display: flex; align-items: baseline; gap: 16px;
  border-bottom: 1px solid var(--linea);
}
.panel__n {
  font-family: var(--display); font-weight: 600; font-size: .6875rem;
  letter-spacing: .2em; color: var(--amarillo); flex: none; width: 26px;
}
.panel__nav a {
  flex: 1;
  display: block;
  padding: 16px 0;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.75rem, 7vw, 2.5rem);
  line-height: 1.06;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
.panel__nav a:hover { color: var(--amarillo); }

.panel__pie { display: grid; gap: 14px; justify-items: start; }
.panel__pie .btn { width: 100%; }
/* Zona de toque del pie del panel. Por su caja de texto el teléfono medía
   113×41 y el correo 112×24: las dos por debajo de los 44 px. La caja se
   levanta AQUÍ, en el componente, porque el panel es el mismo en las ocho
   páginas; hacerlo en cada hoja `p-*.css` es lo que dejó el menú midiendo
   41 / 44 / 61 px según la página. El alto se da con `min-height` sobre una
   caja `inline-flex` centrada: crece la diana, no el texto, y el ancho lo
   sigue marcando el contenido (113 y 112 px, ambos ya sobre 44). */
.panel__tel,
.panel__mail {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.panel__tel { font-family: var(--display); font-weight: 700; font-size: 1.5rem; letter-spacing: .04em; }
.panel__mail { color: var(--apagado); font-size: var(--fs-sm); }

body.sin-scroll { overflow: hidden; }

/* Sin JS: el panel no puede abrirse, así que se oculta el botón.
   (Por debajo de 1259 px la red de `js-ok` de arriba hace lo mismo cuando el
   que falta es main.js y no el JS entero.) */
html:not(.js) .cab__burger { display: none; }

/* Sin JS la cabecera nunca recibe .is-solida (ese guardia vive en main.js), así
   que se queda transparente y el titular comparte píxeles con el wordmark.
   Estaba resuelto SOLO en p-empresa.css, o sea en 1 de las 8 páginas: vive aquí
   porque la cabecera es compartida. Ver §29, NOMBRES RESERVADOS. */
html:not(.js) .cab { background: rgb(var(--fondo-rgb) / .94); }
html:not(.js) .cab .cab__filete { opacity: 1; }


/* --- 5.x · PÁGINA ACTIVA -----------------------------------
   El HTML de las 8 páginas marca su enlace con aria-current="page", pero las
   reglas vivían sueltas en dos hojas de página (ferretería y maquinaria) y las
   otras seis salían sin marcar. Es estado del componente de cabecera: va aquí. */
.cab__lista a[aria-current="page"] { color: var(--amarillo); }
.cab__lista a[aria-current="page"]::after { transform: scaleX(1) skewX(-30deg); }
.panel__nav a[aria-current="page"] { color: var(--amarillo); }
.pie__col a[aria-current="page"] { color: var(--texto); }


/* --- 5.y · MIGAS DE PAN — RETIRADAS (1-sep-2026) -----------
   Se pusieron en los cuatro heroes de división al unificarlos y Juan las
   ha quitado, igual que el antetítulo «NN — Nombre»: el hero de división
   abre directamente con el H1 sobre el mosaico y nada se interpone entre
   la foto y el titular. No queda ni una `.migas` en el HTML: comprobado
   en las 8 páginas. Si algún día vuelven, el bloque está en el respaldo
   _backup/post-unificacion-20260901/css/estilos.css. */


/* ============================================================
   06 · HERO + PERSIANA DIAGONAL
   ------------------------------------------------------------
   El hero deja de estar fijado por scroll: mide 100dvh y se acabó,
   así que la página empieza a leerse antes. En su lugar, un carrusel
   de 4 slides fijas cuya transición es una PERSIANA de 6 lamas
   cortadas con el ángulo del wordmark.

   Reparto de responsabilidades:
   · .hero*      → vocabulario de hero (caja, scrim, bloque de texto).
                   Lo usan las 8 páginas; cada una compone lo suyo.
   · .persiana*  → MECANISMO genérico del carrusel de persiana.
                   Reutilizable: basta con un contenedor
                   position:relative + [data-persiana].
   ============================================================ */

/* --- 6.1 · Caja del hero ---------------------------------- */

.hero {
  position: relative;
  height: 100vh;
  height: 100dvh;
}
.hero__stage {
  position: relative;
  height: 100%;
  /* La cabecera es fija y transparente sobre el hero: su alto se RESERVA aquí
     para que el titular nunca nazca por debajo del wordmark. */
  padding-top: var(--alto-cab);
  overflow: hidden;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  isolation: isolate;
}

.hero__scrim {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background:
    var(--scrim-v),
    var(--scrim-lateral);
}

.hero__contenido {
  position: relative;
  z-index: 6;
  align-self: end;
  /* El arrastre horizontal del hero (§3.2 del encargo) se cuelga del
     contenedor entero, no de la capa de fotos: `.hero__contenido` se pinta
     ENCIMA de `.persiana__pista` y le tapaba entre el 83 % y el 100 % de la
     superficie —a 360×800 no quedaba ni un punto que llegara a la pista, o
     sea que el gesto no existía—. Aquí el navegador se queda el scroll
     vertical y nosotros el horizontal, igual que en la pista. */
  touch-action: pan-y;
  /* Red de seguridad: la fila es minmax(0,1fr) y con align-self:end un bloque
     más alto que la fila desbordaría HACIA ARRIBA, donde vive la cabecera. */
  max-height: 100%;
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding: 0 var(--gutter) var(--bloque);
  /* Rejilla nombrada: en móvil una columna en el orden natural del DOM
     (kicker → H1 → rótulo de división → entrada → botones); en pantalla ancha
     el rótulo salta a la columna derecha sin tocar el HTML. */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "kicker" "h1" "divs" "entrada" "botones";
}
.hero__kicker  { grid-area: kicker; }
.hero__h1      { grid-area: h1; }
.hero__divs    { grid-area: divs; }
.hero__entrada { grid-area: entrada; }
.hero__botones { grid-area: botones; }

.hero__kicker {
  color: var(--amarillo);
  text-shadow: 0 2px 24px rgb(var(--negro-rgb) / .8);
}
.hero__h1 {
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--fs-h1);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: #fff;
  max-width: min(100%, 23ch);
  text-wrap: balance;
  text-shadow: var(--sombra-texto);
  margin-bottom: var(--pieza);
}
.hero__entrada {
  /* 54ch eran 529 px y 69 caracteres reales; `--medida` los deja en 60. */
  max-width: var(--medida);
  color: rgb(var(--texto-rgb) / .86);
  text-shadow: 0 2px 26px rgb(var(--negro-rgb) / .7);
  margin-bottom: clamp(24px, 2.6vw, 40px);
}
.hero__botones { display: flex; flex-wrap: wrap; gap: 14px; }


/* --- 6.2 · PERSIANA · pista y slides ----------------------- */

.persiana { --intervalo: 6000ms; --dur-lama: 900ms; }

.persiana__pista {
  position: absolute; inset: 0; z-index: 0;
  touch-action: pan-y;               /* el arrastre horizontal es nuestro,
                                        el scroll vertical sigue siendo del navegador */
}
.persiana__slide {
  position: absolute; inset: 0;
  visibility: hidden;
  z-index: 0;
}
.persiana__slide.is-activa   { visibility: visible; z-index: 1; }
/* ARRANQUE. Los scripts van con `defer`, así que entre el primer pintado y la
   inicialización hay unos milisegundos en los que ningún elemento lleva
   todavía su clase de estado. Sin esto el hero nace en negro, las pestañas
   nacen vacías y las citas nacen fuera de cuadro. La condición es la marca que
   deja el propio mecanismo al inicializarse (dataset.dyPersiana → atributo
   data-dy-persiana), así que la regla se apaga sola y NO depende de que el
   HTML traiga escrita la clase. Mismo patrón en §24 y §23. */
.persiana:not([data-dy-persiana]) .persiana__slide:first-child { visibility: visible; z-index: 1; }
.persiana:not([data-dy-persiana]) .hero__div:first-child { opacity: 1; transform: none; }
/* La saliente se queda ENCIMA: son sus lamas las que se retiran y descubren
   la entrante, que ya está pintada debajo. */
.persiana__slide.is-saliendo { visibility: visible; z-index: 2; }

/* Ken Burns: un solo elemento animado por slide (no seis), y el filtro de foto
   también vive aquí, así que las 6 lamas comparten un único buffer filtrado. */
.persiana__kb {
  position: absolute; inset: 0;
  filter: var(--foto);
  transform: scale(1.06);
  animation: kenburns var(--kb, 22s) ease-in-out infinite alternate;
}
/* `will-change` SOLO en la slide que se ve. Puesto en `.persiana__kb` a secas
   promovía las CUATRO fotos a sangre de 1.920 px a capa de compositor
   permanente, tres de ellas invisibles, que es memoria de GPU regalada justo
   en 320×568 / 360×800 / 375×667. */
.persiana__slide.is-activa .persiana__kb,
.persiana__slide.is-saliendo .persiana__kb { will-change: transform; }
.persiana__slide--plana .persiana__kb { filter: var(--foto-plana-hero); }
/* Alternar el sentido del Ken Burns evita que las cuatro respiren al unísono.
   ⚠️ LAS IMPARES, Y NO LAS PARES. Aquí está el LCP de la Home, y no es un
   detalle de estilo:
     · el Ken Burns escala de 1,06 a 1,14, así que la MISMA foto pinta un 15 %
       más de superficie al final del recorrido que al principio;
     · una slide solo emite su candidato de LCP la primera vez que se pinta, y
       las que están en `visibility:hidden` no se pintan hasta que les toca;
     · con las pares empezando a 1,14, la slide 1 registraba 810.153 px² a los
       108 ms y la slide 2 —maq-electrica— registraba 861.807 a los 6.160 ms,
       que es MÁS GRANDE, así que se llevaba el LCP: 6,15 s medidos en
       localhost sin throttling, contra un umbral «bueno» de 2,5 s, y para
       cualquier visitante que no interactúe.
   Empezando por el máximo (1,14) la slide 1 registra la superficie más grande
   que puede pintar el hero, y ninguna slide posterior puede superarla: el LCP
   se queda en los ~110 ms de la primera foto. Medido antes y después.
   Si algún día se toca el rango del Ken Burns o el orden de las slides, hay
   que volver a medir el LCP. */
.persiana__slide:nth-child(2n+1) .persiana__kb { animation-direction: alternate-reverse; }
@keyframes kenburns {
  from { transform: scale(1.06); }
  to   { transform: scale(1.14); }
}

/* --- 6.3 · Las lamas --------------------------------------
   Cada lama es una copia completa de la foto recortada a una banda
   diagonal con clip-path. Las 6 bandas reconstruyen la foto entera.

   ⚠️ SOLAPE OBLIGATORIO. Dos clip-path adyacentes que comparten borde
   exacto dejan una costura de 1 px: cada uno pinta su borde con
   antialiasing (alfa parcial) y por el hueco se ve la slide de debajo.
   Medido con una capa magenta debajo, a 900×340 y 5 fronteras:
     bordes exactos → 1.540 px de fuga · solape de 1,5 px → 0.
   Da igual <img> o background-image: el defecto es del clip-path, no
   del origen de los píxeles. Se sirve con <img> porque es el único que
   admite srcset/sizes.

   GEOMETRÍA. La banda k va de la línea a(k) a la a(k+1), donde cada
   línea baja del punto (a, 0) al (a − run, 100%) con
   run = tan(30°) × alto = 57,735dvh. Para cubrir la caja entera hacen
   falta a(0)=0 y a(6)=100% + run, así que el paso es
   (100% + run)/6 = 16,6667% + 9,6225dvh.
   ------------------------------------------------------------ */

.persiana__lama {
  --ov: 1.5px;
  --run: 57.735dvh;
  --paso: calc(16.6667% + 9.6225dvh);
  position: absolute; inset: 0;
  clip-path: polygon(
    calc(var(--paso) * var(--l) - var(--ov))                        0,
    calc(var(--paso) * (var(--l) + 1) + var(--ov))                  0,
    calc(var(--paso) * (var(--l) + 1) - var(--run) + var(--ov))     100%,
    calc(var(--paso) * var(--l) - var(--run) - var(--ov))           100%
  );
}
.persiana__foto { position: absolute; inset: 0; }
.persiana__foto img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
}

/* La retirada: la lama se va POR SU PROPIO EJE (abajo e izquierda, el ángulo
   del corte), así que su silueta no cambia y sigue siendo la misma banda; la
   foto de dentro compensa el mismo desplazamiento y se queda QUIETA. Resultado:
   la imagen no se parte en seis trozos que vuelan, se vacía por bandas.
   Solo transform → compositor puro, 60 fps. */
@keyframes lama-fuera {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-62.35dvh, 108dvh, 0); }
}
@keyframes lama-foto {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(62.35dvh, -108dvh, 0); }
}
/* Curva propia de la lama: arranca con algo de peso y se posa larga. Con la
   --ease-suave de la casa (.16,1,.3,1) el 55 % del recorrido se gastaba en los
   primeros 120 ms y el escalonado de 60 ms no se llegaba a leer. */
.persiana { --ease-lama: cubic-bezier(.46,.03,.22,1); }
.persiana__slide.is-saliendo .persiana__lama {
  animation: lama-fuera var(--dur-lama) var(--ease-lama) calc(var(--l) * 60ms) both;
  will-change: transform;
}
.persiana__slide.is-saliendo .persiana__foto {
  animation: lama-foto var(--dur-lama) var(--ease-lama) calc(var(--l) * 60ms) both;
  will-change: transform;
}

/* --- 6.4 · Barrido de luz amarilla ------------------------
   Cruza en diagonal DURANTE la transición, con la inclinación de las
   lamas (gradiente a 120deg = bandas a 30° de la vertical). Va por
   encima del scrim y por debajo del texto. */
.persiana__barrido {
  position: absolute; inset: -20% -45%; z-index: 4;
  pointer-events: none; opacity: 0;
  background: linear-gradient(120deg,
    transparent 45.5%,
    rgb(var(--am-rgb) / .20) 48.6%,
    rgba(255,252,190,.15) 50%,
    rgb(var(--am-rgb) / .17) 51.4%,
    transparent 55%);
  mix-blend-mode: screen;
}
.persiana.is-pasando .persiana__barrido {
  animation: barrido calc(var(--dur-lama) + 340ms) var(--ease) both;
  will-change: transform, opacity;
}
@keyframes barrido {
  0%   { opacity: 0; transform: translate3d(-62%, -20%, 0); }
  16%  { opacity: 1; }
  84%  { opacity: 1; }
  100% { opacity: 0; transform: translate3d(62%, 20%, 0); }
}

/* --- 6.5 · Rótulo de división de cada slide ----------------
   Nombre y claim salen LITERALES de copy.json → divisiones[i].
   Los cuatro se apilan en la MISMA celda (grid-area:1/1), así que el alto
   reservado es el del más alto y no cambia de slide a slide: el H1 no baila. */

.hero__divs {
  position: relative;
  display: grid;
  margin-bottom: clamp(20px, 2.4vw, 34px);
}
.hero__div {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity 520ms var(--ease), transform 520ms var(--ease);
}
.hero__div.is-activo { opacity: 1; transform: none; }

/* Filete amarillo con la numeración encima: el mismo recurso que abre cada
   bloque de división más abajo, para que el hero y la sección 04 hablen igual. */
.hero__div-r {
  position: relative;
  padding-top: 16px;
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--fs-rotulo-hero);
  line-height: .96;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: #fff;
  text-shadow: var(--sombra-texto), 0 1px 4px rgb(var(--negro-rgb) / .6);
}
.hero__div-r::before {
  content: "";
  position: absolute; left: 0; top: 0;
  width: 54px; height: 2px;
  background: var(--amarillo);
  transform: skewX(-30deg);
}
.hero__div-n {
  display: block;
  margin-bottom: 6px;
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
  text-shadow: 0 1px 12px rgb(var(--negro-rgb) / .9);
}
.hero__div-c {
  margin-top: 10px;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(.9375rem, 1.15vw, 1.125rem);
  line-height: 1.28;
  letter-spacing: .03em;
  color: var(--amarillo);
  max-width: 38ch;
  text-wrap: balance;
  text-shadow: 0 1px 14px rgb(var(--negro-rgb) / .95), 0 1px 4px rgb(var(--negro-rgb) / .8);
}

/* --- 6.6 · Índice, progreso y pausa ------------------------ */

.persiana__indice {
  position: relative;
  z-index: 7;
  border-top: 1px solid rgb(var(--blanco-rgb) / .14);
  background: var(--scrim-pie);
}
.persiana__indice-lista {
  max-width: var(--max);
  margin-inline: auto;
  padding: 0 var(--gutter) 14px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap-s);
}
.persiana__punto {
  position: relative;
  width: 100%;
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 4px 14px;
  text-align: left;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(.8125rem, 1.15vw, 1.0625rem);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: rgb(var(--texto-rgb) / .55);
  transition: color 420ms var(--ease);
}
.persiana__punto::after {           /* carril apagado del progreso */
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: rgb(var(--blanco-rgb) / .14);
  opacity: 0;
  transition: opacity 420ms var(--ease);
}
.persiana__punto.is-active::after { opacity: 1; }
.persiana__punto-barra {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--amarillo);
  transform: scaleX(0) skewX(-30deg);
  transform-origin: left;
}
/* La barra del punto activo ES el temporizador: se llena en --intervalo y al
   terminar la del hero dispara el avance. Pausarla pausa el carrusel entero. */
.persiana__punto.is-active .persiana__punto-barra {
  animation: progreso var(--intervalo) linear forwards;
}
.persiana__punto-n {
  font-size: .625rem; letter-spacing: .18em; color: var(--amarillo);
  opacity: .55; transition: opacity 420ms var(--ease);
}
.persiana__punto:hover { color: #fff; }
.persiana__punto.is-active { color: #fff; }
.persiana__punto.is-active .persiana__punto-n { opacity: 1; }

.persiana__progreso {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 8;
  height: 2px; background: rgb(var(--blanco-rgb) / .10);
  pointer-events: none;
}
.persiana__progreso span {
  display: block; height: 100%; width: 100%;
  background: var(--amarillo);
  transform: scaleX(0);
  transform-origin: left;
  animation: progreso var(--intervalo) linear forwards;
}
@keyframes progreso {
  from { transform: scaleX(0) skewX(-30deg); }
  to   { transform: scaleX(1) skewX(-30deg); }
}

/* Pausa: al pasar el RATÓN y al enfocar con TECLADO.
   ⚠️ 28-sep-2026 · NI `:hover` A SECAS NI `:focus-within`. En una pantalla
   táctil el `:hover` se queda PEGADO tras un toque: medido en WebKit 26.6 con
   perfil de iPhone, un solo toque en la foto del hero dejaba `.persiana:hover`
   activo, la barra (que ES el temporizador) se paraba en el 34 % y el
   carrusel no volvía a avanzar nunca, tampoco al bajar y volver al hero —
   el `:hover` vuelve a casar en cuanto el hero pasa bajo el último toque—.
   El `:focus-within` hacía lo mismo en Android al tocar una pestaña del
   índice (el botón se queda con el foco). Por eso el hover va dentro de
   `(hover: hover) and (pointer: fine)` y el foco con `:has(:focus-visible)`,
   que solo casa con foco de teclado; va en regla PROPIA porque un `:has()`
   no soportado invalidaría la lista entera. El botón de pausa se retiró el
   mismo día (petición de Juan): `.is-pausada` queda para `para()`, que lo
   usa cuando se activa prefers-reduced-motion con la página abierta.
   ⚠️ `.is-pasando` NO va en esta lista. Estuvo, y como la barra ES el
   temporizador, los 1.280 ms que dura la persiana (900 de lama + 5×60 de
   escalonado + 80) se sumaban a CADA vuelta: medido con MutationObserver
   durante 35 s, el carrusel avanzaba cada 7.297 / 7.285 / 7.317 / 7.299 ms en
   vez de los 6.000 del contrato — un 22 % de deriva — y la barra se quedaba
   congelada en 0 durante 1,28 s después de cada cambio, a la vista.
   Que la barra corra durante la transición es además lo correcto: el
   temporizador de la slide entrante empieza cuando empieza a verse. */
@media (hover: hover) and (pointer: fine) {
  .persiana:hover .persiana__progreso span,
  .persiana:hover .persiana__punto.is-active .persiana__punto-barra {
    animation-play-state: paused;
  }
}
.persiana:has(:focus-visible) .persiana__progreso span,
.persiana:has(:focus-visible) .persiana__punto.is-active .persiana__punto-barra {
  animation-play-state: paused;
}
.persiana.is-pausada .persiana__progreso span,
.persiana.is-pausada .persiana__punto.is-active .persiana__punto-barra {
  animation-play-state: paused;
}

/* El botón de pausa (`.persiana__pausa`, 44×44 arriba a la derecha) se
   retiró el 28-sep-2026 a petición de Juan. Copia en
   _backup/cambios-28sep-pausa-hover/. */

/* Ayuda de arrastre: se enseña una vez y desaparece al primer uso. */
.persiana__ayuda {
  position: absolute; z-index: 7;
  /* El desplazamiento de centrado va en una variable porque en móvil la
     ayuda deja de estar centrada abajo (allí choca con los botones) y sube
     a la esquina superior izquierda, enfrente del botón de pausa. */
  --ay-x: -50%;
  left: 50%; bottom: 92px; transform: translateX(var(--ay-x));
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px;
  border: 1px solid rgb(var(--blanco-rgb) / .18);
  border-radius: 999px;
  background: rgb(var(--fondo-rgb) / .5);
  font-family: var(--display); font-weight: 600;
  font-size: .6875rem; letter-spacing: var(--ls-kicker);
  text-transform: uppercase; color: rgb(var(--texto-rgb) / .82);
  pointer-events: none;
  transition: opacity 520ms var(--ease), transform 520ms var(--ease);
}
.persiana.is-usada .persiana__ayuda { opacity: 0; transform: translate(var(--ay-x), 10px); }
/* La pista dice cosas distintas según con qué se maneje la persiana. En un
   teléfono no hay flechas ni ratón que arrastre: «usa las flechas» era una
   instrucción imposible (revisión en móvil, 8-sep-2026). La señal fiable de
   pantalla táctil es `(hover: none) and (pointer: coarse)`; el escritorio, los
   portátiles con ratón y los híbridos con puntero fino siguen viendo la de
   siempre. */
.persiana__ayuda-t--tacto { display: none; }
@media (hover: none) and (pointer: coarse) {
  .persiana__ayuda-t--raton { display: none; }
  .persiana__ayuda-t--tacto { display: inline; }
}


/* --- 6.7 · Composición asimétrica y presupuesto vertical ---
   El rótulo de división se va a la columna derecha, que estaba vacía, en
   cuanto hay ancho — o en cuanto falta ALTO, que es donde este hero sufre. */

@media (min-width: 880px), (min-width: 520px) and (max-height: 620px) {
  .hero__contenido {
    grid-template-columns: minmax(0, 1fr) minmax(180px, 26%);
    grid-template-areas:
      "kicker  divs"
      "h1      divs"
      "entrada divs"
      "botones divs";
    column-gap: clamp(28px, 4vw, 80px);
  }
  .hero__divs {
    align-self: center;
    justify-self: start;
    margin-bottom: 0;
  }
  /* Sombra difuminada bajo el rótulo: la mitad derecha del hero es la parte
     más clara del scrim y ahí la foto puede ser cualquier cosa. Radial que
     muere en transparente: es sombra, no tarjeta. */
  .hero__divs::before {
    content: "";
    position: absolute;
    inset: -54px -60px;
    z-index: -1;
    background: radial-gradient(70% 68% at 50% 50%,
      rgb(var(--fondo-rgb) / .56) 0%,
      rgb(var(--fondo-rgb) / .46) 54%,
      rgb(var(--fondo-rgb) / .20) 80%,
      rgb(var(--fondo-rgb) / 0) 100%);
    pointer-events: none;
  }
}

@media (max-width: 900px) {
  .persiana__indice-lista { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 12px; }
  .hero__h1 { max-width: 100%; }
  .hero__entrada { font-size: .9375rem; line-height: 1.6; }
  /* Abajo se comía los botones y el índice de dos filas: sube a la esquina
     superior izquierda, enfrente del botón de pausa. */
  .persiana__ayuda {
    --ay-x: 0;
    left: var(--gutter); right: auto;
    top: calc(var(--alto-cab) + 20px); bottom: auto;
  }
}
@media (max-width: 480px) {
  .hero__botones .btn { flex: 1 1 100%; }
}

/* El hero es la única sección atada al ALTO de la ventana: en un portátil de
   600–750 px o en un móvil apaisado el bloque de texto no cabía y el titular
   se comía por arriba. Aquí se recorta el resto del presupuesto. */
/* 880 y no 820: un iPhone 15 mide 844 de alto y con el índice a dos filas el
   bloque de texto llegaba a 2 px de agotar el presupuesto. Medido. */
@media (max-height: 880px) {
  .hero__contenido { padding-bottom: clamp(24px, 3vw, 44px); }
  .hero__kicker { margin-bottom: clamp(10px, 1.4vw, 18px); }
  .hero__h1 { margin-bottom: clamp(12px, 1.6vw, 22px); }
  .hero__entrada { margin-bottom: clamp(16px, 1.8vw, 26px); line-height: 1.55; }
  .persiana__punto { min-height: 48px; padding: 12px 4px 10px; }
  .persiana__indice-lista { padding-bottom: 10px; }
}
/* La ayuda de arrastre es lo primero que se sacrifica: es un adorno. */
@media (max-height: 800px) {
  .persiana__ayuda { display: none; }
}

/* Ventana verdaderamente baja: móvil apaisado (que puede medir 932 px de
   ancho, así que el recorte lo pide el ALTO, nunca el ancho). */
@media (max-height: 720px) and (max-width: 900px),
       (max-height: 560px) {
  .hero__contenido { padding-bottom: 16px; }
  .hero__kicker { margin-bottom: 8px; font-size: .6875rem; }
  .hero__h1 { margin-bottom: 10px; }
  .hero__entrada {
    font-size: .875rem;
    line-height: 1.5;
    margin-bottom: 14px;
    /* Aquí el cuerpo baja a 14 px, así que el peldaño es el pequeño: con el
       de 16 px la caja daría 73 caracteres en cuanto la ventana diera ancho. */
    max-width: var(--medida-s);
  }
  .persiana__punto { min-height: 44px; padding: 9px 4px 8px; font-size: .75rem; }
  .persiana__indice-lista { padding-bottom: 8px; }
  .hero__botones .btn { flex: 0 1 auto; min-height: 44px; padding-block: 10px; }
}

/* Apaisado de verdad: el índice a dos columnas ocupa DOS filas (101 px de un
   presupuesto de 390-430). Con el ancho de un móvil tumbado caben las cuatro
   en una sola fila y se recuperan ~50 px. */
@media (max-height: 560px) and (min-width: 560px) {
  .persiana__indice-lista {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--gap-s);
  }
}

/* Ventana baja Y estrecha, donde el rótulo NO puede irse a la columna derecha
   y se apila bajo el H1: el suelo del clamp del H1 tiene que conocer también
   el ancho o el titular se parte en seis líneas. */
@media (max-height: 720px) and (max-width: 879px) {
  .hero__h1 { font-size: clamp(2rem, min(7.2vw, 9.4dvh), 3.5rem); }
  .hero__divs { margin-bottom: 12px; }
  .hero__div-r { padding-top: 10px; font-size: clamp(1.0625rem, 4.4vw, 1.375rem); }
  .hero__div-r::before { width: 40px; }
  .hero__div-n { display: inline; margin: 0 10px 0 0; font-size: .625rem; }
  .hero__div-c { margin-top: 4px; font-size: .8125rem; line-height: 1.26; max-width: 40ch; }
}

/* Ventana tumbada de verdad (≤430 px de alto): el peor caso de toda la página.
   ⚠️ SIN tope de ancho. El recorte lo pide el ALTO y un Pixel apaisado mide
   915 px de ancho: con `and (max-width:780px)` se quedaba fuera justo el
   aparato para el que se escribió. */
@media (max-height: 430px) {
  .hero__h1 {
    font-size: clamp(1.375rem, min(7.2vw, 8.2dvh), 2.25rem);
    margin-bottom: 8px;
  }
  .hero__kicker { margin-bottom: 6px; }
  .hero__entrada { font-size: .8125rem; line-height: 1.42; margin-bottom: 10px; }
  .hero__contenido { padding-bottom: 10px; }
  .hero__divs { margin-bottom: 8px; }
  .hero__div-r { padding-top: 8px; font-size: 1rem; }
  .hero__div-c { margin-top: 3px; font-size: .75rem; line-height: 1.24; }
}
@media (max-height: 430px) and (min-width: 520px) and (max-width: 780px) {
  .hero__contenido {
    grid-template-columns: minmax(0, 1fr) minmax(190px, 42%);
    grid-template-areas:
      "kicker  divs"
      "h1      divs"
      "botones entrada";
    column-gap: 24px;
  }
  .hero__divs { align-self: end; margin-bottom: 12px; }
  .hero__entrada { align-self: end; margin-bottom: 0; max-width: none; }
  .hero__botones { align-self: end; }
}

/* Pantallas diminutas (iPhone SE 1ª gen y similares). */
@media (max-width: 340px) and (max-height: 620px) {
  .hero__h1 { font-size: clamp(1.375rem, 4.5dvh, 1.9rem); }
  .hero__entrada { font-size: .8125rem; line-height: 1.44; }
  .hero__divs { margin-bottom: 10px; }
}


/* --- 6.8 · Sin JS -----------------------------------------
   Se ve la primera slide entera y quieta, y las otras tres apiladas debajo:
   nunca ocultas para siempre. Con el carrusel muerto, las 6 lamas de cada
   slide sobran — se deja la primera sin recortar, que es la foto completa. */

html:not(.js) .hero { height: auto; }
html:not(.js) .hero__stage {
  height: auto;
  display: block;
  overflow: visible;
  padding-bottom: var(--bloque);
  background: var(--fondo);
}
html:not(.js) .hero__contenido {
  max-height: none;
  padding-top: var(--bloque);
  padding-bottom: var(--bloque);
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "kicker" "h1" "divs" "entrada" "botones";
}
html:not(.js) .hero__divs {
  grid-auto-flow: row;
  gap: clamp(18px, 2.2vw, 28px);
}
html:not(.js) .hero__div { grid-area: auto; opacity: 1; transform: none; }
html:not(.js) .hero__divs::before { display: none; }
html:not(.js) .persiana__pista { position: static; display: grid; gap: 8px; }
html:not(.js) .persiana__slide {
  position: relative; inset: auto;
  visibility: visible;
  height: clamp(200px, 34vw, 380px);
  border-radius: var(--radio);
  overflow: hidden;
}
html:not(.js) .persiana__kb { animation: none; transform: none; }
html:not(.js) .persiana__lama { clip-path: none; }
html:not(.js) .persiana__lama:not([data-l="0"]) { display: none; }
html:not(.js) .hero__scrim,
html:not(.js) .persiana__barrido,
html:not(.js) .persiana__indice,
html:not(.js) .persiana__ayuda,
html:not(.js) .persiana__progreso { display: none; }


/* ============================================================
   07 · BARRA DE CONFIANZA
   ============================================================ */

.confianza {
  border-bottom: 1px solid var(--linea);
  /* La banda de cifras es la única sección más compacta que las otras doce, y
     va justo después del hero, que es donde más se nota. Que lo sea a una
     fracción DECLARADA del token de sección deja claro que es una decisión de
     ritmo y no un clamp olvidado. */
  padding-block: calc(var(--sec-y) / 2);
  background: var(--fondo);
  position: relative;
  z-index: 2;
}
.confianza__lista {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap);
}
.confianza__item {
  padding-left: clamp(16px, 2vw, 32px);
  border-left: 1px solid var(--linea);
}
.confianza__item:first-child { border-left: 0; padding-left: 0; }
.confianza__dato {
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--fs-cifra);
  line-height: .9;
  letter-spacing: var(--ls-display);
  color: var(--amarillo);
  font-variant-numeric: tabular-nums;
  margin-bottom: 10px;
}
.confianza__label {
  display: block;
  font-size: var(--fs-sm);
  line-height: var(--lh-corto);
  color: var(--apagado);
  max-width: 22ch;
}

@media (max-width: 900px) {
  .confianza__lista { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px var(--gap); }
  .confianza__item:nth-child(odd) { border-left: 0; padding-left: 0; }
}


/* ============================================================
   08 · LAS 4 DIVISIONES — mismo peso visual las cuatro
   ============================================================ */

.divisiones { padding-block: var(--sec-y); }

.div { padding-block: var(--bloque) 0; }
.div:first-of-type { padding-top: 0; }

.div__filete {
  position: relative;
  border-top: 1px solid var(--linea);
  padding-top: 14px;
  margin-bottom: clamp(24px, 2.6vw, 40px);
  transition: border-color 620ms var(--ease);
}
.div:hover .div__filete { border-color: var(--am-30); }
.div__num {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
}

.div__cuerpo {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gap) var(--gap);
  /* start, no center: con `center` el texto solo cuadraba con la foto cuando
     por casualidad medían casi lo mismo, y en las divisiones 02-04 arrancaba
     16 px por debajo de su propia imagen. Las dos columnas comparten SIEMPRE
     la línea superior. */
  align-items: start;
}
/* grid-row:1 explícito: en las invertidas el texto va a la columna 8 y el medio
   a la 1, y el auto-placement NO retrocede de columna dentro de la misma fila
   (mandaría la foto a una segunda fila y doblaría el alto del bloque).
   Las 4 divisiones tienen que pesar lo mismo. */
.div__texto { grid-column: 1 / span 5; grid-row: 1; }
.div__media { grid-column: 7 / span 6; grid-row: 1; position: relative; }
.div--invertida .div__texto { grid-column: 8 / span 5; grid-row: 1; }
.div--invertida .div__media { grid-column: 1 / span 6; grid-row: 1; }

.div__nombre {
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--fs-rotulo);
  line-height: var(--lh-rotulo);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--texto);
  margin-bottom: 14px;
}
.div__claim {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.0625rem, 1.4vw, 1.3125rem);
  line-height: 1.24;
  letter-spacing: .03em;
  color: var(--amarillo);
  max-width: 30ch;
  margin-bottom: var(--pieza);
}
.div__p {
  color: var(--apagado);
  max-width: var(--medida);
  margin-bottom: var(--pieza);
}
.div__items {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 24px;
  margin-bottom: 10px;
}
.div__items li {
  position: relative;
  padding: 9px 0 9px 20px;
  font-size: var(--fs-sm);
  line-height: var(--lh-corto);
  color: var(--texto);
  border-bottom: 1px solid var(--linea);
}
.div__items li::before {
  content: "";
  position: absolute; left: 0; top: 1.05em;
  width: 9px; height: 2px;
  background: var(--amarillo);
  transform: skewX(-30deg);
}

/* Foto principal: misma proporción en las 4 → mismo peso visual */
.div__foto { aspect-ratio: 16 / 11; }
.div:hover .div__foto img { transform: scale(1.05); }

/* La segunda imagen embutida (`.div__inset`, con filete amarillo superior)
   salió de los cuatro bloques el 28-sep-2026 a petición del cliente: cada
   división se queda con una sola foto. Copia en
   _backup/cambios-28sep-inset-cota/. */

@media (max-width: 1024px) {
  .div__texto,
  .div--invertida .div__texto { grid-column: 1 / -1; grid-row: 2; }
  .div__media,
  .div--invertida .div__media { grid-column: 1 / -1; grid-row: 1; }
  .div__foto { aspect-ratio: 16 / 9; }
}
@media (max-width: 560px) {
  .div__items { grid-template-columns: 1fr; }
  .div__foto { aspect-ratio: 4 / 3; }
}


/* ============================================================
   09 · PROVEEDOR INTEGRAL — banda a sangre, casi altura completa
   ============================================================ */

.integral {
  position: relative;
  isolation: isolate;
  overflow: clip;                /* el refuerzo del scrim sobresale del gutter */
  min-height: 88dvh;
  display: grid;
  align-items: center;
  padding-block: var(--sec-y);
  border-block: 1px solid var(--linea);
}
.integral__fondo { position: absolute; inset: 0; z-index: -1; overflow: hidden; }
/* El sobreancho vertical solo existe para que el parallax nunca descubra el
   borde de la banda. Estaba en 12 % arriba y abajo, que en escritorio son
   105 px por lado, cuando el recorrido REAL del parallax no pasa de ~20 px
   (y main.js lo capa a 40). Ese 24 % de más se pagaba entero en densidad de
   imagen: la caja de `integral` medía 1089 px de alto para una sección de
   878. Ahora la reserva es fija y del tamaño del recorrido, no proporcional
   al alto de la sección. El movimiento no cambia; la foto llega más densa. */
.media--banda {
  --banda-over: 48px;
  position: absolute;
  left: 0; right: 0;
  top: calc(-1 * var(--banda-over));
  height: calc(100% + 2 * var(--banda-over));
  border-radius: 0;
  will-change: transform;
}
/* El scrim se refuerza DONDE SE APOYA EL TEXTO, no como velo plano: la mitad
   derecha lleva los 4 puntos y ahí la fachada tiene un parche amarillo y una
   rejilla clara que dejaban el texto en 1,6:1. La franja central-baja sigue
   siendo la más limpia, que es donde se ve la nave. */
.integral__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgb(var(--fondo-rgb) / .94) 0%, rgb(var(--fondo-rgb) / .72) 42%, rgb(var(--fondo-rgb) / .68) 100%),
    linear-gradient(180deg, rgb(var(--fondo-rgb) / .55) 0%, rgb(var(--fondo-rgb) / .05) 34%, rgb(var(--fondo-rgb) / .10) 64%, rgb(var(--fondo-rgb) / .66) 100%);
}

.integral__cont {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(40px, 5vw, 88px) var(--gap);
  align-items: start;
}
.integral__intro { grid-column: 1 / span 6; }
.integral__p {
  /* El mismo aire que separa titular y párrafo en TODO el sitio: `--pieza`,
     28,8 px a 1440. Era el único bloque apilado que iba a 0 y el titular y
     el párrafo se tocaban; su gemelo de /empresa (`.integ__p`) ya lo usaba.
     Medido en las seis páginas el 9-sep-2026. */
  margin-top: var(--pieza);
  color: rgb(var(--texto-rgb) / .90);
  max-width: var(--medida);
  text-shadow: 0 2px 26px rgb(var(--negro-rgb) / .85);
}
.integral__puntos {
  position: relative;
  grid-column: 7 / span 6;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
  /* La rejilla de puntos acababa 110 px antes que la columna de texto y
     dejaba vacía la esquina inferior derecha, justo en la sección con foto de
     fondo, que es donde más canta. Se estira a la altura de la izquierda y el
     aire se reparte entre las dos filas. */
  align-self: stretch;
  align-content: space-between;
}
/* Refuerzo local del scrim justo bajo la rejilla de puntos, que es donde la
   fachada tiene el parche amarillo y la rejilla clara. Va difuminado a los
   cuatro lados (radial que muere en transparente) para que no se lea como una
   caja: es sombra, no tarjeta. */
.integral__puntos::before {
  content: "";
  position: absolute;
  inset: -72px -70px;
  z-index: -1;
  background: radial-gradient(72% 66% at 50% 50%,
    rgb(var(--fondo-rgb) / .54) 0%,
    rgb(var(--fondo-rgb) / .48) 52%,
    rgb(var(--fondo-rgb) / .22) 80%,
    rgb(var(--fondo-rgb) / 0) 100%);
  pointer-events: none;
}
.punto {
  border-top: 1px solid var(--linea-alta);
  padding-top: 20px;
  transition: border-color 620ms var(--ease);
}
.punto:hover { border-color: var(--amarillo); }
.punto__t {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h4);
  line-height: 1.14;
  letter-spacing: .04em;
  text-transform: uppercase;
  margin-bottom: 10px;
  text-shadow: 0 2px 24px rgb(var(--negro-rgb) / .8);
}
/* sobre foto no vale --apagado: se sube el texto y se le pone sombra */
.punto__p {
  font-size: var(--fs-sm); line-height: 1.62;
  /* Sin tope propio se estiraba a lo que le diera la rejilla: 69 caracteres
     en Empresa a 1024. Como cap, no como anchura: en la Home la columna ya
     lo deja mucho más estrecho y ahí no cambia nada. */
  max-width: var(--medida-s);
  color: rgb(var(--texto-rgb) / .94);
  text-shadow: 0 1px 14px rgb(var(--negro-rgb) / .9), 0 1px 3px rgb(var(--negro-rgb) / .7);
}

@media (max-width: 1024px) {
  .integral__intro,
  .integral__puntos { grid-column: 1 / -1; }
  /* aquí el scrim ya es casi opaco: el refuerzo local sobra */
  .integral__puntos::before { display: none; }
  .integral { min-height: 0; }
  .integral__scrim {
    background:
      linear-gradient(180deg, rgb(var(--fondo-rgb) / .86) 0%, rgb(var(--fondo-rgb) / .94) 52%, rgb(var(--fondo-rgb) / .97) 100%);
  }
}
/* TOPE DE ALTO DE LA FRANJA (ago-2026).
   Con `object-fit: cover`, una caja más alta de proporción que la foto se
   amplía por alto, y entonces el ancho de fuente que hace falta es
   `alto_de_caja × (ancho_foto/alto_foto)`, sin que el ancho de la caja pinte
   nada. Traducido: cada foto tiene un ALTO DE CAJA MÁXIMO por encima del cual
   ya no hay píxeles, y para el 16:10 de 3840×2400 ese tope es 1.200 px a DPR2.
   Por debajo de 620 px los 4 puntos se apilan y la sección llega a 1.340 px de
   alto, así que la franja se topa y el resto lo cubre el fondo de la caja. A
   esa anchura el scrim ya va del 86 % al 97 % de negro (regla de arriba), así
   que el fondo y la foto velada son el mismo color y el corte no se ve.
   El fondo es obligatorio: sin él asomaría el crema del body. */
@media (max-width: 620px) {
  .integral__fondo { background: #0E0E10; }
  .integral__fondo .media--banda { max-height: 1150px; }
}
@media (max-width: 560px) {
  .integral__puntos { grid-template-columns: 1fr; gap: 28px; }
}


/* ============================================================
   10 · CÓMO TRABAJAMOS — 4 pasos
   ============================================================ */

.proceso { padding-block: var(--sec-y); }
.proceso__lista {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap);
}
.paso {
  border-top: 1px solid var(--linea);
  padding-top: 22px;
  transition: border-color 620ms var(--ease);
}
.paso:hover { border-color: var(--amarillo); }
.paso__num {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
  margin-bottom: clamp(28px, 3.4vw, 56px);
}
.paso__t {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.08;
  letter-spacing: .03em;
  text-transform: uppercase;
  margin-bottom: 12px;
}
.paso__p { font-size: var(--fs-sm); line-height: 1.62; color: var(--apagado); max-width: 34ch; }

@media (max-width: 900px) {
  .proceso__lista { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 36px var(--gap); }
}
@media (max-width: 520px) {
  .proceso__lista { grid-template-columns: 1fr; gap: 28px; }
  .paso__num { margin-bottom: 16px; }
}


/* ============================================================
   11 · MARCAS — marquee lento con máscara en los extremos
   ============================================================ */

.marcas { padding-block: var(--sec-y); border-block: 1px solid var(--linea); }
.marcas .cabecera-sec { margin-bottom: clamp(40px, 5vw, 72px); }

.marquee {
  overflow: hidden;
  padding-block: 10px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee--inv { margin-top: 6px; }
.marquee__pista {
  display: flex;
  width: max-content;
  animation: desliza 56s linear infinite;
  will-change: transform;
}
.marquee__pista--b { animation-duration: 68s; animation-direction: reverse; }
/* Solo con ratón: en táctil el `:hover` se queda pegado tras un toque y la
   tira no volvía a moverse (medido en WebKit con perfil de iPhone, 28-sep-2026). */
@media (hover: hover) and (pointer: fine) {
  .marquee:hover .marquee__pista { animation-play-state: paused; }
}

.marquee__grupo {
  display: flex;
  align-items: center;
  gap: clamp(28px, 3.6vw, 64px);
  padding-right: clamp(28px, 3.6vw, 64px);
}
.marquee__grupo li {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.25rem, 2.3vw, 2.125rem);
  line-height: 1;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--apagado);
  white-space: nowrap;
  transition: color 420ms var(--ease);
}
.marquee__grupo li:hover { color: var(--amarillo); }

@keyframes desliza {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}


/* ============================================================
   12 · PERSONALIZACIÓN — 4 técnicas
   ============================================================ */

.perso { padding-block: var(--sec-y); }
/* La sección la resuelve el mecanismo de pestañas (§24) más lo que ajuste la
   hoja de la página. Aquí vivían `.perso__lista` y la familia `.tec*` — una
   rejilla de 4 tarjetas de la PRIMERA maqueta que ninguna de las 8 páginas
   llegó a usar nunca. Borradas ago-2026 con el resto del CSS muerto. */


/* ============================================================
   13 · TRABAJOS RECIENTES — galería de 6
   ============================================================ */

.trabajos { padding-block: var(--sec-y); border-top: 1px solid var(--linea); }
/* La galería es horizontal (§22). `.trabajos__rejilla` y `.trab--ancho` eran
   la rejilla de 12 columnas de la primera maqueta: muertas, borradas. */
.trab { grid-column: span 5; }
.trab:nth-child(5), .trab:nth-child(6) { grid-column: span 6; }

.trab__media { height: clamp(230px, 25vw, 420px); }
.trab:hover .trab__media img { transform: scale(1.05); }

.trab__pie {
  border-top: 1px solid var(--linea);
  margin-top: 16px;
  padding-top: 14px;
  transition: border-color 620ms var(--ease);
}
.trab:hover .trab__pie { border-color: var(--amarillo); }
.trab__cat {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--amarillo);
  margin-bottom: 8px;
}
.trab__t {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h4);
  line-height: 1.12;
  letter-spacing: .03em;
  text-transform: uppercase;
}

@media (max-width: 900px) {
  .trab,
  .trab:nth-child(5), .trab:nth-child(6) { grid-column: span 6; }
}
@media (max-width: 620px) {
  .trab,
  .trab:nth-child(5), .trab:nth-child(6) { grid-column: 1 / -1; }
}


/* ============================================================
   14 · SECTORES — 6
   ============================================================ */

.sectores { padding-block: var(--sec-y); background: var(--sup); border-block: 1px solid var(--linea); }
.sectores__lista {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(32px, 3.4vw, 56px) var(--gap);
}
.sector {
  border-top: 1px solid var(--linea-alta);
  padding-top: 20px;
  transition: border-color 620ms var(--ease);
}
.sector:hover { border-color: var(--amarillo); }
.sector__n {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
  margin-bottom: 18px;
}
.sector__t {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.06;
  letter-spacing: .03em;
  text-transform: uppercase;
  margin-bottom: 10px;
}
.sector__p { font-size: var(--fs-sm); line-height: 1.62; color: var(--apagado); max-width: 36ch; }

@media (max-width: 900px) { .sectores__lista { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .sectores__lista { grid-template-columns: 1fr; } }


/* ============================================================
   15 · EQUIPO — 8 retratos
   ============================================================ */

.equipo { padding-block: var(--sec-y); }
/* El equipo de la Home es el carril de fichas (§21) y el de Empresa una
   nómina de filas (p-empresa.css §5). `.equipo__lista` y la familia
   `.persona*` eran la rejilla de 8 retratos de la primera maqueta: muertas
   en las 8 páginas, borradas. */


/* ============================================================
   16 · RESEÑAS — 3
   ============================================================ */

.resenas { padding-block: var(--sec-y); border-top: 1px solid var(--linea); }
/* Las reseñas giran en el panel vertical (§23). `.resenas__lista` era la
   rejilla de 3 de la primera maqueta: muerta, borrada. */
.resena {
  position: relative;
  background: var(--sup);
  border: 1px solid var(--linea);
  border-radius: var(--radio);
  padding: clamp(28px, 2.6vw, 44px);
  transition: border-color 620ms var(--ease), background 620ms var(--ease);
}
.resena:hover { border-color: var(--am-30); background: var(--sup-2); }
.resena::before {
  content: "\201C";
  position: absolute; top: 6px; right: 20px;
  font-family: var(--display); font-weight: 800; font-size: 6rem; line-height: 1;
  color: var(--amarillo); opacity: .18;
}
/* La tarjeta crece cuando la rejilla pasa a menos columnas: a 834 px la cita
   medía 728 px y daba 89 caracteres por línea. A 1440 nunca se notó porque
   son tres columnas. */
.resena__q p { color: var(--texto); font-size: clamp(1rem, 1.1vw, 1.0625rem); line-height: 1.66; max-width: var(--medida); }
.resena__pie {
  display: flex; flex-direction: column; gap: 2px;
  margin-top: clamp(20px, 2vw, 30px);
  padding-top: 16px;
  border-top: 1px solid var(--linea);
}
.resena__autor {
  font-family: var(--display); font-weight: 700; font-size: 1.0625rem;
  letter-spacing: .06em; text-transform: uppercase;
}
.resena__detalle { font-size: .8125rem; color: var(--apagado); }

@media (max-width: 900px) {
  /* A una columna la primera línea llega hasta el borde y se cruzaba con las
     comillas. Se les reserva hueco con un float, no con padding: así solo
     ceden las dos primeras líneas y el glifo sigue leyéndose como marca de
     agua, que es lo que es. */
  .resena__q p:first-child::before {
    content: "";
    float: right;
    width: 62px;
    height: 58px;
  }
}


/* ============================================================
   17 · TIENDAS — 3 fichas con filete izquierdo amarillo
   ============================================================ */

.tiendas { padding-block: var(--sec-y); background: var(--sup); border-block: 1px solid var(--linea); }
.tiendas__lista {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
}
.tienda {
  background: var(--fondo);
  border: 1px solid var(--linea);
  border-left: 3px solid var(--amarillo);
  border-radius: var(--radio);
  padding: clamp(26px, 2.4vw, 40px);
  transition: transform 620ms var(--ease), border-color 620ms var(--ease);
}
.tienda:hover { transform: translateY(-4px); border-color: var(--linea-alta); border-left-color: var(--amarillo); }
.tienda__n {
  font-family: var(--display); font-weight: 800;
  font-size: var(--fs-h3); line-height: 1.06;
  letter-spacing: var(--ls-display); text-transform: uppercase;
  margin-bottom: 8px;
}
.tienda__div {
  font-family: var(--display); font-weight: 600;
  font-size: var(--fs-kicker); letter-spacing: var(--ls-kicker);
  text-transform: uppercase; color: var(--amarillo);
  margin-bottom: 22px;
}
.tienda__dir {
  font-size: var(--fs-sm); line-height: 1.6; color: var(--apagado);
  padding-bottom: 14px; margin-bottom: 14px;
  border-bottom: 1px solid var(--linea);
}
.tienda__tel {
  display: inline-block;
  font-family: var(--display); font-weight: 700;
  font-size: 1.375rem; letter-spacing: .04em;
  padding-block: 8px; margin-bottom: 12px;
  transition: color var(--dur-ui) var(--ease);
}
.tienda__tel:hover { color: var(--amarillo); }
.tienda__h { font-size: var(--fs-sm); line-height: 1.56; color: var(--texto); }

/* 3 tiendas = 3 columnas o 1: el escalón de 2 dejaba la tercera huérfana con
   media fila vacía al lado y con otra altura. */
@media (max-width: 900px) { .tiendas__lista { grid-template-columns: 1fr; } }


/* ============================================================
   18 · FAQ — acordeón nativo, funciona sin JS
   ============================================================ */

.faq { padding-block: var(--sec-y); }
.faq__cont {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gap);
  align-items: start;
}
.faq__cabecera { grid-column: 1 / span 4; margin-bottom: 0; position: sticky; top: calc(var(--alto-cab) + 32px); }
.faq__lista { grid-column: 6 / span 7; }

.faq__item { border-bottom: 1px solid var(--linea); }
.faq__item:first-child { border-top: 1px solid var(--linea); }
.faq__p {
  position: relative;
  display: block;
  list-style: none;
  cursor: pointer;
  padding: clamp(20px, 2vw, 28px) 52px clamp(20px, 2vw, 28px) 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.0625rem, 1.5vw, 1.4375rem);
  line-height: 1.2;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--texto);
  transition: color var(--dur-ui) var(--ease);
}
.faq__p:hover { color: var(--amarillo); }
.faq__p::after,
.faq__p::before {
  content: "";
  position: absolute; right: 8px; top: 50%;
  width: 16px; height: 2px; background: var(--amarillo);
  transition: transform 420ms var(--ease), opacity 420ms var(--ease);
}
.faq__p::before { transform: translateY(-50%); }
.faq__p::after  { transform: translateY(-50%) rotate(90deg); }
.faq__item[open] .faq__p::after { transform: translateY(-50%) rotate(0deg); opacity: 0; }
.faq__item[open] .faq__p { color: var(--amarillo); }

.faq__r { padding: 0 52px clamp(22px, 2.2vw, 32px) 0; }
.faq__r p { color: var(--apagado); max-width: var(--medida); }

@media (max-width: 900px) {
  .faq__cont { grid-template-columns: 1fr; }
  /* ⚠️ El `grid-column: 1 / span 4` de escritorio SEGUÍA vivo aquí y, con una
     sola columna declarada, la cabecera creaba tres columnas implícitas: la
     lista se quedaba 60 px corta por la derecha —las rayas y los «+» sin
     llegar al margen— mientras la cabecera sí llegaba. Pasaba en las cinco
     páginas que usan el componente, la Home incluida. */
  .faq__cabecera { grid-column: 1 / -1; position: static; margin-bottom: var(--bloque); }
  .faq__lista { grid-column: 1 / -1; }
}


/* ============================================================
   19 · CTA FINAL — casi altura completa
   ============================================================ */

.cierre {
  position: relative;
  isolation: isolate;
  min-height: 82dvh;
  display: grid;
  align-items: center;
  padding-block: var(--sec-y);
}
.cierre__fondo { position: absolute; inset: 0; z-index: -1; overflow: hidden; }
/* La fachada estaba tan oscurecida por igual que lo único que sobrevivía era
   la rejilla de ventilación de arriba a la derecha: un rectángulo gris suelto
   que parecía un error de render. Ahora el scrim es RASANTE, como en el hero
   —fuerte donde se apoya el texto (izquierda y bajos) y flojo en la derecha—,
   y el encuadre sube para que entren el rótulo DYRECO y las bandas amarillas
   de la nave. Se reconoce la nave y el texto sigue en AA. */
.cierre__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgb(var(--fondo-rgb) / .93) 0%, rgb(var(--fondo-rgb) / .87) 32%, rgb(var(--fondo-rgb) / .50) 56%, rgb(var(--fondo-rgb) / .38) 80%, rgb(var(--fondo-rgb) / .34) 100%),
    linear-gradient(180deg, rgb(var(--fondo-rgb) / .78) 0%, rgb(var(--fondo-rgb) / .20) 30%, rgb(var(--fondo-rgb) / .32) 64%, rgb(var(--fondo-rgb) / .90) 100%);
}
.cierre .media--banda img { object-position: 50% 26%; }

.cierre__cont { display: grid; justify-items: start; }
/* Era el ÚNICO H2 centrado de la página junto con el de marcas, y a 80,6 px
   frente a los 57,6 estándar: casi el H1 del hero. Vuelve al borde izquierdo
   de los otros doce y al tamaño de su nivel. */
.cierre__h2 {
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--fs-h2);
  line-height: var(--lh-titulo);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: #fff;
  max-width: 20ch;
  text-wrap: balance;
  text-shadow: 0 2px 40px rgb(var(--negro-rgb) / .7), 0 1px 4px rgb(var(--negro-rgb) / .5);
  margin-bottom: var(--pieza);
}
.cierre__p {
  color: var(--texto);
  /* 58ch eran 568 px y 73 caracteres reales. */
  max-width: var(--medida);
  text-shadow: 0 2px 26px rgb(var(--negro-rgb) / .9), 0 1px 4px rgb(var(--negro-rgb) / .7);
  margin-bottom: clamp(28px, 3vw, 44px);
}
.cierre__botones { display: flex; flex-wrap: wrap; gap: 14px; }

@media (max-width: 480px) {
  .cierre__botones .btn { flex: 1 1 100%; }
}




/* ============================================================
   30 · HERO DE DIVISIÓN — mosaico a sangre, titular encima
   ------------------------------------------------------------
   ⚠️ COMPONENTE COMPARTIDO (1-sep-2026). Era `.rot-hero` en p-rotulacion.css.
   Vivía en la hoja de una sola página; sube aquí porque el cliente
   pidió que las cuatro páginas de división lo vean IGUAL. Cualquier
   retoque de aquí toca a las cuatro: para un ajuste de una sola,
   la regla va en su p-<pagina>.css, nunca en este bloque.
   ============================================================ */

/* `.hero` mide 100dvh en estilos.css: aquí no. El hero es tan alto
   como su contenido, con un suelo que garantiza que el mosaico se
   lea como mosaico y no como una tira. */
.hero-div {
  position: relative;
  height: auto;
  min-height: clamp(620px, 92dvh, 1040px);
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  overflow: clip;                 /* el mosaico sangra: nada se escapa */
  background: var(--fondo);
  border-bottom: 1px solid var(--linea);
  isolation: isolate;
}

/* --- 3.1 · El mosaico ------------------------------------- */

.hero-div__mosaico {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-template-rows: repeat(6, minmax(0, 1fr));
  gap: var(--junta-mosaico);
  background: var(--fondo);
}
.hero-div__p { min-width: 0; min-height: 0; }
/* El barrido de luz de la dirección va una vez por PIEZA, que es lo que
   hace que el mosaico se lea como seis fotos y no como un collage. */
/* ⚠️ El rack de bobinas —la única foto del proyecto donde el color es el
   protagonista— va en la columna DERECHA, no en la izquierda: el titular
   ocupa la mitad inferior izquierda y allí el scrim se come la foto que
   tenga debajo. A la izquierda va el plóter, que es oscuro y aguanta el
   texto encima sin perder nada. Las CAJAS no se han movido, solo su
   contenido: el `sizes` de cada pieza sigue siendo el de su caja. */
.hero-div__p--1 { grid-column: 10 / 13; grid-row: 1 / 7; }  /* vertical alta */
.hero-div__p--2 { grid-column: 4 / 10;  grid-row: 1 / 3; }  /* banda ancha  */
.hero-div__p--3 { grid-column: 4 / 10;  grid-row: 3 / 5; }  /* banda ancha  */
.hero-div__p--4 { grid-column: 4 / 10;  grid-row: 5 / 7; }  /* banda ancha  */
.hero-div__p--5 { grid-column: 1 / 4;   grid-row: 1 / 5; }  /* vertical alta */
.hero-div__p--6 { grid-column: 1 / 4;   grid-row: 5 / 7; }  /* banda corta  */

/* Entrada escalonada. Es una animación CSS pura, no un [data-reveal]:
   el hero está sobre la línea de flotación y no puede depender de que
   llegue el observador. Si el navegador no anima, se ve igual. */
@keyframes hdiv-pieza-entra {
  from { opacity: 0; transform: scale(1.04); }
  to   { opacity: 1; transform: none; }
}
.hero-div__p {
  animation: hdiv-pieza-entra 900ms var(--ease-suave) both;
}
.hero-div__p--2 { animation-delay: 90ms; }
.hero-div__p--3 { animation-delay: 180ms; }
.hero-div__p--4 { animation-delay: 270ms; }
.hero-div__p--5 { animation-delay: 140ms; }
.hero-div__p--6 { animation-delay: 320ms; }

/* --- 3.2 · El scrim --------------------------------------- */

/* Tres capas, cada una con su trabajo:
   · arriba, para que el wordmark blanco de la cabecera transparente no
     se apoye en la banda clara del plóter;
   · abajo, que es donde vive el titular y donde el contraste tiene que
     ser de texto, no de foto;
   · a la izquierda, porque la columna de texto arranca en el gutter y
     por debajo tiene el rack de bobinas, que es la foto más saturada
     del proyecto. */
.hero-div__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(180deg,
      rgba(14,14,16,.60) 0%,
      rgba(14,14,16,.14) 20%,
      rgba(14,14,16,.24) 44%,
      rgba(14,14,16,.86) 86%,
      rgba(14,14,16,.94) 100%),
    linear-gradient(90deg,
      rgba(14,14,16,.78) 0%,
      rgba(14,14,16,.34) 38%,
      rgba(14,14,16,.04) 66%,
      rgba(14,14,16,0) 100%);
}

/* --- 3.3 · El texto --------------------------------------- */

.hero-div__contenido {
  position: relative;
  z-index: 3;
  align-self: end;
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding: calc(var(--alto-cab) + clamp(40px, 6vw, 96px))
           var(--gutter)
           clamp(30px, 3.4vw, 56px);
}

.hero-div__h1 {
  font-family: var(--display);
  font-weight: 800;
  /* Deliberadamente por debajo del H1 de la Home (que es el del
     sitio): esta es una página interior y su titular no compite.
     Los tres términos conocen el alto de la ventana, porque el
     titular vive DENTRO de un hero que persigue el viewport. */
  font-size: clamp(min(2.3rem, 6dvh), min(5.4vw, 9.4dvh), 4.75rem);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: #fff;
  max-width: 16ch;
  text-wrap: balance;
  text-shadow: var(--sombra-texto);
  margin-bottom: var(--pieza);
}
.hero-div__entrada {
  color: rgba(242,242,240,.88);
  /* 50ch eran 489 px y 68 caracteres reales. */
  max-width: var(--medida);
  text-shadow: 0 2px 26px rgba(0,0,0,.72);
  margin-bottom: clamp(24px, 2.6vw, 38px);
}
.hero-div__botones { display: flex; flex-wrap: wrap; gap: 14px; }

/* --- 3.4 · Índice de los 4 servicios, cosido al pie -------- */

/* Va a sangre (el filete cruza la ventana entera) pero su contenido
   se alinea con el resto de la página: el relleno lateral es el
   gutter, o lo que sobre del contenedor de 1720, lo que sea mayor. */
.hero-div__indice {
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid rgba(242,242,240,.24);
  padding-inline: max(var(--gutter), calc((100% - var(--max)) / 2 + var(--gutter)));
  background: linear-gradient(180deg, rgba(14,14,16,.40), rgba(14,14,16,.78));
}
.hero-div__indice li + li { border-left: 1px solid rgba(242,242,240,.14); }
.hero-div__indice a {
  display: flex;
  align-items: baseline;
  gap: 12px;
  min-height: 56px;
  padding: 17px clamp(12px, 1.4vw, 24px);
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(.875rem, 1.15vw, 1.0625rem);
  line-height: 1.2;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 2px 18px rgba(0,0,0,.7);
  transition: color var(--dur-ui) var(--ease), background var(--dur-ui) var(--ease);
}
.hero-div__indice li:first-child a { padding-left: 0; }
.hero-div__indice a:hover { color: var(--amarillo); background: rgba(255,237,0,.06); }
.hero-div__in {
  flex: none;
  font-size: var(--fs-kicker);
  font-weight: 600;
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
}

/* --- 3.5 · Ventanas anchas pero bajas --------------------- */
/* Portátil recortado: el hero deja de perseguir el alto y el mosaico
   se conforma con lo que hay. */
@media (min-width: 1025px) and (max-height: 720px) {
  .hero-div { min-height: 560px; }
  .hero-div__contenido { padding-top: calc(var(--alto-cab) + 30px); padding-bottom: 30px; }
}

/* --- 3.6 · Tableta y móvil -------------------------------- */
/* El mosaico se recompone sobre 6 columnas × 6 filas. Aquí las cajas
   ya no son las mismas y por eso el `sizes` de cada foto tiene su
   tramo `(max-width:1024px)` propio en el HTML: en esta retícula TODAS
   las piezas son más anchas de proporción que su foto, así que `cover`
   casa por ancho y el ancho pintado es 100vw (la banda de la nave) o
   ~50vw (las otras cinco). Se ha medido pieza a pieza a 1024, 834 y
   768: ninguna baja de 1,2 de densidad ni a 1× ni a 2×. */
/* ⚠️ LA COLUMNA IZQUIERDA ES UNA SOLA VERTICAL EN ESCRITORIO (3-sep-2026).
   Medido a 1440×900 en las cuatro divisiones: la caja de texto (H1 de tres o
   cuatro líneas + entrada + botones, x 40–622 · y 213–721) tapa el 89 % del
   ancho y más de la mitad del área de las DOS piezas de la columna 1/4, con el
   scrim al .78 debajo: ninguna se reconocía y la de abajo quedaba entera bajo
   los botones. Dos de las seis fotos del mosaico no las veía nadie.
   La pieza 5 se estira a las seis filas —como la 1 de la derecha— y la 6 se
   apaga. En tableta y móvil (≤1024) vuelve, porque allí la rejilla es otra y su
   celda sí se ve. Por eso las seis siguen en el HTML de las cuatro páginas. */
@media (min-width: 1025px) {
  .hero-div__p--5 { grid-row: 1 / 7; }
  .hero-div__p--6 { display: none; }
}

/* Índice 2×2 (≤560): los rótulos de dos palabras caen en dos líneas (celda de
   68 px) y sus vecinas en una (ancla de 56 px pegada arriba con 12 px vacíos
   debajo): en cada fila un rótulo quedaba alto y el otro centrado. El <li> pasa
   a flex y el ancla ocupa la celda entera, así que los cuatro comparten borde
   superior y área de toque. */
@media (max-width: 560px) {
  .hero-div__indice li { display: flex; }
  .hero-div__indice a { flex: 1; }
}

@media (max-width: 1024px) {
  .hero-div {
    min-height: clamp(680px, 96dvh, 1000px);
  }
  .hero-div__mosaico {
    grid-template-columns: repeat(6, minmax(0, 1fr));
    grid-template-rows: repeat(6, minmax(0, 1fr));
  }
  .hero-div__p--1 { grid-column: 1 / 4; grid-row: 1 / 5; }   /* vertical alta */
  .hero-div__p--5 { grid-column: 4 / 7; grid-row: 1 / 4; }   /* vertical      */
  .hero-div__p--6 { grid-column: 4 / 7; grid-row: 4 / 5; }   /* banda 1/2     */
  .hero-div__p--2 { grid-column: 1 / 7; grid-row: 5 / 6; }   /* banda ancha   */
  .hero-div__p--3 { grid-column: 1 / 4; grid-row: 6 / 7; }   /* banda 1/2     */
  .hero-div__p--4 { grid-column: 4 / 7; grid-row: 6 / 7; }   /* banda 1/2     */
  .hero-div__h1 { max-width: 20ch; }
  .hero-div__entrada { max-width: var(--medida); }
}

@media (max-width: 700px) {
  .hero-div { min-height: clamp(700px, 100dvh, 980px); }
  /* Dos columnas: por debajo de esto una banda de 1/3 de pantalla no
     es una foto, es una muestra de color. */
  .hero-div__mosaico {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(6, minmax(0, 1fr));
  }
  .hero-div__p--1 { grid-column: 1;     grid-row: 1 / 5; }   /* vertical */
  .hero-div__p--5 { grid-column: 2;     grid-row: 1 / 4; }   /* vertical */
  .hero-div__p--2 { grid-column: 2;     grid-row: 4 / 5; }   /* banda 1/2 */
  .hero-div__p--3 { grid-column: 1 / 3; grid-row: 5 / 6; }   /* banda ancha */
  .hero-div__p--4 { grid-column: 1;     grid-row: 6 / 7; }   /* banda 1/2 */
  .hero-div__p--6 { grid-column: 2;     grid-row: 6 / 7; }   /* banda 1/2 */

  /* Con el mosaico a pantalla completa detrás, el texto necesita más
     suelo del que da el degradado general. */
  .hero-div__scrim {
    background:
      linear-gradient(180deg,
        rgba(14,14,16,.70) 0%,
        rgba(14,14,16,.26) 16%,
        rgba(14,14,16,.58) 52%,
        rgba(14,14,16,.94) 84%,
        rgba(14,14,16,.97) 100%);
  }
}

@media (max-width: 560px) {
  /* Cuatro filas de 56 px se comían el hero entero: el índice pasa a
     2 × 2 y el filete divisorio, a rejilla. */
  .hero-div__indice { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero-div__indice li + li { border-left: 0; }
  .hero-div__indice li:nth-child(2n) { border-left: 1px solid rgba(242,242,240,.14); }
  .hero-div__indice li:nth-child(n+3) { border-top: 1px solid rgba(242,242,240,.14); }
  .hero-div__indice li:nth-child(2n+1) a { padding-left: 0; }
  .hero-div__botones .btn { flex: 1 1 100%; }
}


/* ============================================================
   31 · CTA DE DIVISIÓN — banda a sangre a dos columnas
   ------------------------------------------------------------
   ⚠️ COMPONENTE COMPARTIDO (1-sep-2026). Era `.ccta` en p-ferreteria.css.
   Vivía en la hoja de una sola página; sube aquí porque el cliente
   pidió que las cuatro páginas de división lo vean IGUAL. Cualquier
   retoque de aquí toca a las cuatro: para un ajuste de una sola,
   la regla va en su p-<pagina>.css, nunca en este bloque.
   ============================================================ */

.ccta {
  position: relative;
  isolation: isolate;
  overflow: clip;
  /* El tope de 620 px no es estético: la banda del fondo es a sangre y con
     `cover` el ancho al que se PINTA la foto lo manda el alto de la caja
     (alto × 1,506). Con 60dvh a secas, una ventana de 1600 px de alto pedía
     una foto de 1.807 px donde la caja mide 1.200: el `sizes` declara el ancho
     de la ventana y el navegador servía una variante corta. Capando el alto,
     el ancho pintado nunca supera el de la ventana en escritorio. */
  min-height: min(60dvh, 620px);
  display: grid;
  align-items: center;
  padding-block: clamp(88px, 8vw, 140px);
}
.ccta__fondo { position: absolute; inset: 0; z-index: -1; overflow: hidden; }
/* fer-pasillo es de las fotos flojas del lote: se sube el contraste en vez de
   bajar el brillo (contrato de la dirección C), y el peso oscuro lo pone el
   scrim, que es rasante y no un velo plano. */
.ccta__banda img { filter: brightness(.70) contrast(1.30) saturate(.86); }
.ccta__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(14,14,16,.94) 0%, rgba(14,14,16,.84) 38%, rgba(14,14,16,.52) 72%, rgba(14,14,16,.44) 100%),
    linear-gradient(180deg, rgba(14,14,16,.72) 0%, rgba(14,14,16,.22) 32%, rgba(14,14,16,.34) 66%, rgba(14,14,16,.88) 100%);
}

.ccta__cont {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(36px, 4vw, 64px) var(--gap);
  align-items: end;
}
.ccta__texto { grid-column: 1 / span 6; }
.ccta__h2 {
  font-family: var(--display); font-weight: 800;
  font-size: var(--fs-h2); line-height: var(--lh-titulo);
  letter-spacing: var(--ls-display); text-transform: uppercase;
  color: #fff;
  max-width: 18ch;
  text-wrap: balance;
  text-shadow: 0 2px 40px rgba(0,0,0,.7), 0 1px 4px rgba(0,0,0,.5);
  margin-bottom: var(--pieza);
}
.ccta__p {
  color: var(--texto);
  /* 52ch eran 509 px y 68 caracteres reales. */
  max-width: var(--medida);
  text-shadow: 0 2px 26px rgba(0,0,0,.9), 0 1px 4px rgba(0,0,0,.7);
}

.ccta__acciones {
  grid-column: 8 / span 5;
  display: grid;
  justify-items: start;
  gap: 8px;
  padding-top: 22px;
  border-top: 2px solid var(--amarillo);
}
.ccta__k {
  font-family: var(--display); font-weight: 600;
  font-size: var(--fs-kicker); letter-spacing: var(--ls-kicker);
  text-transform: uppercase; color: var(--amarillo);
  text-shadow: 0 1px 12px rgba(0,0,0,.9);
}
.ccta__tel {
  display: inline-flex; align-items: center; min-height: 52px;
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.125rem, 3.4vw, 3.25rem); line-height: 1;
  letter-spacing: .02em;
  color: #fff;
  text-shadow: 0 2px 30px rgba(0,0,0,.85);
  transition: color var(--dur-ui) var(--ease);
}
.ccta__tel:hover { color: var(--amarillo); }
.ccta__botones { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 14px; }
.ccta__nota {
  margin-top: 14px;
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: rgba(242,242,240,.86);
  text-shadow: 0 1px 14px rgba(0,0,0,.9);
  /* 34ch dejaba el horario en tres renglones desde que la nota lleva dirección
     y horario, que es lo que se perdió al retirar las placas de cierre. */
  max-width: 52ch;
}
/* Los rangos horarios («07:00 – 18:00 h», «1 jul – 31 ago») se partían por el
   guion y dejaban «– 15:00 h» huérfano en una línea: cada rango va envuelto en
   un <span class="nowrap"> en el HTML. */
.ccta__nota .nowrap { white-space: nowrap; }

@media (max-width: 1024px) {
  .ccta { min-height: 0; }
  .ccta__texto, .ccta__acciones { grid-column: 1 / -1; }
  .ccta__scrim {
    background: linear-gradient(180deg, rgba(14,14,16,.84) 0%, rgba(14,14,16,.92) 50%, rgba(14,14,16,.96) 100%);
  }
}
@media (max-width: 480px) {
  .ccta__botones { width: 100%; }
  .ccta__botones .btn { flex: 1 1 100%; }
}




/* ============================================================
   32 · BANDA DESTACADA — la pieza pesada de cada división
   ------------------------------------------------------------
   ⚠️ COMPONENTE COMPARTIDO (1-sep-2026). Era `.destacado` en
   p-maquinaria.css. Sube aquí porque el cliente pidió el bloque en las
   cuatro páginas de división. Cualquier retoque de aquí toca a las
   cuatro; para el ajuste de una sola, la regla va en su p-<pagina>.css.
   ============================================================ */

/* ------------------------------------------------------------
   LA CUENTA DE LA BANDA (es el arreglo grave de esta página)

   `cover` amplía hasta CUBRIR, así que el ancho que se pinta de verdad es
       ancho_pintado = max(ancho_caja, alto_caja × 2,3329)
   —2,3329 es la proporción del fichero, 2560×1097—. Con la banda llenando la
   sección de arriba abajo (`min-height:86dvh` + `inset:0`) el alto de caja era
   955 px en 1440×1000 y 1.191 px en 390×844, así que se pintaban 2.228 y
   2.780 px de ancho: más que el fichero mayor que existe. Densidades medidas:
   0,86 · 0,91 · 0,69 · 0,68 a 1×, y 0,57 · 0,61 · 0,46 · 0,45 a 2×.

   El arreglo NO es subir `sizes`: es ACORTAR LA CAJA, que es lo que manda el
   brief. Con
   la franja topada en 500 px de alto, `alto_caja × 2,3329` no pasa de 1.241 px
   y por encima de 1.241 px de ventana el ancho pintado vuelve a ser,
   sencillamente, el ancho de la ventana — que es lo máximo a lo que puede
   aspirar cualquier banda a sangre.

   ⚠️ EL «TECHO DE 2560» QUE ESTABA ESCRITO AQUÍ NO EXISTE (corregido ago-2026).
   Decía que no había fichero por encima de 2560 y que el máster también era
   2560, y de ahí salía una excepción documentada de 0,89 a 1440@2. Es falso: el
   máster es `ferreteria/Nave_DYRECO_copia.jpg` a 4288×2471, la escalera nueva
   (comun.md §3.1) llega a 3840 y en disco están `nave-fachada-wide-3200.webp` y
   `-3840.webp` (3840×1645, 75 KB). Lo que faltaba era declararlos en el
   `srcset`, no una excepción: con el 3840 puesto la franja mide ≥1,00 en toda
   la matriz (medido: 1,00 a 2560@1, 1,00 a 1920@2, 1,11 a 1440@2, 1,03 en móvil
   apaisado a DPR3; antes 0,83–0,86). A 2× una banda a sangre de W px de ventana
   necesita 2W de fichero, así que el techo real está en 1.920 px de ventana en
   retina — y ahí llega justo. Lo que ya se había hecho, y sigue valiendo:
   acortar la caja, que es lo que sacó la franja de 0,57 a 1,11 en 1440@2.
   ------------------------------------------------------------ */

.destacado {
  position: relative;
  isolation: isolate;
  overflow: clip;
  /* Antes 86dvh, que ataba el alto de la banda al alto de la VENTANA y era la
     mitad del problema de densidad. Ahora el alto lo pone el contenido, y el
     padding se ajusta para que la sección no sea mucho más alta que la franja:
     si la sección midiera los 176 px de `--sec-y` por lado quedaría un palmo
     de negro muerto debajo de la foto. */
  min-height: min(58dvh, 560px);
  display: grid;
  align-items: center;
  /* Respira como sus vecinas: a `clamp(72px,7vw,108px)` la banda entraba con
     101 px donde las secciones de al lado entran con 144 y se leía como un
     bache (medido en maquinaria, uniformidad y rotulación). Ferretería, que
     va entera a `--sec-fer`, lo acota en su hoja. */
  padding-block: var(--sec-y);
  border-bottom: 1px solid var(--linea);
}
/* La franja: pegada arriba, de alto acotado y proporcional al ANCHO (no al
   alto de la ventana), para que el ancho pintado sea calculable y `sizes`
   pueda declararlo de verdad. Debajo, carbón: el scrim cierra en opaco. */
.destacado__fondo {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: auto;
  /* 560 y no 500: a 1440 los botones acababan a 4 px de la costura. */
  height: clamp(380px, 80vw, 560px);
  z-index: -1;
  overflow: hidden;
}
/* El sobreancho que reserva `.media--banda` para el parallax son 48 px por
   lado, pensados para el tope de 40 px de main.js. Aquí el recorrido real es
   `data-parallax="0.12"` → 12 px, así que 48 px por lado son 72 px de caja
   sobrantes que se pagaban ENTEROS en densidad. 16 px cubren el movimiento con
   margen de sobra. */
.destacado .media--banda { --banda-over: 16px; }

/* Scrim rasante, reforzado donde se apoya el texto (izquierda y bajos) y flojo
   a la derecha, que es donde se tiene que reconocer la nave. El tramo final
   cierra en OPACO: es la costura entre la franja y el carbón de debajo, y así
   no se ve dónde acaba la foto. */
.destacado__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(14,14,16,.90) 0%, rgba(14,14,16,.78) 38%, rgba(14,14,16,.46) 70%, rgba(14,14,16,.34) 100%),
    linear-gradient(180deg, rgba(14,14,16,.66) 0%, rgba(14,14,16,.16) 28%, rgba(14,14,16,.28) 56%, rgba(14,14,16,.84) 86%, rgba(14,14,16,1) 100%);
}
/* ⚠️ FILETE RETIRADO (sep-2026). Remataba la franja con 1 px amarillo, pero
   la franja es de ALTO FIJO y el contenido no: a 834 px la línea cruzaba la
   columna de puntos y tachaba «y a la vista.», a 390 partía el párrafo y a
   1440 pasaba a 4 px del pie de los botones. Como el scrim vertical ya acaba
   opaco (rgba …,1 al 100 %), sin filete la costura foto/carbón no se ve. */
.destacado__fondo::after {
  content: "";
  display: none;
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--am-30), var(--am-10) 46%, transparent 100%);
  pointer-events: none;
}

.destacado__cont {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(40px, 4.6vw, 84px) var(--gap);
  /* ⚠️ `start` dejaba las dos piezas de la fila superior a su alto natural y
     diferían 113 px: el texto acababa muy por encima del último punto y la
     banda se leía descuadrada. Con `stretch` las dos llegan al mismo pie. */
  align-items: stretch;
}
.destacado__texto  { grid-column: 1 / span 6; }
/* ⚠️ Los puntos eran `8 / span 5`: entre el texto (que acaba en la 6) y ellos
   quedaba la columna 7 SIN NADIE, una columna muerta de ancho completo a lo
   alto de toda la fila. Pasan a `7 / span 6` y la fila cierra 6 + 6. */
.destacado__puntos {
  grid-column: 7 / span 6;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* ⚠️ MISMO HUECO EN LAS DOS DIRECCIONES (1-sep-2026, encargo de Juan). Antes
     el hueco vertical era `clamp(24px,2.4vw,36px)` y, sobre todo, las cuatro
     fichas llevaban `align-content: space-between`: se repartían el alto entero
     de la fila —que lo marca la columna del titular, mucho más alta— y se veían
     muy separadas entre sí. Ahora el hueco es `--gap` en los dos ejes, así que
     la rejilla de 2×2 se lee como una rejilla y no como cuatro islas. */
  gap: var(--gap);
  align-content: start;
}
/* Las tres fotos de apoyo cruzan las DOCE columnas, bajo el texto y los
   puntos. Antes eran dos y vivían en la columna derecha (8/span 5), con las
   seis de la izquierda vacías debajo del titular: la fila ya existía y estaba
   en blanco. Al ocuparla, la sección crece 116 px y la superficie fotografiada
   del bloque pasa de 88.000 a 373.000 px². */
.destacado__fotos {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
  align-items: start;
}
.destacado__fotos .media { aspect-ratio: 3 / 2; }
/* `margin-bottom`: el titular iba PEGADO a su párrafo (0 px medidos en las
   cuatro divisiones y en los cuatro anchos). Es el único sitio de la web donde
   H2 y párrafo no llevaban `--pieza` entre medias. */
.destacado__h2 { color: #fff; text-shadow: var(--sombra-texto); margin-bottom: var(--pieza); }
.destacado__p {
  color: rgba(242,242,240,.92);
  max-width: var(--medida);
  text-shadow: 0 2px 26px rgba(0,0,0,.9), 0 1px 4px rgba(0,0,0,.7);
  margin-bottom: clamp(26px, 3vw, 40px);
}
.destacado__botones { display: flex; flex-wrap: wrap; gap: 14px; }

@media (max-width: 1024px) {
  .destacado { min-height: 0; }
  .destacado__texto, .destacado__puntos, .destacado__fotos { grid-column: 1 / -1; }
  /* Apilado, el texto arranca dentro de la franja y sigue sobre el carbón: el
     scrim va de arriba abajo y cierra en opaco para que el borde inferior de
     la foto no se vea como una costura. */
  .destacado__scrim {
    background: linear-gradient(180deg, rgba(14,14,16,.72) 0%, rgba(14,14,16,.80) 44%, rgba(14,14,16,.92) 78%, rgba(14,14,16,1) 100%);
  }
}
/* La tira de tres se apila antes que el resto: a tres columnas en 720 px cada
   foto mediría 216 px y dejaría de contar nada. */
@media (max-width: 720px) {
  .destacado__fotos { grid-template-columns: 1fr; gap: 18px; }
}
@media (max-width: 560px) {
  .destacado__puntos { grid-template-columns: 1fr; gap: 26px; }
  .destacado__botones .btn { flex: 1 1 100%; }
}


/* ============================================================
   33 · RAÍL DE CUATRO PASOS — cabecera fija y nodos
   ------------------------------------------------------------
   ⚠️ COMPONENTE COMPARTIDO (1-sep-2026). Era `.asesor + .rail` en
   p-maquinaria.css. Sube aquí porque el cliente pidió el bloque en las
   cuatro páginas de división. Cualquier retoque de aquí toca a las
   cuatro; para el ajuste de una sola, la regla va en su p-<pagina>.css.
   ============================================================ */

.asesor { padding-block: var(--sec-y); }
.asesor__int {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(40px, 4vw, 72px) var(--gap);
  /* `start`, no `stretch`: en esta rejilla solo quedan la cabecera pegajosa y
     el raíl, y estirar la cabecera le quita al pin todo su recorrido. Lo que
     había que estirar —la foto— ya no vive aquí. */
  align-items: start;
}
/* ⚠️ EL PIN, QUE NO PEGABA. La columna fija llevaba dentro la foto del
   mostrador y con ella medía 628–960 px según el ancho: SIEMPRE más que el
   raíl de al lado (576–679 px). Un sticky solo tiene recorrido si su columna
   es MÁS CORTA que la que scrollea a su lado —el recorrido es el alto del área
   de rejilla menos el alto del elemento—, así que el recorrido era 0 en los
   siete anchos con el pin activo y el `position:sticky` era código muerto.
   Medido recorriendo la sección en 16 posiciones de scroll con
   `behavior:'instant'`: 0/16 pegadas a 1920, 1512, 1440, 1366, 1280, 1152 y
   1024, con el `top` del bloque cayendo de +722 a −1062 sin clavarse una vez.
   El arreglo es adelgazar la columna fija, no quitar el pin: la foto se ha
   llevado al final del raíl (ver el HTML), que además ALARGA la columna que
   scrollea y con ella el recorrido. Sin la foto el bloque mide 428–520 px
   contra los 576–679 del raíl, que es la relación que el pin necesita.
   La columna se queda en 5 de 12: ya no la cierra ninguna foto por abajo. */
.asesor__cab {
  grid-column: 1 / span 5;
  margin-bottom: 0;
  position: sticky;
  top: calc(var(--alto-cab) + 40px);
  /* Sin esto el bloque se estira a todo el alto de la fila (`align-items:start`
     es del contenedor, pero el pin necesita que el elemento NO ocupe su área
     entera: si la llena, recorrido cero otra vez). */
  align-self: start;
}
.asesor__entrada { color: var(--apagado); margin-block: var(--pieza) var(--pieza); max-width: 38ch; }
.asesor__btn { margin-top: 4px; }
/* ⚠️ LA FOTO CRUZA LAS DOCE COLUMNAS (sep-2026, medido a 1440).
   Estaba en `6 / span 7`, o sea metida en la columna del raíl, y a su
   izquierda quedaban 548 px de nada a lo alto de toda la banda — el hueco más
   grande de la página, 60 % de ocupación en la sección. La columna izquierda
   ya dejaba 548×831 px vacíos bajo la cabecera pegajosa; poner la foto encima
   de ese vacío en vez de al lado no arregla el pin (que necesita una columna
   corta para tener recorrido) pero sí cierra la sección por abajo a todo el
   ancho útil, que es lo que le faltaba.
   La proporción pasa de la del fichero (1,506:1) a una BANDA de 21:8: a 1360
   px de ancho, con la proporción vieja la caja mediría 903 px de alto y la
   sección se dispararía. Con 21:8 mide 518 px — 77 px más que la caja de antes
   (441), con el doble largo de superficie fotografiada.
   `cover` recorta por alto (la caja es más apaisada que el fichero) y el ancho
   pintado ES el ancho de la caja: de ahí el `sizes` nuevo del HTML, y de ahí
   que haga falta declarar los peldaños de 3200 y 3840, que están en disco. */
/* Ya NO es un item de la rejilla del pin: vive como hermana de `.asesor__int`
   dentro de su propio `.cont` (ver la nota del HTML). Conserva el ancho útil
   completo y el titular fijo no puede alcanzarla.
   ⚠️ La proporción baja a 21/8 solo en escritorio: apaisada de 2,6 sobre un
   fichero de 1,506, `cover` recorta por alto y el ancho pintado es el de la
   caja. Por debajo de 900 px vuelve la proporción del fichero, porque a 21/8
   la foto quedaba en una tira de 45 px de alto e ilegible. */
.asesor__foto {
  margin-top: clamp(28px, 3vw, 44px);
  aspect-ratio: 21 / 8;
}
@media (max-width: 900px) { .asesor__foto { aspect-ratio: 800 / 531; } }

.rail {
  /* Una columna más: con 6 de 12 y el texto topado en 54ch el raíl se paraba
     en 1.050 de 1.360 px y la sección medía 53 % de ocupación. */
  grid-column: 6 / span 7;
  position: relative;
  padding-left: var(--rail-x);
}
/* La línea del raíl, apagada: el carril por el que va a crecer la luz. */
.rail::before {
  content: "";
  position: absolute; left: 0; top: 10px; bottom: 10px;
  width: 1px;
  background: var(--linea);
}
/* La línea ENCENDIDA. Crece de arriba abajo siguiendo el scroll: el valor de
   `--rail-avance` (0 → 1) lo escribe js/main.js §8B en cada frame del bucle
   rAF único.
   ⚠️ EL VALOR DE RESERVA ES 1, NO 0. Si la variable no existe —sin JS, o con
   main.js caído— el raíl se ve ENCENDIDO ENTERO, que es como se ve acabado;
   a cero se vería un carril gris que se lee como cuatro pasos deshabilitados.
   Con JS no hay parpadeo: `API.alScroll` ejecuta la tarea en el mismo momento
   de registrarla, antes de que la sección llegue a pantalla. */
.rail::after {
  content: "";
  position: absolute; left: 0; top: 10px; bottom: 10px;
  width: 1px;
  background: var(--amarillo);
  transform: scaleY(var(--rail-avance, 1));
  transform-origin: top;
  will-change: transform;
}
/* Sin JS los nodos también se quedan encendidos, por lo mismo. */
html:not(.js) .rail__paso::before { border-color: var(--amarillo); background: var(--amarillo); }
.rail__paso {
  position: relative;
  padding-bottom: clamp(34px, 4vw, 60px);
}
.rail__paso:last-child { padding-bottom: 0; }
/* El nodo: cuadrado girado 45° sobre la línea */
.rail__paso::before {
  content: "";
  position: absolute;
  left: calc(var(--rail-x) * -1 - 4px);
  top: 8px;
  width: 9px; height: 9px;
  background: var(--fondo);
  border: 2px solid var(--linea-alta);
  transform: rotate(45deg);
  transition: border-color 620ms var(--ease), background 620ms var(--ease);
}
.rail__paso:hover::before { border-color: var(--amarillo); background: var(--amarillo); }
/* El nodo se enciende cuando la línea de lectura lo alcanza. Va con su propia
   transición para que el encendido se lea como un chispazo y no como un salto,
   y con `box-shadow` en vez de `filter` para no crear una capa nueva por nodo. */
.rail__paso.is-encendido::before {
  border-color: var(--amarillo);
  background: var(--amarillo);
  box-shadow: 0 0 0 4px rgb(var(--am-rgb) / .14);
}
.rail__n {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
  margin-bottom: 10px;
}
.rail__t {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.06;
  letter-spacing: .03em;
  text-transform: uppercase;
  margin-bottom: 10px;
}
/* 68ch eran 582 px y NOVENTA Y CUATRO caracteres por línea: el párrafo más
   ancho de todo el sitio, en el raíl de cuatro pasos de las cuatro divisiones.
   Con `--medida-s` (su cuerpo es --fs-sm) se queda en 63. */
.rail__p { font-size: var(--fs-sm); line-height: 1.62; color: var(--apagado); max-width: var(--medida-s); }

@media (max-width: 900px) {
  .asesor__cab { grid-column: 1 / -1; position: static; margin-bottom: var(--bloque); }
  .rail { grid-column: 1 / -1; }
  .asesor__entrada { max-width: var(--medida); }
}
/* ⚠️ GUARDA DE ALTURA DEL PIN — la misma que p-ferreteria.css escribió para
   `.obra__ficha`. Un bloque pinchado a `--alto-cab + 40` px del borde
   superior solo puede estar pinchado si la ventana da para
   `top + alto_del_bloque + aire`. Por debajo de eso el botón amarillo de la
   sección —su CTA— se quedaría bajo el pliegue durante todo el recorrido en el
   que el bloque está pinchado, que es exactamente el defecto que se arregló en
   Ferretería. Se desactiva el pin, no se le mete scroll propio: sin pin el
   bloque rueda con la página y el botón siempre se alcanza.
   Los dos umbrales están MEDIDOS, no estimados (sonda CDP, 16 posiciones de
   scroll por ventana):
     · 901–1479 px de ancho → el bloque mide 349–425 px. Con `top` 116 (o 96
       cuando la ventana baja de 560 y `--alto-cab` cede a 56) hacen falta 537
       px de ventana, así que por debajo de 540 no cabe.
     · ≥1480 px de ancho → el bloque crece a 439–475 px y hacen falta 607, así
       que por debajo de 620 no cabe.
   Por debajo de 900 px de ancho el bloque ya es estático por composición. */
@media (max-height: 540px) { .asesor__cab { position: static; } }
@media (max-height: 620px) and (min-width: 1480px) { .asesor__cab { position: static; } }




/* ============================================================
   34 · TRANSICIÓN DE SECCIÓN DE LAS DIVISIONES — LA COTA
   ------------------------------------------------------------
   ⚠️ COMPONENTE COMPARTIDO (5-sep-2026). Era `.sec-cota` en
   p-maquinaria.css y ahora entra en las cuatro páginas de división,
   por petición del cliente: una sola franja para todas las divisiones
   de bloque, la línea fina amarilla. Con ella se retiran la `regla` de
   ferretería, la `puntada` de uniformidad y la `cinta` de rotulación.
   La Home conserva su `[data-filete]`, que es de otra familia.
   ⚠️ 28-sep-2026: FUERA LOS REMATES. La cota llevaba en cada extremo una
   marca vertical de 9 px (`::after`) y el cliente la quiere recta, sin
   bordes: queda solo el filete de 1 px. Copia de la versión con remates
   en _backup/cambios-28sep-inset-cota/css/estilos.css.
   ============================================================ */

.sec-cota { position: relative; }

/* La línea de cota */
.sec-cota::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; z-index: 4;
  height: 1px;
  background: var(--amarillo);
  transform: scaleX(1);
  transform-origin: center;
  pointer-events: none;
}

html.js:not(.reveals-sellados) .sec-cota::before {
  transform: scaleX(0);
  transition: transform 1080ms var(--ease-suave);
}
html.js .sec-cota.is-in::before { transform: scaleX(1); }

/* La sección que lleva la cota NO se desplaza al entrar: la transición es la
   cota. Si además hiciera el translateY de [data-reveal], se moverían la
   sección entera y sus hijos a la vez y el efecto se emborronaría. */
html.js:not(.reveals-sellados) .sec-cota[data-reveal],
html.js.reveals-sellados .sec-cota[data-reveal]:not(.is-in) {
  opacity: 1;
  transform: none;
}

/* La cota ES el divisor: cualquier filete gris que caiga en la misma costura
   se ve como una línea doble. Se neutralizan los dos lados —el borde superior
   de la propia sección y el inferior de la que va justo encima—, sin tocar
   las reglas de las hojas de página, que siguen valiendo donde no hay cota. */
main > section.sec-cota { border-top: 0; }
main > section:has(+ section.sec-cota) { border-bottom: 0; }


/* ============================================================
   35 · OTRAS DIVISIONES — la venta cruzada entre las cuatro
   ------------------------------------------------------------
   ⚠️ COMPONENTE COMPARTIDO (5-sep-2026). Hasta hoy este bloque
   estaba escrito CUATRO veces, una por página de división, con
   cuatro familias de clases distintas y cuatro composiciones que
   no se parecían en nada: `.cruce-ferreteria`/`.cru` (2×2, cuatro
   tarjetas horizontales con miniatura de 118 px), `.cruce-
   maquinaria` (tres filas de texto más una placa de tres filas
   con la furgoneta), `.ucruce`/`.ucr` (tres filas de texto, SIN
   una sola foto) y `.rot-cruce`/`.cruce__*` (tres filas con
   miniatura y un pie con dos enlaces). ~250 líneas de CSS
   repartidas en cuatro hojas para decir lo mismo.
   El cliente pidió tres tarjetas —fuera la de presupuesto—, una
   por columna, EN VERTICAL, «para que se vean mejor». Como las
   cuatro páginas enseñan exactamente las otras tres divisiones,
   el encargo vale para las cuatro y el bloque sube aquí.

   BAUTIZO: `.otras` (sección) + `.odiv` (tarjeta). Comprobado
   contra §29-A y §29-I: ni `otras` ni `odiv` estaban cogidos. NO
   podía llamarse `divisiones` ni `div*` —los usa la Home (§08)—
   ni `cruce`, que está ocupado por cuatro hojas de página.

   LA IDEA: la foto va PRIMERA, a todo el ancho de la tarjeta y a
   sangre por arriba y por los lados; el número amarillo se posa en
   su esquina inferior izquierda, sostenido por un degradado; debajo
   el rótulo de la división en condensada, el párrafo con aire y, al
   pie, un filete que CRUZA la tarjeta entera con la acción debajo.
   Antes la miniatura ocupaba el 8 % de la tarjeta y no se veía qué
   había en ella; ahora la foto ocupa el 55 % (medido a 1440).
   ============================================================ */

/* La sección no pinta fondo propio: el carbón del <body> es el fondo y las
   tarjetas son la única superficie elevada. La costura de arriba la dibuja
   `sec-cota` (§34) — por eso NO se declara aquí ningún `border-block`, que
   se leería como línea doble. */
.otras { padding-block: var(--sec-y); }
/* La cabecera admite las DOS formas del sistema y el componente no impone
   ninguna: tres de las cuatro páginas la llevan partida (`--split`, con la
   entrada a la derecha) y maquinaria la lleva entera, porque no tiene
   entradilla escrita. Sin `--split` el titular no tiene columna que lo
   detenga, así que se le pone la medida a mano: sin ella «La máquina llega,
   y con ella todo lo demás» se iba a tres líneas contra las dos de sus
   hermanas. Los 34ch venían de `p-maquinaria.css` y suben aquí con el
   bloque. */
.otras .cabecera-sec:not(.cabecera-sec--split) .h2 { max-width: 34ch; text-wrap: balance; }
/* ⚠️ Y LA MEDIDA DEL TITULAR TAMBIÉN ES DEL COMPONENTE. El `.h2` global va a
   20ch (552 px), que en la columna de 743 de una cabecera partida deja el
   titular en TRES líneas. Ferretería lo pisaba con una regla de página a 26ch
   y uniformidad con otra a 28ch; rotulación no tenía ninguna, así que su
   titular era el único a tres líneas. Con el bloque compartido, la medida
   sube aquí y las tres cabeceras partidas se leen igual: dos líneas. */
.otras .cabecera-sec--split .h2 { max-width: 26ch; }

/* Tres columnas iguales. `align-items` se queda en `stretch` (el de serie):
   las tres tarjetas miden lo mismo de alto y por eso los tres «Ver división»
   caen en la misma línea aunque un párrafo ocupe una línea más. */
.otras__lista {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
}

/* El relleno lateral NO se aplica a la tarjeta sino a cada pieza de texto:
   es lo que deja a la foto y al filete del pie llegar limpios a los bordes. */
.odiv { --odiv-x: clamp(20px, 1.85vw, 30px); display: grid; }

.odiv__a {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;   /* foto · cuerpo · pie */
  height: 100%;
  background: var(--sup);
  border: 1px solid var(--linea);
  border-radius: var(--radio);
  overflow: hidden;                 /* la foto sangra hasta el borde */
  isolation: isolate;
  transition: border-color 620ms var(--ease);
}
.odiv__a:hover { border-color: var(--linea-alta); }
/* El foco no se toca: `:focus-visible` es global (§00) y dibuja su anillo
   alrededor de la tarjeta entera, que es el enlace. Solo se separa para que
   el anillo no se coma el recorte de la foto. */
.odiv__a:focus-visible { outline-offset: 4px; }

/* --- La foto ---------------------------------------------------
   Caja 4:3. Las cuatro fotos son apaisadas (1,506 · 1,506 · 1,501 · 1,778),
   así que en una caja de 1,333 `cover` recorta por los LADOS y conserva el
   fotograma entero de arriba abajo: el número de la esquina cae siempre
   sobre la misma zona de la imagen. */
.odiv__foto { aspect-ratio: 4 / 3; border-radius: 0; }
/* La <img> sale del flujo: con la caja gobernada por `aspect-ratio` el alto
   ya está definido, y así ningún alto intrínseco del fichero puede empujar la
   fila. Es la lección que este proyecto lleva escrita tres veces. */
.odiv__foto img { position: absolute; inset: 0; }

/* ⚠️ EL DEGRADADO QUE SOSTIENE EL NÚMERO. Reescribe el `::before` de
   `.media--plana` con dos capas: la viñeta radial de siempre, intacta, y
   encima un degradado al negro por abajo.
   ⚠️ LA CURVA ESTÁ MEDIDA, NO ELEGIDA A OJO. El peor caso es rotulación: en
   la esquina inferior izquierda de la pared de bobinas hay rollos BLANCOS.
   Muestreando el píxel MÁS CLARO bajo la caja del número contra el amarillo:
     sin degradado …… 1,03:1   (el amarillo desaparece)
     .86 al 100 % ……… 4,72:1   (pasa el 4,5:1 raspando)
     ESTA curva ……… 5,90:1
   Las otras suben a 8,2 y 10,0 en ese mismo ancho. Umbral aplicable 4,5:1. */
.odiv__a .odiv__foto::before {
  background:
    linear-gradient(180deg,
      rgb(var(--negro-rgb) / 0) 46%,
      rgb(var(--negro-rgb) / .44) 74%,
      rgb(var(--negro-rgb) / .94) 100%),
    radial-gradient(122% 84% at 50% 46%, transparent 46%, rgb(var(--negro-rgb) / .36) 100%);
}

/* Zoom del sistema: la transición ya vive en `.media img` (--dur-hover), el
   hover solo pone el `scale`, y 1.05 es el valor de §26. */
.odiv__a:hover .odiv__foto img { transform: scale(1.05); }

/* --- El número -------------------------------------------------
   ⚠️ EXCEPCIÓN DE TAMAÑO, a sabiendas. La receta de numeración del sistema
   manda `--fs-kicker` (11-13 px) y ahí el número mide 11 px sobre una foto de
   430×324: el 3,4 % de su alto, una errata en la esquina. Encima de la foto
   deja de ser la etiqueta de una fila y pasa a ser la marca de la pieza, así
   que sube a 20 px — la misma excepción que ya se tomó en `.cap__ord`.
   z-index 3: por encima de la viñeta (1) y del barrido amarillo (2). */
.odiv__n {
  position: absolute;
  left: var(--odiv-x);
  bottom: clamp(14px, 1.3vw, 20px);
  z-index: 3;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  line-height: 1;
  letter-spacing: var(--ls-kicker);
  color: var(--amarillo);
  /* La misma sombra que `.rpaso__n`, el otro número del sitio que va sobre
     una foto: no sustituye al degradado, despega el trazo del grano. */
  text-shadow: 0 1px 12px rgb(var(--negro-rgb) / .9);
}

/* --- El cuerpo -------------------------------------------------
   Es la banda que crece (`1fr`): absorbe la diferencia de líneas entre los
   tres párrafos y deja el pie clavado abajo en las tres tarjetas. */
.odiv__cuerpo { padding: clamp(22px, 2vw, 32px) var(--odiv-x) clamp(24px, 2.2vw, 34px); }
.odiv__t {
  font-family: var(--display);
  font-weight: 800;
  /* Entre el H2 de la sección (57,6 px a 1440) y el H3 corriente (28,8): el
     rótulo tiene que leerse como nombre de división, pero tres de ellos no
     pueden discutirle el sitio al titular de la sección.
     ⚠️ EL SUELO ES 1,35rem Y NO 1,6, y no es cosmética. Con 1,6rem el
     `2.5vw` deja de mandar por debajo de 1024 px y el rótulo se queda
     clavado en 25,6 px mientras la columna sigue estrechándose: medido, a
     621 px «Uniformidad» pinta 137 px de tinta en una caja de 137 —cero
     holgura, y basta una fuente de reserva un pelo más ancha para que
     desborde—. Con 1,35 el rótulo acompaña a la columna: 22 px de holgura
     en ese mismo punto. De 1024 px para arriba no cambia NADA, porque ahí
     el `2.5vw` ya va por encima de los dos suelos. */
  font-size: clamp(1.35rem, 2.5vw, 2.25rem);
  line-height: 1;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--texto);
  margin-bottom: clamp(12px, 1.1vw, 16px);
}
.odiv__p { font-size: var(--fs-sm); line-height: 1.62; color: var(--apagado); max-width: 40ch; }

/* --- El pie ----------------------------------------------------
   El filete cruza la tarjeta ENTERA porque el relleno lateral lo llevan el
   cuerpo y el pie por separado, no la tarjeta. Se enciende en 620 ms, que es
   la duración de todo lo que es borde en el sistema (§26). */
.odiv__pie {
  border-top: 1px solid var(--linea);
  padding-inline: var(--odiv-x);
  transition: border-color 620ms var(--ease);
}
.odiv__a:hover .odiv__pie { border-color: var(--amarillo); }

/* La acción NO es un enlace: la tarjeta entera lo es. Objetivo táctil de
   44 px por el `min-height`, como `.enlace-flecha` (§03). */
.odiv__ir {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding-block: 14px;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--amarillo);
  transition: gap var(--dur-ui) var(--ease);
}
.odiv__ir::after {
  content: "";
  width: 26px; height: 1px; background: var(--amarillo);
  transition: width var(--dur-ui) var(--ease);
}
.odiv__a:hover .odiv__ir { gap: 18px; }
.odiv__a:hover .odiv__ir::after { width: 42px; }

/* --- Apilado ---------------------------------------------------
   TRES COLUMNAS O UNA, nunca dos: con tres tarjetas, una rejilla de dos deja
   la tercera sola en una fila y el bloque se lee como si faltara algo.
   ⚠️ EL CORTE VA EN 620, NO EN 900. Apilar en 900 —que es donde el sistema
   apila sus rejillas— dejaba la tarjeta en 784 px de ancho en un iPad, con
   una foto de 784×523: medido, el bloque pasaba de 805 px de alto a 2.563,
   tres fotos enormes seguidas para lo que es una venta cruzada. A 834 las
   tres columnas dan tarjetas de 249 px, que es un tamaño de tarjeta normal y
   se lee perfectamente. 620 es un corte que la casa ya usa.
   A una columna la foto pasa a 3:2: la tarjeta llega a 572 px de ancho y un
   4:3 daría 429 px de foto, una tapia. */
@media (max-width: 620px) {
  .otras__lista { grid-template-columns: minmax(0, 1fr); }
  .odiv__foto { aspect-ratio: 3 / 2; }
}

/* --- Movimiento reducido ---------------------------------------
   §27 ya deja las transiciones en .001 ms y `.media img` en `transform:
   none !important`. Se escribe igualmente porque este componente viaja a
   cuatro páginas y no puede depender de que esa línea siga ahí. */
@media (prefers-reduced-motion: reduce) {
  .odiv__a:hover .odiv__foto img { transform: none; }
  .odiv__a, .odiv__pie, .odiv__ir, .odiv__ir::after { transition: none; }
}


/* ============================================================
   20 · PIE
   ============================================================ */

.pie {
  /* ⚠️ RELLENO SUPERIOR PROPIO (6-sep-2026). Iba con `--sec-y`, el mismo aire
     que abre y cierra una sección de contenido: 176 px a 1920 y 144 a 1440,
     que el cliente vio como demasiado en escritorio. El pie no es una sección
     más —ya viene detrás del filete que lo separa del contenido—, así que
     lleva su propia medida: 128 px a 1920 y 108 a 1440, un cuarto menos. El
     suelo se queda en 88 para no tocar el móvil, que estaba bien (96 px). */
  padding-block: clamp(88px, 7.5vw, 128px) 40px;
  border-top: 1px solid var(--linea);
  background: var(--fondo);
}
.pie__alto {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: clamp(48px, 5vw, 96px) var(--gap);
  padding-bottom: var(--bloque);
}
.pie__logo { display: block; width: clamp(150px, 15vw, 200px); color: var(--texto); }
.pie__logo img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 24145 / 2219;
  filter: brightness(0) invert(1);
}
.pie__texto {
  margin-top: 26px;
  font-size: var(--fs-pie);
  line-height: 1.66;
  color: var(--apagado);
  max-width: 42ch;
}
/* El teléfono es la conversión principal del pie de una ferretería con
   mostrador; iba al mismo cuerpo y al mismo color que el correo. Se separan
   en dos niveles, la misma relación que ya existe en la dirección A. */
.pie__contacto {
  display: grid;
  justify-items: start;
  gap: 2px;
  margin-top: 26px;
}
.pie__tel {
  display: inline-flex; align-items: center; min-height: 44px;
  font-family: var(--display); font-weight: 800;
  font-size: var(--fs-tel-pie); line-height: 1;
  letter-spacing: .03em;
  color: var(--amarillo);
  transition: color var(--dur-ui) var(--ease);
}
.pie__tel:hover { color: var(--texto); }
.pie__mail {
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: var(--fs-pie);
  color: var(--apagado);
  transition: color var(--dur-ui) var(--ease);
}
.pie__mail:hover { color: var(--texto); }

.pie__cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
}
.pie__ct {
  font-family: var(--display); font-weight: 600;
  /* El rótulo de columna sube con el resto del pie: era --fs-kicker
     (11-13 px) y ahora tiene su propio escalón, 12-14. */
  font-size: clamp(.75rem, .88vw, .875rem); letter-spacing: var(--ls-kicker);
  text-transform: uppercase; color: var(--amarillo);
  padding-bottom: 14px; margin-bottom: 8px;
  border-bottom: 1px solid var(--linea);
}
.pie__col a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 8px 0;
  font-size: var(--fs-pie);
  color: var(--apagado);
  transition: color var(--dur-ui) var(--ease), padding-left var(--dur-ui) var(--ease);
}
.pie__col a:hover { color: var(--texto); padding-left: 6px; }

.pie__bajo {
  display: flex; flex-wrap: wrap; gap: 12px 32px;
  align-items: center; justify-content: space-between;
  padding-top: 26px;
  border-top: 1px solid var(--linea);
}
.pie__copy { font-size: var(--fs-pie-micro); color: var(--apagado); letter-spacing: .04em; }
.pie__legales { display: flex; flex-wrap: wrap; gap: 8px 24px; }
.pie__legales a {
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: var(--fs-pie-micro); color: var(--apagado);
  transition: color var(--dur-ui) var(--ease);
}
.pie__legales a:hover { color: var(--amarillo); }

@media (max-width: 900px) {
  .pie__alto { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .pie__cols { grid-template-columns: 1fr; gap: 32px; }
}


/* ============================================================
   21 · FICHAS — carrusel de fichas ARRASTRABLE  [data-fichas]
   ------------------------------------------------------------
   Mecanismo nº 2 del repertorio. Equipo en la Home y en Empresa.
   Arrastre con ratón y con dedo + scroll-snap por ficha + flechas.
   La ficha CENTRAL se eleva (escala, filete amarillo encendido,
   puesto visible) y las laterales bajan opacidad y saturación.

   Marcado mínimo:
     <div class="fichas" data-fichas>
       <ul class="fichas__pista"> <li class="ficha"> … </li> </ul>
       <div class="fichas__ui">
         <button data-fichas-ir="-1"> <button data-fichas-ir="1">
       </div>
     </div>
   ⚠️ El carril arrastrable es EXCLUSIVO DE LA HOME (paginas.md §3.1,
   mecanismo nº 2). Lo usaba también empresa.html y las dos secciones se
   leían como el mismo componente recolocado; desde ago-2026 el equipo de
   empresa es una NÓMINA de filas (p-empresa.css §5) y aquí ya no hay
   modificador `--grande` que mantener.
   ============================================================ */

.fichas {
  --ficha-an: clamp(178px, 22vw, 214px);
  position: relative;
}

.fichas__pista {
  display: flex;
  gap: var(--gap);
  /* El carril sangra hasta el borde de la ventana y el relleno lateral coloca
     la primera y la última ficha en el CENTRO, que es donde vive el foco. */
  margin-inline: calc(var(--gutter) * -1);
  /* El 50% se resuelve contra el ANCHO DEL PADRE, no contra el del carril, y el
     carril es --gutter más ancho por cada lado por la sangría de arriba: sin el
     `+ var(--gutter)` la ficha central queda 40 px descentrada. */
  padding-inline: max(var(--gutter), calc(50% + var(--gutter) - var(--ficha-an) / 2));
  /* La ficha central escala y proyecta sombra: sin este aire vertical el
     propio overflow-x del carril se las comería. */
  padding-block: 28px 34px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.fichas__pista::-webkit-scrollbar { display: none; }
/* Mientras se arrastra con el ratón, el snap estorba: pelea con el gesto. */
.fichas.is-arrastrando .fichas__pista { scroll-snap-type: none; cursor: grabbing; }

.ficha {
  flex: 0 0 var(--ficha-an);
  width: var(--ficha-an);
  scroll-snap-align: center;
  background: var(--sup);
  border: 1px solid var(--linea);
  border-top: 2px solid var(--linea);
  border-radius: var(--radio);
  padding: 16px 16px 20px;
  opacity: .48;
  filter: saturate(.3);
  transform: scale(.94);
  transition:
    opacity 620ms var(--ease),
    filter 620ms var(--ease),
    transform 620ms var(--ease),
    border-color 620ms var(--ease),
    background 620ms var(--ease),
    box-shadow 620ms var(--ease);
}
.ficha.is-centro {
  opacity: 1;
  filter: none;
  transform: scale(1.06);
  background: var(--sup-2);
  border-top-color: var(--amarillo);
  box-shadow: 0 26px 68px rgb(var(--negro-rgb) / .55);
}
/* Los 10 retratos son 400×400 y no hay más: caja máxima 180 px. */
.ficha__media {
  aspect-ratio: 1 / 1;
  max-width: 180px;
  margin: 0 auto 14px;
}
.ficha__media img { filter: var(--foto) grayscale(1); }
.ficha.is-centro .ficha__media img { filter: brightness(.94) contrast(1.05) saturate(1) grayscale(0); }
.ficha__n {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(.9375rem, 1.1vw, 1.0625rem);
  line-height: 1.16;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.ficha__p {
  font-size: .8125rem; line-height: 1.4; color: var(--apagado);
  margin-top: 4px;
  opacity: 0;
  transition: opacity 520ms var(--ease);
}
.ficha.is-centro .ficha__p { opacity: 1; }

.fichas__ui {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-s);
  margin-top: var(--pieza);
}
.fichas__ayuda {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--display); font-weight: 600;
  font-size: var(--fs-kicker); letter-spacing: var(--ls-kicker);
  text-transform: uppercase; color: var(--apagado);
  transition: opacity 520ms var(--ease), transform 520ms var(--ease);
}
.fichas__ayuda span { color: var(--amarillo); font-size: 1rem; line-height: 1; }
.fichas.is-usada .fichas__ayuda { opacity: 0; transform: translateX(-10px); }

.fichas__flechas { display: flex; gap: 10px; margin-left: auto; }
.fichas__f {
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border: 1px solid var(--linea-alta);
  border-radius: 50%;
  color: var(--texto);
  transition: border-color var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease), background var(--dur-ui) var(--ease);
}
.fichas__f:hover { border-color: var(--amarillo); color: var(--amarillo); background: var(--am-04); }
.fichas__f[disabled] { opacity: .3; pointer-events: none; }
.fichas__f i {
  display: block; width: 11px; height: 11px;
  border-top: 2px solid currentColor; border-right: 2px solid currentColor;
  transform: rotate(45deg); margin-left: -3px;
}
.fichas__f[data-fichas-ir="-1"] i { transform: rotate(-135deg); margin: 0 -3px 0 0; }

/* Sin JS no hay ficha central ni arrastre programado, pero el carril nativo
   sigue scrolleando: se enseñan TODAS las fichas encendidas. */
html:not(.js) .ficha { opacity: 1; filter: none; transform: none; }
html:not(.js) .ficha__p { opacity: 1; }
html:not(.js) .fichas__flechas,
html:not(.js) .fichas__ayuda { display: none; }
/* Y con main.js bloqueado —que NO es lo mismo: la clase `js` la pone el script
   en línea del <head> y la red de arriba no entra— los 8 puestos del equipo se
   quedaban invisibles, porque solo se encienden con `.ficha.is-centro` y esa
   clase la marca main.js. Medido: los 8 `.ficha__p` a opacity 0.
   Misma condición que la navegación de §05: `js-ok` la escribe main.js al
   terminar y `reveals-sellados` el <head> a los 2.500 ms. */
html.reveals-sellados:not(.js-ok) .ficha__p { opacity: 1; }


/* ============================================================
   22 · GALERÍA HORIZONTAL POR SCROLL VERTICAL  [data-galh]
   ------------------------------------------------------------
   Mecanismo nº 3. La sección se fija y el carro se desplaza en
   horizontal según lo recorrido en vertical. El alto del contenedor
   lo calcula el JS (100dvh + lo que sobra de carro).
   Con prefers-reduced-motion o sin JS pasa a rejilla normal.
   ============================================================ */

.galh { position: relative; }
.galh__pin {
  position: sticky;
  top: var(--alto-cab);
  height: calc(100dvh - var(--alto-cab));
  display: flex;
  align-items: center;
  overflow: hidden;
}
.galh__carro {
  display: flex;
  gap: var(--gap);
  padding-inline: var(--gutter);
  will-change: transform;
}
.galh__item { flex: 0 0 clamp(268px, 30vw, 440px); }
.galh__item .trab__media { height: clamp(220px, 38dvh, 430px); }

/* Riel de progreso de la galería: el eco horizontal del recorrido vertical. */
.galh__riel {
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: 26px;
  height: 2px; background: var(--linea);
}
.galh__riel span {
  display: block; height: 100%; width: 100%;
  background: var(--amarillo);
  transform: scaleX(0); transform-origin: left;
}

@media (max-width: 620px) {
  .galh__item { flex: 0 0 78vw; }
}

/* Sin JS: rejilla. El contenedor no lleva alto en línea porque el JS es quien
   lo pone, así que aquí basta con despinar. */
html:not(.js) .galh__pin { position: static; height: auto; overflow: visible; }
html:not(.js) .galh__carro {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  padding-inline: 0;
}
html:not(.js) .galh__item { flex: none; }
html:not(.js) .galh__riel { display: none; }
@media (max-width: 900px) {
  html:not(.js) .galh__carro { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  html:not(.js) .galh__carro { grid-template-columns: 1fr; }
}


/* ============================================================
   23 · PANEL VERTICAL — rotación de citas  [data-panelv]
   ------------------------------------------------------------
   Mecanismo nº 5. Las citas ENTRAN DESDE ABAJO y salen por arriba.
   Cero fundido: aquí no hay una sola transición de opacity, es el
   único mecanismo del sitio que se mueve solo en vertical.
   El filete amarillo se desplaza al paso activo.
   ============================================================ */

.panelv { position: relative; }
.panelv__ventana {
  position: relative;
  overflow: hidden;
  /* El alto se reserva para la cita MÁS LARGA en la columna MÁS ESTRECHA: la
     misma cita que ocupa 2 líneas a 1440 ocupa 5 en un móvil. Un min-height
     fluido con vw hace justo lo contrario de lo que hace falta (se encoge
     cuando la columna se estrecha) y la cita se queda tragada por el
     overflow:hidden. Por eso va al revés: grande de serie, y solo se recorta
     cuando hay ancho de sobra. */
  min-height: 380px;
}
@media (min-width: 341px) { .panelv__ventana { min-height: 330px; } }
@media (min-width: 561px) { .panelv__ventana { min-height: 320px; } }
@media (min-width: 901px) { .panelv__ventana { min-height: clamp(258px, 23vw, 310px); } }
.panelv__cita {
  position: absolute;
  inset: 0;
  transform: translate3d(0, 106%, 0);
  transition: transform 820ms var(--ease-suave);
  will-change: transform;
}
.panelv__cita.is-activa { transform: translate3d(0, 0, 0); }
.panelv__cita.is-fuera  { transform: translate3d(0, -106%, 0); }
/* Arranque antes de que el JS marque estados (ver §06.2). */
.panelv:not([data-dy-panelv]) .panelv__cita:first-child { transform: translate3d(0, 0, 0); }

.panelv__pasos {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  margin-top: var(--pieza);
  border-top: 1px solid var(--linea);
}
.panelv__filete {
  position: absolute; left: 0; top: -1px;
  width: calc(100% / var(--pasos, 3));
  height: 2px;
  background: var(--amarillo);
  transform: translateX(calc(var(--activo, 0) * 100%)) skewX(-30deg);
  transition: transform 620ms var(--ease-suave);
}
.panelv__paso {
  min-height: 52px;
  padding: 14px 8px;
  text-align: left;
  font-family: var(--display); font-weight: 700;
  font-size: var(--fs-kicker); letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--apagado);
  transition: color var(--dur-ui) var(--ease);
}
.panelv__paso:hover { color: var(--texto); }
.panelv__paso.is-active { color: var(--amarillo); }

/* Sin JS: las tres citas apiladas en columna, visibles y quietas. */
html:not(.js) .panelv__ventana { overflow: visible; min-height: 0; display: grid; gap: var(--gap); }
html:not(.js) .panelv__cita { position: static; transform: none; }
html:not(.js) .panelv__pasos { display: none; }


/* ============================================================
   24 · PESTAÑAS CON REVELADO POR CLIP-PATH  [data-pest]
   ------------------------------------------------------------
   Mecanismo nº 6. NO es un carrusel: es un conmutador. Al cambiar de
   pestaña el panel nuevo se revela con una máscara DIAGONAL que barre
   de izquierda a derecha con el ángulo del wordmark.
   Roles ARIA reales (tablist/tab/tabpanel) y flechas del teclado.
   ============================================================ */

.pest {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: clamp(28px, 4vw, 72px);
  align-items: start;
}
.pest__tabs { display: grid; gap: 2px; }
.pest__tab {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 14px;
  width: 100%;
  min-height: 56px;
  padding: 16px 8px 16px 0;
  text-align: left;
  border-top: 1px solid var(--linea);
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--fs-h3);
  line-height: 1.04;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--apagado);
  transition: color 420ms var(--ease), border-color 420ms var(--ease), padding-left 420ms var(--ease);
}
.pest__tab-n {
  font-size: var(--fs-kicker); font-weight: 600;
  letter-spacing: var(--ls-kicker); color: var(--amarillo);
  opacity: .5; transition: opacity 420ms var(--ease);
}
.pest__tab::after {
  content: "";
  position: absolute; left: 0; top: -1px; right: 0;
  height: 2px; background: var(--amarillo);
  transform: scaleX(0) skewX(-30deg); transform-origin: left;
  transition: transform 620ms var(--ease-suave);
}
.pest__tab:hover { color: var(--texto); padding-left: 6px; }
.pest__tab[aria-selected="true"] { color: var(--texto); }
.pest__tab[aria-selected="true"]::after { transform: scaleX(1) skewX(-30deg); }
.pest__tab[aria-selected="true"] .pest__tab-n { opacity: 1; }

.pest__paneles { position: relative; display: grid; }
.pest__panel {
  grid-area: 1 / 1;
  /* Oculto = polígono degenerado a la izquierda; visible = barrido diagonal
     completo. Los 4 vértices son los mismos en los dos estados, que es la
     condición para que clip-path interpole. */
  clip-path: polygon(-100% 0, -100% 0, -158% 100%, -158% 100%);
  transition: clip-path 760ms var(--ease-suave);
  pointer-events: none;
  z-index: 0;
}
/* z-index 3 y no 2: es el cinturón contra un `.is-saliendo` colgado. La regla
   de `.is-saliendo` va DESPUÉS en la hoja y también fijaba capa, así que un
   residuo de una transición interrumpida dejaba al panel elegido al mismo
   nivel que los que están detrás en el DOM y lo tapaban (el índice marcaba
   BORDADO y se veían las fotos y el titular de SERIGRAFÍA a la vez). El
   temporizador por panel de main.js §8 ya no deja residuos; esto es para que
   un residuo no pueda volver a tapar al panel activo. */
.pest__panel.is-activo {
  clip-path: polygon(-100% 0, 160% 0, 102% 100%, -158% 100%);
  pointer-events: auto;
  z-index: 3;
}
/* Arranque antes de que el JS marque estados (ver §06.2). */
.pest:not([data-dy-pest]) .pest__panel:first-child {
  clip-path: polygon(-100% 0, 160% 0, 102% 100%, -158% 100%);
  pointer-events: auto;
  z-index: 2;
}
/* La saliente se queda visible DEBAJO mientras la entrante la barre: sin esto
   el barrido descubriría el fondo carbón en vez del panel anterior. */
.pest__panel.is-saliendo {
  clip-path: polygon(-100% 0, 160% 0, 102% 100%, -158% 100%);
  transition: none;
  z-index: 1;
}
.pest__media { aspect-ratio: 16 / 10; margin-bottom: 22px; }
.pest__t {
  font-family: var(--display); font-weight: 800;
  font-size: var(--fs-h3); line-height: 1.04;
  letter-spacing: var(--ls-display); text-transform: uppercase;
  margin-bottom: 10px;
}
.pest__p { color: var(--apagado); max-width: var(--medida); }

/* --- 24.1 · La tira de pestañas en pantalla estrecha ---------
   ⚠️ NO ES UNA LISTA ABIERTA: son CUATRO técnicas fijas. Convertirla en un
   carril con `overflow-x:auto` y las barras ocultas dejaba pestañas que
   sencillamente no existían para el usuario — medido con la sonda posada en
   `.pest__tabs`: a 430×932 «04 TRANSFER» se veía 41,2 px de 110,9; a 393×852,
   4 px; a 390×844, 1,2; a 375×667 y 360×800, CERO; a 320×568, «03 DTF» 14 px
   y «04 TRANSFER» cero. 452 px de contenido en una caja de 272-382. Y la
   diana de toque quedaba en 1,2×56 y 14×56, muy por debajo de 44×44.
   Por debajo de 900 px la tira pasa a rejilla que ENVUELVE (2×2 con cuatro
   técnicas): se ven enteras sin desplazar nada y cada celda es una diana de
   media caja. Afecta a index.html (.perso, #pt-0…#pt-3) y a uniformidad.html
   (.utec, #ut-0…#ut-3).
   `--pest-tab-min` es la salida para una tira que NO quiera envolver: con
   `100%` se queda en lista vertical. La usa `.ufaq__tabs` de uniformidad, que
   son seis preguntas largas y a dos columnas no se leen. */
@media (max-width: 900px) {
  .pest { grid-template-columns: 1fr; gap: var(--bloque); }
  .pest__tabs {
    grid-auto-flow: row;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--pest-tab-min, 46%)), 1fr));
    gap: var(--gap-s);
    padding-bottom: 2px;
  }
  .pest__tab {
    min-height: 60px;
    font-size: var(--fs-h4);
    white-space: normal;
    text-wrap: balance;
    padding-right: 14px;
  }
  .pest__tab:hover { padding-left: 0; }
  .pest__media { aspect-ratio: 3 / 2; }
}

/* Sin JS: los 4 paneles visibles en columna, cada uno con su rótulo. */
html:not(.js) .pest__paneles { display: grid; gap: var(--bloque); }
html:not(.js) .pest__panel { grid-area: auto; clip-path: none; pointer-events: auto; }
html:not(.js) .pest__tabs { display: none; }


/* ============================================================
   25 · TRANSICIONES ENTRE SECCIONES
   ------------------------------------------------------------
   Repertorio compartido. **Una distinta por página** — si dos páginas
   entran igual, el trabajo está a medias.
     · [data-filete]   filete amarillo que se dibuja de izq. a der.  ← Home
   Aquí vivían además `.sec--diagonal` y `.sec--cambio`, que §29-B presentaba
   como «los compartidos ocupados» y que en realidad no casaban con nada en
   ninguna de las 8 páginas (cada una se hizo su propia transición con nombre
   propio: cinta, compuerta-contacto, porton, puntada, regla, telon).
   Borradas ago-2026: un repertorio que nadie usa no es repertorio, es una
   lista de nombres reservados por error. El registro §29-B queda con la foto
   real, sacada con grep.
   ============================================================ */

[data-filete] { position: relative; }
[data-filete]::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; z-index: 3;
  height: 2px;
  background: var(--amarillo);
  transform: scaleX(1) skewX(-30deg);
  /* `left` era `0 50%`: el origen vertical en el centro hace que el sesgo
     saque medio filete (tan(30°)×1 ≈ 0,58 px) por la DERECHA en cuanto el
     filete se dibuja entero, y `scrollWidth` lo redondea a 1 px de desborde
     en toda la página. Con el origen arriba-izquierda el sesgo solo inclina
     hacia la izquierda, donde no hay scroll que ensanchar. */
  transform-origin: 0 0;
  pointer-events: none;
}
html.js:not(.reveals-sellados) [data-filete]::before {
  transform: scaleX(0) skewX(-30deg);
  transition: transform 1150ms var(--ease-suave);
}
html.js [data-filete].is-in::before { transform: scaleX(1) skewX(-30deg); }


/* ============================================================
   26 · MICROINTERACCIONES
   ------------------------------------------------------------
   Van en TODAS las páginas. El estado de foco es visible siempre
   (:focus-visible global, sección 00) — ninguna de estas lo pisa.
   ============================================================ */

/* Botones: el fondo se desplaza en diagonal al pasar por encima.
   ⚠️ ÚNICA declaración de `transition` de `.btn::before` en toda la hoja
   (§03 solo define la silueta). Si hay que tocar la transición del botón, es
   aquí. */
.btn::before {
  background-size: 230% 100%;
  background-position: 0 0;
  transition:
    background var(--dur-ui) var(--ease),
    background-position 700ms var(--ease-suave),
    box-shadow var(--dur-ui) var(--ease);
}
.btn--luz::before {
  background-color: var(--amarillo);
  background-image: linear-gradient(100deg,
    var(--amarillo) 0 42%, #FFF7A6 50%, var(--amarillo) 58% 100%);
}
.btn--luz:hover::before { background-position: 100% 0; }
.btn--linea::before {
  background-image: linear-gradient(100deg,
    rgb(var(--texto-rgb) / .10) 0 42%, rgb(var(--am-rgb) / .20) 50%, rgb(var(--texto-rgb) / .10) 58% 100%);
}
.btn--linea:hover::before { background-position: 100% 0; }
.btn:active { transform: translateY(1px); }

/* Enlace de texto con subrayado que se DIBUJA desde la izquierda. Funciona
   aunque el enlace parta en dos líneas, que es lo que no hace un ::after.

   DOS CAPAS, y las dos hacen falta:

     1 · el filete AMARILLO de 2 px que se dibuja de izquierda a derecha —
         la microinteracción de la dirección de arte (§3.4 del encargo);
     2 · debajo, un subrayado de 1 px en `currentColor` que está SIEMPRE.

   La capa 2 no es decoración: sin ella el enlace hereda el color del
   párrafo, el mismo peso y ningún trazo, así que en reposo no se
   distingue del texto —solo existe para quien pasa el ratón por encima,
   que en táctil no es nadie— y el único indicio sería el color, que
   incumple WCAG 1.4.1. Va en `currentColor` para acompañar al color del
   texto sea cual sea el fondo, y en 1 px para que el filete de 2 px que
   se dibuja encima siga leyéndose como un cambio de estado.

   ⚠️ Al sobrescribir esto por página hay que declarar los DOS valores de
   `background-size` (`0% 2px, 100% 1px`): con uno solo, el navegador se
   lo aplica a las dos capas y el amarillo aparece dibujado en reposo. */
.enlace-sub {
  padding-bottom: 2px;
  background-image:
    linear-gradient(var(--amarillo), var(--amarillo)),
    linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 2px, 100% 1px;
  background-position: 0 100%;
  transition: background-size 560ms var(--ease-suave), color var(--dur-ui) var(--ease);
}
.enlace-sub:hover,
.enlace-sub:focus-visible { background-size: 100% 2px, 100% 1px; color: var(--amarillo); }

/* Campo de formulario: etiqueta flotante + filete amarillo al enfocar.
   ⚠️ El <input> va ANTES que el <label> en el HTML (el selector es `+`), y el
   input necesita placeholder=" " para que :placeholder-shown funcione.
     <p class="campo">
       <input class="campo__i" id="x" placeholder=" ">
       <label class="campo__l" for="x">Nombre</label>
     </p> */
.campo { position: relative; display: block; }
.campo__i {
  width: 100%;
  min-height: 62px;
  padding: 26px 16px 12px;
  background: var(--sup);
  border: 1px solid var(--linea);
  border-radius: var(--radio);
  color: var(--texto);
  font-family: var(--lectura);
  font-size: var(--fs-body);
  line-height: 1.4;
  transition: border-color var(--dur-ui) var(--ease), background var(--dur-ui) var(--ease);
}
textarea.campo__i { min-height: 148px; resize: vertical; }
.campo__i::placeholder { color: transparent; }
.campo__i:hover { border-color: var(--linea-alta); }
.campo__i:focus { border-color: var(--amarillo); background: var(--sup-2); }
.campo__l {
  position: absolute; left: 17px; top: 20px;
  font-size: var(--fs-sm);
  line-height: 1.2;
  color: var(--apagado);
  pointer-events: none;
  transform-origin: left top;
  transition: transform 280ms var(--ease), color 280ms var(--ease);
}
.campo__i:focus + .campo__l,
.campo__i:not(:placeholder-shown) + .campo__l {
  transform: translateY(-13px) scale(.8);
  color: var(--amarillo);
}
.campo::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--amarillo);
  transform: scaleX(0) skewX(-30deg);
  transform-origin: left;
  transition: transform 460ms var(--ease-suave);
  pointer-events: none;
}
.campo:focus-within::after { transform: scaleX(1) skewX(-30deg); }
/* El rojo vive en tokens.css (`--error`), no aquí: estaba escrito a pelo dos
   veces en esta hoja y tokenizado en el `:root` de p-presupuesto.css, así que
   contacto y presupuesto lo resolvían por caminos distintos. */
.campo--error .campo__i { border-color: var(--error); }
.campo--error .campo__l { color: var(--error); }
.campo__error {
  display: block; margin-top: 8px;
  font-size: var(--fs-micro); color: var(--error);
}

/* Marquee: además del hover, se para al ENFOCAR con teclado dentro. Con
   `:focus-visible` y no `:focus-within`, por lo mismo que el hero (§6): el
   foco que deja un toque no debe parar nada. Hoy la tira no tiene nada
   enfocable; la regla queda para cuando lo tenga. */
.marquee:has(:focus-visible) .marquee__pista { animation-play-state: paused; }

/* ============================================================
   27 · PREFERS-REDUCED-MOTION
   Todo lo que anima queda visible y quieto.
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  /* El raíl no se enciende por scroll: se queda encendido entero. A medias
     sería peor que sin efecto, porque el gris se leería como paso apagado. */
  .rail::after { transform: scaleY(1); }
  .rail__paso::before { border-color: var(--amarillo); background: var(--amarillo); }

  /* La cota se queda DIBUJADA y quieta: es la transición de las cuatro
     divisiones y sin ella la costura entre secciones desaparece. */
  html.js:not(.reveals-sellados) .sec-cota::before,
  html.js .sec-cota.is-in::before { transform: scaleX(1); }

  /* El mosaico del hero de división se queda quieto y visible: la entrada
     escalonada es lo único que se mueve ahí, y `both` dejaría las seis piezas
     en su estado inicial (opacidad 0) si se anulara la animación a medias. */
  .hero-div__p { animation: none; opacity: 1; transform: none; }


  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }

  /* Misma especificidad que la regla que oculta (…:not(.reveals-sellados)…),
     y va después: gana. Si se rebaja, el contenido se queda oculto. */
  html.js:not(.reveals-sellados) [data-reveal],
  html.js.reveals-sellados [data-reveal]:not(.is-in),
  html.js [data-reveal].is-in { opacity: 1; transform: none; }

  /* --- Hero: el carrusel de persiana se desmonta en rejilla ---
     Sin movimiento no hay persiana que valga: las 4 slides se apilan visibles
     y quietas, y la UI del carrusel (índice, progreso, pausa, barrido) sobra. */
  .hero { height: auto; }
  .hero__stage {
    height: auto;
    display: block;
    overflow: visible;
    padding-top: calc(var(--alto-cab) + var(--bloque));
    padding-bottom: var(--bloque);
    background: var(--fondo);
  }
  .hero__contenido {
    max-height: none;
    padding-bottom: var(--bloque);
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "kicker" "h1" "divs" "entrada" "botones";
  }
  .hero__h1, .hero__entrada, .hero__kicker { text-shadow: none; }
  .hero__divs {
    grid-auto-flow: row;
    gap: clamp(18px, 2.2vw, 28px);
    margin-bottom: clamp(20px, 2.4vw, 34px);
  }
  .hero__divs::before { display: none; }
  .hero__div { grid-area: auto; opacity: 1 !important; transform: none !important; }
  .hero__div-r, .hero__div-c, .hero__div-n { text-shadow: none; }
  .persiana__pista { position: static; display: grid; gap: 8px; }
  /* relative, no static: la viñeta y el barrido de .media son ::after
     absolutos y con static se anclarían a la sección entera. */
  .persiana__slide {
    position: relative; inset: auto;
    visibility: visible !important;
    z-index: auto !important;
    height: clamp(200px, 34vw, 380px);
    border-radius: var(--radio);
    overflow: hidden;
  }
  .persiana__kb { animation: none !important; transform: none !important; }
  .persiana__lama { clip-path: none !important; animation: none !important; }
  .persiana__lama:not([data-l="0"]) { display: none; }
  .persiana__foto { animation: none !important; transform: none !important; }
  .hero__scrim,
  .persiana__barrido,
  .persiana__indice,
  .persiana__ayuda,
  .persiana__progreso { display: none; }

  /* --- Fichas: rejilla, sin ficha central ni arrastre --- */
  .fichas__pista {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--gap);
    overflow: visible;
    margin-inline: 0;
    padding-inline: 0;
    padding-block: 0;
    scroll-snap-type: none;
  }
  .ficha {
    flex: none; width: auto;
    opacity: 1; filter: none;
    transform: none !important;
    box-shadow: none;
  }
  .ficha__p { opacity: 1; }
  .ficha__media img { filter: var(--foto) grayscale(1) !important; }
  .fichas__flechas, .fichas__ayuda { display: none; }

  /* --- Galería horizontal: rejilla. El alto en línea lo pone el JS, que en
         este modo ni siquiera se ejecuta; el !important es el cinturón. --- */
  .galh { height: auto !important; }
  .galh__pin { position: static; height: auto; overflow: visible; }
  .galh__carro {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    padding-inline: 0;
    transform: none !important;
  }
  .galh__item { flex: none; }
  .galh__riel { display: none; }

  /* --- Panel vertical de reseñas: lista --- */
  .panelv__ventana { overflow: visible; min-height: 0; display: grid; gap: var(--gap); }
  .panelv__cita { position: static; transform: none !important; }
  .panelv__pasos { display: none; }

  /* --- Pestañas: los 4 paneles en columna, sin máscara --- */
  .pest__paneles { display: grid; gap: var(--bloque); }
  .pest__panel { grid-area: auto; clip-path: none !important; pointer-events: auto; }
  .pest__tabs { display: none; }

  /* --- Transiciones de sección: dibujadas y quietas --- */
  html.js:not(.reveals-sellados) [data-filete]::before,
  html.js [data-filete].is-in::before { transform: scaleX(1) skewX(-30deg); }

  .media--banda { position: absolute; top: 0; height: 100%; transform: none !important; }
  .marquee__pista { animation: none; transform: none; }
  .marquee {
    overflow-x: auto;
    -webkit-mask-image: none; mask-image: none;
  }
  .marquee__pista--b { flex-direction: row; }
  .media img { transform: none !important; }
}


/* Las rejillas del modo reduce necesitan sus propios cortes: no se pueden
   anidar dentro del bloque de arriba sin repetir la condición. */
@media (prefers-reduced-motion: reduce) and (max-width: 1200px) {
  .fichas__pista { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (prefers-reduced-motion: reduce) and (max-width: 900px) {
  .fichas__pista { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .galh__carro { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (prefers-reduced-motion: reduce) and (max-width: 560px) {
  .galh__carro { grid-template-columns: 1fr; }
}


/* ============================================================
   36 · CONSENTIMIENTO DE COOKIES — banner y panel de finalidades
   ------------------------------------------------------------
   POR QUÉ VIVE AQUÍ Y NO EN UNA HOJA PROPIA. El banner sale en las OCHO
   páginas, igual que la cabecera (§05) y el pie (§20), y §29 lo dice sin
   ambigüedad: «Si el componente va a repetirse en varias, sube a esta hoja».
   Una hoja aparte añadiría una novena petición de CSS a cada página y
   obligaría a repetir el orden fuentes → tokens → hoja en ocho <head>.
   Aquí ya está cargada antes de que el banner pinte, así que no parpadea.

   El vocabulario es el de la casa: carbón de fondo, el amarillo SOLO como luz
   (filete, kicker, punto del interruptor), display condensada en mayúsculas
   contra cuerpo humanista, corte en diagonal y filetes de 1 px.

   ⚠️ EL FILETE DE 1 PX SOBRE UN CORTE DIAGONAL. §03 ya anota que «un borde de
   1 px se parte contra el corte diagonal del clip-path». Se resuelve con dos
   capas: la caja lleva el AMARILLO de fondo y el recorte, y un ::before con
   `inset: 1px` y el mismo recorte pinta el carbón encima. Lo que asoma es el
   filete, y sigue la diagonal sin partirse.

   ⚠️ NADA DE `box-shadow` EN ESTAS CAJAS: el clip-path recorta también la
   sombra y no se ve. Si algún día hace falta, va con `filter: drop-shadow`.
   ============================================================ */

/* --- Capa raíz: sostiene las dos capas -------------------------------- */
/* ⚠️ EL AVISO YA NO ES MODAL (9-sep-2026, petición de Juan: «más sutil y
   elegante, no tan tosco»). Antes ocupaba el 38 % de la ventana en escritorio
   y el 61 % en móvil, de borde a borde, con velo y desenfoque sobre toda la
   página: se leía como una sección más del sitio. Ahora es una TARJETA
   pequeña anclada abajo a la izquierda y la web se puede leer y usar detrás.
   Lo que NO cambia es lo único que de verdad protege al visitante: hasta que
   decide no se descarga nada (modo estricto de js/cookies.js). El bloqueo era
   técnico, nunca visual.
   EL PANEL DE FINALIDADES SÍ SIGUE SIENDO MODAL —un cuadro de ajustes debe
   serlo— y es el único que enciende el velo, con `.ck.is-panel`. */
.ck {
  position: fixed;
  inset: 0;
  /* Por encima del menú móvil (120) y del salto al contenido (200). */
  z-index: 300;
  display: grid;
  align-items: end;
  justify-items: start;
  opacity: 0;
  transition: opacity 320ms var(--ease);
  /* La capa no intercepta nada: solo la tarjeta y el panel son pulsables. Sin
     esto, una capa fija a pantalla completa deja la web muerta al clic aunque
     se vea entera. */
  pointer-events: none;
}
.ck[hidden] { display: none; }
.ck.is-abierto { opacity: 1; }

/* El velo solo aparece con el panel abierto, que es el único modal de los dos.
   Difumina en vez de tapar, así que la web se sigue reconociendo detrás. */
.ck__velo {
  position: absolute;
  inset: 0;
  background: rgb(var(--fondo-rgb) / .72);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 320ms var(--ease);
}
.ck.is-panel .ck__velo { opacity: 1; pointer-events: auto; }

/* --- CAPA 1 · el aviso, una tarjeta ---------------------------------- */
/* El corte en diagonal de la marca se queda, pero pequeño: era de hasta 44 px
   cruzando 1.440 px de barra y ahora es la esquina de una tarjeta de 440.
   El filete amarillo de 1 px sigue saliendo del mismo truco: la caja se pinta
   de amarillo y el `::before` pone el carbón encima con `inset: 1px` y el
   mismo recorte, así que el borde no se parte en la diagonal.
   ⚠️ La sombra va con `filter: drop-shadow` y NO con `box-shadow`: el
   clip-path recorta también la sombra y no se vería (nota de esta sección). */
.ck__caja {
  --ck-corte: 18px;
  position: relative;
  z-index: 1;
  width: min(440px, calc(100vw - var(--gutter) * 2));
  max-height: min(74dvh, 560px);
  margin: 0 0 clamp(16px, 2vw, 28px) var(--gutter);
  margin-bottom: calc(env(safe-area-inset-bottom, 0px) + clamp(16px, 2vw, 28px));
  /* EL AMARILLO, SOLO EN LA DIAGONAL. Con la caja entera de amarillo el filete
     rodeaba los cuatro lados y la tarjeta se leía como una alerta (visto en
     captura a 390, 9-sep-2026). En la marca el amarillo es el CORTE, no un
     marco. El degradado a 135° corre hacia la esquina inferior derecha, así
     que su primer tramo cae justo sobre la esquina recortada: amarillo ahí y
     el gris de filete de siempre en los tres lados rectos. */
  background: linear-gradient(135deg, var(--amarillo) 0 34px, var(--linea) 34px);
  clip-path: polygon(var(--ck-corte) 0, 100% 0, 100% 100%, 0 100%, 0 var(--ck-corte));
  filter: drop-shadow(0 18px 40px rgb(0 0 0 / .55));
  transform: translate3d(0, 14px, 0);
  transition: transform 380ms var(--ease);
  pointer-events: auto;
}
.ck.is-abierto .ck__caja { transform: none; }
.ck__caja::before {
  content: "";
  position: absolute;
  inset: 1px;                           /* tarjeta: el filete la rodea entera */
  background: var(--sup);
  clip-path: inherit;
}

.ck__int {
  position: relative;
  max-height: inherit;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(20px, 2vw, 26px) clamp(20px, 2vw, 26px) clamp(18px, 1.8vw, 24px);
  display: grid;
  /* UNA SOLA COLUMNA. Las tres rejillas de antes (apilada, dos mitades desde
     700 y tres columnas desde 1280) existían para repartir una barra de 1.440
     px de ancho. En una tarjeta de 440 no hay nada que repartir: el orden de
     lectura es el de siempre —quién habla, qué se guarda, qué eliges— y cabe
     en columna a cualquier ancho. */
  gap: clamp(12px, 1.4vw, 16px);
}

.ck__kicker {
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--amarillo);
}
.ck__kicker::after {
  content: "";
  flex: 1;
  max-width: 72px;
  height: 1px;
  background: rgb(var(--am-rgb) / .55);
}

.ck__titulo {
  margin-top: 8px;
  font-family: var(--display);
  font-weight: 800;
  /* Era `clamp(1.7rem, 3.2vw, 3rem)` = 46 px a 1440: el mismo cuerpo que un
     H2 de sección, y por eso el aviso competía con la página. En la tarjeta
     baja a 20-22 px, que es tamaño de titular de ficha. */
  font-size: clamp(1.25rem, 1.5vw, 1.375rem);
  line-height: 1.12;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--texto);
}

.ck__texto { margin-top: 0; }
/* Medida de lectura corta a propósito, y MEDIDA de verdad, no calculada de
   memoria. Este aviso fue el primer sitio donde se corrigió la trampa del
   `ch` (8-sep-2026, por la mañana); por la tarde se corrigió el sitio entero
   y la medida pasó a ser la escalera de píxeles de tokens.css §2.bis. El
   valor de abajo se queda como está —366 px, más estrecho que `--medida-s`—
   porque el aviso es una caja flotante y no una columna de página: aquí la
   línea corta es intencionada, no un tope heredado. */
.ck__texto p {
  /* En px y MEDIDO, no en `ch`. Estaba en 47ch = 403 px a 14 px de cuerpo:
     la `ch` de esta tipografía es el ancho del «0», bastante más ancha que su
     letra media, así que la unidad miente por arriba.
     ⚠️ CORRECCIÓN DE LA CIFRA (tarde del 8-sep-2026). Aquí ponía «67
     caracteres» y son 56. No cambió el CSS: cambió la forma de contar. La
     sonda contaba UNO A UNO los espacios que el navegador COLAPSA —el HTML va
     indentado, y entre dos <a> hay un salto de línea y doce espacios que en
     pantalla son uno solo—, así que inflaba la cuenta de todo bloque con
     inline dentro. `_tools/medida_sitio.py` los colapsa antes de contar;
     `_tools/medida_lectura.py`, la sonda de la plantilla de artículo, TODAVÍA
     NO (ver el aviso de la entrega): sus cifras salen por arriba, nunca por
     abajo, así que no ha dejado pasar nada, solo ha exagerado.
     Los 366 px se quedan: el aviso es una caja flotante y su línea corta es
     intencionada. Medido en los siete anchos: 56 caracteres de 1920 a 620 y
     52 a 390. */
  /* Sin tope propio: la medida la fija ya el ancho de la tarjeta (440 − 52 de
     relleno = 388 px), y un `max-width` menor solo dejaría un canal muerto a
     la derecha. Medido en la tarjeta: 53 caracteres reales a 1440 y 50 a 390. */
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--apagado);
}
.ck__texto p + p { margin-top: 10px; }
.ck__texto strong { color: var(--texto); font-weight: 600; }

/* El enlace a la política es un objetivo táctil por derecho propio, no una
   palabra subrayada dentro de un párrafo. */
.ck__mas {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: 2px;
  font-size: var(--fs-sm);
  color: var(--amarillo);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  transition: color var(--dur-ui) var(--ease);
}
.ck__mas:hover,
.ck__mas:focus { color: var(--texto); text-decoration: underline; }

/* --- Las tres salidas ------------------------------------------------ */
.ck__elige {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(14px, 1.4vw, 20px);
}

/* ⚠️ ACEPTAR Y RECHAZAR SON EL MISMO BOTÓN. Los dos llevan `.btn .btn--linea`
   y ningún modificador propio: misma silueta, mismo tamaño, mismo color, mismo
   hover. No es una preferencia estética. La guía de cookies de la AEPD (mayo
   2024, 3.1.2.2) exige que el botón de rechazar sea «similar al anterior» y
   añade que «el color o contraste del texto y los botones no podrán ser
   obviamente engañosos para los usuarios, de forma que lleven a un
   consentimiento involuntario». Un aceptar amarillo contra un rechazar gris es
   exactamente eso. Aquí el amarillo no está en ninguno de los dos.
   ⚠️ NO añadir `.btn--luz` a ACEPTAR por mucho que se pida «que destaque el
   CTA»: invalida el consentimiento de toda la web. */
/* `flex: 1 1 0` en los dos, sin `min-width` propio: los dos botones salen
   EXACTAMENTE del mismo ancho pase lo que pase con la longitud del texto. Es
   la paridad medible, no la que se ve a ojo. */
.ck__acciones { display: flex; gap: 12px; width: 100%; }
.ck__btn { flex: 1 1 0; min-width: 0; }

/* Configurar: «claramente visible, pero no necesariamente similar a los
   anteriores» (AEPD 3.1.2.2 c). Es la única de las tres que lleva luz. */
.ck__config {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 44px;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--amarillo);
  transition: gap var(--dur-ui) var(--ease);
}
.ck__config-linea {
  width: 26px;
  height: 1px;
  background: var(--amarillo);
  transition: width var(--dur-ui) var(--ease);
}
.ck__config:hover { gap: 18px; }
.ck__config:hover .ck__config-linea { width: 42px; }

/* --- CAPA 2 · el panel de finalidades -------------------------------- */
.ck-panel {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  padding: clamp(16px, 3vw, 40px) var(--gutter);
  /* 🔴 IMPRESCINDIBLE. La capa raíz `.ck` va con `pointer-events: none` desde
     que el aviso dejó de ser modal (9-sep-2026), para que la web se pueda usar
     detrás de la tarjeta. Cada capa que SÍ tiene que recibir clics los
     recupera por su cuenta, y al panel se le olvidó: los clics lo atravesaban
     enteros y no se podía ni tocar un interruptor ni cerrarlo. Si algún día se
     añade una tercera capa dentro de `.ck`, lleva esta línea. */
  pointer-events: auto;
}
.ck-panel[hidden] { display: none; }

.ck-panel__caja {
  --ck-corte: clamp(22px, 3.4vw, 44px);
  position: relative;
  width: 100%;
  max-width: 720px;
  /* Medido a 1440x900: con 88dvh el contenido (808 px) se pasaba 16 px y la
     tercera finalidad quedaba cortada a media frase en una ventana donde
     sobraba sitio. Con esto y el `padding-block` de las filas un punto más
     corto, las tres entran enteras desde 900 px de alto para arriba; por
     debajo sigue habiendo scroll, que es lo correcto. */
  max-height: min(90dvh, 920px);
  display: flex;
  flex-direction: column;
  /* Mismo tratamiento que la tarjeta del aviso (9-sep-2026): el amarillo solo
     en el corte de la esquina y el gris de filete en los tres lados rectos.
     El tramo del degradado es mayor que en la tarjeta porque aquí el corte
     también lo es (hasta 44 px frente a 18). */
  background: linear-gradient(135deg, var(--amarillo) 0 76px, var(--linea) 76px);
  clip-path: polygon(var(--ck-corte) 0, 100% 0, 100% 100%, 0 100%, 0 var(--ck-corte));
  filter: drop-shadow(0 24px 60px rgb(0 0 0 / .5));
}
.ck-panel__caja::before {
  content: "";
  position: absolute;
  inset: 1px;
  background: var(--sup);
  clip-path: inherit;
}
.ck-panel__cab,
.ck-panel__cuerpo,
.ck-panel__pie { position: relative; }

.ck-panel__cab {
  padding: clamp(22px, 2.4vw, 30px) clamp(58px, 6vw, 78px)
           clamp(15px, 1.6vw, 19px) clamp(22px, 2.6vw, 36px);
  border-bottom: 1px solid var(--linea);
}
.ck-panel__t {
  margin-top: 12px;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1.45rem, 2.2vw, 2.1rem);
  line-height: var(--lh-rotulo);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--texto);
}
.ck-panel__x {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  font-size: 1.05rem;
  color: var(--apagado);
  transition: color var(--dur-ui) var(--ease);
}
.ck-panel__x:hover { color: var(--amarillo); }

.ck-panel__cuerpo {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-inline: clamp(22px, 2.6vw, 36px);
}

.ck-fin {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 14px clamp(20px, 2.4vw, 36px);
  padding-block: clamp(16px, 1.7vw, 21px);
  border-bottom: 1px solid var(--linea);
}
.ck-fin:last-child { border-bottom: 0; }
.ck-fin__t {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h4);
  line-height: 1.1;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--texto);
}
.ck-fin__d {
  margin-top: 9px;
  max-width: 47ch;   /* mismo motivo que arriba: 54ch se iban a 71 reales */
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--apagado);
}
.ck-fin__d code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .92em;
  color: var(--texto);
}
/* «Siempre activas»: un rótulo, no un interruptor apagado ni una casilla
   marcada. Una casilla premarcada aunque fuera de las exentas se lee como
   opción premarcada, y la AEPD las declara inadmisibles sin matices. */
.ck-fin__fija {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--amarillo);
  white-space: nowrap;
}

/* --- El interruptor -------------------------------------------------- */
.ck-sw {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  min-height: 44px;
  padding-block: 6px;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-kicker);
  letter-spacing: var(--ls-kicker);
  text-transform: uppercase;
  color: var(--apagado);
  white-space: nowrap;
  transition: color var(--dur-ui) var(--ease);
}
.ck-sw__pista {
  position: relative;
  flex: none;
  width: 56px;
  height: 28px;
  border: 1px solid var(--linea-alta);
  border-radius: var(--radio);
  background: rgb(var(--texto-rgb) / .06);
  transition: background var(--dur-ui) var(--ease), border-color var(--dur-ui) var(--ease);
}
.ck-sw__punto {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 1px;
  background: var(--apagado);
  transition: transform var(--dur-ui) var(--ease), background var(--dur-ui) var(--ease);
}
.ck-sw[aria-checked="true"] { color: var(--amarillo); }
.ck-sw[aria-checked="true"] .ck-sw__pista {
  border-color: var(--amarillo);
  background: rgb(var(--am-rgb) / .14);
}
.ck-sw[aria-checked="true"] .ck-sw__punto {
  background: var(--amarillo);
  transform: translateX(28px);
}
.ck-sw:hover .ck-sw__pista { border-color: var(--amarillo); }

/* --- Pie del panel --------------------------------------------------- */
/* Rechazar todo y Aceptar todo comparten fila y ancho: la misma paridad que
   en la primera capa. Guardar selección ocupa la fila entera por debajo —es
   la acción neutra, la que hace lo que digan los interruptores— y por eso
   puede llevar el peso de la posición sin empujar hacia el «sí». */
.ck-panel__pie {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  padding: clamp(18px, 2vw, 26px) clamp(22px, 2.6vw, 36px)
           calc(env(safe-area-inset-bottom, 0px) + clamp(18px, 2vw, 26px));
  border-top: 1px solid var(--linea);
}
.ck__btn--guardar { grid-column: 1 / -1; }

/* --- El disparador permanente del pie -------------------------------- */
/* Lo inyecta cookies.js dentro de `.pie__legales` si el bloque de textos
   legales no ha puesto ya el suyo. Tiene que ser indistinguible de sus
   vecinos: es un enlace legal más. */
.pie__legales .pie__ck {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  /* El reset de §00 le quita el borde y el fondo al <button> pero NO el
     padding, y Chrome le pone 1px/6px: el hueco con «Política de cookies»
     salía 12 px más ancho que el de los demás enlaces legales. */
  padding: 0;
  font-family: var(--lectura);
  font-size: var(--fs-pie-micro);
  color: var(--apagado);
  transition: color var(--dur-ui) var(--ease);
}
.pie__legales .pie__ck:hover { color: var(--amarillo); }

/* --- Anchos ---------------------------------------------------------- */
/* El banner deja de ser dos columnas cuando la de texto baja de una medida
   decente. Medido: por debajo de 1020 px la columna de botones se comía el
   párrafo y las líneas caían a 30 caracteres. */
@media (max-width: 700px) {
  /* El interruptor baja debajo de su texto: a esta anchura, en dos columnas,
     la descripción se quedaba en 128 px de ancho. */
  .ck-fin { grid-template-columns: 1fr; gap: 12px; }
  .ck-fin__fija,
  .ck-sw { justify-content: flex-start; }
}
@media (max-width: 440px) {
  .ck-panel__pie { grid-template-columns: 1fr; }
}

/* --- Movimiento reducido --------------------------------------------- */
/* §27 ya anula toda transición con `!important`, pero el banner aparece de
   cero opacidad: si algún día se relaja aquella regla, esto garantiza que se
   ve igual y quieto en vez de quedarse invisible. */
@media (prefers-reduced-motion: reduce) {
  .ck { transition: none; }
  .ck__caja { transform: none; transition: none; }
}

/* --- Impresión ------------------------------------------------------- */
@media print {
  .ck { display: none !important; }
  .pie__legales .pie__ck { display: none; }
}


/* ============================================================
   28 · IMPRESIÓN — mínimo decente
   ============================================================ */

/* Papel blanco. `body { color:#000 }` NO basta: los descendientes llevan su
   propio color y en el PDF real quedaban 27 elementos de texto claro en
   contacto y 53 en presupuesto —el teléfono «971 270 687» y «info@dyreco.com»
   en AMARILLO sobre blanco, que es la conversión de la página—, los H1 con la
   `text-shadow` del carbón convertida en un borrón gris y el enlace de saltar
   impreso como una caja.
   Las dos páginas que alguien imprime son justo esas: las tres direcciones con
   su NAP y el resguardo del presupuesto. La hoja va aquí, en la compartida, y
   no por página. */
@media print {

  /* 1 · Red de seguridad: todo el texto a negro y nada de efectos de pantalla.
     Un `!important` general es lo correcto aquí: hay 11 hojas tiñendo. */
  *, *::before, *::after {
    color: #000 !important;
    background: transparent !important;
    text-shadow: none !important;
    box-shadow: none !important;
    filter: none !important;
    -webkit-mask-image: none !important;
            mask-image: none !important;
    animation: none !important;
    transition: none !important;
  }

  body { background: #fff; color: #000; }
  html { scroll-behavior: auto; }

  /* 2 · Lo que en papel no significa nada. */
  .cab, .panel, .saltar,
  .persiana__indice, .persiana__progreso,
  .persiana__barrido, .persiana__ayuda, .hero__scrim,
  .cab__cta, .galh__riel, .fichas__ayuda, .fichas__flechas,
  .fichas__f, .panelv__pasos, .pest__tabs { display: none !important; }

  /* 3 · El amarillo era jerarquía, no adorno: en papel la lleva el peso.
     (El color ya lo ha puesto a negro la red de seguridad de arriba.) */
  .pie__tel, .most__tel, .hero__div-n, .kicker, .pie__ct,
  .sector__n, .trab__cat { font-weight: 800; }

  /* 4 · Los mecanismos se despliegan: en papel no hay pestañas ni carruseles
     que pulsar, así que se imprime TODO el contenido, no solo lo activo. */
  .pest__paneles { display: block; }
  .pest__panel {
    grid-area: auto;
    clip-path: none !important;
    z-index: auto;
    margin-bottom: 18pt;
  }
  .panelv__ventana { position: static; overflow: visible; min-height: 0; }
  .panelv__cita { position: static; transform: none !important; margin-bottom: 12pt; }
  .persiana__slide { position: static; visibility: visible !important; }
  .persiana__slide:not(.is-activa) { display: none; }
  .galh, .galh__pin { height: auto !important; position: static; overflow: visible; }
  .galh__carro { display: block; transform: none !important; }
  .fichas__pista { display: block; overflow: visible; }
  .ficha { opacity: 1 !important; filter: none !important; transform: none !important; }
  [data-reveal], [data-filete] { opacity: 1 !important; transform: none !important; }

  /* 5 · Un enlace impreso sin su destino no es un enlace. Solo los externos y
     los de acción: los internos de la maqueta no llevan URL útil en papel. */
  a[href^="http"]::after,
  a[href^="tel:"]::after,
  a[href^="mailto:"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    word-break: break-all;
  }

  /* 6 · Nada que se pueda partir a la mitad. */
  .tda, .hito, .fpaso, .resena, .ficha, .sector, .trab,
  figure, blockquote { break-inside: avoid; }
  h1, h2, .h2, h3 { break-after: avoid; }
}


/* ============================================================
   29 · NOMBRES RESERVADOS POR EL SISTEMA COMPARTIDO
   ------------------------------------------------------------
   Esta hoja, tokens.css y js/main.js los cargan las 8 páginas. Todo
   nombre de aquí abajo es del SISTEMA: una hoja de página
   (css/p-<pagina>.css) puede AJUSTARLO en su contexto —
   `.trabajos .galh__carro { … }` está bien— pero no puede
   REDEFINIRLO ni reutilizarlo para otro componente.

   Por qué está escrito esto. `sec--cortina` llegó a existir con DOS
   implementaciones distintas: en p-empresa.css un telón `::after` que
   sube y desplaza a los hijos con `overflow: clip`, y en p-contacto.css
   unas capas `.cortina__fondo` / `.cortina__luz`. No reventaba solo
   porque cada una vivía en su hoja y nunca coinciden en la misma
   página; el día que alguien junte dos hojas, la que cargue después
   gana y la otra deja de existir sin un solo error en consola.
   La de contacto se renombró a `sec--compuerta-contacto` y el 8-sep-2026 se
   retiró entera: esa página usa ya la cota compartida.

   REGLA para una página nueva: si el componente es SUYO, el nombre
   lleva su página dentro (`.compuerta-contacto`, `.cruce-ferreteria`).
   Si el componente va a repetirse en varias, sube a esta hoja. Lo que
   no vale es inventar un nombre corto y genérico en una hoja de página
   y confiar en que nadie más lo piense.

   ⚠️ ESTE REGISTRO SE REGENERA, NO SE ESCRIBE A MANO. Estuvo desfasado y
   daba respuestas equivocadas: el apartado B declaraba tres `sec--` ocupados
   cuando había nueve, listaba uno (`sec--cortina`) que ya no existe, y el
   apartado A reservaba cinco familias muertas (`rej12`, `filete`, `tec`,
   `icono`, `persona`). Antes de tocarlo, saca la foto real:

     # A · familias vivas de la hoja compartida
     grep -ohE '\.[a-z][a-z0-9-]*' css/estilos.css | sed 's/__.*//;s/--.*//' | sort -u
     # B · todos los `sec--` del proyecto
     grep -ohE '\.sec--[a-z0-9-]+' css/*.css | sort -u
     # C · todos los estados
     grep -ohE '\.is-[a-z0-9-]+' css/*.css | sort -u
     # y para un nombre concreto, antes de bautizarlo:
     grep -n '\.<nombre>' css/estilos.css css/tokens.css css/p-*.css

   ------------------------------------------------------------
   A · FAMILIAS DE BLOQUE  (reservan también sus `__elemento` y
       `--modificador`: `pie` reserva `pie__col`, `pie__legales`…)
       Foto de ago-2026, sacada con el grep de arriba:

     banda · btn · cab · cabecera-sec · campo · cierre · confianza ·
     cont · div · divisiones · enlace-flecha · enlace-pendiente ·
     enlace-sub · equipo · faq · ficha · fichas · fila-botones · galh ·
     h2 · hero · integral · kicker · marcas · marquee · media · num ·
     odiv · otras · panel · panelv · paso · persiana · perso · pest ·
     pie · proceso · punto · resena · resenas · saltar · sector ·
     sectores · tienda · tiendas · trab · trabajos · visualmente-oculto

     Añadidos 5-sep-2026 al subir componentes a esta hoja, con el grep
     de arriba pasado antes de bautizar: `hero-div` y `rail`/`asesor`
     (§30, §33), `ccta` (§31), `destacado` (§32), `sec-cota` (§34) y
     `otras`/`odiv` (§35, el bloque de venta cruzada entre divisiones).

     Añadido 8-sep-2026: `ck` (§36, el aviso de cookies — reserva también
     `ck-panel`, `ck-sw`, `ck-fin` y `pie__ck`, el disparador que este mismo
     JS inyecta en el pie de las trece páginas).

   A-bis · FAMILIAS QUE SON DE UNA HOJA DE PÁGINA, NO DE ESTA
       No están en estilos.css, así que no las reserva el sistema; se
       apuntan aquí para que nadie las reutilice creyéndolas libres.
       Foto del 8-sep-2026, con el grep de arriba y descontando las
       apariciones que solo estaban dentro de comentarios:

         p-legal.css     legal · lcab · lidx · lficha · ltab · lnota · lpend
                         (aviso-legal.html · privacidad.html · cookies.html)
         p-blog.css      blog · blogcab · blog-cruce · nota   (blog.html)
         p-articulo.css  art · sumario                        (blog-*.html)

       ⚠️ `nota` es el nombre corto y genérico contra el que avisa la nota
       de arriba, y está en una hoja de página. Hoy no choca —solo lo usa
       blog.html y ningún documento carga p-blog.css junto a otra hoja que
       lo declare—, pero es el candidato número uno a colisión el día que
       alguien quiera una «nota» en otra página. Si hace falta ahí, que se
       llame `nota-<pagina>`; el de blog no se renombra por una colisión
       hipotética (93 reglas y sesenta nodos), pero queda advertido.

     Añadidos 8-sep-2026 con el consentimiento de cookies (§36), grep
     pasado antes: las cuatro estaban libres en las 11 hojas.
       ck        · la capa entera y la primera capa (banner): `ck__velo`,
                   `ck__caja`, `ck__int`, `ck__kicker`, `ck__titulo`,
                   `ck__texto`, `ck__mas`, `ck__dice`, `ck__elige`, `ck__acciones`,
                   `ck__btn` (+`--guardar`), `ck__config`, `ck__config-linea`
       ck-panel  · la segunda capa: `ck-panel__caja`, `__cab`, `__t`, `__x`,
                   `__cuerpo`, `__pie`
       ck-fin    · una finalidad dentro del panel: `ck-fin__t`, `__d`, `__fija`
       ck-sw     · el interruptor: `ck-sw__pista`, `ck-sw__punto`
     Y un elemento nuevo de una familia que ya existía:
       pie__ck   · el disparador permanente que cookies.js mete en
                   `.pie__legales` para poder cambiar la decisión.

     Ya NO están (borradas ago-2026 por no casar con nada en ninguna de
     las 8 páginas): `filete`, `icono`, `persona`, `rej12`, `tec`. Si
     hace falta alguno de esos nombres, está libre.

   B · PREFIJO `sec--`  → transición de entrada de una sección (§25)
       El repertorio compartido se quedó en UNO: `[data-filete]`.
       Los CINCO vivos son de página, uno por página, cada uno en su hoja:

         sec--cinta                p-rotulacion.css   · rotulacion.html
         sec--porton               p-empresa.css      · empresa.html
         sec--puntada              p-uniformidad.css  · uniformidad.html
         sec--regla                p-ferreteria.css   · ferreteria.html
         sec--telon                p-presupuesto.css  · presupuesto.html

       (index.html usa `[data-filete]`; maquinaria.html no tiene ninguna; y
       contacto.html usa desde el 8-sep-2026 la cota compartida `sec-cota`,
       al retirarse su compuerta — ver el aviso al principio de p-contacto.css.)
       LIBRES y sin dueño: cualquier otro nombre.
       BORRADOS: `sec--diagonal` y `sec--cambio` (declarados y sin uso),
       `sec--cortina` (renombrado a `sec--porton` en Empresa).

       REGLA — y las excepciones que hay, anotadas. La regla escrita era
       `sec--<algo>-<pagina>` y solo la cumple `sec--compuerta-contacto`.
       No se renombran los otros cuatro: cada uno vive en la hoja de UNA
       página, no hay dos que coincidan nunca en el mismo documento y la
       tabla de arriba dice de quién es cada uno. Lo que no puede volver a
       pasar es que el registro mienta. Para uno NUEVO sigue valiendo el
       sufijo de página, que es lo que evita el choque de entrada.

   C · PREFIJO `is-`  → estados, los pone y los quita el JS
       Del sistema (main.js + esta hoja):
     is-abierto · is-activa · is-active · is-activo · is-arrastrando ·
     is-centro · is-fuera · is-in · is-pasando ·
     is-pausada · is-saliendo · is-solida · is-usada
       De página (los escribe un js/p-*.js; no reutilizar para otra cosa):
     is-armado · is-entrando · is-entrando--atras · is-enviando ·
     is-hecho · is-luz · is-oculta · is-pasado · is-revision · is-visto

   D · CLASES EN <html> / <body>
     js                 ← script EN LÍNEA del <head>. Solo dice «hay JS».
     reveals-sellados   ← script en línea, a los 2.500 ms. Caduca el
                          estado oculto de los reveals pase lo que pase.
     js-ok              ← lo escribe MAIN.JS al final de arranca(), y solo
                          él. Dice «el vocabulario de motion está montado».
                          ⚠️ Todo lo que esconda algo porque «ya lo
                          enseñará el JS» tiene que colgar de `js-ok`, no
                          de `js`: con main.js bloqueado la clase `js`
                          está puesta igual y la promesa no la cumple
                          nadie (así se quedó la cabecera sin navegación y
                          el equipo sin puestos). El patrón es
                          `html.reveals-sellados:not(.js-ok) …`, que
                          espera al sello para no parpadear en la carga
                          normal.
     sin-scroll         ← main.js, con el menú abierto.

   E · HOOKS `data-` que lee main.js  — poner uno de estos en un
       elemento LO ENGANCHA a un mecanismo compartido, con o sin
       intención:
     [data-reveal] · [data-filete] · [data-contador] (+ data-hasta,
     data-pre, data-post, data-miles) · [data-parallax] ·
     [data-persiana] (+ data-l) · [data-fichas] (+ data-fichas-ir) ·
     [data-galh] · [data-panelv] · [data-pest]

   F · MARCAS `data-dy-*` / `dataset.dy*`  — main.js las escribe para
       no montar dos veces el mismo mecanismo, y esta hoja las lee para
       el fallback sin JS. No se ponen a mano ni se limpian:
     dyRevelado · dyContado · dyParallax · dyPersiana · dyFichas ·
     dyGalh · dyPanelv · dyPest

   G · GLOBAL DE JS
     window.Dyreco  →  reduce · alScroll · revelar · contadores ·
     parallax · persiana · fichas · galh · galhProgreso · panelv ·
     pestanas · init

   H · VARIABLES DE AJUSTE que expone un mecanismo compartido — se
       declaran en el contenedor desde una hoja de página, NO se
       redefinen aquí:
     --pest-tab-min   (§24.1) ancho mínimo de una pestaña cuando la tira
                      envuelve en móvil. `100%` = lista vertical.
                      La usa `.ufaq__tabs` en p-uniformidad.css.
     --ficha-an       (§21) ancho de la ficha del carril arrastrable.
     --intervalo, --dur-lama, --kb   (§06) tiempos de la persiana.

   ------------------------------------------------------------
   I · YA COGIDOS POR UNA PÁGINA (no son del sistema, pero están
       ocupados: una página nueva que los use choca con la existente).
       Y los cuatro primeros ya están repetidos entre hojas de página
       con implementaciones distintas — misma bomba que `sec--cortina`,
       todavía sin desactivar:

     cruce, cruce__*   presupuesto  (ferretería, maquinaria, uniformidad y
                       rotulación lo soltaron el 5-sep-2026 al subir su
                       bloque de venta cruzada a §35 `.otras`/`.odiv`)
     hito, hito__*     empresa · presupuesto
     most, most__foto  contacto · ferretería
     aviso             maquinaria · presupuesto
     campo--sel        contacto · presupuesto

     ⚠️ Tres de esos nombres de página los CITA la hoja de impresión
     compartida (§28, `break-inside: avoid`): `.tda` (las 3 tiendas de
     contacto), `.hito` y `.fpaso`. Si se renombra alguno, hay que tocar
     también §28 o el bloque volverá a partirse entre dos A4.
   ============================================================ */
