/* Canal Cero — Componentes del rediseño growth.
   Clases reutilizables de interacción. Se carga DESPUÉS de cc-tokens.css
   (para resolver las variables --cc-*) y de style.css. */

/* ── Hover de cards: elevación + borde de color ───────────────────────
   Uso: añadir la clase al bloque-card en Bricks (campo "CSS classes").
     .cc-card-hover-violet → cards de servicios (S5): borde violeta.
     .cc-card-hover-green  → cards de casos (S8): borde verde.
   NOTA de especificidad: Bricks emite el borde base de cada card como
   regla de ID (#brxe-<id>), que gana a una clase; por eso border-color
   en :hover lleva !important (único modo de que una utilidad reutilizable
   pise el borde por-elemento). transform y box-shadow no colisionan
   (las cards no los definen), así que no necesitan !important. */

.cc-card-hover-violet,
.cc-card-hover-green {
  transition:
    transform    var(--cc-dur-base) var(--cc-ease-out),
    border-color var(--cc-dur-base) var(--cc-ease-out),
    box-shadow   var(--cc-dur-base) var(--cc-ease-out);
}

/* ⚠️ 8.45 — Esta regla mandaba las tarjetas al violeta genérico AL PASAR EL PUNTERO,
   borrando la unidad que la tarjeta acababa de afirmar. Es el mismo bug que 8.35 arregló
   en la home… pero solo en la home: `home.json` no lleva esta clase y
   `page-servicios.json` la lleva **8 veces**, así que 7 de las 10 tarjetas de
   `/servicios/` seguían saltando al violeta y las 3 de Canal.Data —que no la llevan— sí
   afirmaban su color. O sea la MISMA página se comportaba de dos maneras.

   El arreglo es el de la regla hermana de abajo: `--cc-unidad` con el violeta como
   FALLBACK. Dentro de una isla la tarjeta afirma su unidad; fuera, todo queda como
   estaba, así que las tarjetas que no son de unidad no se enteran. Se arregla en el CSS
   y no quitando la clase del JSON a propósito: la clase también aporta el `transform` y
   la sombra, y hay 8 apariciones que habría que tocar a mano. */
.cc-card-hover-violet:hover {
  transform: translateY(-6px);
  border-color: var(--cc-unidad, var(--cc-violet-600)) !important;
  /* Sombra propia y no `--cc-shadow-card-dark`: ese token desplaza 14px hacia abajo, que
     con la caja recta deja un borde oscuro asomando bajo la tarjeta. Aquí basta un halo
     casi centrado — profundidad sin segunda caja. */
  box-shadow: 0 6px 22px -12px rgba(0, 0, 0, 0.7);
}

/* ⚠️ El borde del hover sale de `--cc-unidad` con el verde como FALLBACK, y eso es
   un arreglo del mismo tipo que el de 8.35: desde el 2026-08-06 las tres tarjetas de
   caso son islas de unidad (el caso de Copec lo resolvió Canal.Media, el de retail
   Canal.Commerce, el de consumo masivo Canal.Data), así que un `--cc-fg-accent` fijo
   mandaba las tres al mismo verde **justo al pasar el puntero** — o sea el hover
   borraba la identidad que la tarjeta acababa de afirmar. Con el fallback, las
   tarjetas que NO son de unidad se comportan igual que antes. */
.cc-card-hover-green:hover {
  transform: translateY(-6px);
  border-color: var(--cc-unidad, var(--cc-fg-accent)) !important;
  /* Sombra propia y no `--cc-shadow-card-dark`: ese token desplaza 14px hacia abajo, que
     con la caja recta deja un borde oscuro asomando bajo la tarjeta. Aquí basta un halo
     casi centrado — profundidad sin segunda caja. */
  box-shadow: 0 6px 22px -12px rgba(0, 0, 0, 0.7);
}

/* Accesibilidad: sin desplazamiento si el usuario reduce el movimiento.
   Sin animar, pero manteniendo feedback: la sombra base
   (--cc-shadow-card-dark) es negra y casi no se ve sobre #0A0A0F, así que
   como fallback estático damos un realce con el color de acento de la card
   (ring + glow), visible sin movimiento. */
@media (prefers-reduced-motion: reduce) {
  .cc-card-hover-violet:hover,
  .cc-card-hover-green:hover { transform: none; }
  /* El anillo estático sigue al borde: si el hover afirma la unidad, el fallback sin
     movimiento tiene que afirmarla también, o quien reduce el movimiento ve el violeta
     genérico que 8.45 vino a quitar. `color-mix` en vez de un rgba fijo por lo mismo. */
  .cc-card-hover-violet:hover {
    box-shadow:
      0 0 0 1px var(--cc-unidad, var(--cc-violet-600)),
      0 0 26px -6px color-mix(in srgb, var(--cc-unidad, var(--cc-violet-600)) 55%, transparent);
  }
  .cc-card-hover-green:hover {
    box-shadow: 0 0 0 1px var(--cc-green-500), 0 0 26px -6px rgba(61, 237, 180, 0.45);
  }
}

/* ── Blog · nota (single post) — rediseño dark 2026-07-24 ─────────────
   Estructura y componentes del mockup "Blog Canal Cero" (pieza 1b, dark),
   RE-MAPEADOS a los tokens canónicos del home — no a los hex viejos del
   mockup: verde #3DEDB4 en vez de #0BE5A2, fondo #0A0A0F en vez de
   #191919, superficie #14121F en vez de #1E1E1E — para que el blog calce
   con la portada y no haya "ensalada de diseño".

   SEO: esto es SOLO presentación. No se toca el contenido, ni el H1
   (post-title), ni los H2/H3 internos del artículo (post-content); el TOC
   se DERIVA de esos encabezados. Los adornos (punto de marca, barras) son
   pseudo-elementos → cero texto extra en el DOM ni jerarquía de headings
   alterada.

   Uso en Bricks: asignar las clases por el campo "CSS classes"
   (_cssClasses en el JSON del template). Gotcha de especificidad
   ID-vs-clase (ver bloque de cards arriba): donde la regla por-elemento
   (#brxe-<id>) pise a la clase, va !important. */

/* Lienzo de la nota */
.cc-post { background: var(--cc-bg); }

/* Grilla: columna de lectura (700px) + riel del TOC (280px) */
.cc-post__grid {
  display: grid;
  /* minmax(0,…) deja que la columna de lectura ceda ancho en vez de forzar
     overflow horizontal cuando un hijo (tabla/código) es más ancho. */
  grid-template-columns: minmax(0, var(--cc-measure)) var(--cc-toc-rail);
  gap: var(--cc-read-gap);
  justify-content: center;
  /* width:100% ancla el container de Bricks (que trae width:1100px por
     defecto) al ancho del padre → 390 en mobile, tope 1240 en desktop.
     Sin esto, en un section flex el container no se ciñe al viewport. */
  width: 100%;
  max-width: 100%;
}

/* Título (H1) + punto verde de marca al final */
.cc-post__title {
  font-family: var(--cc-font-display);
  font-size: clamp(27px, 3.1vw, 44px);
  font-weight: var(--cc-fw-extrabold);
  line-height: var(--cc-lh-snug);
  letter-spacing: var(--cc-tracking-tight);
  color: var(--cc-fg-strong);
  text-wrap: balance;
}
.cc-post__title::after {
  content: "";
  display: inline-block;
  width: 0.25em; height: 0.25em;
  border-radius: var(--cc-radius-pill);
  /* Token semántico y no el verde de marca: en modo claro este punto sobre blanco
     daba 1.50:1 y se leía como una mancha pálida (medido el 2026-08-03). */
  background: var(--cc-fg-accent);
  margin-left: 0.18em;
}

/* Cuerpo del artículo (HTML de Gutenberg dentro de post-content) */
.cc-post__content { color: var(--cc-fg-read); min-width: 0; overflow-wrap: break-word; }
/* El contenido no debe desbordar la columna: media/tablas/código acotados,
   y las tablas anchas hacen scroll dentro de su propia caja (nunca ensanchan
   la página → sin overflow horizontal en mobile). */
.cc-post__content img,
.cc-post__content table,
.cc-post__content pre { max-width: 100%; }
.cc-post__content table { display: block; overflow-x: auto; }
.cc-post__content p {
  font-size: var(--cc-fs-base);   /* 18px */
  line-height: 1.75;
  color: var(--cc-fg-read);
  margin: 0 0 24px;
}
.cc-post__content h2 {
  font-size: 28px;
  font-weight: var(--cc-fw-extrabold);
  line-height: var(--cc-lh-normal);
  color: var(--cc-fg-strong);
  margin: 40px 0 16px;
  padding-left: 19px;
  border-left: 5px solid var(--cc-green-500);
}
.cc-post__content h3 {
  font-size: 21px;
  font-weight: var(--cc-fw-bold);
  color: var(--cc-fg-strong);
  margin: 32px 0 12px;
}
.cc-post__content a { color: var(--cc-green-500); text-decoration: none; }
.cc-post__content a:hover { text-decoration: underline; }
.cc-post__content strong { color: var(--cc-fg-strong); }
.cc-post__content img { border-radius: var(--cc-radius-lg); }
/* código inline (no dentro de <pre>) */
.cc-post__content :not(pre) > code {
  font-family: var(--cc-mono);
  font-size: 0.85em;
  padding: 2px 7px;
  border-radius: var(--cc-radius-sm);
  background: rgba(255,255,255,0.08);
  color: var(--cc-green-200);
}

/* Bloque de código: la barra ("SQL" + Copiar) la inyecta cc-blog.js */
.cc-code {
  border-radius: var(--cc-radius-md);
  overflow: hidden;
  margin: 20px 0 44px;
  border: 1px solid var(--cc-border-on-dark);
}
.cc-code__bar {
  background: var(--cc-bg-surface);
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 20px;
}
.cc-code__lang {
  color: var(--cc-fg-muted); font-size: var(--cc-fs-xxs);
  font-weight: var(--cc-fw-bold); letter-spacing: 0.1em; text-transform: uppercase;
}
.cc-code__copy {
  color: var(--cc-green-500); font-size: 13px; font-weight: var(--cc-fw-semibold);
  cursor: pointer; background: none; border: none; font-family: var(--cc-font-sans);
}
.cc-code__copy:hover { color: var(--cc-green-400); }
.cc-post__content pre,
.cc-code pre {
  margin: 0; background: var(--cc-bg-deep); color: var(--cc-green-200);
  padding: 22px 24px; font-family: var(--cc-mono);
  font-size: 14px; line-height: 1.7; overflow-x: auto; white-space: pre;
}

/* Chip de categoría (junto al breadcrumb "Blog /") */
.cc-chip {
  display: inline-block; padding: 4px 12px; border-radius: var(--cc-radius-pill);
  font-size: var(--cc-fs-xxs); font-weight: var(--cc-fw-bold);
  letter-spacing: 0.04em; text-transform: uppercase;
  border: 1px solid rgba(61,237,180,0.5); color: var(--cc-green-500);
}

/* Caja "En resumen" al inicio de la nota */
.cc-resumen {
  background: var(--cc-bg-surface); border: 1px solid var(--cc-border-on-dark);
  border-radius: var(--cc-radius-md); padding: 28px 32px; margin: 0 0 40px;
}
.cc-resumen li {
  list-style: none; position: relative; padding-left: 17px; margin-bottom: 10px;
  color: var(--cc-fg-read); font-size: var(--cc-fs-sm); line-height: 1.6;
}
.cc-resumen li::before {
  content: ""; position: absolute; left: 0; top: 4px;
  width: 5px; height: 18px; background: var(--cc-green-500);
}

/* CTA de cierre (violeta) */
.cc-cta {
  background: var(--cc-violet-ink); border-radius: var(--cc-radius-lg);
  padding: 40px 44px; display: flex; align-items: center;
  justify-content: space-between; gap: 32px; margin: 12px 0 56px;
}
.cc-cta__title { color: var(--cc-fg-strong); font-size: 26px; font-weight: var(--cc-fw-extrabold); line-height: 1.2; }
.cc-cta__btn {
  flex: none; background: var(--cc-green-500); color: var(--cc-ink-900);
  font-weight: var(--cc-fw-bold); font-size: var(--cc-fs-sm);
  padding: 14px 30px; border-radius: var(--cc-radius-pill); text-decoration: none;
}
.cc-cta__btn:hover { background: var(--cc-green-400); }

/* Índice de contenidos (TOC): sticky al costado en desktop */
.cc-toc { position: sticky; top: 32px; border-left: 1px solid var(--cc-rule-on-dark); padding-left: 24px; }
.cc-toc__title {
  color: var(--cc-fg-strong); font-size: 13px; font-weight: var(--cc-fw-extrabold);
  letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 16px;
}
.cc-toc a {
  display: block; color: var(--cc-fg-muted);
  font-size: var(--cc-fs-xs); font-weight: var(--cc-fw-medium); line-height: 1.4;
  padding: 6px 0 6px 23px; margin-left: -25px;
  border-left: 2px solid transparent; text-decoration: none;
}
.cc-toc a:hover { color: var(--cc-fg-strong); }
.cc-toc a.is-active { color: var(--cc-fg-strong); font-weight: var(--cc-fw-bold); border-left-color: var(--cc-green-500); }

/* "Te puede interesar": cards de relacionados al fondo, en grilla.
   FIX (2026-07-24) — este bloque estaba ROTO y era CSS muerto:
   se habia escrito contra un markup inventado (.cc-card / .cc-card__body /
   .cc-card__date / .cc-card__title) que el elemento `related-posts` de Bricks
   NUNCA emite, y que el cc-blog.js tampoco inyecta. El markup real es:
     .cc-related > ul.related-posts > li.repeater-item
        > figure > a > img.image
        > div.post-content > p.dynamic (fecha) + h3.dynamic > a (titulo)
   Ademas el `display:grid` estaba en el WRAPPER, cuyo unico hijo es el <ul>:
   el ul quedaba metido en la 1a de 2 columnas -> la seccion medía 338px dentro
   de una columna de lectura de 700px, con cards de 157px y sin estilo alguno.
   La grilla de 2 columnas y el fondo de `.post-content` ya los aporta Bricks
   desde las settings del elemento (`columns`, `gap`, `contentBackground`), asi
   que aqui solo se agrega lo que Bricks no da: hairline, radio y hover. */
.cc-related .repeater-item {
  background: var(--cc-bg-surface);
  border: 1px solid var(--cc-border-on-dark);
  border-radius: var(--cc-radius-md);
  overflow: hidden;
  transition: border-color var(--cc-dur-fast) var(--cc-ease-out);
}
.cc-related .repeater-item:hover { border-color: color-mix(in srgb, var(--cc-fg-accent) 40%, transparent); }
/* imagen al tope, alto uniforme para que las 2 cards queden parejas */
.cc-related .repeater-item figure { margin: 0; }
.cc-related .repeater-item img { width: 100%; height: 160px; object-fit: cover; display: block; }

/* Mobile: una sola columna, TOC colapsado arriba, tipografía a escala */
@media (max-width: 991px) {
  .cc-post__grid { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .cc-post__content p { font-size: 16.5px; line-height: 1.7; }
  .cc-post__content h2 { font-size: 21px; border-left-width: 4px; padding-left: 15px; }
  .cc-post__content img { border-radius: var(--cc-radius-md); }
  .cc-code pre, .cc-post__content pre { font-size: 12.5px; padding: 16px 18px; }
  .cc-toc { position: static; border-left: 0; padding: 14px 18px; border: 1px solid var(--cc-rule-on-dark); border-radius: var(--cc-radius-md); margin-bottom: 24px; }
  .cc-cta { flex-direction: column; align-items: flex-start; padding: 26px 24px; }
}
/* Mobile: relacionados a 1 columna. NO se hace aqui con `.cc-related ul`
   porque Bricks emite la grilla como regla de ID (`#brxe-<id> ul{...}`) y una
   clase no le gana: se resuelve con la setting responsive del propio elemento
   (`columns:mobile_portrait` en single-post.json), que Bricks emite dentro de
   su media query. Ver el gotcha de especificidad ID-vs-clase en CLAUDE.md. */

/* ── Blog · índice (elemento Posts de Bricks) — rediseño 2026-07-24 ────
   Da a las cards del índice el mismo lenguaje que las de la nota: superficie
   oscura, hairline, esquinas redondeadas y hover (elevación + borde verde).
   Se asigna con la clase .cc-postgrid al elemento Posts (campo "CSS classes").
   La asignación vive en la BD (la página /blog/ NO es un template versionado,
   ver §2), pero ESTE CSS sí se versiona. Markup de Bricks:
     ul.bricks-layout-wrapper > li.bricks-layout-item > .bricks-layout-inner
        > figure.image-wrapper + campos (fecha/título/ver-más). */

/* Aire arriba de la grilla para que el "lift" del hover no choque con el
   subtítulo/encabezado que está encima (fix del solape de la 1ª fila). */
.cc-postgrid { margin-top: 16px; }

/* ⚠️ FIX (2026-07-24) — la 1ª fila de cards se CORTABA al hacer hover.
   Causa: el CORE de Bricks trae `.brxe-posts{overflow:hidden}` (está en
   frontend.min.css, NO en el CSS de la página ni en una clase global), y el
   `translateY(-6px)` del hover saca la card fuera de la caja del contenedor
   → se recortaban esos 6px del borde superior en la primera fila.
   Se usa el selector doble `.brxe-posts.cc-postgrid` (0,2,0) para ganarle a
   `.brxe-posts` (0,1,0) por especificidad, sin depender del orden de carga
   ni de `!important`.
   `overflow: clip visible` = recorta en X (evita scroll horizontal lateral por
   la sombra, sobre todo en mobile) pero deja escapar el lift en Y. La
   declaración `visible` previa es el fallback para navegadores sin `clip`. */
.brxe-posts.cc-postgrid {
  overflow: visible;
  overflow: clip visible;
}

/* ⚠️ FIX (2026-07-24) — el H1 "Blog CC" + subtítulo quedaban PEGADOS al header.
   La sección de la página 28 no tiene padding vertical (settings vacíos), así
   que el H1 arrancaba exactamente en el borde inferior del header (85px).
   Se resuelve en CSS versionado (no en la BD) apuntando a la sección que
   contiene la grilla, así NO hay que re-asignar nada a mano al desplegar.
   Padding fluido, en la línea del ritmo vertical del home. */
body.blog .brxe-section:has(.cc-postgrid) {
  padding-top: clamp(48px, 6vw, 76px);
}

/* card = cada item del grid */
.cc-postgrid .bricks-layout-item {
  position: relative;                 /* + z-index en hover: la card elevada
                                         queda sobre sus vecinas, no debajo */
  background: var(--cc-bg-surface);
  border: 1px solid var(--cc-border-on-dark);
  border-radius: var(--cc-radius-md);
  overflow: hidden;
  transition:
    transform    var(--cc-dur-base) var(--cc-ease-out),
    border-color var(--cc-dur-base) var(--cc-ease-out),
    box-shadow   var(--cc-dur-base) var(--cc-ease-out);
}
.cc-postgrid .bricks-layout-item:hover {
  transform: translateY(-6px);
  border-color: var(--cc-fg-accent);
  box-shadow: var(--cc-shadow-card-dark);
  z-index: 2;
}
@media (prefers-reduced-motion: reduce) {
  .cc-postgrid .bricks-layout-item:hover {
    transform: none;
    box-shadow: 0 0 0 1px var(--cc-green-500), 0 0 26px -6px rgba(61, 237, 180, 0.45);
  }
}
/* La imagen ocupa la card de borde a borde hasta arriba (como el mockup):
   se anula el padding-top del inner y el margen de la figura. Las esquinas
   superiores redondeadas las da el overflow:hidden + border-radius de la card. */
.cc-postgrid .bricks-layout-inner { padding-top: 0 !important; }
.cc-postgrid .image-wrapper { margin: 0; }
/* inset horizontal solo para el texto (fecha/título/leer-más), no la imagen */
.cc-postgrid .bricks-layout-inner > :not(.image-wrapper) {
  padding-left: 18px; padding-right: 18px;
}
.cc-postgrid .bricks-layout-inner > :not(.image-wrapper):last-child {
  padding-bottom: 18px;
}

/* Grilla uniforme: cards de igual altura por fila (el contenido se
   distribuye; la imagen mantiene su proporción arriba). Sin "destacado"
   que estire a sus vecinos de fila. */
.cc-postgrid .bricks-layout-inner { height: 100%; display: flex; flex-direction: column; }

/* "Leer más" como link de texto (no píldora): verde + flecha */
.cc-postgrid .bricks-layout-inner a[href]:last-child {
  color: var(--cc-green-500);
  font-weight: var(--cc-fw-semibold);
}
.cc-postgrid .bricks-layout-inner a[href]:last-child:hover { color: var(--cc-green-400); }

/* ⚠️ FIX (2026-07-27) — la animación de entrada del índice del blog podía dejar
   la página EN BLANCO DE FORMA PERMANENTE.
   Antes vivía en un elemento `code` de Bricks (id `qghesi`) dentro del content
   de la página 28, con este mecanismo: en DOMContentLoaded ponía
   `opacity:0; transform:translateX(-50px)` INLINE en <main>, y delegaba el
   revert a requestAnimationFrame → setTimeout(50ms).
   Medido con Playwright antes de tocarlo:
     · carga normal ............ 324ms con el contenido invisible, en cada visita
     · prefers-reduced-motion .. IGNORADO, el slide ocurría igual
     · sin JavaScript .......... se veía bien (el script no corre, no aplica opacity:0)
     · rAF que no dispara ...... opacity:0 PERMANENTE = página en blanco
   El último caso es el grave y no es teórico: rAF no dispara en pestañas
   ocultas ni en renderers que no producen frames, así que abrir /blog/ en una
   pestaña de segundo plano (o rastrearla con un renderer sin frames) daba una
   página vacía. Y es una página indexada.
   Ahora es una @keyframes: el estado final es el NATURAL del elemento, así que
   nada puede dejarlo en opacity:0 — si el CSS no carga, si la animación no
   corre o si el JS falla, el contenido se ve. Es fail-safe por construcción.
   Se apunta a `body.blog #brx-content` (markup del theme, no un elemento de
   Bricks al que se le pueda asignar clase), en la misma línea que el fix del
   padding de arriba: el arreglo viaja en git y no suma una fila a la tabla de
   la sección 10 de CLAUDE.md.
   El mismo snippet está copiado en 11 páginas más y 4 plantillas de CPT: esas
   NO se migran a @keyframes (su content no está versionado), las cubre la red de
   seguridad de más abajo. Gotcha 8.11 de CLAUDE.md. */
@keyframes cc-entrada-lateral {
  from { opacity: 0; transform: translateX(-50px); }
  to   { opacity: 1; transform: none; }
}
/* SIN animation-fill-mode a propósito: `backwards`/`both` dejarían el elemento
   en el estado `from` (opacity:0) si la animación no llegara a correr. Sin
   fill-mode, fuera de la ejecución manda el estilo natural = visible. Y sin
   delay, así que no hay parpadeo al inicio. */
body.blog #brx-content {
  animation: cc-entrada-lateral var(--cc-dur-base) var(--cc-ease-out);
}
@media (prefers-reduced-motion: reduce) {
  body.blog #brx-content { animation: none; }
}

/* ⚠️ RED DE SEGURIDAD (2026-07-27) — neutraliza la misma animación por JS en las
   15 piezas del diseño VIEJO que todavía la traen (gotcha 8.11): 11 páginas
   publicadas (`/servicios/` y sus 4 hijas, `/portafolio/` y sus 4 sub-páginas,
   `/equipo/`) y 4 plantillas de CPT.
   Por qué acá y no en la BD: el content de esas páginas NO está versionado, así
   que un arreglo página por página se perdería con el próximo dump de prod. Esta
   regla viaja en git y se aplica sola al desplegar.
   Cómo funciona: `!important` de una hoja de autor le gana al style INLINE que
   pone el JS, así que el contenido nunca queda en `opacity:0`. Cuando el JS
   revierte (inline `opacity:1`), el selector deja de matchear y la regla se
   retira sin dejar rastro.
   Verificado el 2026-07-27 con `rAF` anulado: las 11 páginas quedaban EN BLANCO
   y con esta regla quedan visibles.
   Selector doble a propósito: `[style*="opacity: 0"]` a secas también matchea
   `opacity: 0.5` (comprobado), así que se exige además la firma `translateX(-50px)`,
   que es la de este snippet concreto y no puede dar falso positivo.
   EFECTO SECUNDARIO ACEPTADO: esas páginas pierden el slide de entrada. El estado
   final es idéntico; solo desaparece la animación, que el diseño nuevo no usa en
   ninguna parte. Decisión del equipo, 2026-07-27.
   Se puede borrar esta regla cuando las 15 piezas estén rediseñadas. */
main#brx-content[style*="opacity: 0"][style*="translateX(-50px)"] {
  opacity: 1 !important;
  transform: none !important;
}

/* ==========================================================================
   MOVIMIENTO DE LA HOME (2026-07-27)
   Por qué: la home tenía 11 secciones sin un solo scroll-reveal. Lo único que se
   movía era el crossfade del hero y el hover de las cards, y por eso se leía
   "sosa" aunque la composición estuviera bien.
   Fail-safe por diseño: SOLO se oculta bajo `html.cc-js`, clase que pone un
   script inline de <head> (functions.php → ccd_motion_head_guard) que antes
   verifica IntersectionObserver y prefers-reduced-motion. Sin JS no se oculta
   nada. Y `html.cc-motion-off` (timeout de 2.5s del mismo guard) revela todo si
   cc-motion.js no llegó a cargar. Ver gotcha 8.11.
   ========================================================================== */

/* --- Reveal individual ----------------------------------------------------
   PATRÓN CLAVE: se oculta con `:not(.cc-in)` y el estado revelado NO declara
   nada. Es deliberado. Si la regla revelada dijera `opacity:1`, estaría
   AFIRMANDO un estado final, y entonces le ganaría por especificidad a la
   opacidad de diseño que un elemento pudiera tener (pasó con la trust bar, que
   vive al 0.72 y quedaba forzada a 1). Al solo RETIRAR el ocultamiento, cada
   elemento vuelve a su valor natural, sea 1 o el que tenga.
   Es la misma idea que hace fail-safe a la animación del blog: no fijar un
   estado final, dejar que manden los valores naturales. */
html.cc-js .cc-reveal {
  transition:
    opacity   var(--cc-dur-reveal) var(--cc-ease-out),
    transform var(--cc-dur-reveal) var(--cc-ease-out);
}
html.cc-js .cc-reveal:not(.cc-in) {
  opacity: 0;
  transform: translateY(14px);
}

/* --- Reveal en cascada: la clase va en el CONTENEDOR, no en cada hijo ------
   Se observa el contenedor y sus hijos entran escalonados por nth-child. Así el
   JSON del template suma UNA clase por sección en vez de una por elemento, y el
   orden lo da el DOM sin tener que numerar a mano. */
/* ⚠️ EL ESCALONADO SE HACE CON `animation`, NO CON `transition`. Medido el
   2026-07-30: la versión anterior ponía los `transition-delay` en el estado
   OCULTO, y eso NO ESCALONA NADA. Una transición usa las propiedades del estado
   DESTINO, y al revelar el destino tiene delay 0:

     :not(.cc-in) → 0s 0.08s 0.16s 0.24s      (el estado del que se sale)
     .cc-in       → 0s 0s    0s    0s         (el que la transición usa)

   Los cuatro hijos arrancaban juntos. La ventana medida entre el primero y el
   último de un grupo era de 0 ms, en TODOS los grupos, desde que se construyó el
   sistema. El comentario que lo justificaba tenía razón en el problema —un
   `transition-delay` en el estado revelado se queda pegado y después se nota como
   lag en el hover— y su solución silenciaba el efecto entero.

   `animation` resuelve las dos cosas a la vez:
     · `animation-delay` sí se aplica cuando la animación corre, así que escalona;
     · terminada la animación no queda nada pegado, así que el hover no arrastra
       ningún retardo — que era el motivo original del cambio;
     · el keyframe declara solo `from`. Sin `to`, el elemento termina en SU valor
       natural, no en uno afirmado: es la regla de 8.12 y la que mantiene viva la
       trust bar a su `opacity: .72` en vez de forzarla a 1;
     · `backwards` mantiene el primer keyframe durante el retardo, así que no hay
       parpadeo entre que aparece `.cc-in` y arranca la animación.  */
@keyframes cc-entrar {
  from { opacity: 0; transform: translateY(16px); }
}

/* Solo oculta; el estado revelado no declara nada (ver la nota de arriba). */
html.cc-js .cc-stagger:not(.cc-in) > * {
  opacity: 0;
  transform: translateY(16px);
}
html.cc-js .cc-stagger.cc-in > * {
  animation: cc-entrar var(--cc-dur-reveal) var(--cc-ease-out) backwards;
}
/* ⚠️ LA COLA SE ACORTÓ A LA MITAD el 2026-08-17: los pasos eran de 80ms y el tope 480,
   o sea el último hijo terminaba 1.040ms después del disparo (560 de reveal + 480). Con la
   rueda del ratón eso va SIEMPRE por detrás del lector. Ahora el paso es de 50ms y el tope
   250, así que el último cierra a los 810ms. El escalonado se sigue leyendo —es lo que
   distingue una entrada de un encendido— pero deja de ser una espera. */
html.cc-js .cc-stagger.cc-in > *:nth-child(2)   { animation-delay:  50ms; }
html.cc-js .cc-stagger.cc-in > *:nth-child(3)   { animation-delay: 100ms; }
html.cc-js .cc-stagger.cc-in > *:nth-child(4)   { animation-delay: 150ms; }
html.cc-js .cc-stagger.cc-in > *:nth-child(5)   { animation-delay: 200ms; }
html.cc-js .cc-stagger.cc-in > *:nth-child(6)   { animation-delay: 250ms; }
html.cc-js .cc-stagger.cc-in > *:nth-child(n+7) { animation-delay: 250ms; }

/* --- La barra de acento vertical que crece ------------------------------- */
/* Es `blo037`, la barrita verde de 5x56px sobre el H2 de S4. Su _cssCustom es
   una regla de ID que define width/height/background/border-radius pero NO
   transform, así que esta clase gana sin pelear especificidad (gotcha 8.1). */
@keyframes cc-trazar-y { from { transform: scaleY(0); } }
@keyframes cc-trazar-x { from { transform: scaleX(0); } }

html.cc-js .cc-bar-draw { transform-origin: top center; }
html.cc-js .cc-bar-draw:not(.cc-in) { transform: scaleY(0); }
/* Es el PRIMER hijo de su contenedor, así que sin retardo se trazaba ANTES que el
   titular al que acompaña — un acento no puede llegar antes que aquello que
   acentúa. Con 320ms entra después de la bajada y se lee como lo que es: el
   remate de la sección, no su apertura. Va como `animation-delay` y no como
   `transition-delay` por lo explicado arriba: en una transición el retardo del
   estado que se abandona no se aplica. */
html.cc-js .cc-bar-draw.cc-in {
  animation: cc-trazar-y var(--cc-dur-reveal) var(--cc-ease-spring) 620ms backwards;
}

/* --- Reglas que se TRAZAN junto al dato que encabezan ---------------------
   Las tres columnas de la banda violeta llevaban un `border-top` estático: la
   línea ya estaba ahí cuando el número empezaba a contar, o sea eran dos cosas
   separadas ocurriendo cerca. Ahora la línea se traza mientras el número sube y
   las dos leen como un solo gesto.
   Va en un pseudo-elemento y no en el borde porque un `border-width` no se puede
   escalar: `transform` sobre el elemento entero deformaría también el texto.
   El estado oculto lo dispara el ANCESTRO (`.cc-stagger:not(.cc-in)`), no una
   clase propia: así no hace falta sumar entradas al observer y el trazo queda
   sincronizado con la entrada de su columna por construcción. */
.cc-rule-draw { position: relative; }
.cc-rule-draw::before {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  width: 100%;
  height: 3px;
  background: var(--cc-green-500);
  transform-origin: left center;
}
html.cc-js .cc-stagger:not(.cc-in) > .cc-rule-draw::before { transform: scaleX(0); }
html.cc-js .cc-stagger.cc-in > .cc-rule-draw::before {
  animation: cc-trazar-x var(--cc-dur-reveal) var(--cc-ease-out) 120ms backwards;
}
html.cc-js .cc-stagger.cc-in > .cc-rule-draw:nth-child(2)::before { animation-delay: 240ms; }
html.cc-js .cc-stagger.cc-in > .cc-rule-draw:nth-child(3)::before { animation-delay: 360ms; }

/* ⚠️ AQUÍ VIVÍA `.cc-rule-draw--lado` (la regla vertical de la tercera columna de la banda
   de Números) y se retiró el 2026-08-14 al quedarse sin elemento: la banda se reestructuró
   el 2026-08-13 —fuera el 17x, titular y cifra en la misma fila— y esa columna dejó de
   existir. Comprobado antes de borrar: cero usos en `bricks-templates/` y cero en TODAS las
   páginas de la BD. Se anota en vez de borrarse en silencio porque el gesto (una regla que
   se traza en otro eje para marcar "esto es la conclusión, no un dato") puede volver a
   hacer falta. */

/* --- RED DE SEGURIDAD: si cc-motion.js no cargó, revelar todo -------------
   La pone el guard de <head> a los 2.5s. `!important` porque tiene que ganarle a
   las reglas de arriba sin importar el orden. */
html.cc-motion-off .cc-reveal:not(.cc-in),
html.cc-motion-off .cc-stagger:not(.cc-in) > *,
html.cc-motion-off .cc-bar-draw:not(.cc-in) {
  opacity: 1 !important;
  transform: none !important;
}
/* Los pseudo-elementos NO los cubre la regla de arriba: `transform:none` sobre el
   elemento no llega a su `::before`. Sin esta línea, un fallo de cc-motion.js
   dejaría las tres reglas de la banda violeta en scaleX(0) —invisibles— para
   siempre. Es exactamente el fallo que el resto de esta red existe para evitar. */
html.cc-motion-off .cc-stagger:not(.cc-in) > .cc-rule-draw::before {
  transform: none !important;
}

/* --- Sin movimiento si el usuario lo pidió ------------------------------- */
/* El guard ya no pone `cc-js` con reduced-motion, así que estas reglas son
   defensa en profundidad: cubren el caso de que la preferencia cambie DESPUÉS
   de cargar la página, sin recargar. */
@media (prefers-reduced-motion: reduce) {
  html.cc-js .cc-reveal:not(.cc-in),
  html.cc-js .cc-stagger:not(.cc-in) > *,
  html.cc-js .cc-bar-draw:not(.cc-in) {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  html.cc-js .cc-stagger:not(.cc-in) > .cc-rule-draw::before {
    transform: none !important;
    transition: none !important;
  }
  /* Si la preferencia cambia DESPUÉS de cargar, `.cc-in` ya puede estar puesto y
     entonces lo que corre es una animación, no una transición: hay que apagarla
     explícitamente o el movimiento seguiría ocurriendo. */
  html.cc-js .cc-stagger.cc-in > *,
  html.cc-js .cc-stagger.cc-in > .cc-rule-draw::before,
  html.cc-js .cc-bar-draw.cc-in {
    animation: none !important;
  }
}

/* ==========================================================================
   TRUST BAR (S2) — de 6 textos grises a logos reales (2026-07-27)
   Antes: 6 textos de 13px en gris al 60% de opacidad. Era la parte más "sosa"
   de la home: una banda de 83px sin peso visual que decía credenciales fuertes
   (Google Premier, VTEX, Shopify…) con la voz más débil posible.
   Los logos salen de los assets del home ANTERIOR, traídos de prod. Se
   referencian por ID de attachment y sin `url`/`full` en el JSON, a propósito:
   esas URLs quedan cacheadas con el dominio del entorno donde se exportó y no
   deben viajar en el repo. Los ids sí son estables porque vienen del dump de prod.
   ========================================================================== */

.cc-trust {
  --cc-trust-h: 40px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.cc-trust__item {
  display: flex;
  align-items: center;
  justify-content: center;
  /* SIN !important, y eso importa: estos bloques traían `#brxe-blo008{opacity:.6}`
     en su _cssCustom, una regla de ID que solo se podía pisar con !important
     (gotcha 8.1) — pero entonces el !important también le ganaba al reveal, que
     nunca podía llevar el item a opacidad 1. En vez de pelear especificidad se
     eliminó el _cssCustom del template: una sola fuente de verdad, cero
     !important, y el reveal funciona igual que en el resto de la página. */
  opacity: 0.72;
  transition: opacity var(--cc-dur-base) var(--cc-ease-out);
}
.cc-trust__item:hover { opacity: 1; }

/* Los logos son FONDOS CSS de archivos del child theme, no elementos `image` de
   Bricks. La razón es de portabilidad, no de estilo: un elemento image referencia
   un ID de attachment, y esos ids NO sobreviven un dump fresco de prod (es el
   mismo problema del logo del header, §10). Un archivo en
   `assets/img/partners/` viaja en git y funciona en cualquier entorno el día que
   se despliegue, sin re-apuntar nada y sin sumar una fila a esa tabla.
   El label de texto sigue en el DOM con `.cc-vh`, así que la credencial es
   contenido indexable y el fondo es puramente decorativo — que es lo correcto.

   ⚠️ CAMBIO DE FONDO EL 2026-08-13: la fila dejó de ser de WORDMARKS y pasó a ser
   de BADGES DE PARTNER. Es el pedido de Mauricio en el acta del 2026-08-10 —que los
   logos declaren nivel y tipo de asociación— y Ricardo entregó los seis archivos
   oficiales de cada portal. Eso cambia dos reglas que antes eran correctas:

   1. **Se fue el tope de 118px de ancho.** Existía para que el wordmark de "Google
      Ads" (180x29) no se viera el doble de grande que el de "PrestaShop" (182x43),
      o sea para normalizar piezas de proporciones muy dispares. Los badges ya
      vienen normalizados entre sí (2.2:1 a 4:1) y el tope, en cambio, RECORTABA a
      los tres más anchos. Ahora se normaliza por ALTURA, que es lo que iguala el
      peso óptico de piezas del mismo lenguaje gráfico.
   2. **La fila subió de 26 a 40px.** Un wordmark de una línea se lee a 26px; un
      badge lleva texto DENTRO del arte ("Level 2 Expert", "Business Partner") y a
      26px esa letra caía a ~4px, o sea ilegible. Es la misma altura a la que los
      puso el mockup aprobado por el equipo.

   Cada logo declara su `aspect-ratio` real, así que con la altura fija el ancho sale
   solo y `background-size: contain` nunca deforma. */
/* ⚠️ LA ALTURA COMÚN NO IGUALA EL TAMAÑO PERCIBIDO (2026-08-13). Ricardo lo vio a ojo
   —"que no se vea uno más grande que otro"— y al medirlo era grande: con los seis a 40px
   de alto, la TINTA que pone cada uno iba de 550 px² (Meta) a 1671 (PrestaShop), o sea
   **3,04x de diferencia**. Por eso PrestaShop se leía enorme y Meta diminuto midiendo lo
   mismo: un badge ancho de letra fina pesa mucho menos que uno compacto y negrita.
   La corrección es un multiplicador por logo que lleva a todos a la misma tinta —la media
   geométrica, 1082 px²— acotado a la banda 0.85–1.25 para que ninguno se salga de la fila.
   Resultado medido: de 3,04x a **1,41x**. Meta queda en el tope de la banda y aun así por
   debajo (859): es un lockup genuinamente ligero y subirlo más lo volvería el más alto de
   la fila, que es cambiar un desequilibrio por otro.
   ⚠️ Si se cambia un archivo de esta carpeta hay que RECALCULAR su multiplicador: el
   número depende del arte, no del nombre. */
.cc-trust__logo {
  height: var(--cc-trust-h);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}

/* Los cinco badges de portal. Todos monocromo BLANCO salvo TikTok, que trae sus dos
   acentos de marca; ninguno necesita filtro sobre el fondo oscuro.
   Las credenciales están confirmadas por Ricardo el 2026-08-13 y hay un trinquete en
   `home.mjs` que impide añadir una sin confirmar. */
.cc-trust__logo--google-premier {
  /* El lockup horizontal "PREMIER Google Partner", monocromo blanco (elección de
     Ricardo el 2026-08-13: es el que usa el mockup que el equipo revisó en la reunión
     del 2026-08-10).
     ⚠️ Es el TERCER asset de Google que pasó por aquí en el día, así que conviene dejar
     escrito por qué este y no los otros dos:
       · `google-ads.svg` era el logo del PRODUCTO — no decía nada de la credencial, que
         es justo lo que pidió Mauricio.
       · el badge CUADRADO del kit oficial (`PremierPartner-RGB.svg`, 152x145.5) dice la
         credencial, pero es una tarjeta a color con fondo propio: en una fila de cinco
         marcas monocromas blancas y transparentes se leía como una estampilla pegada,
         y obligaba a dos excepciones —altura propia (1.4x) y quedar fuera del teñido
         del modo claro—.
       · este lockup dice lo mismo ("PREMIER Google Partner") en el mismo lenguaje
         gráfico que sus cinco vecinos: 2.7:1, blanco, sin fondo. Con él las dos
         excepciones desaparecen y la fila vuelve a tener UNA sola definición (8.40).
     O sea la decisión no es de gusto: el badge cuadrado era correcto como asset y
     equivocado como pieza de ESTA fila. */
  aspect-ratio: 1241 / 460;
  background-image: url("../img/partners/google-premier-partner.png");

  /* Peso óptico: ponía 1020 px² de tinta a la altura base y ahora pone 1083
     (medido, ver el bloque de arriba). */
  height: calc(var(--cc-trust-h) * 1.03);
}
.cc-trust__logo--meta {
  /* Variante APILADA (562x252). La anterior era el lockup de 16:1, que obligaba a un
     tope de ancho propio para no verse la mitad que sus vecinos — un problema que la
     doc pedía resolver "si el portal ofrece la variante compacta o apilada".
     La ofrecía. Con 2.2:1 la excepción sobra. */
  aspect-ratio: 562 / 252;
  background-image: url("../img/partners/meta-business-partner-2026.png");

  /* Peso óptico: ponía 550 px² de tinta a la altura base y ahora pone 859
     (medido, ver el bloque de arriba). */
  height: calc(var(--cc-trust-h) * 1.25);
}
.cc-trust__logo--prestashop {
  /* Expert Level 2. El anterior era el logo del PRODUCTO, sin nivel. */
  aspect-ratio: 445 / 164;
  background-image: url("../img/partners/prestashop-expert-level2.png");

  /* Peso óptico: ponía 1671 px² de tinta a la altura base y ahora pone 1207
     (medido, ver el bloque de arriba). */
  height: calc(var(--cc-trust-h) * 0.85);
}
.cc-trust__logo--shopify {
  aspect-ratio: 800 / 200;
  background-image: url("../img/partners/shopify-partner.png");

  /* Peso óptico: ponía 809 px² de tinta a la altura base y ahora pone 1089
     (medido, ver el bloque de arriba). */
  height: calc(var(--cc-trust-h) * 1.16);
}
.cc-trust__logo--vtex {
  aspect-ratio: 308 / 94;
  background-image: url("../img/partners/vtex-partner.png");

  /* Peso óptico: ponía 1377 px² de tinta a la altura base y ahora pone 1091
     (medido, ver el bloque de arriba). */
  height: calc(var(--cc-trust-h) * 0.89);
}
.cc-trust__logo--tiktok {
  /* ⚠️ TikTok SHOP Partner, y el nombre importa. El mockup llegó con el alt "TikTok
     Agency Gold Partner" y el archivo `tiktok-shop-partner_white.png`; el repo tenía
     un tercero, `tiktok-marketing-partner.png`. Son TRES programas distintos. El
     nombre que vale es el del archivo que entregó Ricardo desde el portal.
     ⚠️ Y queda dicho, porque no es menor: el barrido de las 29 páginas publicadas de
     canalcero.com (GET anónimo, 2026-08-13) NO encontró ninguna afirmación de
     asociación con TikTok — el sitio solo declara Google Premier, Yandex Gold y VTEX.
     Entra porque Ricardo entregó el asset oficial, no porque el sitio ya lo dijera.
     Es el único de los seis en esa situación.
     ⚠️ LLEVA `grayscale(1)` DESDE EL 2026-08-23, y el porqué está en la frase que había
     aquí antes: «esta variante ya viene en blanco **con los acentos de marca**». Los
     acentos eran el problema y no se habían pesado. Medido pixel a pixel sobre los seis
     badges: este tenía **16,9 % de tinta claramente cromática** (pico de saturación 0,84)
     y los otros cinco **0 %** (pico ≤0,15). En una fila cuyo trabajo es leerse como UN
     conjunto, uno de los seis se adelantaba — y es el único sin asociación confirmada en
     el sitio (ver arriba), o sea el que menos autoridad aporta.

     ⚠️ Y NO es la receta de la cinta de marcas, aunque lo obvio fuera copiarla. Medidos
     los cuatro candidatos contra la luminancia media de los otros cinco (150):

       | filtro                        | color | luminancia | delta |
       | (ninguno)                     | 16,9% |    149     |   −1  |
       | grayscale(1)                  |   0%  |    151     |   +1  |  ← elegido
       | saturate(0) brightness(1.1)   |   0%  |    152     |   +2  |
       | brightness(0) invert(1)       |   0%  |    161     |  +11  |  ← la de la cinta

     `brightness(0) invert(1)` —lo que usa la cinta de marcas y lo que este archivo usaba
     con el PNG viejo— apaga el color pero deja el badge como el MÁS BRILLANTE de los
     seis: cambia una incoherencia por otra. `grayscale(1)` respeta la luminancia que el
     archivo ya tenía. El modo claro no se toca: su regla es más específica y ya lo pasa
     a `brightness(0)` con los otros cinco. */
  aspect-ratio: 654 / 178;
  background-image: url("../img/partners/tiktok-shop-partner.png");
  filter: grayscale(1);

  /* Peso óptico: ponía 1531 px² de tinta a la altura base y ahora pone 1106
     (medido, ver el bloque de arriba). */
  height: calc(var(--cc-trust-h) * 0.85);
}

/* El item sin logo (hoy solo TikTok, sin asociación confirmada — ver arriba) se
   iguala al peso óptico de los wordmarks: mayúsculas, tracking y el mismo alto de
   caja. */
.cc-trust__text {
  display: flex;
  align-items: center;
  height: var(--cc-trust-h);
  font-family: var(--cc-font-sans);
  font-size: 13px;
  font-weight: var(--cc-fw-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--cc-fg-strong);
  white-space: nowrap;
}

/* Mobile.
   NO se usa grid acá, y quedó documentado porque costó descubrirlo: Bricks emite
   `#brxe-con007{display:flex;flex-flow:row wrap;…}` en su CSS por-página, o sea una
   regla de **ID**, que le gana a cualquier clase — incluso a un selector doble
   `.brxe-container.cc-trust` (0,2,0). Es el gotcha 8.1. Se podría forzar con
   !important, pero no hace falta: basta acotar el ancho del logo más largo para que
   el propio `wrap` del flex reparta 3 y 3 en vez de 3-2-1, que dejaba a TikTok solo
   en la última fila como si fuera un sobrante.
   El tope de Meta baja de 172 a 150px: su lockup de 16:1 es el que descuadra la
   fila. Lo ideal sería la variante compacta/apilada del badge de Meta. */
@media (max-width: 767px) {
  /* 30px y no 20: con badges la letra va DENTRO del arte, así que bajar la caja la
     vuelve ilegible antes que estrecha. El tope de ancho de Meta desapareció con el
     lockup de 16:1 al que corregía (ahora es la variante apilada, 2.2:1). */
  .cc-trust { --cc-trust-h: 30px; }
  .cc-trust__text { font-size: 11px; }
}

/* Oculto visualmente pero legible por lectores de pantalla y buscadores.
   Se usa en los labels de la trust bar: el logo comunica la marca, y el texto
   ("Google Premier Partner") conserva la credencial como contenido indexable.
   NO se usa display:none, que lo sacaría del árbol de accesibilidad. */
.cc-vh {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   BADGE "GOOGLE PREMIER PARTNER" ANIMADO (S10) — 2026-07-27
   El home anterior mostraba acá un GIF (690 KB) y un PNG (454 KB) del badge
   oficial. Los dos se DESCARTARON a propósito: ambos traen fondo blanco/celeste
   horneado —irreconciliable con una página dark-first— y el PNG además dice
   "Partner Premier del 2024", un año viejo.
   Se reconstruye en CSS: nativo del tema oscuro, sin peso de red, sin año que
   caduque, y con la animación que el GIF aportaba. Los 4 colores de Google
   viven en el punto cónico, que es la única cita de marca que hace falta.
   ========================================================================== */

/* --- La credencial de capacidad -------------------------------------------
   ⚠️ ACÁ VIVÍA `.cc-badge-premier`: una pill con un PUNTO QUE LATÍA y un barrido
   de brillo metálico recorriéndola cada 5,5s. Se retiró el 2026-08-06 porque
   Ricardo señaló la fila entera (*"las píldoras de Capacidad son muy de IA, esos
   detalles no me gustan"*) y tenía razón: punto pulsante + shimmer + contorno de
   color es, literalmente, el vocabulario de una landing generada. La credencial
   más fuerte del negocio se estaba presentando con el recurso que más la abarata.

   Ahora las tres son texto en una ficha de datos, con el mismo separador de
   hairline que la fila de TRAYECTORIA de arriba —que es la que SÍ funcionaba—.
   Eso resuelve además una incomodidad que el código anterior admitía en su propio
   comentario: "Stack medios + commerce + data" no es algo que certifique un
   tercero, y en formato de sello lo parecía. Sin cápsula, las tres son lo que son:
   hechos declarados.

   Y de paso desaparece el ÚNICO uso de `border-radius: 999px` en una caja de la
   home, que era el radio que hacía que estas tres se leyeran como tags. */
/* ==========================================================================
   FOOTER — redes sociales y links legales (2026-07-27)
   Los dos faltaban respecto del footer anterior. Los legales no son cosméticos:
   en un sitio con Complianz GDPR instalado, no enlazar privacidad y cookies
   desde el footer es lo más cercano a un problema de cumplimiento que había.
   Van como HTML dentro de elementos `text` de Bricks (que rinde HTML, igual que
   ya hacían los links del nav del footer) en vez de un elemento `code`: ese es
   el mecanismo que causó el bug de la página en blanco (gotcha 8.11).
   ========================================================================== */

/* ⚠️ El flex va en el <p> INTERNO, no en el wrapper. El elemento `text` de Bricks
   envuelve su contenido en un <p>, así que `.cc-social{display:flex}` tenía un
   solo hijo (ese <p>) y el `gap` no se aplicaba nunca: la separación que se veía
   era el espacio entre palabras del HTML. Es el gotcha 8.9 otra vez —verificar el
   markup renderizado antes de escribir el selector. */
.cc-social { margin-top: 18px; }
.cc-social > p {
  display: flex;
  gap: 10px;
  margin: 0;
}
.cc-social__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  border-radius: var(--cc-radius-md);
  background: var(--cc-bg-surface);
  border: 1px solid var(--cc-border-on-dark);
  color: var(--cc-green-500);
  transition:
    transform    var(--cc-dur-base) var(--cc-ease-out),
    border-color var(--cc-dur-base) var(--cc-ease-out),
    background   var(--cc-dur-base) var(--cc-ease-out);
}
.cc-social__link svg {
  width: 18px; height: 18px;
  fill: currentColor;            /* el color lo manda el link, no el SVG */
}
.cc-social__link:hover {
  transform: translateY(-2px);
  border-color: var(--cc-green-500);
  background: rgba(61, 237, 180, 0.08);
}
/* Foco visible por teclado: son los únicos links con icono y sin texto, así que
   sin esto un usuario de teclado no sabría dónde está. */
.cc-social__link:focus-visible {
  outline: 2px solid var(--cc-fg-accent);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .cc-social__link:hover { transform: none; }
}

/* Fila de links legales, sobre una línea fina que la separa del copyright.
   Idem: el flex va en el <p> interno (ver la nota de .cc-social). Sin esto los
   cuatro links salían pegados y se leían como una sola frase corrida. */
.cc-legal {
  padding-top: 26px;
  margin-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.cc-legal > p {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 30px;
  margin: 0;
}
/* En móvil los cuatro links quebraban 1-2-1, que se lee como desalineado y no como
   una lista. Uno por línea: cuatro filas parejas dicen "índice", el quiebre irregular
   no dice nada. */
@media (max-width: 479px) {
  .cc-legal > p { flex-direction: column; gap: 12px; }
}
.cc-legal a {
  color: var(--cc-fg-muted);
  font-size: var(--cc-fs-sm);
  text-decoration: none;
  transition: color var(--cc-dur-base) var(--cc-ease-out);
}
.cc-legal a:hover { color: var(--cc-green-500); }
.cc-legal a:focus-visible {
  outline: 2px solid var(--cc-fg-accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ==========================================================================
   BANDAS DE SECCIÓN (2026-07-27) — el ritmo lo hace la línea, no el relleno
   La home tenía 5 fondos distintos. Medido el contraste real entre ellos:
     base #0A0A0F -> superficie #101016 ....... 1.042
     superficie   -> card #14121F ............. 1.026
     base         -> card #14121F ............. 1.069
     base         -> violeta-ink #201E2C ...... 1.207
     card         -> violeta #4B2FBF .......... 2.164
   El ojo separa dos superficies planas recién cerca de 1.10–1.15, así que TRES de
   los cinco fondos eran indistinguibles entre sí: no leían como ritmo, leían como
   bandeo sucio — invisible en un monitor sin calibrar, y en uno bueno un artefacto.
   Se pagaba la complejidad de 5 valores sin ganancia perceptual.
   Ahora: base para todo, y los dos fondos que SÍ se distinguen se quedan (la banda
   violeta de métricas y el cierre violeta-ink del CTA). Donde separar zonas aporta,
   lo hace un hairline de 1px, que a este nivel de oscuridad separa mucho mejor que
   un relleno 4% más claro — es lo que ya hacía de hecho la trust bar.
   ========================================================================== */
.cc-band {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* ==========================================================================
   FOOTER — encabezado de columna y badge oficial de Google (2026-07-27)
   La columna derecha era una lista de 4 links sin ningún encabezado: se leía como
   texto suelto, no como navegación. Y el badge de Google faltaba desde el
   rediseño (el footer anterior sí lo tenía).
   ========================================================================== */

.cc-foot__titulo {
  font-size: var(--cc-fs-xxs);
  font-weight: var(--cc-fw-bold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cc-fg-muted);
  margin-bottom: 14px;
}

/* Badge oficial de Google Premier Partner.
   Es la variante "Clickable" del kit que entrega Google (Google Ads → Admin →
   Partners programme → Badge status), tal cual: NO se recolorea ni se oscurece.
   Viene con fondo blanco a propósito, porque es un sello de certificación —
   igual que un sello impreso en un documento— y su G a 4 colores es parte de la
   marca de Google. Aporta el único punto de color cálido de la página, y es
   legítimo porque es el asset oficial, no una recreación (ver el chip de S10,
   que a propósito NO imita la paleta de Google).
   El enlace va a la ficha de la agencia en Google Partners, que es el uso que la
   variante Clickable contempla. */
.cc-badge-google-wrap { margin-top: 26px; }
.cc-badge-google {
  display: inline-block;
  line-height: 0;
  border-radius: var(--cc-radius-sm);
  /* Halo tenue: separa el sello blanco del fondo casi negro sin taparlo ni
     alterar sus colores. */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 10px 26px -12px rgba(0, 0, 0, 0.8);
  transition: transform var(--cc-dur-base) var(--cc-ease-out),
              box-shadow var(--cc-dur-base) var(--cc-ease-out);
}
.cc-badge-google img {
  width: 96px;
  height: auto;
  display: block;
  border-radius: var(--cc-radius-sm);
}
.cc-badge-google:hover {
  transform: translateY(-3px);
  box-shadow: 0 0 0 1px rgba(61, 237, 180, 0.45), 0 14px 30px -12px rgba(0, 0, 0, 0.85);
}
.cc-badge-google:focus-visible {
  outline: 2px solid var(--cc-fg-accent);
  outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .cc-badge-google:hover { transform: none; }
}

/* ==========================================================================
   EL HEADER PEGADO NUNCA SE PEGÓ (2026-08-03)
   El template declara `position: sticky; top: 0` en su sección desde que se
   construyó, y este documento habla del "header sticky translúcido" en dos
   lugares. Medido: a 3000px de scroll el header tenía `top: -3000`, o sea se iba
   con la página. En los 4 anchos probados.

   La causa no es un `overflow:hidden` (la cadena de ancestros está limpia, se
   revisó) ni un `transform`: es que **el padre del elemento pegado mide lo mismo
   que él**. `header#brx-header` mide 85px y la sección sticky que contiene mide
   85px, y un sticky solo puede desplazarse DENTRO de la caja de su padre. Con cero
   píxeles de recorrido, `sticky` se comporta igual que `static`. El síntoma es
   engañoso porque `getComputedStyle` dice `position: sticky` y todo parece bien.

   Regla general: `position: sticky` en un hijo cuyo padre lo ciñe no hace nada.
   El pegado va en el elemento cuyo PADRE es el contenedor de scroll — acá
   `#brx-header`, cuyo padre es `body` (6755px).

   ¿Por qué no la setting nativa de Bricks? Existe: el core trae
   `#brx-header.brx-sticky{position:fixed}` y `.brx-sticky.on-scroll{position:sticky}`,
   y esa clase la pone la configuración del template de header. Pero eso vive en la
   BD, así que habría que re-aplicarlo tras cada dump y sumaría una fila a §10 —que
   la idea es que encoja—. Esta línea hace lo mismo, viaja en git y se aplica sola
   al desplegar. `body #brx-header` es (1,0,1) y le gana al (1,0,0) del core sin
   depender del orden de carga.
   ========================================================================== */
body #brx-header {
  position: sticky;
  top: 0;
  z-index: var(--cc-z-header);
}

/* Con el header pegado, un ancla deja su destino DEBAJO del header: los 5 links
   del nav son anclas a secciones de la home y el índice de la nota son anclas a
   sus H2. `scroll-padding-top` es la forma correcta —la que respeta el
   `scroll-behavior: smooth` que emite Bricks— en vez de un `margin-top` negativo
   por destino. */
html {
  scroll-padding-top: calc(var(--cc-header-h) + 12px);
}

/* Y el índice de la nota es sticky a 32px del borde: con el header encima quedaba
   tapado a media lectura. Se le suma el alto del header. */
.cc-toc { top: calc(var(--cc-header-h) + 24px); }

/* ==========================================================================
   BOTONERA FLOTANTE (2026-08-03) — pedida por Miguel Gutiérrez en la revisión
   del 2026-07-30: un contacto siempre a mano, sin tener que volver al header.

   Es UNA pila y no dos elementos sueltos a propósito: si el botón de contacto y
   el de tema se posicionaran por separado, cada cambio de tamaño de uno tendría
   que recalcularse en el otro para que no se solapen. Con una columna flex el
   espacio lo reparte el layout.

   Reparto de responsabilidades, y es la contracara deliberada de 8.22:
   · El botón de CONTACTO lo renderiza el SERVIDOR (functions.php → wp_footer),
     porque es un enlace: funciona con o sin JS, así que no hay razón para que
     dependa de un script. Sale en las 32 páginas menos /contacto/ (ofrecer
     "Contacto" estando en contacto es ruido, no ayuda).
   · El botón de TEMA lo inyecta cc-theme.js, porque sin JS no hay nada que
     alternar y un control muerto es peor que ninguno (la misma razón por la que
     el pausa del hero se inyecta y la hamburguesa no).
   ========================================================================== */

.cc-fab {
  position: fixed;
  /* El inset crece con el viewport: pegado al borde en móvil se toca sin querer
     al hacer scroll con el pulgar. */
  right: clamp(14px, 2vw, 26px);
  /* ⚠️ +70px NO es aire de diseño: es la banda que ocupa la insignia de reCAPTCHA
     (2026-08-26, gotcha 8.108). Google la fija en `bottom:14px` con 60px de alto, y
     v3 la carga en TODO el sitio —medido en las 9 páginas rediseñadas, también en las
     que no tienen formulario—, así que no es un caso particular de `/contacto/`.
     Sin esto el conmutador se dibujaba DENTRO de esa caja: solape de 44x44, o sea el
     100 % del botón, idéntico a 390 y a 1440. Los toques sí llegaban al botón, así que
     no era un fallo funcional; era nuestro control pintado encima de una caja de
     Google. Y en el staging esa caja sale en ROJO («Invalid domain for site key»,
     porque la clave es de canalcero.com), que es lo que se ve como «un cuadrado de
     error» debajo del conmutador. 60px de la insignia + 10 de aire. */
  bottom: calc(clamp(14px, 2vw, 26px) + 70px);
  /* El token existía sin usarse desde el principio; este es su caso. Queda sobre
     el header (60) y bajo un modal (80). */
  z-index: var(--cc-z-chat);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}

/* Al imprimir, un botón fijo se estampa encima del contenido de la página. */
@media print { .cc-fab { display: none; } }


.cc-fab__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  border: 0;
  cursor: pointer;
  text-decoration: none;
  font-family: var(--cc-font-sans);
  /* 15px/1.4 y el radio pill ya existen en el sistema (los usa .cc-cardlink), así
     que esta pieza no suma valores nuevos a la escala. */
  font-size: 15px;
  line-height: 1.4;
  font-weight: var(--cc-fw-bold);
  border-radius: var(--cc-radius-pill);
  transition:
    background-color var(--cc-dur-fast) var(--cc-ease-out),
    color            var(--cc-dur-fast) var(--cc-ease-out),
    border-color     var(--cc-dur-fast) var(--cc-ease-out),
    transform        var(--cc-dur-fast) var(--cc-ease-out),
    box-shadow       var(--cc-dur-fast) var(--cc-ease-out);
}

/* --- Contacto: la acción primaria, la más cerca del pulgar ---------------- */
.cc-fab__contacto {
  /* 48px de alto: pasa WCAG 2.5.8 (24px) por su propia caja, sin depender del
     `::after` de 8.17 — que acá además no serviría, porque el vecino de arriba
     es el botón de tema y las áreas se solaparían. */
  min-height: 48px;
  padding: 0 20px;
  /* Verde de marca con texto casi negro: 13.14:1, y es el MISMO par en los dos
     temas, así que el CTA no cambia de identidad al cambiar de modo. */
  background-color: var(--cc-green-500);
  color: var(--cc-ink-900);
  /* Sombra propia y no `--cc-shadow-card-dark`: ese token desplaza 14px hacia abajo, que
     con la caja recta deja un borde oscuro asomando bajo la tarjeta. Aquí basta un halo
     casi centrado — profundidad sin segunda caja. */
  box-shadow: 0 6px 22px -12px rgba(0, 0, 0, 0.7);
}
.cc-fab__contacto:hover,
.cc-fab__contacto:focus-visible {
  /* green-700 con el mismo texto da 6.90:1: sigue pasando AA. */
  background-color: var(--cc-green-700);
  transform: translateY(-2px);
}
.cc-fab__contacto:active { transform: translateY(0); }

/* ⚠️ El botón de contacto solo existe DONDE EL DEL HEADER NO SE ALCANZA (2026-08-03).
   Con el header ya pegado de verdad (ver el bloque de arriba), sobre 767px el
   Contacto del header queda a la vista durante todo el scroll: un segundo botón que
   hace exactamente lo mismo no es más acceso, es la misma oferta dos veces.
   Bajo 767px sí aporta: ahí el Contacto se mudó DENTRO del panel del menú (8.19),
   así que sin esto son dos toques (abrir el menú, elegir Contacto) y con esto uno.
   El breakpoint no es un número redondo: es el mismo en el que el nav colapsa, o
   sea exactamente el ancho desde el cual el Contacto del header deja de verse.
   El conmutador de tema NO se oculta: no tiene equivalente en el header.

   Va DESPUÉS de las reglas del botón y no junto al `@media print` de arriba: una
   media query no aporta especificidad, y `.cc-fab__contacto{display:none}` (0,1,0)
   empata con el `display:inline-flex` de `.cc-fab__btn`. Puesta antes, perdía por
   orden y el botón seguía saliendo en desktop — medido. */
@media (min-width: 768px) {
  .cc-fab__contacto { display: none; }
}

/* ⚠️ EN TELÉFONO ES UN ICONO DENTRO DEL HEADER, Y HASTA EL 2026-08-23 FUE UNA
   PÍLDORA FLOTANDO SOBRE EL CONTENIDO (pedido de Ricardo, 2026-08-03).

   Se cumplió ese pedido y estuvo veinte días. Lo que lo cambió no es una opinión de
   diseño: es lo que costaba, medido a 390px recorriendo la portada de 100 en 100.
     · Solapaba texto en 40 de 94 posiciones de scroll —43% del recorrido— y no en
       huecos: sobre el H2 de casi todas las secciones, incluidos los remates en verde
       («ventas», «subimos») que son justo la palabra que el titular acentúa.
     · Y en y≈8580 su caja caía sobre el botón ENVIAR del formulario. Dos píldoras
       verdes apiladas, indistinguibles a la vista, y `elementFromPoint` en el centro
       del Enviar devolvía ESTE botón: el último toque del único embudo de la portada
       abría otra vez Contacto.
   O sea 8.17 con el destino invertido: no un control demasiado chico, un control que
   le roba los toques a otro. Y el chequeo que ya preguntaba esto daba verde porque
   sondeaba en una sola posición de scroll — ver 8.85.

   Y coincide con lo que hacen las referencias medidas el mismo día —Linear, Stripe,
   Vercel, Webflow, Intercom—: ninguna de las cinco flota un control suelto sobre el
   contenido. Lo persistente, cuando existe, va en la barra de arriba; o sea el sitio
   correcto era el header desde el principio.

   Lo que NO cambió, porque sigue siendo cierto:
   · el botón solo existe bajo 767px (ver el bloque de arriba: sobre ese ancho el
     Contacto del header se ve durante todo el scroll y serían dos ofertas iguales);
   · se oculta con el panel del menú abierto, que ya trae su propio CTA de Contacto
     (8.19). La clase la pone `cc-nav.js` al abrir.

   ⚠️ SE PROBARON CUATRO SITIOS Y TRES NO SIRVEN. Vale escribirlo porque el resultado
   es contraintuitivo y ahorra rehacer el experimento. Medido a 390px recorriendo la
   portada de 100 en 100 (94 ventanas), contando área de texto tapada y sondeando con
   `elementFromPoint` el centro de cada control:

     | sitio                          | solape |     área | robos | ¿roba el Enviar? |
     | píldora arriba-derecha (antes) |    43% |  76k px² |     1 | SÍ               |
     | píldora abajo-derecha          |    43% |  61k px² |     1 | SÍ               |
     | barra inferior a sangre        |    77% | 404k px² |    15 | SÍ               |
     | dentro de la banda del header  |     0% |    0 px² |     0 | no               |

   La barra a sangre parecía LA respuesta —reserva su alto al final de la página— y es
   la PEOR de las cuatro: reservar el hueco final no impide que tape una franja de 56px
   durante todo el scroll intermedio, y multiplica por 15 los controles que quedan
   debajo. Y las tres primeras siguen robando el Enviar, porque el formulario vive EN la
   página: un elemento fijo sobre el contenido la cruza a alguna altura, siempre. No es
   cuestión de elegir mejor la esquina.

   Lo único que da cero es no estar sobre el contenido. La banda del header ya ocupa sus
   85px y el layout ya los descuenta (`--cc-header-h` alimenta el `scroll-padding` de las
   anclas), así que un control DENTRO de esa banda no tapa un píxel que no estuviera
   tapado. Comprobado con línea base, que es lo que hace válida la cifra: el texto que
   pasa bajo el header mide 662k px² con el botón y 662k sin él — delta 0. Sin la línea
   base ese 662k parece culpa del botón; es del header, y estaba antes.

   Queda ICONO SOLO, 44x44. El sobre ya venía en el markup (`functions.php`, wp_footer)
   y la palabra sigue en el nombre accesible, así que no se pierde el nombre: se pierde
   el ancho, que es lo que no había. `right: 74px` son los 20px de inset de la
   hamburguesa + sus 44 + 10 de aire, así que deja 10px entre los dos controles en TODOS
   los anchos — los dos cuelgan del mismo borde derecho y se mueven juntos.

   Y esto sí es una pérdida, dicha sin adornos: un sobre persuade menos que la palabra
   «Contacto». Se acepta a cambio de las cuatro filas de la tabla. */
@media (max-width: 767px) {
  .cc-fab__contacto {
    position: fixed;
    /* Centrado en la banda: el header mide 85px en los once anchos (8.26), así que el
       centrado es aritmética sobre el token y no un número a ojo. */
    top: calc((var(--cc-header-h) - 44px) / 2);
    right: 74px;
    left: auto;
    bottom: auto;
    width: 44px;
    height: 44px;
    min-height: 44px;
    padding: 0;
    border-radius: 50%;
  }
  /* La palabra sale de la PANTALLA, no del árbol de accesibilidad. Un `display:none` en
     el span la quitaría también del nombre accesible y el botón se quedaría sin nombre
     —el sobre es `aria-hidden`—. Con la técnica sr-only el lector sigue anunciando
     "Contacto". Es 8.14 otra vez: lo que se oculta a la vista no se oculta al lector. */
  .cc-fab__contacto .cc-fab__txt {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  html.cc-nav-abierto .cc-fab__contacto { display: none; }

  /* ⚠️ Y CUANDO EL JS LO HA MUDADO DENTRO DEL HEADER, deja de ser `fixed` (2026-08-26,
     gotcha 8.111). Reportado por Ricardo desde su teléfono: «el círculo baja y sube
     dependiendo del scroll, como que tirita». El header es `sticky` y esto era `fixed`:
     dos modelos de posicionamiento para dos botones que se ven pegados. En un móvil real
     el navegador no repinta los `fixed` durante el gesto —los congela y los recoloca al
     terminar— mientras el `sticky` va compositado, así que el círculo derivaba y volvía
     de golpe contra la hamburguesa. Dentro del header es un hijo en flujo y no puede
     desincronizarse, por construcción.
     Va bajo `html.cc-fab-en-header` —clase que pone el propio JS al mudarlo— por lo mismo
     que `cc-nav-js`: si la mudanza no ocurre, mandan las reglas de arriba y el botón
     sigue donde estaba. Nunca se queda sin sitio.
     `margin-left:auto` absorbe el espacio libre del `space-between` del shell, así que el
     círculo queda pegado a la hamburguesa; los 10px son el mismo hueco que daba
     `right:74px`, ahora dicho una sola vez y no derivado de tres sumandos. */
  html.cc-fab-en-header .cc-fab__contacto {
    position: static;
    top: auto;
    right: auto;
    margin-left: auto;
    margin-right: 10px;
    flex: 0 0 auto;
  }
}

/* El wordmark cede 32px en los dos anchos más estrechos, para que el sobre tenga aire.
   Solo hasta 360px: a 390 ya sobran 72px entre el logo y el botón, y a 320 sin esto la
   tinta del logo termina a 2px del sobre. Se toca la IMAGEN y no el enlace: el ancho del
   `<a>` lo declara Bricks como `#brxe-46b8b8` (1,0,0) y una regla de clase pierde (8.1).
   Que el enlace conserve sus 180px conviene igual — su área de golpeo queda más grande
   que su tinta y sigue sin solaparse con el sobre (20..200 contra 202..246). */
@media (max-width: 360px) {
  .cc-hit-logo img { width: 148px; height: auto; }
}

/* ==========================================================================
   «VER MÁS» DEL BLOG (2026-08-17, R-06)

   `/blog/` era scroll infinito (`infinite_scroll` en la consulta de Bricks). Pedido de
   Ricardo: *«/blog/ deja de ser infinito: filtro o botón Ver más»*. Se hizo el botón, que
   es lo barato; el filtro por categoría sigue pendiente porque antes hay que decidir POR
   QUÉ se filtra (el mapeo de categorías, §10 de `pendientes.md`).

   ⚠️ NO se escribió JavaScript. Bricks trae la interacción nativa `loadMore` apuntando al
   id de la consulta, y con ella se lleva gratis lo que sí cuesta hacer a mano: pide la
   página siguiente por AJAX y **esconde el botón solo** cuando `page === maxPages`. Un
   botón propio habría tenido que replicar eso, y el día que se quede a medias deja un «Ver
   más» que no lleva a ninguna parte.

   ⚠️ Y aquí escribí de más: puse una regla `.cc-vermas.brx-load-more-hidden { display:none }`
   dando por hecho que esconderlo era cosa nuestra. **No lo es** — Bricks ya trae
   `.brx-load-more-hidden { display: none !important }` en `frontend.min.css`. La regla era
   redundante y su comentario afirmaba algo falso. Lo destapó la contraprueba: al retirarla,
   el chequeo siguió en verde. Es 8.74 otra vez —dibujar lo que ya existe— y la lección se
   sostiene: **antes de añadir, preguntarle al tema padre si ya lo hace.**

   El registro es SECUNDARIO a propósito: paginar no compite con «Conversemos». Los valores
   no se inventaron, son los mismos de `.cc-fab__tema`, que resolvió esta misma pregunta
   —«un control que es utilidad, no llamada»— y por 8.71 lo que se copia es la DECISIÓN.
   ========================================================================== */
.cc-vermas {
  display: block;
  /* Medido: con 8px el botón se leía como una tarjeta más de la lista. `--cc-space-8` lo
     separa lo suficiente para que se lea como el control que cierra la lista. */
  margin: var(--cc-space-8) auto 0;
  padding: 12px 26px;
  border-radius: var(--cc-radius-pill, 999px);
  background-color: var(--cc-bg-surface);
  border: 1px solid var(--cc-border-on-dark);
  color: var(--cc-fg-muted);
  font-family: var(--cc-font-sans);
  font-size: var(--cc-fs-sm);
  font-weight: var(--cc-fw-bold, 700);
  cursor: pointer;
  transition: color var(--cc-dur-fast, .18s) var(--cc-ease-out, ease),
              border-color var(--cc-dur-fast, .18s) var(--cc-ease-out, ease),
              transform var(--cc-dur-fast, .18s) var(--cc-ease-out, ease);
}
.cc-vermas:hover,
.cc-vermas:focus-visible {
  color: var(--cc-fg-strong);
  border-color: var(--cc-fg-accent);
  transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
  .cc-vermas:hover, .cc-vermas:focus-visible { transform: none; }
}

/* --- Tema: secundario, así que se lee como utilidad y no como llamada ----- */
.cc-fab__tema {
  width: 44px;
  height: 44px;
  padding: 0;
  background-color: var(--cc-bg-surface);
  border: 1px solid var(--cc-border-on-dark);
  /* El icono es un gráfico de interfaz: WCAG 1.4.11 pide 3:1, y este token da
     5.65:1 sobre la superficie oscura y 6.55:1 sobre la clara (§7). */
  color: var(--cc-fg-muted);
  /* Sombra propia y no `--cc-shadow-card-dark`: ese token desplaza 14px hacia abajo, que
     con la caja recta deja un borde oscuro asomando bajo la tarjeta. Aquí basta un halo
     casi centrado — profundidad sin segunda caja. */
  box-shadow: 0 6px 22px -12px rgba(0, 0, 0, 0.7);
}
.cc-fab__tema:hover,
.cc-fab__tema:focus-visible {
  color: var(--cc-fg-strong);
  border-color: var(--cc-fg-accent);
  transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
  .cc-fab__btn:hover { transform: none; }
}

.cc-fab__ico { display: inline-flex; flex: none; }
.cc-fab__ico svg { display: block; }

/* El icono del tema se DIBUJA en CSS, no se importa: `peso.mjs` prohíbe librerías
   de iconos y un círculo medio relleno no justifica ni un archivo (misma regla que
   el botón de pausa del hero y la flecha de .cc-cardlink).
   Y es el círculo mitad-y-mitad —el icono universal de "apariencia"— y no un
   sol/luna a propósito: un sol dice "claro" y deja al usuario adivinando si eso es
   el estado actual o la acción. La mitad rellena indica el tema al que se va, y el
   nombre accesible lo dice con palabras. */
.cc-fab__ico--tema {
  width: 18px;
  height: 18px;
  border: 2px solid currentColor;
  border-radius: 50%;
  background-image: linear-gradient(90deg, currentColor 0 50%, transparent 50% 100%);
}

/* Dots del hero: `background` lo declara la regla de ID del template, así que se
   usa `filter`, que no. Aclara el dot inactivo al pasar por encima. */
#brxe-hesdot > .brxe-block { transition: filter var(--cc-dur-fast) var(--cc-ease-out); }
#brxe-hesdot > .brxe-block:hover { filter: brightness(2.2); }
#brxe-hesdot > .brxe-block.is-active:hover { filter: none; }

/* El botón de pausa lo inyecta hero-slider.js como último hijo de la fila de
   dots. La regla de ID del template NO declara `align-items`, así que se puede
   fijar desde aquí: sin esto el botón se estira a los 6px de alto de los dots. */
#brxe-hesdot { align-items: center; }

/* --- Control de pausa del hero (WCAG 2.2.2, nivel A) ----------------------
   El porqué está en hero-slider.js. Acá solo el dibujo, hecho con CSS y no con
   un icono: `peso.mjs` prohíbe librerías de iconos en el sitio, y dos barras y
   un triángulo no justifican ni un SVG.
   El área es de 28×28 y NO se agranda con el `::after` absoluto de 8.17: los
   pseudo-elementos están ocupados dibujando el icono, y con 28px ya pasa el
   mínimo de 24×24. Queda a 8px del último dot, cuya área SÍ se extiende 5px
   hacia los lados, así que sobran 3px y nunca se roban el toque entre sí. */
.cc-hero__pausa {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 28px;
  height: 28px;
  margin-left: 8px;
  padding: 0;
  border: 0;
  background: none;
  border-radius: var(--cc-radius-pill);
  cursor: pointer;
  /* 0.55 de alfa sobre #0A0A0F da 3:1, el mínimo de WCAG 1.4.11 para un
     componente de interfaz. Ver la regla de §7: el número no se elige a ojo. */
  color: rgba(255, 255, 255, 0.55);
  transition: color var(--cc-dur-fast) var(--cc-ease-out);
}

.cc-hero__pausa:hover { color: var(--cc-green-500); }

.cc-hero__pausa::before,
.cc-hero__pausa::after {
  content: "";
  width: 3px;
  height: 11px;
  background: currentColor;
  border-radius: 1px;
}

/* Estado pausado: el icono pasa a "play", porque un botón de control muestra la
   ACCIÓN que ejecuta, no el estado en que está. */
.cc-hero__pausa.is-paused::after { display: none; }
.cc-hero__pausa.is-paused::before {
  width: 10px;
  border-radius: 0;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
/* ==========================================================================
   MODO CLARO — excepciones por elemento (2026-08-03)
   Los VALORES del tema están en cc-tokens.css. Acá va lo que un token no puede
   resolver, y es de dos tipos:

   A. Reglas por-ID de Bricks con el color escrito LITERAL. Son 72 declaraciones
      en la home, pero solo 14 valores distintos y 65 de ellas son blanco con
      alpha (§8.18 otra vez: el sistema existe y el template lo esquiva). Se
      pisan con `html[data-theme="light"] #brxe-…`, que es (1,1,1) contra la
      (1,0,0) de Bricks, así que gana por especificidad y NO depende del orden de
      carga (gotcha 8.1).
      ⚠️ Bricks emite DOS reglas por elemento de texto: `#brxe-x` y `#brxe-x a`.
      Pisar solo la primera deja los ENLACES de ese texto en blanco sobre blanco
      — invisibles. Por eso cada grupo lleva su variante con ` a`.

   B. Superficies que se quedan OSCURAS en modo claro (la banda violeta de
      métricas y el cierre violeta-ink). Ahí el texto blanco es correcto, pero los
      tokens que lo pintan (`fdhndm`, `--cc-fg-strong`) acaban de voltearse, así
      que hay que re-afirmarlo. Son pocas y están enumeradas una por una.
   ========================================================================== */

/* --- El fondo de PÁGINA lo pinta Bricks en `html`, con una var de paleta -----
   Y no siempre la misma: en la home y el blog es `xyartc` (#0a0a0f) y en la nota
   es `fdhndm` (#ffffff), o sea el MISMO id que allá pinta 23 textos. Sin esta
   línea, voltear `fdhndm` a tinta oscura dejaba la nota con el fondo negro en
   modo claro. Se fija el fondo aquí y los dos ids quedan libres para su otro uso. */
html[data-theme="light"] { background-color: var(--cc-bg); }

/* --- A1. (retirado el 2026-08-03) ------------------------------------------
   Acá vivía una lista de 28 ids con `color: var(--cc-fg-read)`, porque el template
   pintaba el cuerpo con blanco a OCHO alphas distintos (0.5 a 0.8) para el mismo
   papel. Ya no hace falta: esos 33 valores (29 en la home, 3 en el footer, 1 en la
   nota) pasaron a `var(--cc-fg-read)` y `var(--cc-fg-muted)` DENTRO de los
   templates, así que el tema los voltea solo y las islas oscuras los recuperan sin
   una regla propia. Es la lección de §7 aplicada al tema base: un token verificado
   una vez, no ocho alphas elegidos a ojo. El modo claro los había colapsado a uno;
   ahora el oscuro también. */

/* --- A2. Verde de marca usado como TEXTO ------------------------------------
   #3DEDB4 sobre blanco da 1.50:1: en claro el acento tiene que ser el verde de
   tinta (5.34:1). Son los números de las métricas y los rótulos de los chips.

   ⚠️ LOS CUATRO MARCADORES DE ORDEN (01–04) SALIERON DE ESTA LISTA el 2026-08-04,
   y es 8.32 por CUARTA vez. Desde que el número toma el color de su unidad, esta
   regla —(1,1,0) contra la regla de ID del `_typography`, que resuelve `var()` por
   herencia— lo pisaba y devolvía los cuatro al verde de tinta: en oscuro cada
   número con su color, en claro los cuatro verdes. Lo cazó una captura del modo
   claro, no una medición, porque el chequeo de unidades de `home.mjs` corría solo
   en oscuro (ahora corre en los dos).
   No hacía falta reemplazarla por una versión con `var(--cc-unidad, …)`: el
   template ya escribe `var(--cc-unidad, var(--cc-fg-accent))`, y ese fallback ES
   el verde de tinta en claro. O sea la regla no solo estorbaba, era redundante —
   el patrón de fallback de 8.34 hace el trabajo de A2 solo. */
html[data-theme="light"] :is(
  #brxe-hea090, #brxe-hea095, #brxe-hea100,
  #brxe-tex122, #brxe-tex124, #brxe-tex126
),
html[data-theme="light"] :is(
  #brxe-hea090, #brxe-hea095, #brxe-hea100,
  #brxe-tex122, #brxe-tex124, #brxe-tex126
) a {
  color: var(--cc-fg-accent);
}

/* El acento dentro de los titulares (el punto verde de "Canal.Media", el
   "Canal Cero" del cierre) venía como `style="color:#3dedb4"` INLINE en el HTML
   del template, y un style inline le gana a cualquier hoja salvo con !important.
   Se cambió a la clase `.cc-acento` en los 12 casos (home.json), que además quita
   12 colores escritos a mano.
   Apunta al token SEMÁNTICO y no al verde directo: así el modo claro lo cambia una
   vez y las islas oscuras (más abajo) lo recuperan sin una regla propia. */
/* El fallback es lo que hace que esto no sea una lista: fuera de una unidad, `--cc-unidad`
   no está definido y el acento sigue siendo el verde de marca de siempre. Dentro de una,
   la isla `.cc-u-*` lo redefine y el punto, el número, el borde y la flecha cambian a la
   vez sin que ninguna de esas reglas sepa en qué unidad está. */
.cc-acento { color: var(--cc-unidad, var(--cc-fg-accent)); }

/* --- A3. Cards de servicios y casos: blanco al 4% sobre sección oscura ------
   En claro la sección pasa a paper, así que la card se distingue al revés: papel
   blanco sobre gris, con el hairline haciendo el borde.

   ⚠️ LAS 4 TARJETAS DE UNIDAD SALIERON DE LA LISTA DE IDS el 2026-08-04, y esto es
   un bug que llevaba desde que existe el modo claro: `:is(#brxe-blo046, …)` pesa
   (1,1,1) —un `:is()` toma la especificidad de su argumento más específico, y ahí
   hay un ID—, así que le ganaba también al `:hover` de la línea de abajo, que es
   (0,3,1). Resultado medido: **en modo claro el borde de la tarjeta no cambiaba
   nunca al pasar el puntero**. Estuvo tapado hasta hoy porque la clase de hover
   anterior (`.cc-card-hover-violet`) usaba `!important` y sí pisaba la lista; al
   cambiar a la elevación por unidad —sin `!important`— el fallo salió a la luz. Es
   8.24: un bug tapado por otro no está arreglado, está esperando.
   Las cuatro ya llevan `.cc-svc-card`, así que la clase de abajo las cubre. Los
   tres ids que quedan son las tarjetas de CASO, que no son `.cc-svc-card`. */
html[data-theme="light"] :is(
  #brxe-blo087, #brxe-blo092, #brxe-blo097
),
/* ⚠️ `.cc-caso` estuvo aquí unas horas del 2026-08-17 y SALIÓ el mismo día, y las dos
   cosas enseñan.
   ENTRÓ porque, siendo tarjeta, se pintaba con `rgba(255,255,255,.06)` y su borde con
   `rgba(255,255,255,.10)` —dos blancos con alpha— así que sobre papel desaparecía entera:
   las cifras quedaban flotando sin caja y por eso el titular de la sección «no se
   distinguía». No era el titular, era la estructura que lo sostiene. El contraste daba 0
   nodos malos porque una SUPERFICIE que se pierde no es texto sobre fondo (8.68), y de ahí
   salió el chequeo de superficies de `tema.mjs`.
   SALIÓ porque `.cc-caso` dejó de ser una tarjeta: al reconstruir la sección como tabla
   comparativa es una FILA, y la regla de tarjeta le ponía fondo blanco y borde de unidad a
   cada una — seis cajas apiladas donde debía haber una matriz con hairlines. Un componente
   que cambia de papel tiene que salir de las reglas del papel anterior; heredarlas es cómo
   una hoja acumula reglas que ya no describen nada. */
html[data-theme="light"] .cc-svc-card {
  background: var(--cc-white);
  /* El teñido de unidad se repite acá y no se hereda de la regla base, por 8.32: esta
     regla es (1,1,0) y la base (0,1,0), así que sin esto el borde de color funcionaba
     en oscuro y en claro volvía al hairline neutro en las cuatro unidades.
     Los dos porcentajes suben con los de la regla base (34→55 en reposo, 78→100 al
     hover) al retirarse la marca superior: si se movieran solo en oscuro, el modo
     claro se quedaría con el borde invisible que motivó el cambio. */
  border-color: color-mix(in srgb, var(--cc-unidad, transparent) 55%, var(--cc-border-on-dark));
}
html[data-theme="light"] .cc-svc-card:hover {
  border-color: color-mix(in srgb, var(--cc-unidad, transparent) 100%, var(--cc-border-on-dark));
}

/* Y sus LOGOS y sus iconos, que son monocromo BLANCO: sobre papel no se veía ni uno.
   `brightness(0)` los pasa a negro conservando el alpha, que es exactamente lo que el
   sistema ya hace con los badges de partner de la portada. Los iconos de métrica van
   además atenuados: en oscuro son un acento menta discreto y en negro pleno competirían
   con la cifra, que es lo que importa de esa fila. */
html[data-theme="light"] img.cc-caso__logo { filter: brightness(0); }
/* ⚠️ MK queda FUERA, y se reconoce por su archivo. Los otros cinco son monocromo blanco
   puro; este trae además dos grises y un fondo, así que `brightness(0)` no lo pasa a
   negro: lo aplana en un cuadrado negro macizo. Sin filtro al menos se lee la letra.
   Es el mismo asset que en OSCURO tampoco se ve bien (un cuadrado oscuro con la letra en
   gris), o sea el problema es del archivo y no del tema: hace falta pedirle al cliente
   una variante monocroma, y hasta entonces cualquier regla aquí es un parche. */
html[data-theme="light"] img.cc-caso__logo[src$="icon-mklogo.svg"] { filter: none; }
html[data-theme="light"] img.cc-caso__icono { filter: brightness(0); opacity: .55; }

/* ⚠️ …PERO NO DENTRO DE UNA ISLA OSCURA (2026-08-26, gotcha 8.108). Las dos reglas de
   arriba dan por hecho que en modo claro la superficie de debajo se vuelve papel. En
   `/servicios/agencia-seo/` no: su sección lleva `cc-isla-oscura` y se queda en
   #0A0A0F en los dos temas —a propósito—, así que `brightness(0)` pasaba los cinco
   logos a NEGRO PLENO sobre casi negro. Medido: `fondo=rgb(10,10,15)` con el `body` en
   blanco; en la captura se leen como siluetas.
   No es que la regla estuviera mal escrita: es 8.37 —la isla se añadió DESPUÉS y nadie
   volvió a medir el modo claro de esa banda—. Y hoy `cc-caso__logo` solo existe ahí, o
   sea la regla de arriba no acierta en ninguna página; se conserva porque el día que
   un caso de éxito viva sobre papel volverá a hacer falta.
   Dentro de la isla el tratamiento correcto es el de OSCURO, que es no tocar nada. */
html[data-theme="light"] .cc-isla-oscura img.cc-caso__logo,
html[data-theme="light"] .cc-isla-oscura img.cc-caso__icono {
  filter: none;
  opacity: 1;
}

/* --- A4. DEGRADADOS y fondos con alpha --------------------------------------
   ⚠️ Esta familia se me pasó en el inventario y la cazó una captura, no una
   medición: un degradado NO es un objeto de color en el JSON de Bricks (es una
   cadena dentro de `_cssCustom`), así que un barrido que busca `{hex, id}` o
   `{raw}` no lo ve. Y dos de los tres escriben el token CRUDO (`--cc-ink-900`)
   en vez del semántico (`--cc-bg`), o sea se saltan la capa que hace posible el
   tema — 8.18 otra vez.
   Peor: el chequeo de contraste OMITE los nodos sobre un degradado (no se puede
   medir sin muestrear píxeles), y el hero tiene 13. Con el degradado oscuro
   intacto, el titular quedaba en tinta sobre casi-negro y las 45 pruebas pasaban.
   Por eso tema.mjs ahora mide el FONDO DECLARADO de cada sección raíz, paradas de
   degradado incluidas: es el chequeo que faltaba, no el que falló. */

/* El scrim del hero: mismo gesto (opacar hacia la izquierda para que el titular
   se lea sobre la aurora), invertido. Se reconstruye con `--cc-bg` para que no
   vuelva a quedar atado a un tema. */
html[data-theme="light"] #brxe-sec001 {
  background-color: var(--cc-bg);
  background-image: linear-gradient(
    90deg,
    var(--cc-bg) 0%,
    color-mix(in srgb, var(--cc-bg) 85%, transparent) 34%,
    transparent 70%
  );
}
/* El header es translúcido con `backdrop-filter`, y su color venía de
   `--cc-ink-900` directo: en claro quedaba una barra negra sobre la página blanca.
   Se mantiene el 92% y el desenfoque; solo cambia de qué color es el 92%. */
html[data-theme="light"] #brxe-27e477 {
  background: color-mix(in srgb, var(--cc-bg) 92%, transparent);
  border-bottom-color: rgba(32, 30, 44, 0.10);
}
/* El radial verde de S8 (6% de alfa) funciona igual sobre claro: no se toca. */
/* Dots inactivos del hero: 0.22 de blanco sobre oscuro = 0.22 de tinta sobre
   claro. El activo es verde de marca y no cambia. */
html[data-theme="light"] #brxe-hesdot > .brxe-block { background: rgba(32, 30, 44, 0.28); }
html[data-theme="light"] :is(#brxe-blo111, #brxe-blo113, #brxe-blo115, #brxe-blo117) {
  border-color: rgba(32, 30, 44, 0.16);
}
/* Acá vivían TRES overrides de modo claro para los chips de credencial: el borde de
   los tres, el del de Premier —que necesitaba `!important` propio para ganarle a su
   regla de ID— y el degradado de su barrido de brillo. Los tres desaparecieron el
   2026-08-06 junto con la decoración que compensaban (ver el aviso en
   `.cc-cred`). Es 8.27.6 otra vez, y en su forma más barata: **quitar el adorno
   quitó también las excepciones que el adorno obligaba a mantener.** */

/* La barrita de acento sobre el H2 de S4 y el dot activo del hero son verde de
   marca escrito LITERAL en el `_cssCustom` del template, así que no siguen al
   token. Los dos son indicadores —uno acentúa un titular, el otro dice en qué
   slide estás—, y a 1.50:1 sobre blanco el segundo además incumple el 3:1 que
   WCAG 1.4.11 pide para un componente de interfaz. */
html[data-theme="light"] #brxe-blo037,
html[data-theme="light"] #brxe-hesdot.js > .brxe-block.is-active,
html[data-theme="light"] #brxe-hesdot:not(.js) > .brxe-block:first-child {
  background: var(--cc-fg-accent);
}

/* En modo claro los silos van a PAPER, no a blanco. Con blanco sobre blanco y un
   hairline de 1px volvían a leerse como el wireframe que el bloque de S2 existe
   para no parecer: "cajas punteadas sobre transparente" fue justamente el
   diagnóstico de esa sección. Sobre la página blanca, el objeto tiene que ser el
   que aporta el tono. */
html[data-theme="light"] .cc-silo {
  background: var(--cc-white);
  border-color: rgba(32, 30, 44, 0.16);
}

/* --- A5. Nav del header ----------------------------------------------------- */
html[data-theme="light"] #brxe-navgrp .brxe-text a { color: var(--cc-fg); }
html[data-theme="light"] #brxe-navgrp .brxe-text a:hover { color: var(--cc-fg-accent); }

/* --- A6. Loop del índice del blog y de los relacionados ---------------------
   Bricks emite el color de cada campo del loop como
   `#brxe-<id> .repeater-item [data-field-id="…"]`, que es (1,2,0). Con el
   atributo del tema queda (1,3,0) y gana. Se usa el id del elemento (que viaja en
   el JSON versionado) y no el `data-field-id`, que Bricks regenera al editar. */
html[data-theme="light"] #brxe-uqjcws .repeater-item [data-field-id="mqahuc"],
html[data-theme="light"] #brxe-uqjcws .repeater-item [data-field-id="cc9e9d"],
html[data-theme="light"] #brxe-yeyaqd .repeater-item [data-field-id="teyjdi"] {
  color: var(--cc-fg-accent);
}
html[data-theme="light"] #brxe-yeyaqd .repeater-item [data-field-id="34cf05"] {
  color: var(--cc-fg-strong);
}
/* Botones de compartir de la nota: círculos de blanco al 6% con el icono blanco.
   En claro serían blanco sobre blanco, o sea invisibles — y no se ve venir
   leyendo el template, porque ahí el color es solo "#FFFFFF". */
html[data-theme="light"] #brxe-pplaic li a {
  background-color: rgba(32, 30, 44, 0.07);
  color: var(--cc-fg);
}

/* --- B. ISLAS OSCURAS: las superficies que NO se voltean --------------------
   La banda violeta de métricas (#4b2fbf), el cierre violeta-ink (#201e2c) y la
   caja CTA de la nota son piezas con color propio, no "fondo", así que en modo
   claro siguen oscuras y su texto sigue siendo claro.

   ⚠️ Este bloque es un ARREGLO, no una precaución. Sin él, la primera versión del
   modo claro dejaba dos textos ilegibles y la medición los cazó con el número
   exacto: el "Placeholder…" del cierre a **2.37:1** y el subtítulo del CTA de la
   nota a **1.68:1** (el mínimo es 4.5). Los dos por la misma causa: su color no
   estaba escrito a mano —venía de `--cc-fg-muted` y `--cc-fg-read`— así que se
   volteó con el resto y quedó gris oscuro sobre violeta oscuro.

   Y se resuelve RE-DECLARANDO LOS TOKENS para el subárbol, no enumerando los
   elementos afectados. La diferencia importa: una lista de ids cubre lo que hoy
   está dentro de esas secciones y falla en silencio con el próximo elemento que se
   agregue ahí. Las custom properties heredan, así que la isla protege también lo
   que todavía no existe. Es la misma idea de §7 —un token verificado una vez, no
   nueve números elegidos a ojo— aplicada al alcance en vez de al valor.

   Los valores son los del tema oscuro, verificados sobre las dos superficies:
     blanco   sobre #201E2C 16.36:1   · sobre #4B2FBF 8.54:1
     #C6C6CE  sobre #201E2C  9.64:1   · sobre #4B2FBF 5.03:1
     #898989  sobre #201E2C  4.68:1   · sobre #4B2FBF 2.44:1  ← ver la nota
     #3DEDB4  sobre #201E2C 10.89:1   · sobre #4B2FBF 5.68:1
   El #898989 no pasa sobre el violeta-600, y se usa igual porque en esa banda no
   hay ni un texto atenuado. Si algún día se pone texto atenuado en la banda violeta,
   el chequeo de contraste de tema.mjs lo va a decir.
   (El ejemplo que citaba esta nota era `tex134`, la nota de desarrollo del cierre de
   la home; se eliminó el 2026-08-10 al poner ahí el formulario real, así que se quita
   para que el comentario no describa algo que ya no existe — 8.37.) ── */
/* ⚠️ `.cc-proyectos` entró el 2026-08-10, y es la isla que faltaba. El carrusel de
   proyectos de las 4 hijas de /servicios/ pinta un velo `rgba(10,10,15,.78)` SOBRE la
   foto, así que su fondo es oscuro en los dos temas — pero su texto se volteaba a tinta
   con el resto de la página: `rgb(32,30,44)` sobre ese velo da **1.58:1** y el mínimo AA
   es 4.5. Eran 28 nodos en cuatro páginas terminadas el día anterior, y las nueve suites
   pasaban en verde porque ninguna recorría los nodos del carrusel.
   Se añade a la LISTA en vez de escribir una regla nueva a propósito: es la misma
   pregunta que ya resolvía este bloque, y así el día que el carrusel gane un texto
   atenuado o un color de unidad, ya está cubierto. Medido: los 10 textos del carrusel
   viven DENTRO del velo y ninguno fuera, o sea la isla no se pasa de alcance. */
html[data-theme="light"] :is(#brxe-sec061, #brxe-sec127, #brxe-ftr001, .cc-cta, .cc-isla-oscura, .cc-proyectos) {
  --cc-fg:        var(--cc-white);
  --cc-fg-strong: var(--cc-white);
  --cc-fg-read:   var(--cc-fg-read-on-dark);
  --cc-fg-muted:  var(--cc-ink-meta);
  --cc-fg-link:   var(--cc-green-500);
  --cc-fg-accent: var(--cc-green-500);
  /* Y las SUPERFICIES, que en la primera pasada faltaron (2026-08-03, reportado por
     Ricardo: "las redes del footer se ven muy claras y no se notan los logos").
     La isla devolvía los colores de TEXTO pero no los de superficie, así que las cajas
     de las 3 redes seguían pintadas con el `--cc-bg-surface` del tema claro —papel
     `#EEEEF1`— dentro de un footer `#201E2C`, con el icono verde de marca encima:
     **1.30:1**, o sea tres cuadrados casi blancos con el logo lavado. En oscuro el
     mismo par da 11.35:1, que es la prueba de que el color del icono no era el
     problema: lo era su fondo.
     Va acá y no en `.cc-social__link` por la razón de todo este bloque: un token
     re-declarado cubre también lo que se agregue mañana dentro de la isla; una regla
     por componente cubre las 3 cajas de hoy. Medido tras el arreglo: 10.89:1. */
  --cc-bg-surface:     var(--cc-ink-800);
  --cc-border-on-dark: var(--cc-hairline-on-dark);
  /* Los titulares de esas secciones se pintan con la paleta de Bricks, así que la
     isla también tiene que devolverle su valor original a ese id. */
  --bricks-color-fdhndm: var(--cc-white);
}

/* Texto sobre los botones de marca. `xyartc` (el casi-negro) se usa como COLOR DE
   TEXTO en los 3 CTA del hero y en el del header, encima del verde de marca —
   que no cambia. Al voltear el token a blanco, ese par pasaba de 13:1 a 1.5:1:
   texto blanco sobre verde claro, ilegible. Es el fallo más fácil de introducir
   al voltear una paleta, porque el mismo id hace dos trabajos opuestos. */
html[data-theme="light"] :is(#brxe-hsl1b, #brxe-hsl2b, #brxe-hsl3b, #brxe-5fac28, .cc-btn-marca) {
  color: var(--cc-ink-900);
}
/* El botón de envío de /contacto/ entra en la MISMA regla, y no por parecido: es
   literalmente el mismo par —relleno de marca con tinta encima, 13:1— solo que lo pinta
   Gravity Forms, así que no se le puede poner `.cc-btn-marca` desde aquí. Sin esta línea
   heredaba el color del formulario y en claro daba **3.7:1** (medido el 2026-08-16). */
html[data-theme="light"] .cc-contacto-form :is(.gform_footer input[type="submit"], .gform_footer button, .gform_button) {
  color: var(--cc-ink-900);
}
/* `.cc-btn-marca` es la forma de entrar a esta regla sin sumar un id más: la estrenan
   los 2 CTA de /servicios/ (2026-08-03). Y no es una precaución teórica — los dos
   botones nuevos FALLARON esta prueba en la primera corrida, con exactamente el
   1.5:1 que describe el comentario de arriba. La trampa no se aprende una vez: se
   vuelve a pisar en cuanto se escribe un botón nuevo. Por eso el chequeo de tema.mjs
   también reconoce la clase, no una lista. */
/* Y el botón de email del footer es violeta con texto `fdhndm`: mismo caso. */
html[data-theme="light"] #brxe-ftr010 { color: var(--cc-white); }

/* --- Los LOGOS son blancos: sobre fondo claro desaparecen -------------------
   No es un problema de contraste que una medición de texto pueda ver — son
   imágenes. El isologo tiene las letras en blanco y el acento en el verde
   ANTERIOR al cambio del 2026-07-23 (#0BE5A2, 178 trazos), así que la variante
   oscura se derivó recolorando las dos cosas: letras a --cc-violet-ink y acento
   al verde vigente. Vive en el child theme y no en uploads por la misma razón que
   los logos de partners: un archivo versionado funciona en cualquier entorno sin
   re-apuntar un id de attachment (§10).
   Se pinta como fondo del ENLACE y se esconde el <img> con opacidad —no con
   display:none— para que el img siga ocupando su caja y el layout no se mueva. */
html[data-theme="light"] .cc-hit-logo {
  background-image: url("../img/canal-cero-isologo-ink.svg");
  background-repeat: no-repeat;
  background-position: left center;
  background-size: contain;
}
html[data-theme="light"] .cc-hit-logo .bricks-site-logo { opacity: 0; }

/* Los logos de partners son monocromo BLANCO: `brightness(0)` los pasa a negro
   conservando el alpha, que es la contraparte correcta de un monocromo. */
html[data-theme="light"] .cc-trust__logo { filter: brightness(0); }
/* TikTok ya venía con `brightness(0) invert(1)` para pasarlo de negro a blanco;
   en claro se queda en el primer paso. (Está parqueado, pero si se reactiva no
   debería aparecer invertido.) */
html[data-theme="light"] .cc-trust__logo--tiktok { filter: brightness(0); }

/* Nota: el badge de Google tuvo aquí una excepción (`filter: none`) mientras se sirvió
   la tarjeta CUADRADA a color del kit oficial, que `brightness(0)` habría dejado como
   una silueta. Al pasar al lockup horizontal monocromo blanco (2026-08-13) la excepción
   sobra: se voltea igual que sus cinco vecinos. Menos casos especiales, no más. */
/* El sello oficial de Google viene con fondo blanco: sobre blanco necesita que el
   halo pase a tinta, o queda flotando sin borde. */
html[data-theme="light"] .cc-badge-google {
  box-shadow: 0 0 0 1px rgba(32, 30, 44, 0.14), 0 10px 26px -12px rgba(32, 30, 44, 0.25);
}

/* --- Componentes con blanco-alpha escrito a mano en ESTA hoja ---------------
   Todos cumplen el mismo papel (separar superficies) y en claro cambian de signo. */
html[data-theme="light"] .cc-trust,
html[data-theme="light"] .cc-band {
  border-top-color: rgba(32, 30, 44, 0.10);
  border-bottom-color: rgba(32, 30, 44, 0.10);
}
html[data-theme="light"] .cc-legal,
html[data-theme="light"] .cc-clientes { border-top-color: rgba(32, 30, 44, 0.12); }
html[data-theme="light"] .cc-fnav a { color: var(--cc-fg-read); }
html[data-theme="light"] .cc-fnav a:hover { color: var(--cc-fg-accent); }
html[data-theme="light"] .cc-social__link { color: var(--cc-fg-accent); }
html[data-theme="light"] .cc-social__link:hover {
  border-color: var(--cc-fg-accent);
  background: color-mix(in srgb, var(--cc-fg-accent) 7%, transparent);
}
html[data-theme="light"] .cc-legal a:hover,
html[data-theme="light"] .cc-code__copy,
html[data-theme="light"] .cc-postgrid .bricks-layout-inner a[href]:last-child { color: var(--cc-fg-accent); }
/* `.cc-chip` sale de la lista de arriba por la MISMA trampa de 8.32 que obligó a
   sacar a `.cc-cardlink`, y por eso está escrito justo antes: desde el 2026-08-09 el
   chip vive también dentro de islas de unidad (las tarjetas de Desarrollo), donde
   tiene que tomar `--cc-unidad`. Con la regla en la lista, esa herencia funcionaba en
   oscuro y en claro quedaba pisada — (1,1,0) contra (0,1,0)—, así que los chips de
   Commerce volvían al verde de tinta dentro de un borde violeta.
   El fallback deja intacto el chip del blog, que no está en ninguna isla. */
html[data-theme="light"] .cc-chip {
  color: var(--cc-unidad, var(--cc-fg-accent));
  border-color: color-mix(in srgb, var(--cc-unidad, var(--cc-fg-accent)) 45%, transparent);
}
/* `.cc-cardlink` sale de la lista de arriba y va con el fallback del color de unidad,
   por la trampa de 8.32: esta regla es (1,1,0) y la de `.cc-cardlink` a secas (0,1,0),
   así que el `var(--cc-unidad)` de la isla funcionaba en oscuro y en claro quedaba
   pisado — los cuatro enlaces volvían al verde de tinta. Medido antes del arreglo: en
   oscuro cada unidad con su color, en claro los cuatro en rgb(6,122,87).
   El fallback mantiene intacto el comportamiento fuera de una isla. */
html[data-theme="light"] .cc-cardlink { color: var(--cc-unidad, var(--cc-fg-accent)); }
html[data-theme="light"] .cc-cardlink:hover {
  color: color-mix(in srgb, var(--cc-unidad, var(--cc-fg-accent)) 80%, var(--cc-fg-strong));
}
html[data-theme="light"] .cc-nav-toggle { border-color: rgba(32, 30, 44, 0.18); }
html[data-theme="light"] .cc-nav-toggle:hover { background-color: rgba(32, 30, 44, 0.04); }
html[data-theme="light"] .cc-hero__pausa { color: var(--cc-fg-muted); }
html[data-theme="light"] .cc-hero__pausa:hover { color: var(--cc-fg-accent); }
/* El panel del menú móvil se pintaba con el hex del fondo oscuro.
   ⚠️ VA DENTRO DE LA MISMA MEDIA QUERY que la regla que corrige, y eso costó un
   bug: la regla original (`background: var(--cc-ink-900)`) solo existe bajo 767px,
   pero mi override estaba FUERA, así que pintaba el nav de DESKTOP en blanco
   opaco. Sobre el header translúcido eso se veía como una costura —bloque blanco
   en el centro, banda gris a los lados— y solo aparecía al pasar el header pegado
   por encima de una sección oscura. Lo cazó una captura, no una medición.
   Regla: al pisar una declaración que vive en una media query, el override va en
   la misma condición; si no, se aplica donde nadie lo pidió.

   ⚠️ Y VA CON `.cc-nav-js`, que es la segunda mitad de la misma lección y costó un
   segundo bug (2026-08-03, reportado por Ricardo: "desplegable en negro en el modo
   teléfono, modo claro"). Con la media query ya corregida, el override seguía
   perdiendo: `html[data-theme="light"] #brxe-navgrp` es (1,1,1) y la regla oscura
   `html.cc-nav-js #brxe-navgrp` es (1,1,1) también —un atributo pesa igual que una
   clase—, así que el empate lo rompía el ORDEN y la oscura vive 400 líneas más
   abajo. Medido a 390px con el panel abierto: fondo rgb(10,10,15) con los links ya
   volteados a rgb(68,67,78), o sea texto oscuro sobre panel casi negro (1.9:1).
   Al pedir también `.cc-nav-js` —que es la MISMA condición bajo la que existe la
   regla oscura, no un selector inflado— queda (1,2,1) y gana sin depender del
   orden. Regla completa: un override tiene que reproducir las dos condiciones de
   lo que pisa (la media query Y el estado), y ganar por especificidad, no por
   estar más abajo en el archivo. */
@media (max-width: 767px) {
  html[data-theme="light"].cc-nav-js #brxe-navgrp {
    background: var(--cc-bg);
    border-bottom-color: rgba(32, 30, 44, 0.12);
  }
  html[data-theme="light"].cc-nav-js #brxe-navgrp .brxe-text { border-bottom-color: rgba(32, 30, 44, 0.08); }
}
/* Código: en claro va claro. La alternativa —dejar el bloque oscuro como pieza
   con color propio— obligaba a re-afirmar a mano el fondo, la barra, el rótulo y
   el texto, o sea cuatro excepciones para ganar una. */
html[data-theme="light"] .cc-code pre,
html[data-theme="light"] .cc-post__content pre { color: var(--cc-fg); }
html[data-theme="light"] .cc-post__content :not(pre) > code {
  background: rgba(32, 30, 44, 0.07);
  color: var(--cc-fg-accent);
}
html[data-theme="light"] .cc-post__content a { color: var(--cc-fg-accent); }
/* La barrita de acento del H2 de la nota y la del H2 de sección son verde de
   marca sobre el fondo: en claro necesitan el verde de tinta para verse. */
html[data-theme="light"] .cc-post__content h2 { border-left-color: var(--cc-fg-accent); }
html[data-theme="light"] .cc-toc a.is-active { border-left-color: var(--cc-fg-accent); }

/* Las dos auroras del hero están calibradas contra #0A0A0F: sobre blanco, el
   violeta al 55% sería una mancha, así que bajan — pero el primer intento las bajó
   DEMASIADO. Medido el 2026-08-03: con 0.14 y 0.10 daban **1.269 y 1.149** de
   contraste sobre el blanco, o sea el hero claro era un vacío de 702px con el
   titular a un lado. En oscuro la misma composición tiene la aurora violeta a
   **1.446** haciendo todo el trabajo atmosférico.
   Estos valores son el punto en que aportan lo mismo que en oscuro sin ensuciar: el
   verde vuelve al de MARCA y no al de tinta, porque acá es un velo de color, no un
   texto que deba pasar contraste. */
html[data-theme="light"] #brxe-sec001::before,
html[data-theme="light"] .cc-aurora::before {
  background: radial-gradient(55% 65% at 78% 30%, rgba(75, 47, 191, 0.26) 0%, rgba(75, 47, 191, 0) 70%);
}
html[data-theme="light"] #brxe-sec001::after,
html[data-theme="light"] .cc-aurora::after {
  background: radial-gradient(40% 45% at 88% 85%, rgba(61, 237, 180, 0.24) 0%, rgba(61, 237, 180, 0) 70%);
}

/* ==========================================================================
   ELEVACIÓN EN REPOSO (2026-08-03)
   Medido: la tarjeta contra su sección daba **1.096** en oscuro y **1.053** en
   claro, y la sombra existía SOLO en `:hover`. En una interfaz clara la elevación
   es lo que crea jerarquía, así que un relleno a 1.05 con una línea de 1px se lee
   como wireframe, no como tarjeta.
   El arreglo es distinto por tema a propósito, porque el mecanismo es distinto:
   · en OSCURO la elevación la da la superficie más clara (el relleno subió a 0.06
     y la superficie de sección a #1C1A2A), no una sombra: `--cc-shadow-card-dark`
     es negro al 60% y sobre #0A0A0F no se ve nada;
   · en CLARO la da la sombra, que es como funciona el papel.
   ========================================================================== */
html[data-theme="light"] :is(
  #brxe-blo046, #brxe-blo050, #brxe-blo054, #brxe-blo058,
  #brxe-blo087, #brxe-blo092, #brxe-blo097
),
html[data-theme="light"] .cc-svc-card,
html[data-theme="light"] .cc-postgrid .bricks-layout-item,
html[data-theme="light"] .cc-related .repeater-item,
html[data-theme="light"] .cc-silo {
  /* Dos capas: un contacto corto que asienta el borde y una difusa que da la altura.
     Alfas bajos a propósito — sobre papel, una sombra al 20% se lee como suciedad. */
  box-shadow: 0 1px 2px rgba(32, 30, 44, 0.06), 0 8px 20px -8px rgba(32, 30, 44, 0.10);
}
/* El hover sube un paso, así que sigue habiendo diferencia entre reposo y puntero
   (que es lo que se perdería si la sombra de reposo fuera la misma que la de hover). */
html[data-theme="light"] .cc-postgrid .bricks-layout-item:hover,
html[data-theme="light"] :is(#brxe-blo046, #brxe-blo050, #brxe-blo054, #brxe-blo058,
  #brxe-blo087, #brxe-blo092, #brxe-blo097):hover {
  box-shadow: 0 2px 4px rgba(32, 30, 44, 0.08), 0 18px 36px -12px rgba(32, 30, 44, 0.18);
}

/* ==========================================================================
   EL FOOTER TERMINA LA PÁGINA, NO LA REABRE (2026-08-03)
   En claro la secuencia era: secciones blancas → cierre #201E2C (salto **16.4**) →
   footer blanco otra vez. Blanco → casi negro → blanco en 1.200px es un golpe y
   deja el footer flotando como si empezara otra página. En oscuro esa misma
   secuencia es 1.2 y 1.0, o sea fluida.
   Se resuelve llevando el footer a la superficie del cierre: los dos forman un solo
   bloque de cierre y la página termina donde termina. El footer entra a la lista de
   ISLAS oscuras de más arriba, así que todo su texto recupera los valores del tema
   oscuro sin una sola regla por elemento.
   ========================================================================== */
html[data-theme="light"] #brxe-ftr001 { background-color: var(--cc-violet-ink); }
/* Y su logo vuelve al blanco: la variante en tinta es para el header, que en claro
   SÍ es claro. Es el único sitio donde los dos logos coexisten en el mismo tema. */
html[data-theme="light"] #brxe-ftr005 { background-image: none; }
html[data-theme="light"] #brxe-ftr005 .bricks-site-logo { opacity: 1; }
/* El sello de Google y las redes vuelven a su tratamiento sobre oscuro. */
html[data-theme="light"] #brxe-ftr001 .cc-badge-google {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 10px 26px -12px rgba(0, 0, 0, 0.8);
}
html[data-theme="light"] #brxe-ftr001 :is(.cc-legal, .cc-clientes) { border-top-color: rgba(255, 255, 255, 0.08); }

/* El anillo de foco global no cambia: su halo exterior ya es oscuro justamente
   para contrastar sobre fondo claro (era el caso de las 25 páginas del diseño
   viejo), así que sirve igual en los dos temas. */


/* ==========================================================================
   HERO SLIDER — los slides ocultos tienen que QUITAR PRESENCIA (2026-07-29)
   Bug medido el 2026-07-28: los 3 slides del hero están siempre en el DOM y los
   2 inactivos quedaban en `opacity:0` con `visibility:visible`, sin `inert` y
   sin `aria-hidden`, así que sus CTA conservaban `tabIndex:0`. Verificado a 390
   y 1280px: navegando con teclado el foco DESAPARECÍA de pantalla al pasar por
   "Quiero crecer" y "Quiero ver su trabajo", y un lector de pantalla anunciaba
   los 3 CTA del hero como si coexistieran.
   Causa raíz: `opacity:0` no saca del orden de tabulación. Es el mismo principio
   de 8.12 — el estado oculto debe quitar presencia, no solo dejar de pintar.
   Gotcha 8.14 de CLAUDE.md.

   El arreglo del camino normal vive en `hero-slider.js` (`inert` + `aria-hidden`
   por slide), que es donde ya se conoce cuál está activo y no pelea con la
   especificidad del `_cssCustom` por-ID del elemento.
   Esta regla cubre el camino SIN JS, que el JS por definición no puede cubrir:
   ahí manda `#brxe-heswrp:not(.js) > .brxe-block:not(:first-child)`, que oculta
   los slides 2 y 3 con `opacity:0` y los deja igual de tabulables.
   `visibility:hidden` sí los saca del orden de tabulación Y del árbol de
   accesibilidad, sin JS de por medio.
   Por qué acá y no en el `_cssCustom` de home.json: `visibility` no la declara
   ninguna regla del elemento, así que aplica sin depender del orden de carga
   (gotcha 8.1) y el arreglo viaja en git sin re-exportar el template ni escribir
   en la BD. Y no afecta el crossfade: sin `.js` no hay rotación que animar.
   ========================================================================== */
#brxe-heswrp:not(.js) > .brxe-block:not(:first-child) {
  visibility: hidden;
}
/* ==========================================================================
   RESPUESTA AL TOQUE (2026-08-23)

   Toda la interacción de la portada vivía en `:hover`, y en táctil no hay hover. Medido
   en un contexto móvil real (`hasTouch`, sin puntero): al tocar una tarjeta de servicio,
   una de caso o el CTA del hero **no cambiaba absolutamente nada**.

   ⚠️ Y la primera versión de este bloque decía «no había NI UNA regla `:active` en toda la
   hoja». **Era falso**: existían `.brxe-button:active` y `.cc-fab__contacto:active`, las
   dos poniendo `translateY(0)`. El defecto era más fino y más interesante: esos `:active`
   solo DESHACÍAN el lift del hover, así que en táctil —donde el hover nunca ocurrió— iban
   de `none` a `0` y no producían ningún cambio. **Un estado que existe y es un no-op donde
   más se usa.** Lo destapó preguntarle al navegador con `CSS.getMatchedStylesForNode` por
   qué mi regla no aplicaba: perdía por ORDEN contra `.brxe-button:active`, que ya estaba.
   Por eso el press de los botones se arregla EN esa regla (arriba) y no aquí.

   ⚠️ Y el destello del toque era el AZUL POR DEFECTO DE CHROME.
   `-webkit-tap-highlight-color` resolvía a `rgba(51, 181, 229, 0.4)` — un cian que este
   design system nunca eligió, destellando en cada toque de cada enlace sobre una paleta
   de verde y violeta. No se ve en escritorio, así que nadie lo había visto.

   Se tiñe de marca en vez de apagarse con `transparent`, y el orden importa: apagarlo sin
   poner un estado propio a cambio habría quitado la ÚNICA respuesta que existía. Primero
   el estado, después el tinte. El 18 % de alfa es lo que hace que se note sin pintar el
   texto de verde.

   Los `:active` van por PRIMITIVA, no por sección: `.bricks-button` son los 5 botones del
   sitio, `.cc-cardlink` los 5 enlaces de tarjeta, y el resto son los del nav, el pie y el
   panel de contacto. Un botón se HUNDE (scale) y un enlace se APAGA (opacity), que es lo
   que cada uno hace en cualquier sistema operativo.
   ========================================================================== */
html {
  -webkit-tap-highlight-color: rgba(61, 237, 180, 0.18);
}

/* Los botones que NO son `.brxe-button` (el de Gravity Forms, el flotante, el «Ver más»
   del blog): mismo hundimiento. Los `.brxe-button` se resuelven en su propia regla, más
   arriba, porque ahí ya había un `:active` que gana a este por orden. */
.gform_button:active,
.cc-fab__btn:active,
.cc-vermas:active {
  transform: scale(0.97);
}

/* Enlaces: el press se lee como apagado. No se toca el color, porque estos enlaces ya
   distinguen su estado por color en hover y encadenar dos cambios de color en la misma
   pieza deja el toque ilegible. */
.cc-cardlink:active,
.cc-nav a:active,
.cc-nav__padre:active,
.cc-nav__sublink:active,
.cc-social__link:active,
.cc-contacto-valor:active,
.cc-hit-logo:active,
.cc-fnav a:active,
.cc-legal a:active {
  opacity: 0.6;
}

/* Los controles de interfaz que ya son un icono: el press baja su opacidad como los
   enlaces, porque un `scale` sobre 44px se lee como un salto, no como una pulsación. */
.cc-nav-toggle:active,
.cc-nav__sub-toggle:active,
.cc-hero__pausa:active,
.cc-marcas__pausa:active {
  opacity: 0.6;
}

/* Quien pidió menos movimiento no pierde la respuesta, la cambia: el hundimiento se
   sustituye por el mismo apagado que usan los enlaces (8.23). */
@media (prefers-reduced-motion: reduce) {
  .brxe-button:active,
  .gform_button:active,
  .cc-fab__btn:active,
  .cc-vermas:active {
    transform: none;
    opacity: 0.6;
  }
}

/* ==========================================================================
   ANILLO DE FOCO GLOBAL (2026-07-29)
   Antes solo tenían foco visible 3 componentes (`.cc-social__link`, `.cc-legal a`
   y `.cc-badge-google`); el resto del sitio dependía del anillo por defecto del
   navegador. Esto lo unifica para todo lo enfocable.
   Dos detalles que no son adorno:
   · `:where()` mantiene la especificidad en CERO, así que cualquier regla de
     componente (o de ID de Bricks) puede pisarlo sin pelear (gotcha 8.1).
   · El anillo es DOBLE a propósito. El verde de marca sobre blanco da 1.5:1, y
     un indicador de foco necesita 3:1 contra lo que tiene al lado (WCAG 1.4.11).
     El halo oscuro exterior le da ese contraste en las páginas claras del diseño
     viejo, y el verde lo da en las oscuras del nuevo. Sirve en las dos.
   `:focus-visible` y no `:focus`: el ratón no dispara el anillo, el teclado sí.
   ========================================================================== */
:where(a, button, input, select, textarea, summary, [tabindex="0"], [role="button"]):focus-visible {
  outline: 3px solid var(--cc-green-500);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px rgba(10, 10, 15, 0.55);
}

/* ==========================================================================
   ÁREAS DE TOQUE (2026-07-29) — WCAG 2.5.8 pide 24x24 CSS px como mínimo
   Medido: los 4 links del nav salían 67x19, 75x19, 70x19 y 35x19, y los dots del
   hero 26x6 / 36x6. O sea: fallaban el mínimo por 5px el nav y por 18px los dots.
   Se agranda el ÁREA, no el dibujo: un `::after` transparente que no ocupa flujo,
   así no se mueve un pixel del layout (verificado por bounding box).
   Las medidas salen del espacio real disponible, no de un número redondo:
   ========================================================================== */

/* Nav del header. A 1440 el grupo mide 20–64px de alto y los links 32–51, así que
   caben 44px centrados sin tocar nada. A 390 el LOGO termina a 17px y los links
   empiezan a 20: solo hay 3px arriba, y un área centrada de 44px se comería el
   logo (se solapan en x). Por eso en móvil el área crece SOLO HACIA ABAJO, donde
   hay 21px libres hasta el botón Contacto. */
.cc-nav a { position: relative; }
.cc-nav a::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; transform: translateY(-50%); height: 44px;
}
@media (max-width: 767px) {
  .cc-nav a::after { top: 0; transform: none; height: 40px; }
}


/* Dots del hero: 6px de alto con 10px de separación. El área se lleva a 44px de
   alto y se extiende 5px a cada lado, justo la mitad del gap: quedan pegadas
   entre sí pero nunca superpuestas (una superposición haría que el dot 2 recibiera
   toques dirigidos al 1). */
.cc-dot { position: relative; }
.cc-dot::after {
  content: ""; position: absolute; left: -5px; right: -5px;
  top: 50%; transform: translateY(-50%); height: 44px;
}
/* Enlaces de logo (header y footer). El SVG rinde a 180x17, así que el enlace medía
   17px de alto: por debajo del mínimo de 24. En desktop hay 84px de header para
   repartir, así que el área se centra en 44px.
   En móvil NO se puede centrar: el logo vive en la primera fila y el nav empieza
   13px más abajo, de modo que un área centrada le robaría los toques al nav (se
   solapan en x). Ahí crece solo hacia arriba/abajo hasta el borde disponible. */
/* Blindaje del logo contra el CSS de "logos partners" (8.102).
   Scripts Organizer sirve `142-header.css` desde la BD con un bloque
   `.brxe-logo { flex: 1 1 calc(20% - 16px); margin-bottom: 35px; max-width:
   calc(20% - 16px); display: flex; padding: 0 1rem }` escrito para la fila de
   logos de partners del footer. Pero `.brxe-logo` es la clase que Bricks pone a
   TODO elemento Logo, así que la regla alcanza también al del header: dentro de
   `.cc-nav-shell`, que centra con `align-items:center`, esos 35px de margen
   inferior entran en la caja centrada y suben el logo 17px — la mitad exacta.
   El CSS culpable vive en la BD y en un plugin de licencia, así que el arreglo
   va aquí, donde sí está versionado, y apunta solo a NUESTRO logo: los de
   partners siguen recibiendo su regla intacta. Dos clases ganan a una, así que
   no depende del orden de carga (8.1). */
.brxe-logo.cc-hit-logo {
  flex: 0 0 auto;
  max-width: none;
  margin-bottom: 0;
  padding: 0;
  display: block;
}

.cc-hit-logo { position: relative; }
.cc-hit-logo::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; transform: translateY(-50%); height: 44px;
}
/* Ya no hay excepción móvil. La había cuando el header se apilaba en 3 filas y el
   nav empezaba 3px debajo del logo: un área centrada le robaba los toques. Con el
   menú colapsado (≤767px) el header vuelve a UNA fila y el logo tiene los 84px del
   container para él, así que los 44px centrados aplican en todos los anchos. */

/* Nav del footer: los 4 items están a 34px de distancia entre sí (18px de texto +
   gap de 10px + márgenes). Un área de 44px se solaparía 10px con el vecino y los
   toques del borde irían al link equivocado, que es PEOR que un target chico. Se
   usan 32px: pasa el mínimo de 24 y deja 2px de aire. */
.cc-fnav a { position: relative; }
.cc-fnav a::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; transform: translateY(-50%); height: 32px;
}

/* Chip de categoría del single post: el enlace es solo el texto (90x14) dentro de
   un chip que sí tiene relleno visual. El área cubre el chip completo. */
.cc-chip a { position: relative; }
.cc-chip a::after {
  content: ""; position: absolute; left: -10px; right: -10px;
  top: 50%; transform: translateY(-50%); height: 28px;
}

/* ==========================================================================
   ESTADOS DE INTERACCIÓN (2026-07-29) — auditoría UI/UX
   Medido con Playwright hoverendo cada elemento interactivo y comparando estilos
   computados antes/después: **11 controles de la home no cambiaban NADA**, entre
   ellos el CTA principal del hero ("Muéstrame los números"), el botón de email del
   footer, los dots del slider y los dos enlaces de logo. Un control que no
   responde al puntero no se lee como control.
   (Las cards del blog SÍ reaccionan —lift de -6px y borde verde—; ese hallazgo era
   un falso positivo de medir el `<a>` en vez del contenedor donde vive el hover.)

   ⚠️ CÓMO se hace importa, por 8.1: Bricks emite el fondo de cada elemento como
   regla de ID, así que un `:hover` desde una clase NO puede cambiar
   `background-color`. Dos salidas, y acá se usan las dos:
   · Para los 2 botones con color de marca, el hover se declara EN EL TEMPLATE
     (`_background:hover`), que es lo que Bricks emite como `#brxe-xxx:hover`. Es la
     vía nativa: no pelea especificidad y queda visible en el builder.
   · Para el resto se usan propiedades que la regla de ID NO declara —`filter`,
     `transform`, `opacity`, `text-decoration`— que aplican sin pelear (8.1).
   ========================================================================== */

/* Botones: micro-lift universal. El color lo pone el template; esto añade la
   respuesta física, que es lo que hace que se sienta un botón. */
.brxe-button {
  transition: transform var(--cc-dur-fast) var(--cc-ease-out),
              box-shadow var(--cc-dur-fast) var(--cc-ease-out),
              filter var(--cc-dur-fast) var(--cc-ease-out);
}
.brxe-button:hover { transform: translateY(-1px); }
/* ⚠️ EL PRESS PASA DE `translateY(0)` A `scale` EL 2026-08-23, y el porqué es que en
   TÁCTIL el anterior no hacía nada. `translateY(0)` solo DESHACE el `-1px` del hover — y
   en un teléfono no hay hover, así que el botón iba de `none` a `0`: cero cambio visible.
   Medido en un contexto con `hasTouch` y sin puntero: al tocar el CTA del hero no cambiaba
   ni una propiedad. El estado existía y era un no-op donde más se usa.
   Con `scale` el press se ve venga o no de un hover, y en escritorio además compone bien:
   una sola `transform` gana, así que el botón cae de su elevación y se comprime a la vez. */
.brxe-button:active { transform: scale(0.97); }
@media (prefers-reduced-motion: reduce) {
  .brxe-button:hover { transform: none; filter: brightness(0.94); }
}

/* Enlaces de logo: no tenían ningún estado. Un atenuado leve basta para decir
   "esto es un enlace" sin competir con la marca. */
.cc-hit-logo { transition: opacity var(--cc-dur-fast) var(--cc-ease-out); }
.cc-hit-logo:hover { opacity: 0.72; }

/* Nav del footer. Antes el color venía en un `style=` INLINE en el HTML del
   template, que le gana a cualquier hoja de estilos salvo con !important: era
   imposible darle hover. Se quitó del template y el color vive acá. */
.cc-fnav a {
  color: rgba(255, 255, 255, 0.7);
  text-decoration: none;
  transition: color var(--cc-dur-fast) var(--cc-ease-out);
}
.cc-fnav a:hover { color: var(--cc-green-500); }



/* Iconos de compartir de la nota (X, LinkedIn, Facebook). */
.brxe-post-sharing a {
  transition: transform var(--cc-dur-fast) var(--cc-ease-out), filter var(--cc-dur-fast) var(--cc-ease-out);
}
.brxe-post-sharing a:hover { transform: translateY(-2px); filter: brightness(1.9); }

/* Migas de pan: subrayado al pasar, que es lo que se espera de una miga. */
.brxe-breadcrumbs a:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ==========================================================================
   ENLACES EN PROSA: no pueden distinguirse solo por color (WCAG 1.4.1, nivel A)
   Medido en la nota: los enlaces del cuerpo salían **sin subrayado**, con el mismo
   peso que el texto que los rodea y con **1.13:1** de contraste contra ese texto.
   La técnica de WCAG pide 3:1 contra el texto vecino SI el único distintivo es el
   color; con 1.13 no hay forma de verlos. Es nivel **A**, el más básico, y además
   es un problema de negocio: son los enlaces internos del blog.
   Se resuelve con subrayado, que es lo que la gente ya sabe leer como enlace. El
   `text-underline-offset` y el grosor fino evitan que se vea pesado sobre el texto
   de 18px/1.75 de la nota.
   Se limita al CUERPO del artículo a propósito: en un menú o una card el subrayado
   sería ruido, porque ahí el contexto ya dice que es navegable.
   ========================================================================== */
.cc-post__content a[href] {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
  transition: text-decoration-color var(--cc-dur-fast) var(--cc-ease-out);
}
.cc-post__content a[href]:hover {
  text-decoration-thickness: 2px;
  text-decoration-color: currentColor;
}

/* Tipografía del footer: 5 nodos ("Navegación" y los 4 links legales) no declaraban
   familia y caían al `-apple-system` del navegador, así que el footer mezclaba dos
   tipografías. Se hereda desde la raíz del footer: los nodos que ya declaran
   Montserrat no cambian, y los que no, dejan de ser la excepción. */
.cc-foot { font-family: var(--cc-font-sans); }

/* ==========================================================================
   MENÚ MÓVIL DEL HEADER (2026-07-29)
   El problema, medido: a 390px el header ocupaba **124px** (14% del alto de
   pantalla) apilado en 3 filas —logo / 4 links / botón—, con los links a 19px de
   alto reales; a 320px se iba a 4 filas. El breakpoint no es un número redondo:
   **a ≤767px el logo y el nav dejan de caber en la misma fila** (medido de 1440 a
   320px de 40 en 40), y ahí es donde entra el menú.

   Reparto de responsabilidades:
   · El botón se renderiza en el SERVIDOR (elemento `navtgl` de header-2026.json),
     así existe en el primer pintado.
   · Este CSS solo colapsa bajo `html.cc-nav-js`, clase que pone un script inline de
     <head> antes de pintar. Sin JS: botón oculto, nav visible como siempre.
   · `cc-nav.js` alterna el atributo `hidden` del panel — no una clase. `hidden`
     saca el panel del árbol de accesibilidad Y del orden de tabulación de una vez;
     con `opacity`/`transform` los links seguirían siendo tabulables (gotcha 8.14).
   Es un DESPLEGABLE, no un modal: no tapa la pantalla, así que no hay foco
   atrapado ni scroll bloqueado, y el orden de tabulación entra al panel solo
   porque es el siguiente elemento del DOM después del botón.
   ========================================================================== */

/* El botón no existe visualmente salvo en móvil CON JS. Sin la clase del guard
   queda oculto en todos los anchos: nunca se ve un control que no hace nada. */
.cc-nav-toggle-wrap { display: none; }
.cc-nav-toggle-wrap > p { margin: 0; display: flex; }

.cc-nav-toggle {
  /* 44x44 desde el principio: es el control de navegación en móvil, no puede
     depender de un `::after` para cumplir el mínimo (WCAG 2.5.8). */
  width: 44px; height: 44px;
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  gap: 5px;
  padding: 0;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--cc-radius-sm);
  cursor: pointer;
  transition: border-color var(--cc-dur-fast) var(--cc-ease-out),
              background-color var(--cc-dur-fast) var(--cc-ease-out);
}
.cc-nav-toggle:hover { border-color: var(--cc-green-500); background-color: rgba(255, 255, 255, 0.04); }
.cc-nav-toggle__bar {
  display: block; width: 20px; height: 2px; border-radius: 2px;
  background: var(--cc-fg-strong);
  transition: transform var(--cc-dur-fast) var(--cc-ease-out),
              opacity var(--cc-dur-fast) var(--cc-ease-out);
}
/* Hamburguesa -> X. El estado sale de `aria-expanded`, o sea del MISMO atributo que
   lee un lector de pantalla: no hay forma de que lo visual y lo anunciado se
   desincronicen. */
.cc-nav-toggle[aria-expanded="true"] .cc-nav-toggle__bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.cc-nav-toggle[aria-expanded="true"] .cc-nav-toggle__bar:nth-child(2) { opacity: 0; }
.cc-nav-toggle[aria-expanded="true"] .cc-nav-toggle__bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
  .cc-nav-toggle__bar { transition: none; }
}

@media (max-width: 767px) {
  html.cc-nav-js .cc-nav-toggle-wrap { display: block; }

  /* El grupo del nav pasa de barra horizontal a panel bajo el header.
     `position:absolute` y no `fixed` a propósito: el header tiene
     `backdrop-filter`, que crea bloque contenedor para los descendientes fijos, así
     que un `fixed` NO se posicionaría contra el viewport. Absolute contra el header
     hace exactamente lo que se quiere y no depende de ese detalle. */
  html.cc-nav-js #brxe-navgrp {
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0;
    padding: 8px 20px 20px;
    background: var(--cc-ink-900);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: var(--cc-shadow-card-dark);
    /* si algún día el menú crece, hace su propio scroll en vez de desbordar */
    max-height: calc(100vh - 100%);
    overflow-y: auto;
    /* ⚠️ Sin esto el `max-height` de arriba NO produce scroll: produce una SEGUNDA
       COLUMNA. Bricks emite `flex-wrap: wrap` en el bloque, y una columna flex que
       envuelve reparte lo que no cabe en una columna nueva a la derecha en vez de
       desbordar hacia abajo — o sea el `overflow-y` no tiene nada que desbordar y no
       se dispara nunca. La red de scroll llevaba ahí desde el menú móvil sin haber
       funcionado jamás, porque hasta hoy nada la superaba en alto: el mega-menú de
       cuatro unidades fue lo primero que la pasó, y entonces "Blog" y "Contacto"
       saltaron a una segunda columna encima del contenido (8.52). */
    flex-wrap: nowrap;
  }
  /* Cerrado. El `display:none` explícito es necesario: la regla de arriba declara
     `display` (via flex del elemento) y le ganaría al `[hidden]` del navegador. */
  html.cc-nav-js #brxe-navgrp[hidden] { display: none; }

  /* Cada destino es una fila de 56px con separador: cómodo para el pulgar y con la
     jerarquía que la barra horizontal no puede dar. */
  html.cc-nav-js #brxe-navgrp .brxe-text { border-bottom: 1px solid rgba(255, 255, 255, 0.06); }
  html.cc-nav-js #brxe-navgrp .brxe-text a {
    display: flex; align-items: center;
    min-height: 56px;
    font-size: 17px;
  }
  /* En el panel el área ya la da el `min-height` de la fila: el `::after` de 40px
     que se usa en la barra horizontal sobraría y se solaparía con la fila vecina. */
  html.cc-nav-js #brxe-navgrp .cc-nav a::after,
  html.cc-nav-js #brxe-navgrp .brxe-text a::after { content: none; }

  /* El CTA cierra el panel, a lo ancho: es la acción primaria. */
  html.cc-nav-js #brxe-navgrp .brxe-button {
    width: 100%;
    margin-top: 16px;
    justify-content: center;
    min-height: 48px;
    /* 14px es la medida del pill compacto del desktop; en un botón de 48px a todo
       el ancho el texto se veía perdido. */
    font-size: 16px;
  }
}

/* ==========================================================================
   DESPLEGABLE DE SERVICIOS EN EL HEADER (2026-08-04)
   Pedido por Ricardo: que "Servicios" abra el listado de sus contenidos, como en
   el header original. Ese header lo hacía con el elemento `nav-menu` de Bricks y el
   menú "Header" de WordPress; acá NO se usa, por la razón de siempre: ese menú vive
   en la BD y sumaría una fila a §10, que la idea es que encoja. El desplegable se
   escribe en el template versionado y viaja en git.

   El contenido del desplegable son los MISMOS 6 servicios, con las mismas etiquetas
   y los mismos destinos que las tarjetas de /servicios/. Es a propósito una sola
   definición: si el header y la página listaran cosas distintas, se desincronizarían
   sin que nadie se enterara (es el argumento de `ccd_seo_noindex_slugs()` aplicado a
   la navegación). Por eso `a11y.mjs` compara las dos listas, no solo cuenta enlaces.

   Reparto de responsabilidades, y NO es el mismo que el de la hamburguesa:
   · El desplegable se sirve del SERVIDOR (está en `header-2026.json`), igual que la
     hamburguesa, porque su contenido son enlaces: sin JS siguen siendo navegables.
   · Pero el BOTÓN de chevron solo existe con JS —`html:not(.cc-nav-js)` lo oculta—,
     porque sin JS no hay nada que alternar y un control muerto es peor que ninguno
     (mismo criterio que el conmutador de tema, §7, y opuesto al de la hamburguesa,
     que sin JS se oculta pero el nav queda desplegado).
   · Sobre 768px y sin JS el desplegable se abre con `:hover` y `:focus-within`, así
     que el camino de teclado y de puntero está cubierto igual. Bajo 767px sin JS el
     desplegable queda cerrado y no hay pérdida: el padre lleva a /servicios/, que
     lista los 6 con enlace (lo verifica `servicios.mjs`).
   · Con JS es el JS el que mueve `hidden` Y `aria-expanded`, los dos a la vez. Por
     eso las reglas de `:hover` van gateadas a `:not(.cc-nav-js)`: si el CSS pudiera
     revelar el panel por su cuenta, quedaría visible con `aria-expanded="false"`, o
     sea lo que se ve y lo que se anuncia diciendo cosas distintas.
   · Cerrado lleva el atributo `hidden`, no una clase: saca los 6 enlaces del árbol
     de accesibilidad Y del orden de tabulación de una vez (gotcha 8.14).
   ========================================================================== */

/* El grupo es el elemento `text` `lnksrv`: Bricks envuelve su contenido en un <p>
   (gotcha 8.9), así que el link y el botón viven DENTRO de ese <p> y el <ul> del
   submenú queda como su hermano — wpautop no mete un bloque dentro de un párrafo.
   El posicionamiento va en el div del elemento, que es el flex item del nav. */
.cc-nav__grupo { position: relative; }
.cc-nav__grupo > p { display: flex; align-items: center; gap: 4px; }

/* El chevron se dibuja en CSS, no con un SVG ni con una fuente: `peso.mjs` prohíbe
   librerías de iconos y esto no justifica ni un archivo (mismo criterio que el botón
   de pausa del hero, 8.22). */
.cc-nav__sub-toggle {
  /* 24x24 desde el principio, el mínimo de WCAG 2.5.8, sin depender de un `::after`
     que después haya que cuadrar con el del vecino (8.17). */
  width: 24px; height: 24px;
  display: flex; align-items: center; justify-content: center;
  padding: 0; background: none; border: 0; cursor: pointer;
  color: var(--cc-fg-strong);
  border-radius: var(--cc-radius-sm);
  /* Explícita y desde la escala de tokens: un `transition` heredado de otra parte
     ensucia el trinquete de duraciones de `ux.mjs` sin que se vea nada (8.28.4). */
  transition: color var(--cc-dur-fast) var(--cc-ease-out);
}
/* El color cambia en el BOTÓN y el chevron se dibuja con `currentColor`: así el
   control responde al puntero de verdad (8.18) y no solo gira su hijo. */
.cc-nav__sub-toggle:hover { color: var(--cc-fg-accent); }
.cc-nav__chevron {
  width: 7px; height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--cc-dur-fast) var(--cc-ease-out);
}
/* El giro sale de `aria-expanded`, o sea del MISMO atributo que lee un lector de
   pantalla: no hay forma de que lo visual y lo anunciado se desincronicen. */
.cc-nav__sub-toggle[aria-expanded="true"] .cc-nav__chevron { transform: translateY(1px) rotate(-135deg); }
@media (prefers-reduced-motion: reduce) {
  .cc-nav__chevron { transition: none; }
  /* Quien pidió menos movimiento no recibe ni el despliegue ni el fundido del velo.
     ⚠️ `transition: none` y NO `display: none` en el velo: el difuminado no es
     movimiento, es estado. Quitarle la animación es lo que se pidió; quitarle el
     efecto sería decidir por esa persona que además no quiere ver el velo. */
  .cc-nav__sub,
  body::before { transition: none; }
}

/* Sin JS el botón no alterna nada, así que no se muestra. */
html:not(.cc-nav-js) .cc-nav__sub-toggle { display: none; }

.cc-nav__sub { list-style: none; margin: 0; padding: 0; }
.cc-nav__sub li { margin: 0; }
/* El `display:none` explícito acompaña al `[hidden]` por la lección de 8.19: basta
   que alguien declare `display` en un selector con más peso para que el atributo
   deje de ocultar y los 6 enlaces vuelvan a ser tabulables sin que se vea nada. */
.cc-nav__sub[hidden] { display: none; }
/* El `::after` de 44px de `.cc-nav a` está pensado para una barra HORIZONTAL. En una
   columna se solaparía con la fila vecina y le robaría los toques, que es peor que un
   target chico (8.17). Acá el área la da el `min-height` de la fila. */
#brxe-navgrp .cc-nav__sub a::after { content: none; }

@media (min-width: 768px) {
  /* ── El mega-menú se ancla al CONTAINER del header, no al link (2026-08-10) ──
     La versión anterior era una lista plana de 7 destinos colgada del propio grupo.
     Con cuatro columnas eso ya no sirve: MEDIDO de 768 a 1920px, el grupo "Servicios"
     NO está centrado y se corre a la derecha al crecer la ventana (x=266 a 768px,
     x=974 a 1920px), así que un panel ancho colgado de él desborda por la izquierda en
     pantallas chicas y por la derecha en grandes. Anclado al container cae sobre la
     misma rejilla que el resto del sitio y no puede desbordar: el container ya está
     limitado a 1180px (8.7). */
  .cc-nav-shell { position: relative; }
  .cc-nav__grupo { position: static; }
  /* El puente del camino SIN JS se anclaba al grupo, que ahora es `static`. Pasa a su
     propia fila — que además es el ancho CORRECTO: el puente no puede pasar del ancho
     del link o le robaría los toques al vecino (8.17). */
  .cc-nav__grupo > p { position: relative; }

  /* ⚠️ EL PANEL CUELGA DEL HEADER, NO DEL CONTAINER (2026-08-17)

     Ricardo: *"no sé si me gusta el desplegable... siento que se ve mal chocando con el
     navbar"*. Medido, el choque tenía tres causas y ninguna era el color:

       · el panel medía 1180px y el header ocupa el ancho completo, así que se leía como
         una TARJETA CLAVADA a una barra, no como parte de ella;
       · sus cuatro esquinas iban redondeadas a 12px, así que las dos de ARRIBA dejaban
         dos muescas justo en la línea del header;
       · y se pegaba a cero píxeles de él: dos superficies distintas a tope, sin ninguna
         señal de si son la misma cosa o dos.

     Ahora es una EXTENSIÓN de la barra: ancho del header, esquinas solo abajo, y un
     hairline arriba que hace de separación entre navegar y elegir.

     ⚠️ Se probó también translúcido —que es lo que Ricardo proponía— y se descartó
     MIDIENDO: un `h1` blanco de 64px detrás se marca a través del panel incluso al 94%
     de opacidad, y compite con los enlaces. El problema nunca fue la opacidad.

     ⚠️ Y el ancho completo NO se hace con `100vw`: eso incluye la barra de scroll y
     desborda la página (8.7). Se consigue anclando el panel al HEADER —que ya es de
     ancho completo y es `position: sticky`, o sea establece bloque contenedor— en vez de
     a `.cc-nav-shell`. Cero unidades de viewport, cero riesgo de desborde. */
  .cc-nav-shell { position: static; }

  .cc-nav__sub {
    position: absolute;
    /* Cuelga del borde inferior del header, que es el mismo sitio del que colgaba la
       lista plana cuando lo calculaba desde `--cc-header-h` (8.26). */
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1;
    /* El contenido se queda en la rejilla de 1180 aunque el panel llegue a los bordes.
       `%` del propio panel y no `vw`, por lo de arriba. */
    /* El 10px no es un número redondo elegido: es el padding que el container del header
       tiene por debajo de 1180 (medido de 768 a 1080). Con él las cuatro columnas caen
       EXACTAMENTE bajo los enlaces del nav en todos los anchos; con 24 se corrían 14px y
       el menú dejaba de estar alineado con lo que lo abre. Lo vigila `a11y.mjs`. */
    padding: 22px max(10px, calc((100% - 1180px) / 2)) 26px;
    background: var(--cc-bg-surface);
    border: 0;
    border-top: 1px solid var(--cc-border-on-dark);
    border-radius: 0 0 var(--cc-radius-md) var(--cc-radius-md);
    box-shadow: var(--cc-shadow-card-dark);
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px 24px;
  }
  /* ⚠️ `display: grid` en un selector que le gana al `[hidden]` es el bug de 8.19
     esperando a ocurrir: el panel se vería en pantalla mientras se anuncia como
     cerrado y sus enlaces volverían a ser tabulables. La regla de abajo lo cierra con
     (1,3,0), que gana a cualquiera de las de este bloque. */
  html #brxe-navgrp .cc-nav__sub[hidden] { display: none; }

  /* ── El desplegable BAJA en vez de aparecer, y la página se difumina detrás ──
     ─────────────────────────────────────────────────────────────────────────────
     2026-08-18, a pedido de Ricardo: *«que sea parecido al de Apple, baja más suave,
     y se puede poner blur cuando está abierto?»*.

     **Punto de partida medido:** el panel no tenía NINGUNA transición. Era
     `display: grid` ↔ `display: none`, o sea aparecía de golpe — que es exactamente
     lo que se siente brusco. Y ese `display: none` no es negociable: es lo que
     mantiene sus enlaces fuera del orden de tabulación (8.19). La suavidad no puede
     pagarse dejando el panel medio visible con sus enlaces tabulables.

     La salida es `transition-behavior: allow-discrete` + `@starting-style`, que animan
     HACIA y DESDE `display: none` sin sacar el `display: none` del estado de reposo.
     **Cero JavaScript nuevo:** `cc-nav.js` sigue conmutando solo el atributo `hidden`.
     Y es fail-safe: en un navegador sin soporte no hay transición y el panel aparece
     como aparecía hoy, que es el peor caso aceptable (8.24).

     ⚠️ Se anima `clip-path`, y las dos alternativas se descartaron por una razón cada una:
      · `height` obligaría a un valor fijo, y el panel tiene 2 columnas a 768 y 4 a 1080;
      · `translateY` lo movería POR ENCIMA de la barra, no por debajo: el panel es
        descendiente del header y pinta sobre su fondo (`z-index: 1` dentro de él), así
        que «subirlo» lo hace salir de dentro del logo en vez de bajar de su borde.
      · `clip-path: inset()` lo despliega hacia abajo sin mover ni relayoutear nada.

     ⚠️ La duración sale de la ESCALA y la curva también. No es prolijidad: `ux.mjs`
     falla si aparece una duración fuera de las cinco declaradas, y estrenar valores
     necesita decisión de equipo (§7). **Cero tokens nuevos.** */
  .cc-nav__sub {
    clip-path: inset(0 0 0 0);
    opacity: 1;
    transition:
      clip-path var(--cc-dur-slow) var(--cc-ease-out),
      opacity   var(--cc-dur-slow) var(--cc-ease-out),
      display   var(--cc-dur-slow) allow-discrete;
  }
  /* Va en una regla APARTE de la de arriba a propósito: aquella lleva el `display:none`
     y su porqué de 8.19, que es carga estructural y conviene no mezclar con lo visual.
     Misma especificidad (1,3,0), o sea le gana al `opacity: 1` de `.cc-nav__sub`. */
  html #brxe-navgrp .cc-nav__sub[hidden] {
    clip-path: inset(0 0 100% 0);
    opacity: 0;
  }
  @starting-style {
    html #brxe-navgrp .cc-nav__sub:not([hidden]) {
      clip-path: inset(0 0 100% 0);
      opacity: 0;
    }
  }

  /* El velo de la página. Va en `body::before` y NO dentro del header, y eso es 8.77
     aplicado en vez de sufrido: el header ya tiene `backdrop-filter`, así que es
     *backdrop root* y nada que cuelgue de él puede muestrear la página de detrás.
     Medido hoy: `body` no tiene ni filtro ni `transform`, o sea es el primer ancestro
     que sí la ve.

     **Y ahí está la diferencia con el intento que se descartó**: aquel difuminaba A
     TRAVÉS del panel —imposible, 8.77— y este no toca el panel. **No se difumina el
     panel: se difumina LA PÁGINA.** El panel y la barra quedan nítidos encima, que es
     lo que hace Apple.

     `:has()` en vez de una clase puesta por JS: el estado ya vive en el DOM
     (`[hidden]`), y copiarlo a una clase sería una segunda fuente de verdad que se
     puede desincronizar. ⚠️ Sin anidar dentro de otro `:has()`, que el navegador
     descarta la regla entera en silencio (8.73).

     El `backdrop-filter` se declara SOLO en el estado abierto: una capa de pantalla
     completa con blur permanente se compone en cada scroll de las 32 páginas, y eso es
     el aviso de 8.77 sobre el coste. Cerrado, esta regla no existe.
     `z-index: 50` < el 60 del header. `pointer-events: none` para no robar el clic:
     el cierre por clic fuera que ya tiene `cc-nav.js` sigue recibiéndolo. */
  body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 50;
    pointer-events: none;
    opacity: 0;
    background: var(--cc-velo-nav, rgba(10, 10, 15, .42));
    transition: opacity var(--cc-dur-slow) var(--cc-ease-out);
  }
  body:has(#brxe-navgrp .cc-nav__sub:not([hidden]))::before {
    opacity: 1;
    backdrop-filter: blur(6px) saturate(.92);
    -webkit-backdrop-filter: blur(6px) saturate(.92);
  }

  .cc-mega__col { margin: 0; }
  /* El nombre de la unidad NO es un enlace ni un encabezado: es un rótulo. Con `<p>` y
     no con `<h*>` a propósito — el header sale en las 32 páginas, y meterle encabezados
     le añadiría cuatro niveles al esquema de TODAS, que es justo lo que vigilan los
     chequeos de jerarquía. El color sale de `--cc-unidad`, o sea de la clase `.cc-u-*`
     que ya lleva la columna: el modo claro se resuelve con la regla única que ya
     existe y esto no necesita ni una excepción. */
  #brxe-navgrp.cc-nav .cc-nav__sub .cc-mega__unidad {
    margin: 0 0 10px;
    padding: 0 10px 8px;
    /* 15px es el tamaño del nav. La jerarquía la dan el peso, el color de unidad y la
       línea de abajo, NO un tamaño nuevo: un paso más en la escala movería el trinquete
       tipográfico de `ux.mjs` en las 32 páginas a cambio de nada. */
    font-size: 15px;
    font-weight: 700;
    color: var(--cc-unidad);
    border-bottom: 1px solid color-mix(in srgb, var(--cc-unidad) 30%, transparent);
  }
  .cc-mega__lista { list-style: none; margin: 0; padding: 0; }
  .cc-mega__lista li { margin: 0; }
  /* (1,3,0) a propósito. Hay dos reglas que hay que ganar y por dos razones
     distintas: la de Bricks `#brxe-navgrp .brxe-text a` (1,1,1), que fija el color
     y los 15px del nav, y la del modo claro `html[data-theme="light"] #brxe-navgrp
     .brxe-text a` (1,2,1). Un selector de (1,2,0) le gana a la primera y PIERDE con
     la segunda, así que el submenú habría cambiado de criterio de color según el
     tema. Con `.cc-nav` en el selector queda (1,3,0) y manda en los dos — y como el
     color sale de tokens semánticos, el modo claro no necesita ni una excepción
     (8.27.6). */
  #brxe-navgrp.cc-nav .cc-nav__sub .cc-nav__sublink {
    display: flex; align-items: center;
    min-height: 44px;
    padding: 0 10px;
    /* 15px es el mismo tamaño del nav: no añade un paso a la escala tipográfica ni
       mueve el trinquete de `ux.mjs`. */
    font-size: 15px;
    color: var(--cc-fg-read);
  }
  /* El hover tiñe del color de SU unidad, no del acento: es la misma información que
     da la columna, y así el menú enseña la estructura en vez de solo listarla. Fuera
     de una columna `--cc-unidad` no existe y cae al acento de siempre. */
  #brxe-navgrp.cc-nav .cc-nav__sub .cc-nav__sublink:hover {
    color: var(--cc-unidad, var(--cc-fg-accent));
  }

  /* Canal.Data todavía no tiene páginas propias (decisión abierta, `docs/pendientes.md`).
     Sus tres capacidades se listan como TEXTO y no como enlaces: un elemento que parece
     clicable y no lleva a ninguna parte es peor que no estar (8.18, y es exactamente el
     bug de los 7 enlaces muertos que se arregló el 2026-08-09). Sin hover y sin cursor
     de mano, para que la diferencia se vea antes de hacer clic. */
  #brxe-navgrp.cc-nav .cc-nav__sub .cc-mega__proximo {
    display: flex; align-items: center;
    min-height: 44px;
    padding: 0 10px;
    font-size: 15px;
    color: var(--cc-fg-muted);
    cursor: default;
  }

  /* Camino SIN JS: el puntero y el foco abren el panel. `:focus-within` es lo que lo
     hace navegable con teclado — al enfocar "Servicios" el panel se muestra, y el
     Tab siguiente entra en él, que es lo que sostiene el estado. */
  html:not(.cc-nav-js) .cc-nav__grupo:hover .cc-nav__sub[hidden],
  html:not(.cc-nav-js) .cc-nav__grupo:focus-within .cc-nav__sub[hidden] { display: block; }
  /* Y el puente que hace usable ese hover: entre el link y el panel hay media altura
     de header de espacio vacío, y al cruzarlo el puntero saldría del grupo y el panel
     se cerraría en la mano. Ocupa SOLO el ancho del grupo —no el del panel—, así que
     no le puede robar clics al link vecino (8.17), y solo existe mientras se hoverea,
     así que no bloquea nada en reposo. Con JS no hace falta: ahí el cierre lleva un
     retardo y el puntero puede cruzar. */
  html:not(.cc-nav-js) .cc-nav__grupo:hover > p::after {
    content: ""; position: absolute; left: 0; right: 0;
    top: 100%; height: calc(var(--cc-header-h) / 2);
  }
}

/* Cuatro columnas solo cuando caben de verdad. A 768px cada una tendría ~166px y
   "Performance Marketing" no entra en una línea; en 2×2 dispone de ~350px. El corte
   está en 1080 y no en 1024 porque a 1024 el container mide 1004px y las columnas
   quedan en 220px, que sigue partiendo esa etiqueta. */
@media (min-width: 1080px) {
  .cc-nav__sub { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 767px) {
  /* En el panel el desplegable es un ACORDEÓN, no un panel flotante: una lista
     indentada dentro de la misma fila. Las cuatro unidades se apilan, y el rótulo de
     cada una hace de separador — que en móvil es MÁS útil que en desktop, porque una
     tira de siete enlaces sin agrupar es justo lo que se venía criticando. */
  html.cc-nav-js #brxe-navgrp .cc-nav__sub { display: block; }
  /* Misma trampa de 8.19 que en desktop, y aquí con más razón: el selector de arriba
     lleva ID, así que sin esto el `[hidden]` no cerraría nada. */
  html.cc-nav-js #brxe-navgrp .cc-nav__sub[hidden] { display: none; }
  .cc-mega__col + .cc-mega__col { margin-top: 12px; }
  #brxe-navgrp.cc-nav .cc-nav__sub .cc-mega__unidad {
    margin: 0 0 4px;
    padding: 0 0 6px;
    font-size: 15px;
    font-weight: 700;
    color: var(--cc-unidad);
    border-bottom: 1px solid color-mix(in srgb, var(--cc-unidad) 30%, transparent);
  }
  .cc-mega__lista { list-style: none; margin: 0; padding: 0; }
  #brxe-navgrp.cc-nav .cc-nav__sub .cc-mega__proximo {
    display: flex; align-items: center;
    min-height: 44px;
    font-size: 15px;
    color: var(--cc-fg-muted);
    cursor: default;
  }
  html.cc-nav-js #brxe-navgrp .cc-nav__grupo > p { justify-content: space-between; }
  /* El botón crece a 44x44 y se va al extremo derecho, donde cae el pulgar: el link
     se queda con el resto de la fila, así que las dos áreas no se solapan. */
  html.cc-nav-js #brxe-navgrp .cc-nav__sub-toggle { width: 44px; height: 44px; margin-right: -10px; }
  html.cc-nav-js #brxe-navgrp .cc-nav__sub { padding: 0 0 8px 14px; }
  /* Mismo motivo de especificidad que en desktop, más la regla de fila del panel
     (`html.cc-nav-js #brxe-navgrp .brxe-text a`, 1,2,1): (1,3,0) gana a las tres. */
  html.cc-nav-js #brxe-navgrp.cc-nav .cc-nav__sub .cc-nav__sublink {
    display: flex; align-items: center;
    min-height: 44px;
    font-size: 15px;
    color: var(--cc-fg-read);
  }
}

/* ==========================================================================
   FIDELIDAD AL MOCKUP APROBADO (2026-07-29)
   Se trajo el mockup renderizado desde el proyecto de diseño ("Home Canal Cero",
   Claude Design MCP) y se comparó sección por sección con lo implementado. Estas
   reglas restauran lo que se había perdido al reconstruirlo en Bricks. No son
   criterio propio: es el diseño que el equipo aprobó.
   ========================================================================== */

/* --- Hero: las dos capas de aurora ANIMADAS --------------------------------
   El mockup tiene dos radiales que respiran en bucles de 14s y 18s (violeta
   arriba-derecha, verde abajo-derecha). La implementación en Bricks conservó los
   radiales pero ESTÁTICOS: medido, `animationName: none` y cero capas separadas.
   Era lo único que le daba vida al primer viewport, que ocupa 792px con 254px de
   vacío sobre el titular.
   Van en pseudo-elementos y no en el `background` de la sección porque cada capa
   necesita su propia animación, y un `background` multicapa no se puede animar
   por capa. El `> * { z-index:1 }` garantiza que el contenido quede por encima.
   Se añade lo que el mockup no tiene: respeto por `prefers-reduced-motion`. Los
   glows se quedan quietos, que es lo que se pidió, no desaparecen. */
@keyframes cc-aurora-a {
  0%   { transform: translate(-8%, -6%) scale(1);    opacity: 0.85; }
  50%  { transform: translate(6%, 4%)   scale(1.15); opacity: 1; }
  100% { transform: translate(-8%, -6%) scale(1);    opacity: 0.85; }
}
@keyframes cc-aurora-b {
  0%   { transform: translate(4%, 6%)   scale(1.05); opacity: 0.7; }
  50%  { transform: translate(-5%, -4%) scale(0.95); opacity: 0.95; }
  100% { transform: translate(4%, 6%)   scale(1.05); opacity: 0.7; }
}
/* `.cc-aurora` existe para que otra pieza pueda usar el MISMO hero sin copiar los
   valores: lo estrena el hero de /servicios/ (2026-08-03). Va como clase y no como
   una lista de ids que crece con cada página, que es la lección de 8.25.5. El id de
   la home se conserva porque su template ya está exportado y no vale re-exportarlo
   solo por esto. */
#brxe-sec001::before,
#brxe-sec001::after,
.cc-aurora::before,
.cc-aurora::after {
  content: ""; position: absolute; inset: -20%; pointer-events: none;
}
#brxe-sec001::before,
.cc-aurora::before {
  background: radial-gradient(55% 65% at 78% 30%, rgba(75, 47, 191, 0.55) 0%, rgba(75, 47, 191, 0) 70%);
  animation: cc-aurora-a 14s ease-in-out infinite;
}
#brxe-sec001::after,
.cc-aurora::after {
  background: radial-gradient(40% 45% at 88% 85%, rgba(61, 237, 180, 0.14) 0%, rgba(61, 237, 180, 0) 70%);
  animation: cc-aurora-b 18s ease-in-out infinite;
}
#brxe-sec001 > *,
.cc-aurora > * { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) {
  #brxe-sec001::before, #brxe-sec001::after,
  .cc-aurora::before, .cc-aurora::after { animation: none; }
}

/* --- Hero: la MEDIDA del titular ------------------------------------------
   El mockup limita los titulares a `13ch` (y el tercero, que es más largo, a
   `22ch`). No es un detalle: con 13ch "Tu jefe te pide números." se APILA en dos
   líneas cortas y se lee como una declaración. Sin el límite salía en una sola
   línea de 994px, y el tercer slide ocupaba los 1180px completos del container.
   `max-width` no la declara la regla de ID del heading, así que la clase aplica
   sin pelear especificidad (8.1). El tamaño del tercero SÍ está en la regla de
   ID, por eso ese cambio va en el template. */
/* ⚠️ EL PANEL DE CANALES DEL CIERRE, EN LA ESCALA DE LA PORTADA (2026-08-23).
   `sec127` reusa `.cc-contacto-datos` de `/contacto/` tal cual (8.71), y con él llegó un
   tamaño de fuente que la portada no tenía. `ux.mjs` lo cazó al instante: «home: 13
   tamaños de fuente distintos (techo 12)» — el trinquete de 8.18 haciendo su trabajo.

   ⚠️ Y el culpable NO era el que parecía. Aposté al rótulo de 13px y el override no movió
   la cuenta: **la portada ya usaba 13px** en los rótulos de los badges de partner. El
   tamaño nuevo era el del VALOR, `--cc-fs-base` (18px), que la portada no usa en ninguna
   parte — tiene 20 para el lead y 16 para el texto de tarjeta. Se vio ENUMERANDO los 13
   tamaños con tres ejemplos cada uno, no razonando sobre el CSS: adivinar cuál de trece
   es el nuevo cuesta más que medirlo (8.15).

   El valor baja a `--cc-fs-sm` (16px), que la portada ya usa en las tarjetas de servicio.
   El rótulo se queda en `--cc-fs-xs` (14px) aunque 13 también pasaría: 14 es el de los
   OTROS rótulos de la portada («TRAYECTORIA», «CAPACIDAD» en `sec061`), así que la
   coherencia sale gratis. **NO se sube el techo: el techo es el punto.**

   Va SCOPEADO a la sección y no al componente, porque en `/contacto/` los 18 y los 13 son
   correctos: esa página tiene su propia escala y su propio techo, y pasa. Reusar un
   componente entre piezas no obliga a unificar sus escalas — obliga a decidir en cuál
   está cada instancia. */
#brxe-sec127 .cc-contacto-rotulo { font-size: var(--cc-fs-xs); }
#brxe-sec127 .cc-contacto-valor  { font-size: var(--cc-fs-sm); }

.cc-hero__title { max-width: 13ch; }
.cc-hero__title--largo { max-width: 22ch; }


/* --- Hero: el OBJETO VISUAL (2026-08-04) ------------------------------------
   Ricardo: *"todo muy genérico, sin identidad, muy IA"*. Medido en el hero a 1440×900:
   **21.8% de densidad, 595px de ancho vacío a la derecha y cero imágenes/SVG/video.**
   El titular usaba la mitad izquierda y la otra mitad estaba literalmente en blanco.

   ⚠️ El `13ch` de arriba NO se toca, y esa es la decisión de fondo: viene del mockup
   aprobado por el equipo, donde apila el titular en dos líneas cortas para que se lea
   como una declaración (§7 — el mockup manda). El vacío no se cierra estirando el
   texto: se cierra poniendo algo del otro lado. Y hay evidencia de que el mockup lo
   esperaba — el `_cssCustom` del hero trae
   `linear-gradient(90deg, #0a0a0f 0%, …0.85 34%, …0 70%)`, o sea un scrim que oscurece
   la izquierda y deja el 30% derecho TRANSPARENTE. Ese degradado no protege de nada si
   no hay nada detrás: estaba esperando este objeto desde que se construyó la pieza.

   El canvas lo inyecta `cc-hero-viz.js`. Va en z-index 1 y el contenido del hero en 2
   (`#brxe-con002`), así que el titular nunca compite con la malla. */
/* ⚠️ El selector lleva el ID a propósito, y esto costó dos corridas. `#brxe-sec001 > *`
   (unas líneas más arriba, la que levanta las auroras sobre el scrim) declara
   `position: relative` y es (1,0,0): le gana a `.cc-hero-viz` (0,1,0). El síntoma es el
   de 8.1 en su versión engañosa —`inset`, `z-index` y la máscara SÍ se aplicaban, así
   que la regla parecía haber entrado del todo y solo `position` no—, y la consecuencia
   fue peor que cosmética: sin `absolute` el canvas cuenta en el flujo, así que su alto
   sumaba al del hero, `medir()` leía el hero ya crecido y volvía a agrandar el canvas.
   Medido en ese bucle: hero de 7.784px y canvas de 7.146px, con el dibujo perdido
   dentro de un lienzo gigante. La guarda de idempotencia de `medir()` no podía salvarlo
   porque el hero de verdad estaba creciendo: no era una realimentación del observador,
   era el layout. */
#brxe-sec001 > canvas.cc-hero-viz {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Decorativo: no debe robar ni un clic al CTA ni a los dots. */
  pointer-events: none;
  /* El desvanecido del borde izquierdo va en CSS y no en el JS a propósito: es una
     propiedad de cómo el objeto se INTEGRA al hero, no de lo que dibuja, y así se puede
     ajustar por breakpoint sin volver a medir la malla. Sin él la malla arranca en un
     corte vertical recto y se lee como una textura pegada encima. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 30%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 30%);
}
/* Bajo 767px el hero es una columna y el texto ocupa el ancho completo, así que la
   malla pasa a ser un fondo bajo el contenido —atenuada, y desvanecida por ARRIBA en
   vez de por la izquierda—. Ponerla al lado no es opción: no hay lado. */
@media (max-width: 767px) {
  /* Mismo ID por la misma razón de arriba: la regla `#brxe-sec001 > *` no vive dentro de
     una media query, así que sigue ganándole a una clase pelada acá dentro. */
  #brxe-sec001 > canvas.cc-hero-viz {
    opacity: 0.55;
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 45%);
            mask-image: linear-gradient(180deg, transparent 0%, #000 45%);
  }
}

/* ==========================================================================
   AUDITORÍA VISUAL DE LA HOME (2026-07-30)
   Recorrido por las 11 secciones a 1440 y 390 midiendo ritmo, ejes, densidad y
   elementos accionables. El hallazgo que manda sobre todos los demás: la home
   tenía TRES enlaces en 7.066px de scroll, y como los tres eran los CTA del hero
   —que es un carrusel— solo UNO estaba en pantalla a la vez. Ni las tarjetas de
   servicio, ni los casos, ni los logos llevaban a ninguna parte.
   El resto de las reglas de esta sección son el soporte de esos arreglos; el
   grueso del trabajo (ritmo, escala tipográfica, ejes) va en el template porque
   son settings de elemento, no utilidades.
   ========================================================================== */

/* --- Enlace de tarjeta ----------------------------------------------------
   Se implementa como `text` con <a> crudo, que es el patrón ya usado en el
   footer (.cc-social__link), y NO como `button`: el elemento button arrastra el
   estilo base de `.bricks-button` y habría que pelearlo entero.
   Tres cosas que no son decorativas:
   · La flecha es un `::after`, o sea NO entra en el nombre accesible. Un lector
     de pantalla anuncia "Ver Canal.Media", no "Ver Canal.Media flecha derecha".
   · `min-height:26px` en un inline-flex da el área de golpeo de WCAG 2.5.8 sin
     el `::after` absoluto de 8.17 — aquí no hace falta, porque el enlace está
     solo en su tarjeta y no tiene vecino con el que solaparse.
   · El subrayado al hover es lo que impide que el estado dependa solo del color
     (WCAG 1.4.1). La flecha ya distingue el enlace en reposo. */
.cc-cardlink {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 26px;
  color: var(--cc-unidad, var(--cc-fg-accent));
  font-weight: 700;
  font-size: 15px;
  line-height: 1.4;
  text-decoration: none;
  transition:
    color           var(--cc-dur-base) var(--cc-ease-out),
    text-decoration var(--cc-dur-base) var(--cc-ease-out);
}

/* La flecha se DIBUJA, no se escribe. Antes era el carácter "→", y Montserrat no
   tiene glifo para él: caía a una fuente del sistema cuyo trazo es un pelo, al lado
   de un texto de 700 con astas gruesas. Eran dos tipografías en la misma línea, y a
   4x de zoom no hay forma de no verlo.
   Con `clip-path` el grosor lo elijo yo y calza con el peso del texto. De paso sigue
   sin entrar en el nombre accesible —un lector anuncia "Ver Canal.Media", no
   "…flecha derecha"— y no arrastra ninguna fuente de iconos, que `peso.mjs` prohíbe. */
.cc-cardlink::after {
  content: "";
  flex: none;
  width: 17px;
  height: 11px;
  background: currentColor;
  clip-path: polygon(0 40%, 60% 40%, 60% 16%, 100% 50%, 60% 84%, 60% 60%, 0 60%);
  transition: transform var(--cc-dur-base) var(--cc-ease-out);
}

/* El hover mezcla con el PRIMER PLANO del tema y no con un hex fijo, y eso no es
   coquetería: `--cc-fg-strong` es blanco en oscuro y casi negro en claro, así que la
   mezcla ACLARA sobre fondo oscuro y OSCURECE sobre fondo claro. En los dos casos el
   contraste contra el fondo sube, que es lo que un hover debe hacer. Mezclar con blanco
   a secas funcionaba en oscuro y en claro empujaba el color hacia el fondo — bajando el
   contraste justo en el estado en que el usuario está mirando.
   Sustituye a `--cc-green-700`, que era un verde fijo y contradecía el color de la
   unidad en tres de las cuatro. */
.cc-cardlink:hover {
  color: color-mix(in srgb, var(--cc-unidad, var(--cc-fg-accent)) 80%, var(--cc-fg-strong));
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 2px;
}

.cc-cardlink:hover::after { transform: translateX(4px); }

@media (prefers-reduced-motion: reduce) {
  .cc-cardlink:hover::after { transform: none; }
}

/* OJO al reutilizar la clase: sobre la banda violeta el verde de marca da 2.4:1
   y no pasaría AA. Hoy los cinco enlaces viven sobre superficies oscuras, así que
   no se añade la regla — sería CSS muerto (§7). Si algún día se pone un
   `.cc-cardlink` sobre violeta, hay que fijarle color aquí. */

/* --- Unidades: pestañas numeradas 01-04 (2026-08-06) ----------------------
   Reemplaza a `.cc-svc-wide`, que era la cuarta unidad renderizada como franja
   ancha bajo las otras tres. Ese formato existía porque 4 tarjetas en una grilla
   de 3 dejan un huérfano, y el intento anterior fue hacer que el ancho "se
   leyera como deliberado". No alcanzó: Ricardo lo volvió a marcar el 2026-08-06
   (*"tal vez la tarjeta de abajo, la larga"*). El problema de fondo no era el
   tamaño sino que **eran dos componentes para una misma cosa** — las tres
   apilaban número/título/texto/enlace y la cuarta ponía todo en una línea, así
   que Canal.Brand se leía como nota al pie siendo una de cuatro unidades
   iguales. Es el error de 8.35 otra vez: la forma contradecía al contenido.

   Ahora son pestañas, como infracommerce.lat (la referencia que Ricardo pidió
   mirar, anotada en 8.34): un huérfano no puede existir si no hay grilla.

   SIN JAVASCRIPT, y no por deporte. El patrón es el del control de pausa del
   carrusel (8.28.3): `<input type="radio">` + `<label>`, y el panel se muestra
   con `:has()`. Un tablist con JS habría necesitado su propia red de seguridad
   en el `<head>` (8.19, 8.24) porque un fallo del script dejaría **3 de 4
   unidades inalcanzables** — y eso es perder contenido, no una animación. Con
   CSS no hay ningún estado en que eso pase. De paso el grupo de radios ya trae
   navegación por flechas y anuncio de estado nativos, sin `aria-selected` que
   mantener sincronizado.

   `:has()` en vez de `~` a propósito: desacopla el orden del DOM, así que los
   radios pueden vivir dentro del elemento `text` de la fila de pestañas y los
   paneles ser hermanos suyos. Con `~` habría que forzar a Bricks a emitir los
   inputs como hermanos de los 4 bloques, que no se puede sin HTML crudo en todo
   el subárbol. El repo ya usa `:has()` en `.cc-svc-card`. */
.cc-tabs { display: grid; gap: 30px; }

/* El control real. Sigue siendo enfocable (no `display:none`), porque es lo que
   recibe el foco y las flechas; solo deja de verse. */
.cc-tabs__radio {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
  /* Gotcha 8.28.4, segunda aparición: un input hereda la `transition: .2s` que
     Bricks pone a los campos de formulario. En un control de 1×1 no significa
     nada, pero ensucia la escala de duraciones y lo caza el trinquete de
     `ux.mjs` — que es justo lo que pasó al escribir esto. */
  transition: none;
}

/* La fila. El flex va en el `<p>` que envuelve todo elemento `text` (8.9): sobre
   el contenedor no haría nada, porque tendría un solo hijo. */
.cc-tabs__lista > p {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin: 0;
  border-bottom: 1px solid var(--cc-border-on-dark);
}

.cc-tabs__tab {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  padding: 14px 22px 16px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;              /* pisa el hairline de la fila */
  transition: color var(--cc-dur-fast) var(--cc-ease-out),
              border-color var(--cc-dur-fast) var(--cc-ease-out),
              background-color var(--cc-dur-fast) var(--cc-ease-out);
}
/* Los dos van a 15px —el mismo tamaño del nav— y se distinguen por peso y color,
   NO por tamaño. Estrenar un 17px para el nombre sumaba un tamaño más al conjunto
   de la home y lo cazó el trinquete de `ux.mjs` (13 sobre un techo de 12). Es
   8.18: si hace falta un valor nuevo, falta un token; y acá no faltaba ninguno,
   sobraba el invento. */
.cc-tabs__num {
  font-family: var(--cc-font-sans);
  font-weight: 800;
  font-size: 15px;
  color: var(--cc-fg-muted);
  transition: color var(--cc-dur-fast) var(--cc-ease-out);
}
.cc-tabs__nom {
  font-family: var(--cc-font-sans);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  color: var(--cc-fg-muted);
  transition: color var(--cc-dur-fast) var(--cc-ease-out);
}

/* Reposo -> hover: un control que no responde al puntero no se lee como control
   (8.18, y `ux.mjs` lo mide). */
.cc-tabs__tab:hover .cc-tabs__num,
.cc-tabs__tab:hover .cc-tabs__nom { color: var(--cc-fg-read); }

/* La pestaña activa toma el color de SU unidad, que es el mismo mecanismo de
   islas de 8.34: el número, el punto del nombre y el subrayado salen todos de
   `--cc-unidad` sin que esta regla sepa en qué unidad está. */
.cc-tabs__radio:checked + .cc-tabs__tab {
  border-bottom-color: var(--cc-unidad, var(--cc-fg-accent));
}
.cc-tabs__radio:checked + .cc-tabs__tab .cc-tabs__num {
  color: var(--cc-unidad, var(--cc-fg-accent));
}
.cc-tabs__radio:checked + .cc-tabs__tab .cc-tabs__nom { color: var(--cc-fg-strong); }

/* El anillo de foco va en el label, porque el input mide 1px y su outline no se
   vería. Mismo criterio que el área de golpeo del checkbox del carrusel (8.28). */
.cc-tabs__radio:focus-visible + .cc-tabs__tab {
  outline: 3px solid var(--cc-unidad, var(--cc-fg-accent));
  outline-offset: -3px;
  border-radius: var(--cc-radius-sm);
}

/* Los paneles. `:has()` mira qué radio está marcado y el emparejamiento va por la
   clase de unidad que el panel YA tenía (`.cc-u-media`…), no por contar posiciones.
   Contar era un bug: `:nth-of-type()` cuenta por TIPO de elemento, y como la fila
   de pestañas también es un `div`, los paneles serían el 2.º al 5.º y ninguna
   regla habría coincidido nunca. Emparejar por unidad además documenta solo qué
   pestaña abre qué panel. El `>` es imprescindible: las etiquetas llevan las
   mismas clases `cc-u-*` y viven más abajo, dentro del `<p>` de la fila. */
/* ⚠️ `.cc-tabs > .cc-tabs__panel` y no `.cc-tabs__panel` pelado: el panel ES una
   `.cc-svc-card`, que declara `display:flex` y vive MÁS ABAJO en esta hoja. Con
   la misma especificidad (0,1,0) gana la última, así que un `display:none` suelto
   no oculta nada — medido: los 4 paneles visibles a la vez. Con (0,2,0) gana sin
   depender del orden. No es 8.1 (no hay regla de ID en juego) sino su vecino:
   dos clases empatadas y la que manda es la de abajo. */
.cc-tabs > .cc-tabs__panel { display: none; }
.cc-tabs:has(#cc-unidad-1:checked) > .cc-u-media,
.cc-tabs:has(#cc-unidad-2:checked) > .cc-u-commerce,
.cc-tabs:has(#cc-unidad-3:checked) > .cc-u-data,
.cc-tabs:has(#cc-unidad-4:checked) > .cc-u-brand {
  display: flex;
}

/* ⚠️ Al cambiar de pestaña el panel pasa de `display:none` a visible, y eso
   REINICIA su animación de entrada. Cuando este contenedor era además un
   `.cc-stagger`, cada panel heredaba el retardo de SU POSICIÓN: Canal.Brand es el
   5.º hijo, o sea 320ms de espera + 560ms de animación = casi un segundo desde el
   clic hasta verlo. Un tab que tarda eso se siente roto. Lo cazó una captura
   tomada 400ms después del clic: el texto salía a medio opacar.

   Por eso el contenedor dejó de ser `.cc-stagger` (escalonar una fila de pestañas
   y cuatro paneles de los que solo uno se ve no significa nada) y el panel lleva
   su propia entrada: corta, sin retardo y sin depender del sistema de reveals. El
   reveal de la sección sigue existiendo, un nivel más arriba, en `con041`.
   El keyframe declara solo `from`, así que al terminar el panel queda en SU valor
   natural y no en uno afirmado (8.12). */
.cc-tabs > .cc-tabs__panel {
  animation: cc-entrar var(--cc-dur-base) var(--cc-ease-out) backwards;
}


/* ══════════════════════════════════════════════════════════════════════════
   YA NO HAY PESTAÑAS, EN NINGÚN ANCHO (2026-08-10)

   Ricardo: *"no sé si me gustan que las tarjetas de 01 Canal.Media, Canal.Commerce,
   etc., estén en pestañas, ¿no hay forma de ponerlo más bonito?"*. La duda es
   correcta y la respuesta no era decorar la pestaña, era quitarla.

   **Lo que estaba mal, y se veía en la propia página:** el panel abierto es una caja
   de 1180×250 con un párrafo de dos líneas dentro. O sea la pestaña no resolvía un
   problema de espacio —sobraba— y a cambio escondía **tres de las cuatro unidades**
   detrás de un clic. Un componente que oculta contenido se paga con algo; aquí no
   compraba nada.

   Y era peor de lo que parece por tres razones que no se ven mirando:
     · **Móvil ya las mostraba las cuatro.** O sea la pantalla PEQUEÑA enseñaba más
       oferta que la grande, que es exactamente al revés.
     · **SEO.** Las cuatro unidades son las cuatro líneas de negocio de la agencia y
       tres estaban en `display:none`. Google indexa lo oculto, pero lo pondera menos,
       y para un sitio posicionado eso es regalar tres cuartas partes de la sección.
     · **Nadie hace clic en las pestañas de una portada.** Es de lo más medido en UX:
       el contenido tras una interacción en una landing lo ve una minoría. El patrón
       vale cuando comparas alternativas excluyentes (planes de precio), no cuando
       enumeras lo que haces — ahí lo que quieres es que se vean todas a la vez.

   **Ahora es una grilla 2×2.** Cuatro cajas iguales, cada una con su color de unidad,
   sin jerarquía inventada entre ellas — que además es lo que Miguel pidió en la
   revisión del 2026-07-30 (*"que las cuatro tengan espacio equilibrado en lugar de
   tres"*) y que ni `.cc-svc-wide` ni las pestañas llegaron a dar: la primera hacía a
   Canal.Brand una franja distinta, las segundas mostraban una sola.

   No se borra el mecanismo de radios del template: los `<input>` siguen ahí, ocultos e
   inertes visualmente, porque volver a pestañas es descomentar dos reglas. Lo que se
   quita es la FILA, que es lo que Ricardo señaló.
   ══════════════════════════════════════════════════════════════════════════ */
.cc-tabs__lista { display: none; }
/* (0,2,0) para ganarle al `display:none` de arriba. */
.cc-tabs > .cc-tabs__panel { display: flex; }
.cc-tabs {
  /* ⚠️ CUATRO columnas desde el 2026-08-17, no dos. Ricardo: *«tarjetas del homepage de
     corrido, 4 horizontales»*. Y cierra de paso el pedido de Miguel del 2026-07-30 —«que
     las cuatro áreas tengan espacio equilibrado en lugar de tres»—: en 2×2 la lectura era
     dos parejas, no cuatro capacidades del mismo rango.
     `auto-fit` con mínimo en `min()` y no cuatro columnas rígidas: un mínimo fijo en una
     grilla es una promesa de desborde (8.7), y así la fila se parte sola a 2 y a 1 según
     el ancho sin necesitar un breakpoint escrito a mano. */
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: 24px;
  /* ⚠️ `stretch` explícito, y es GOTCHA 8.33 POR TERCERA VEZ. El elemento `block` de
     Bricks emite `align-items: flex-start` en su propio CSS, y eso **sobrevive** al
     cambiar la caja a `display: grid`: cada tarjeta toma su altura natural en vez de la
     de su fila. Aquí lo cazó Ricardo a ojo —*"una de las tarjetas es más corta, la 04
     para ser exactos"*— y la medición le dio la razón por **6px**: 246 contra 252, porque
     la bajada de Canal.Brand es la más corta de las cuatro y ocupa 45px donde las otras
     ocupan 51.
     Seis pixeles es poco y por eso importa: es justo la magnitud que se ve como
     descuido y no como decisión. El chequeo de más abajo compara los altos RENDERIZADOS,
     que es lo que 8.33 enseñó: "ser la misma definición" y "ocupar la misma caja" son
     dos preguntas distintas, y la primera ya pasaba. */
  align-items: stretch;
}
/* Los cuatro paneles entran escalonados, como cualquier grilla del sistema, en vez de
   con la entrada instantánea que necesitaba el cambio de pestaña. */
.cc-tabs > .cc-tabs__panel:nth-child(2) { animation-delay: 80ms; }
.cc-tabs > .cc-tabs__panel:nth-child(3) { animation-delay: 160ms; }
.cc-tabs > .cc-tabs__panel:nth-child(4) { animation-delay: 240ms; }

/* En una caja de media anchura la medida de lectura sobra: manda el ancho de la caja. */
.cc-tabs__panel .cc-svc-card__texto,
.cc-tabs__panel > .brxe-text > p { max-width: 100%; }

@media (max-width: 767px) {
  .cc-tabs { grid-template-columns: minmax(0, 1fr); gap: 20px; }
}

/* --- Roster de clientes ---------------------------------------------------
   Copec, Falabella, Cencosud, Walmart y Mercado Libre son el activo de prueba
   social más fuerte de la página y estaban en blanco al 40% de alfa: pesaban
   MENOS que la barra de partners de arriba, que sí tiene logos. El color pasó a
   `--cc-fg-muted` en el template (token verificado, 5.65:1, ver §7). Acá solo se
   le da marco: un hairline que lo separa del titular y lo hace leer como un
   roster y no como una línea de texto suelta.
   PENDIENTE DE CONTENIDO: esto se arregla de verdad con los logos reales; sin
   ellos el techo de lo que puede hacer el CSS es este. */
.cc-clientes {
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  padding-top: 26px;
}

/* ==========================================================================
   S2 — LOS CUATRO PROVEEDORES QUE NO SE HABLAN (2026-07-30)
   La metáfora es buena y la ejecución la contradecía. Medido antes:
     · borde `dashed` a rgba(255,255,255,0.22) sobre fondo TRANSPARENTE
     · `transition: 0s` — inertes, no respondían a nada
     · rotaciones de 1° a 2°
   El punteado sobre transparente es el lenguaje universal de "esto es un
   placeholder": la sección se leía como un wireframe sin terminar. Y 1-2° es el
   valle incómodo de la rotación — suficiente para verse torcido, insuficiente
   para leerse como desorden intencional. O se alinea, o se inclina de verdad.

   Ahora: superficie sólida (objetos reales que no se tocan), inclinación
   deliberada y una deriva lentísima para que el grupo NUNCA se asiente. La
   sección actúa lo que el texto dice en vez de explicarlo.
   ========================================================================== */

.cc-silo {
  width: fit-content;
  padding: 14px 22px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: var(--cc-radius-md);
  /* Misma familia que las cards de servicios pero SIN hover: tienen que leerse como
     objetos inertes, no como algo que invita a hacer clic.
     0.06 y no 0.04 desde el 2026-08-03: sobre la superficie nueva, 0.04 daba 1.07
     de contraste (invisible) y 0.06 da 1.18.
     ⚠️ Esta frase decía «SIN sombra ni hover» hasta el 2026-08-29, y era FALSA desde el
     2026-08-04: ese día se le dio sombra propia justo porque 1.18 de contraste no basta
     para separarla del fondo. La sombra —y su porqué— viven en la segunda regla de
     `.cc-silo`, unas 1.700 líneas más abajo. Dos reglas para el mismo selector en una hoja
     de 8.000 líneas es cómo un comentario se queda describiendo un pasado. */
  background: rgba(255, 255, 255, 0.06);
}

/* La deriva anima `translate`, la propiedad INDEPENDIENTE, no `transform`. Así se
   compone sola con el `rotate` de abajo sin que haya que repetir el ángulo en cada
   keyframe, y quedan dos ejes que se pueden tocar por separado.
   Sin `animation-fill-mode`, a propósito: fuera de la ejecución manda el estilo
   natural del elemento, así que ningún fallo puede dejar una caja desplazada. Es
   la misma garantía fail-safe de 8.11. */
/* ⚠️ AMPLITUD SUBIDA el 2026-08-10. Ricardo: *"quiero que se muevan un poco más, ya que
   no es perceptible a la vista"*. Y era medible, no una impresión: la deriva era de 3-5px
   en ciclos de 15-19s, o sea **menos de 0,7 px por segundo**. El umbral por debajo del
   cual el ojo humano no detecta movimiento en visión periférica ronda 1-2 px/s, así que
   la animación existía en el CSS y no existía en la pantalla — el peor de los dos mundos,
   porque costaba repintados y no comunicaba nada.

   Ahora son 14-20px en ciclos de 9-12s: ~3 px/s, perceptible sin ser inquieto. Se sube
   la amplitud Y se baja la duración, porque lo que se percibe es la VELOCIDAD, no el
   recorrido: 20px en 19s seguiría siendo invisible.
   Se añade además un vaivén de rotación (`--cc-silo-dr`), que es lo que hace que la caja
   parezca flotar en vez de deslizarse — un desplazamiento puro se lee como un error de
   layout, no como flotación.

   ⚠️ El `dr` lleva **el mismo signo que su `rot`**, o sea la inclinación se acentúa y
   nunca se acerca a cero. La primera versión los puso con signos opuestos y el chequeo de
   inclinación de `home.mjs` falló en el acto: el silo 3 pasaba por **-0,5°**, que no se
   lee como "inclinado a propósito" sino como "casi alineado", que es el defecto que esa
   prueba existe para atrapar. Una animación no puede atravesar el estado que el diseño
   declara inválido. */
@keyframes cc-silo-deriva {
  0%, 100% { translate: 0 0; rotate: var(--cc-silo-rot); }
  50%      { translate: var(--cc-silo-dx) var(--cc-silo-dy);
             rotate: calc(var(--cc-silo-rot) + var(--cc-silo-dr)); }
}

/* ⚠️ ROTACIÓN A 0 (2026-08-13, decisión de Ricardo). Pasó por ±3° (apilado vertical),
   ±1,5° (rejilla) y termina en recto. Y el motivo de fondo no era el ángulo sino lo que
   el ángulo hacía visible: la SOMBRA. Con `0 14px 30px -10px` y la caja girada, el halo
   oscuro quedaba desplazado y torcido respecto del contenido, y se leía como *"un
   rectángulo oscuro detrás de la tarjeta"* — Ricardo lo reportó como posible bug de un
   pseudo-elemento. No lo era: `::before` y `::after` son `none` en las cuatro (medido).
   Una sombra desplazada bajo una caja recta se lee como profundidad; bajo una caja
   girada, como una segunda caja mal puesta.
   La deriva de traslación se queda —Ricardo la pidió el 2026-08-10— pero sin componente
   de rotación: mover no es torcer. */
/* ⚠️ Y la deriva VERTICAL también a 0 (2026-08-13). Con ±3px los iconos caían en
   300/302/306/306 — 6px de desfase entre las cuatro—, y con la rotación ya en 0 eso deja
   de leerse como intención y pasa a leerse como error de alineación.
   Hay además una razón que no es de gusto: **la fila vacía convirtió las cuatro tarjetas
   en un set comparable**. Cuando cuatro cosas se ofrecen para compararse, la línea base
   común no es decoración, es lo que permite la comparación — el ojo lee «una de estas no
   tiene venta atribuida» solo si las cuatro filas están a la misma altura.
   El movimiento se queda, en HORIZONTAL, que desplaza sin romper la línea. */
.cc-silo:nth-child(1) { --cc-silo-rot: 0deg; rotate: 0deg;
                        --cc-silo-dx:  10px; --cc-silo-dy: 0px; --cc-silo-dr: 0deg; }
.cc-silo:nth-child(2) { --cc-silo-rot: 0deg; rotate: 0deg;
                        --cc-silo-dx:   9px; --cc-silo-dy: 0px; --cc-silo-dr: 0deg; }
.cc-silo:nth-child(3) { --cc-silo-rot: 0deg; rotate: 0deg;
                        --cc-silo-dx:  -8px; --cc-silo-dy: 0px; --cc-silo-dr: 0deg; }
.cc-silo:nth-child(4) { --cc-silo-rot: 0deg; rotate: 0deg;
                        --cc-silo-dx: -11px; --cc-silo-dy: 0px; --cc-silo-dr: 0deg; }

/* ⚠️ LOS DOS DE LOS EXTREMOS DERIVAN HACIA ADENTRO (2026-08-13). El primero llevaba
   `dx: -16px` y el cuarto `+20px`, o sea los dos se movían HACIA EL BORDE de la página.
   Con los silos estrechos del apilado anterior (`width: fit-content`) sobraba sitio; a
   todo el ancho de su columna en la rejilla, el cuarto **se salía 2px del viewport a
   1080** y `a11y.mjs` lo cazaba como scroll horizontal. Otra invariante que dependía de
   la maqueta vieja (8.56).
   Invertirles el signo lo resuelve por construcción y no cuesta nada: la amplitud, la
   velocidad y el desfase entre las cuatro quedan idénticos, y ninguna se mueve nunca
   hacia el borde. Es mejor que recortar con `overflow`, que habría cortado la esquina
   de la tarjeta rotada justo cuando deriva.
/* ⚠️ VELOCIDAD BAJADA EL 2026-08-13, y conviene ver la tensión con el aviso de
   arriba. El 2026-08-10 Ricardo pidió MÁS movimiento porque a 0,7 px/s no se
   percibía, y se subió a ~3 px/s. Con la rejilla nueva —tarjetas más grandes, en
   fila y con cifras dentro— esos mismos 3 px/s se leen inquietos, y pidió frenarlas.
   No es que una de las dos peticiones estuviera mal: **la velocidad correcta depende
   del tamaño y del número de objetos en pantalla**, y la maqueta cambió los dos.
   Los ciclos pasan de 9-12s a 15-19s con la misma amplitud, o sea ~2 px/s: por
   encima del umbral de percepción (1-2 px/s) y por debajo de lo que distrae.
   Duraciones primas entre sí y RETRASOS NEGATIVOS: los negativos arrancan la
   animación ya empezada, así que las cuatro nunca están en fase — ni siquiera en el
   primer frame. Con retrasos positivos las cuatro saldrían sincronizadas al cargar,
   que es justo lo contrario de lo que la sección quiere decir. */
.cc-silo:nth-child(1) { animation: cc-silo-deriva 15s ease-in-out  -1s infinite; }
.cc-silo:nth-child(2) { animation: cc-silo-deriva 18s ease-in-out  -7s infinite; }
.cc-silo:nth-child(3) { animation: cc-silo-deriva 16s ease-in-out  -4s infinite; }
.cc-silo:nth-child(4) { animation: cc-silo-deriva 19s ease-in-out  -9s infinite; }

/* Quien pidió menos movimiento conserva la inclinación —que es composición, no
   movimiento— y pierde solo la deriva.
   `:nth-child(n)` y no `.cc-silo` a secas: la clase sola es (0,1,0) y PIERDE contra
   el (0,2,0) de las reglas de arriba, así que la primera versión de esta regla no
   apagaba nada. Lo cazó la medición, no la lectura: `animationName` seguía siendo
   `cc-silo-deriva` con reduced-motion emulado. Es el mismo error de especificidad
   de 8.12, ahora en el sentido contrario. */
@media (prefers-reduced-motion: reduce) {
  .cc-silo:nth-child(n) { animation: none; }
}


/* ==========================================================================
   S3 — LOS CUATRO SILOS, AHORA CON CIFRA Y HACES (2026-08-13)
   El mockup que el equipo revisó el 2026-08-10 lleva esta sección más lejos que
   la versión que estaba publicada, y Ricardo pidió traerla ("es como fuegos
   artificiales"). Lo que cambia no es decoración: es el ARGUMENTO dibujado.

   La sección dice que contratas cuatro proveedores y ninguno te responde cuánto
   vendiste. Antes lo decía en una línea de texto ("…y ninguno se habla con el
   otro"). Ahora lo MUESTRA en tres tiempos:
     1. cuatro tarjetas, cada una con su cifra;
     2. cuatro haces que bajan de ellas y se apagan sin juntarse;
     3. el remate: "¿Ventas atribuidas? / Nadie responde esta pregunta."

   ⚠️ Las cuatro cifras son ILUSTRATIVAS y así lo confirmó Ricardo el 2026-08-13.
   Por eso cada una lleva su rótulo —inversión, sesiones, paneles, impresiones— y
   ese rótulo no es adorno: es lo que vuelve honesto el número y, de paso, lo que
   hace funcionar el remate. Son cuatro métricas INCOMPARABLES entre sí y ninguna
   es una venta. Sin el rótulo, "$148M" a secas se lee como el gasto real de un
   cliente, que sería afirmar un dato que no tenemos (mismo criterio que dejó a
   Meta y TikTok en texto gris durante meses).

   ⚠️ Y las tarjetas NO se pintan del color de unidad a fondo lleno, aunque el
   mockup lo haga. Dos razones: texto blanco sobre `--cc-u-media` (#3DEDB4) da
   1.5:1 —el fallo exacto que tema.mjs existe para atrapar— y, más de fondo, en
   S5 esos mismos cuatro colores significan "nuestras cuatro unidades". Pintar con
   ellos a los proveedores del problema los reclama demasiado pronto. El color
   entra donde no compite: la cifra y el haz.
   ========================================================================== */

.cc-silos {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* ⚠️ El hueco tiene que pagar la ROTACIÓN, no solo separar cajas. Las tarjetas van
     inclinadas 2,5-3° y derivando, así que sus esquinas salen de la caja de rejilla:
     con el hueco de 18px que heredó del mockup —que las tiene casi rectas— el
     rectángulo pintado de cada una **invadía 16px** el de su vecina y las dos
     centrales casi se tocaban. Lo cazó el chequeo nuevo de solape, no el ojo.
     34px era 18 + los 16 medidos. Un hueco que no cuenta la inclinación es un hueco
     calculado para otro diseño.
     ⚠️ Y al bajar la rotación a ±1,5° el 2026-08-13, el sobrecoste de la inclinación
     cae a la mitad: el hueco baja a 26px. El chequeo de solape sigue siendo quien lo
     defiende — si alguien vuelve a subir el ángulo sin tocar esto, falla. */
  gap: clamp(16px, 1.7vw, 24px);   /* sin rotación, el hueco ya no paga su sobrecoste */
  width: 100%;
  align-items: stretch;
}
@media (max-width: 900px)  { .cc-silos { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px)  { .cc-silos { grid-template-columns: 1fr; } }

/* La rejilla sustituye a los `margin-left` escalonados que tenía el apilado
   vertical: dentro de una celda un margen izquierdo no desordena, descuadra. */
.cc-silo:nth-child(n) { margin-left: 0; }
.cc-silo {
  width: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  text-align: left;
}
/* ⚠️ NEUTRO, y no es una preferencia estética (2026-08-13). Los cuatro silos vestían
   `--cc-unidad`, o sea la paleta de las unidades de Canal Cero: los mismos cuatro
   colores que 300px más abajo significan Canal.Media, Canal.Commerce, Canal.Data y
   Canal.Brand. Aquí visten a los PROVEEDORES QUE NO TE RESPONDEN. La sección le estaba
   poniendo los colores de la marca a los malos de la película.
   Traía además dos consecuencias que Ricardo vio como "se ve muy feo" y "los rayos no
   están sincronizados con lo de abajo", y las dos salen de la misma causa:
     · cuatro hues neón en una fila es la firma visual de un template generado;
     · S3 en arcoíris y S4 en blanco no se leen como el mismo objeto por más que
       compartan keyframe y tamaño (8.58). El color los delataba.
   Con el problema en gris, el color queda SOLO en la solución y el verde de S4 aterriza
   como el único acento de las dos secciones, que es el argumento. */
/* ⚠️ EL ICONO VA ATENUADO, con la MISMA mezcla que su rótulo (2026-08-29, punto V1 de la
   auditoría visual del 27). No es una decisión nueva: es la que ya está escrita en
   `.cc-silo__rot` —«el reparto que queda: color en el ICONO y en el RÓTULO»— y unas
   líneas más abajo, en el bloque del emparejamiento silo↔unidad: «el color va DEBILITADO
   acá a propósito… si estuviera a pleno las dos secciones dirían lo mismo con la misma
   fuerza y el contraste narrativo —que es todo el punto— se perdería».

   Medido el 2026-08-29, el rótulo estaba al 62 % y el icono se había quedado al 100 %:
   `rgb(61,237,180)`, EXACTAMENTE el mismo valor que el `01` y el `Ver Canal.Media` de la
   tarjeta Canal.* de S5. O sea el único elemento del silo que seguía a plena marca era
   justo el que más pesa visualmente, y con él la sección del problema competía de tú a tú
   con la de la solución. Los otros tres portadores ya estaban resueltos —nombre en
   `--cc-fg-read`, cifra en blanco, borde neutro—; faltaba este.

   La auditoría lo levantó como «colisión de paleta» y como una decisión pendiente de
   elegir entre romper el emparejamiento o reforzar con composición. No era ninguna de las
   dos: era una decisión YA TOMADA que no estaba aplicada — 8.37 otra vez. Por eso no se
   toca el emparejamiento, que es lo que hace legible la rima S3→S5.

   La mezcla es la misma expresión que la del rótulo a propósito: dos números distintos
   para el mismo papel serían dos decisiones donde hay una. */
.cc-silo__icowrap {
  color: color-mix(in srgb, var(--cc-unidad) 62%, var(--cc-fg-read));
  display: flex;
}
.cc-silo__ico { display: block; }
/* ⚠️ El apilado va sobre el `<p>`, NO sobre el elemento. Bricks envuelve el texto de
   un elemento `text` en su propio `<p>`, así que `display:flex` en la clase gobierna
   un contenedor cuyo único hijo es ese `<p>` — la cifra y su rótulo quedaban EN LÍNEA
   dentro de él. Es 8.9 literal, y se ve midiendo: `innerHTML` del nodo con la clase
   devolvía `<p>$148M<span…></p>`. */
.cc-silo__cifra { margin-top: auto; }
/* ⚠️ `nowrap` + `tabular-nums` (2026-08-13). «48 KPIs» partía en dos y dejaba esa
   tarjeta con **3 líneas contra 2** de sus vecinas: la fila entera se leía descuadrada
   y era el defecto más visible de la sección. La cifra es un dato, no un párrafo — no
   tiene por qué envolver nunca. `tabular-nums` alinea los dígitos entre las cuatro,
   que es lo que hace que se lean como una tabla y no como cuatro carteles. */
.cc-silo__cifra p {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
/* El rótulo de la métrica, que es lo que hace honesta a la cifra (ver arriba). */
/* ⚠️ Dos correcciones que salieron de las suites al neutralizar el silo (2026-08-13),
   y las dos son gotchas ya escritos mordiendo otra vez:
   · **El color no puede ser `--cc-fg-muted`**: ese token está calibrado contra el fondo
     de SECCIÓN, y aquí el texto va sobre la superficie de la tarjeta
     (`rgba(255,255,255,.06)`), que la aclara. Compuesto daba **4.14:1** y `a11y.mjs` lo
     cazó. Es 8.51 literal —un token de texto no es seguro por sí mismo, lo es contra una
     superficie— y aparece justo cuando el rótulo deja de llevar color de unidad, que sí
     pasaba. `--cc-fg-read` es el token que corresponde a texto sobre esta superficie.
   · **El tamaño vuelve a 12px.** Bajarlo a 11 estrenaba un tamaño de fuente nuevo en la
     portada y `ux.mjs` saltó (13 contra un techo de 12). Ese trinquete existe para que
     la entropía tipográfica no se recupere sola: 12px ya está en la escala
     (`--cc-fs-xxs`) y hace el mismo trabajo. */
.cc-silo__rot {
  font-size: var(--cc-fs-xxs);
  font-weight: var(--cc-fw-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* ⚠️ El color de unidad VUELVE al rótulo, corrigiendo una sobre-corrección propia. Se
     habían neutralizado los cuatro silos con el argumento de que la paleta de la marca no
     debe vestir al problema. El argumento era demasiado absoluto: el diseño original
     emparejaba cada silo con la unidad que lo REEMPLAZA (agencia de medios → Canal.Media),
     y ese emparejamiento es lo que hace que S3 y S5 se lean como la misma historia en dos
     estados. Lo que sí sobraba era el neón repartido por toda la tarjeta.
     El reparto que queda: color en el ICONO y en el RÓTULO; borde, fondo y cifra neutros.
     Se conserva el 90% del efecto sobrio y se recupera el emparejamiento.
     El alfa lo fija el contraste, no el gusto: `color-mix` con el token de lectura hasta
     pasar AA sobre la superficie compuesta de la tarjeta (8.51). */
  color: color-mix(in srgb, var(--cc-unidad) 62%, var(--cc-fg-read));
}

/* ── LA FILA VACÍA: el argumento, dentro del dato (2026-08-13) ──────────────
   Idea de Ricardo, y es la mejor de la tanda: cada proveedor reporta su métrica y los
   cuatro dejan «Ventas atribuidas» EN BLANCO. El vacío está a propósito — es lo que la
   sección viene diciendo con palabras («¿Ventas atribuidas? Nadie responde esta
   pregunta») puesto donde de verdad se nota, que es en la ficha de cada uno.
   El guion va en un `::after` y no en el texto: es señalización, no contenido, así que
   no debe leerse en voz alta ni traducirse. */
.cc-silo__vacio {
  /* ⚠️ `align-self: stretch` es obligatorio aquí: `.cc-silo` es un flex en columna con
     `align-items: flex-start`, así que sin esto la fila se encoge a su contenido y tanto
     el filete punteado como el guion se quedan a media tarjeta — el hueco vacío deja de
     leerse como una casilla sin rellenar y parece texto suelto. */
  align-self: stretch;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed rgba(255, 255, 255, 0.14);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.cc-silo__vacio p { margin: 0; display: contents; }
.cc-silo__vacio::after {
  content: "—";
  color: var(--cc-fg-muted);
  font-weight: var(--cc-fw-bold);
  line-height: 1;
}
html[data-theme="light"] .cc-silo__vacio { border-top-color: rgba(32, 30, 44, 0.18); }

/* ── Los haces ──────────────────────────────────────────────────────────────
   Cuatro barras inclinadas que salen de debajo de cada tarjeta y se desvanecen
   antes de tocarse. El desvanecido lo hace un velo con el color del SUELO de la
   sección, no una máscara: `mask-image` no compone igual en los dos temas y aquí
   el fondo cambia con el tema (8.25). Con el velo tomando `--cc-bg` el efecto
   sigue al tema solo.
   Los ángulos abren hacia fuera (21°, 7°, -7°, -21°): divergen, que es el punto.
   En S4 el mismo objeto converge. */
/* ⚠️ REJILLA, no porcentajes (2026-08-13). Los haces iban a `left: 12.5/37.5/62.5/
   87.5%`, que son los centros de cuatro columnas iguales... del CONTENEDOR, no de
   las tarjetas: la rejilla tiene huecos, las tarjetas están rotadas y el `skewX`
   desplaza la masa del haz media altura por la tangente del ángulo. Medido, el
   centro de cada haz caía a **+55, +13, −7 y −40px** del centro de su tarjeta, o sea
   el primero salía por la derecha y el último por la izquierda.
   Con la misma rejilla de 4 columnas que los silos y `justify-self: center`, el
   ARRANQUE de cada haz coincide con el centro de su tarjeta por construcción, y la
   inclinación desplaza solo el pie — que es lo que se quiere: la luz se abre. */
.cc-haces {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(16px, 2.4vw, 34px);
  width: 100%;
  max-width: 1180px;
  height: clamp(96px, 13vw, 170px);
  /* Los haces SALEN de debajo de las tarjetas: sin esto arrancaban 25px más abajo
     (medido) y se leían como cuatro rayas sueltas en vez de como luz que cae. */
  margin-top: clamp(-34px, -2.4vw, -14px);
}
.cc-haz {
  justify-self: center;
  height: 100%;
  width: clamp(5px, 0.62vw, 8px);
  border-radius: 4px;
  transform-origin: top center;
  /* Color pleno el primer tercio y luego caída. La primera versión iba de color a
     transparente en todo el recorrido Y encima llevaba el velo del suelo tapando el
     58%: las dos caídas se sumaban y el haz quedaba invisible (medido: existía,
     170px de alto, y no se veía). Un desvanecido, no dos. */
  /* ⚠️ BLANCO, no el color de la unidad (2026-08-13). Es la otra mitad de la
     neutralización de los silos, y es lo que por fin hace que S3 y S4 sean el mismo
     objeto: allá la luz cae y se apaga, aquí converge y llega. Mismo material, dos
     actos. Con cuatro colores en S3 y blanco en S4 el ojo no podía enlazarlos por más
     que compartieran keyframe, duración y tamaño (8.58). */
  background: linear-gradient(180deg,
    var(--cc-haz-a) 0%,
    var(--cc-haz-b) 22%,
    transparent 96%);
}
/* ⚠️ El haz SÍ se voltea con el tema, y esto lo cazó una captura en claro: en blanco
   sobre el papel claro de S3 los cuatro **desaparecían**. Es 8.24 en su forma más
   directa — un color que funciona en oscuro y no existe en claro—, y aquí era fácil de
   pasar por alto porque acababan de dejar de ser de color: al quitarles el teñido de
   unidad heredaron un blanco fijo.
   Ojo con la diferencia con los de S4: aquellos van sobre la banda violeta, que es una
   ISLA OSCURA en los dos temas (está en las PERMITIDAS de `tema.mjs`), así que ahí el
   blanco es correcto siempre. Mismo objeto, distinto suelo. */
/* ⚠️ GROSOR Y ALFA BAJADOS (2026-08-13), y el criterio salió de medir TINTA, no contraste
   puntual — que es la trampa de 8.54. Por contraste el haz nunca fue el elemento más fuerte
   del bloque (5.12:1 contra los 17:1 de la cifra), pero la tinta cuenta el área: un haz de
   14px x 170px sumaba **407k**, prácticamente lo mismo que la fila vacía (387k), o sea la
   decoración competía de tú a tú con el elemento que la sección quiere como protagonista.
   14px → 8px y alfa 0.5 → 0.3 lo dejan claramente por debajo. Los cuatro juntos seguían
   siendo solo 0,30x una tarjeta, así que el problema nunca fue el conjunto: era la pieza. */
.cc-haz { --cc-haz-a: rgba(255, 255, 255, 0.3); --cc-haz-b: rgba(255, 255, 255, 0.24); }
html[data-theme="light"] .cc-haz { --cc-haz-a: rgba(32, 30, 44, 0.26); --cc-haz-b: rgba(32, 30, 44, 0.2); }
/* ⚠️ SIGNOS INVERTIDOS EL 2026-08-13, y es el fallo más de fondo de las dos secciones.
   El comentario de S4 decía que aquí los haces "divergían (21° / 7° / -7° / -21°,
   abriéndose)". **Medido, hacían lo contrario:** con `transform-origin: top center` un
   skew positivo mueve el PIE hacia la derecha, así que el haz de la izquierda caía
   hacia el centro. Los cuatro convergían — el pie a 390px del centro contra 455 la
   cabeza.
   O sea las dos secciones decían lo MISMO. S3 debe abrirse: cuatro proveedores cuya
   inversión se dispersa y no se junta con nada («¿Ventas atribuidas? Nadie responde»).
   Que converjan es literalmente el argumento de S4, y tenerlo en las dos deja la
   segunda sin nada que aportar. Es lo que Ricardo describía como *"algo le falta, no
   está sincronizado con lo de abajo"*: no era el estilo, era que el dibujo contaba dos
   veces la misma historia.
   Ahora: aquí se ABREN y se apagan; allá se CIERRAN sobre el verde. */
.cc-haz:nth-child(1) { transform: skewX(-21deg); }
.cc-haz:nth-child(2) { transform: skewX(-7deg); }
.cc-haz:nth-child(3) { transform: skewX(7deg); }
.cc-haz:nth-child(4) { transform: skewX(21deg); }
/* ⚠️ AQUÍ HUBO UN VELO Y SE QUITÓ (2026-08-13). La primera versión desvanecía los
   haces con una capa que iba de transparente a `--cc-bg`, como hace el mockup. En el
   mockup funciona porque su fondo está escrito a mano en la propia página; aquí no:
   el fondo de la sección lo pinta la PALETA DE BRICKS desde la BD (8.3), así que
   `--cc-bg` y el suelo real no coinciden y el velo se veía como una **banda negra**
   de bordes rectos cruzando la página.
   La salida no fue perseguir el color correcto sino borrar la capa: el haz ya sabe
   desvanecerse solo con su propio degradado. Una capa que tiene que adivinar el color
   de lo que hay detrás es una capa de más — y en un sitio con dos temas, adivina mal
   en uno de los dos. */
/* Bajo 900px las tarjetas pasan a 2x2 y los haces dejarían de salir de debajo de
   cada una: se retiran en vez de mentir sobre de dónde vienen. */
@media (max-width: 900px) { .cc-haces { display: none; } }

/* ── El remate ────────────────────────────────────────────────────────────── */
.cc-remate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  /* ⚠️ ANTES ERA UN MARGEN NEGATIVO de -43px, "para que el texto naciera de donde la luz
     se apaga". Medido, lo que hacía era dejar el titular a **1px** del final de los haces
     —y metido dentro de su zona—, con los pies de los haces 2 y 3 cayendo justo encima
     del texto. Ricardo: *"está muy arriba, choca con los rayos"*.
     La idea era buena y el valor la traicionaba: para que el texto NAZCA de la luz tiene
     que haber un tramo en que la luz ya se apagó y el texto todavía no empieza.
     ⚠️ Y desde el 2026-08-13 el reparto es DELIBERADAMENTE ASIMÉTRICO. Con ~93px arriba y
     ~112px abajo el remate quedaba casi equidistante entre los haces y el corte violeta, o
     sea flotando entre dos bloques sin pertenecer a ninguno. Ricardo: *"no está muy abajo,
     está mal repartido"*.
     «¿Ventas atribuidas? / Nadie responde esta pregunta» es la CONCLUSIÓN de las tarjetas,
     así que tiene que pesar hacia arriba: ~90px al bloque del que forma parte y ~160 al
     que no. La proximidad es lo que agrupa; a distancias iguales el ojo no puede decidir.
     ⚠️ EL «~90 ARRIBA» NO SALE SOLO DE AQUÍ, y anotarlo cuesta una línea y ahorra una
     auditoría: son **44px de `gap` del contenedor de Bricks (`con021`) + 46,08 de este
     `margin-top`** a 1440 (medido el 2026-08-27). Buscar los 90 en este valor solo no los
     encuentra, y de ahí salió el falso hallazgo de que «los haces mueren 91px antes del
     remate»: ese hueco no es un olvido, es este reparto. El «~160 abajo» vive en el
     `padding-bottom` de `#brxe-sec020`, arriba en el bloque de alternancia.
     ⚠️ Y cerrarlo sería volver a lo que Ricardo ya rechazó —*«no está muy abajo, está mal
     repartido»*—, además de contradecir el argumento de la sección: aquí la luz **se apaga
     sin llegar a nada**, y en S4 converge (ver `.cc-haz`). Que los haces alcanzaran el
     remate le daría a S3 el dibujo de S4. */
  margin-top: clamp(28px, 3.2vw, 90px);
}



/* El violeta de la banda va AQUÍ y no en el ajuste de fondo de la sección, y es una
   decisión, no un rodeo: Bricks no acepta un degradado en el campo de color —lo
   ignora en silencio y la sección sale negra (medido: el fondo quedaba en #0a0a0f)—
   y, más de fondo, un color puesto desde el builder vive en la BD y se pierde con el
   próximo volcado (8.3, §10). En CSS versionado viaja en git y se despliega solo.
   El ángulo (100deg) y los dos extremos salen del mockup que revisó el equipo. */
/* La clase nombra la intención; la regla que PINTA está en el bloque de alternancia
   de superficies, con especificidad de ID (ver allí el porqué). */


/* ── LOS RAYOS SUBEN (2026-08-13) ───────────────────────────────────────────
   Pedido de Ricardo: *"¿no se puede poner una animación... como si subieran los
   rayos y las tarjetas?"*. Encaja con el nombre que él mismo le puso a la sección
   —"fuegos artificiales"— y de paso le da sentido temporal al argumento: primero
   aparecen los proveedores, después la luz sube desde cada uno... y se apaga sin
   llegar a ninguna parte. La sección pasa de describir el problema a representarlo.

   ⚠️ El crecimiento va con `clip-path` y NO con `scaleY`, y es por geometría, no por
   gusto: el haz ya usa `transform: skewX()` con `transform-origin: top center`, que
   es lo que lo hace nacer bajo su tarjeta. `scale` comparte ese origen, así que para
   crecer hacia arriba habría que llevar el origen al pie — y entonces la inclinación
   pivota desde abajo y la punta se desplaza 65px a los 170px de alto (medido con el
   ángulo de 21°). `clip-path: inset()` revela sin tocar la transformación.
   `inset(100% 0 0 0)` recorta desde arriba, así que al bajar a 0 lo visible CRECE
   desde el pie: sube. */
/* ⚠️ El `to` va ESCRITO, y no es redundante. Sin él, el estado final es el natural del
   elemento —`clip-path: none`— y `inset()` → `none` **no es interpolable**: el haz
   saltaba de invisible a completo de golpe, o sea la animación existía y no se veía.
   Es primo de 8.11 (una animación que no hace lo que dice) y se cazó midiendo el
   `clipPath` computado cada 200ms: pasaba de `inset(100%…)` a `none` sin valores
   intermedios. `inset(0 0 0 0)` es visualmente idéntico a `none` y sí interpola. */
@keyframes cc-haz-subir {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

html.cc-js .cc-reveal-haces:not(.cc-in) .cc-haz { clip-path: inset(100% 0 0 0); }
html.cc-js .cc-reveal-haces.cc-in .cc-haz {
  animation: cc-haz-subir 900ms var(--cc-ease-out) backwards;
}
/* Escalonados, y arrancan DESPUÉS de que sus tarjetas hayan entrado (las cuatro
   tardan 240ms en escalonarse + los 560 de la entrada): un haz no puede salir de
   una tarjeta que todavía no está. */
html.cc-js .cc-reveal-haces.cc-in .cc-haz:nth-child(1) { animation-delay: 620ms; }
html.cc-js .cc-reveal-haces.cc-in .cc-haz:nth-child(2) { animation-delay: 740ms; }
html.cc-js .cc-reveal-haces.cc-in .cc-haz:nth-child(3) { animation-delay: 860ms; }
html.cc-js .cc-reveal-haces.cc-in .cc-haz:nth-child(4) { animation-delay: 980ms; }

/* ── Y las tarjetas, escalonadas, SIN perder su deriva ──────────────────────
   ⚠️ Aquí está la trampa, y costó verla: poner `.cc-stagger` en `.cc-silos` habría
   sido lo obvio y habría BORRADO la deriva. La regla genérica del sistema
   (`html.cc-js .cc-stagger.cc-in > *`) declara el atajo `animation`, que reescribe la
   propiedad entera — la deriva de `.cc-silo` desaparece y las tarjetas se quedan
   quietas para siempre. Un atajo no convive con otro atajo sobre la misma propiedad.
   La salida es declarar LAS DOS animaciones en la misma lista, con la entrada AL
   FINAL: cuando dos animaciones tocan `translate`, manda la última, así que durante
   sus 560ms gobierna la entrada; al terminar —sin `forwards`, que es la red de
   8.11— el control vuelve solo a la deriva.
   Los retrasos de la deriva siguen siendo NEGATIVOS a propósito: arrancan el ciclo ya
   empezado, que es lo que impide que las cuatro floten en fase. */
@keyframes cc-silo-subir { from { opacity: 0; translate: 0 26px; } }

html.cc-js .cc-reveal-silos:not(.cc-in) > .cc-silo { opacity: 0; translate: 0 26px; }
html.cc-js .cc-reveal-silos.cc-in > .cc-silo:nth-child(1) {
  animation: cc-silo-deriva 15s ease-in-out -1s infinite,
             cc-silo-subir var(--cc-dur-reveal) var(--cc-ease-out)   0ms backwards;
}
html.cc-js .cc-reveal-silos.cc-in > .cc-silo:nth-child(2) {
  animation: cc-silo-deriva 18s ease-in-out -7s infinite,
             cc-silo-subir var(--cc-dur-reveal) var(--cc-ease-out)  80ms backwards;
}
html.cc-js .cc-reveal-silos.cc-in > .cc-silo:nth-child(3) {
  animation: cc-silo-deriva 16s ease-in-out -4s infinite,
             cc-silo-subir var(--cc-dur-reveal) var(--cc-ease-out) 160ms backwards;
}
html.cc-js .cc-reveal-silos.cc-in > .cc-silo:nth-child(4) {
  animation: cc-silo-deriva 19s ease-in-out -9s infinite,
             cc-silo-subir var(--cc-dur-reveal) var(--cc-ease-out) 240ms backwards;
}

/* Quien pidió menos movimiento ve la composición terminada, no una pantalla vacía:
   es la red de 8.11/8.12 — el estado oculto solo puede existir si algo garantiza que
   se sale de él. Aquí se sale sin animación ninguna. */
@media (prefers-reduced-motion: reduce) {
  html.cc-js .cc-reveal-haces:not(.cc-in) .cc-haz,
  html.cc-js .cc-reveal-haces.cc-in .cc-haz { clip-path: none; animation: none; }
  html.cc-js .cc-reveal-silos:not(.cc-in) > .cc-silo { opacity: 1; translate: none; }
}
/* Y si el JS no llega, el guard de <head> retira `cc-js` y todo queda visible; este
   respaldo cubre además el caso de que el guard corra con el observador ya muerto. */
html.cc-motion-off .cc-reveal-haces:not(.cc-in) .cc-haz { clip-path: none; }
html.cc-motion-off .cc-reveal-silos:not(.cc-in) > .cc-silo { opacity: 1; translate: none; }

/* ── S4: UNA SOLA LÍNEA VERDE, Y NADA MÁS ───────────────────────────────────
   Esta sección tuvo cuatro haces convergiendo sobre un verde —el segundo acto de los de
   S3— y el 2026-08-13 se retiraron por decisión de Ricardo. El motivo es bueno y conviene
   dejarlo escrito para no reconstruirlo dentro de un mes:

   > «La línea verde cruzando el borde inferior ya dice "cuatro se vuelven una y una
   >  continúa". El abanico repite eso y es el único elemento decorativo que queda.»

   O sea el abanico no se fue por feo sino por REDUNDANTE: decía con cuatro objetos lo que
   el cruce del borde ya dice con uno. Los haces de S3 se quedan intactos —esos sí
   comunican fragmentación, que es otra cosa— y siguen abriéndose sin juntarse.

   Lo que queda es una línea de 4px que **nace por encima del H2 y sale por el borde
   inferior** hacia la sección siguiente. Es un solo elemento, no dos: conserva su
   `.cc-bar-draw`, así que se traza de arriba abajo al entrar en pantalla y el trazo
   atraviesa el borde como parte del mismo gesto.
   ⚠️ Va con `z-index: -1` y la sección aislada, así que pasa POR DETRÁS del titular. Sin
   `isolation: isolate` un z-index negativo se escapa al contexto del ancestro y la línea
   se esconde detrás del propio fondo de la sección.
   ⚠️ Y `overflow` de la sección tiene que quedar visible: es lo que permite que asome por
   abajo. Si algún día alguien pone `overflow:hidden` aquí, la línea se corta en el borde
   y se pierde justo el gesto que la sección existe para hacer. */
/* ⚠️ DOS SEGMENTOS, NO UNA LÍNEA CONTINUA (2026-08-13), y el porqué es la lección de la
   ronda. La versión anterior era **una sola línea** que cruzaba toda la banda por detrás
   del texto. Geométricamente perfecta, y todos los chequeos en verde: 4px, verde de marca,
   arranque de 112px, cruce de 48px, `overflow` visible. Ninguno medía lo único que
   importaba.
   ⚠️ Y el apilado tampoco era el problema — se comprobó por PÍXEL: donde hay glifo el color
   es `rgb(255,255,255)`, o sea el texto sí iba encima. Lo que rompía la lectura es que la
   línea asomaba en **todos los huecos entre letras**: entre «otra» y «forma», dentro de
   «crecer», y partiendo el subtítulo entre «tu» e «inversión». Ricardo: *"partiendo cada
   línea de texto en dos mitades"*.
   > Una línea que pasa por detrás de un texto no lo respeta: lo tacha.
   La línea debe INTRODUCIR el texto, no atravesarlo. Segmento 1 nace arriba y muere con
   aire antes del H2; segmento 2 arranca bajo el subtítulo con el mismo aire y sale por el
   borde. Mismo ancho, mismo color, ninguno toca texto — así el ojo los lee como **una**
   línea que el bloque de copia interrumpe, que era la intención desde el principio. */
/* ⚠️ ESTE BLOQUE SE PERDIÓ Y SE RESTAURÓ EL 2026-08-13. Un reemplazo por rango de texto
   sobre este archivo —hecho para reescribir la sección de S4— se llevó por delante las
   cuatro reglas de abajo, que vivían en medio del rango. El resultado: `sec020` perdió su
   superficie y cayó a base, `sec077` se quedó en superficie junto a `sec040` (1.751px de
   tramo plano, el fallo exacto que este bloque documenta), el violeta perdió su degradado
   y `sec061` perdió los tokens que sostienen el contraste de sus píldoras.
   **Y las 9 suites daban verde**: ninguna medía la alternancia de fondos entre secciones.
   La lección práctica: editar CSS por índices de texto es afilado, y lo que se corta en
   medio no avisa. Comparar contra `git show <commit>:<archivo>` antes de dar por buena una
   reescritura larga. */

/* ==========================================================================
   ALTERNANCIA DE SUPERFICIE (2026-08-03)
   El hallazgo que mandaba sobre el resto de la auditoría visual: **el tramo más
   largo con el mismo fondo medía 2.162px, o sea el 32% de la página**. Eran el
   hero, la barra de partners, los silos y la apertura de S3, todos sobre la misma
   base. Subir el contraste de la banda (§7) arregló los saltos que existían, pero
   no crea los que faltan: con las secciones agrupadas 3-y-3 el ritmo no aparece
   por más contraste que tenga cada salto.

   Ahora las superficies ALTERNAN: base (hero + partners) · superficie (silos) ·
   base (S3) · superficie (S4) · violeta · base (S6) · superficie (S8) · base
   (S9 + chips) · ink (cierre + footer). El tramo más largo baja a **792px**.

   Y S3 pierde su degradado: existía para pre-anunciar la superficie de S4, y con
   la alternancia esa transición ya la hace el salto de sección. Un degradado de
   677px entre dos valores a 1.157 es además el candidato perfecto a bandeo en
   pantallas de 6 bits.

   `body #brxe-…` es (1,0,1) y le gana al (1,0,0) que Bricks emite por elemento sin
   depender del orden de carga (8.1). Se usa el token semántico, así que los dos
   temas lo heredan y no hay una regla por tema.
   ========================================================================== */
body #brxe-sec020 { background-color: var(--cc-bg-surface); }
/* ⚠️ Relleno inferior propio (2026-08-13): el remate necesita MÁS aire por debajo que por
   encima para que se lea como cierre del bloque de tarjetas y no como pieza suelta entre
   dos secciones. Va por id porque es una excepción de ESTA sección, no un cambio de la
   escala.
   ⚠️ CORREGIDO EL 2026-08-27: este comentario decía que «`--cc-sec-lg` da 112px». Daba **152**
   (`clamp(96px, 12vw, 152px)`); el que daba 112 era `--cc-sec-md`.
   ⚠️ ACTUALIZADO EL 2026-08-31: los tres tokens se recortaron un escalón, así que hoy
   `--cc-sec-lg` da **120** (`clamp(72px, 9vw, 120px)`) y `--cc-sec-md` da **88**. La
   conclusión de abajo NO cambia —este valor sigue sin poder sustituirse por el token— y de
   hecho ahora es más simple: con los valores nuevos las dos curvas **ya no se cruzan**, la de
   aquí es mayor en todo el rango. El valor de aquí siempre fue
   el correcto — lo que estaba mal era el token que se citaba para justificarlo.
   ⚠️ Y NO se puede sustituir por `--cc-sec-lg` «para no tener un tercer valor»: las dos
   curvas se CRUZAN. Medido: a 1440 esto da 158,4 y el token 152; a 1280 esto da 140,8 y el
   token 152; a 1100, 121 contra 132. O sea el cambio no sería cosmético, movería el reparto
   en todo el rango — y ese reparto es load-bearing: es el «~160 abajo» de la asimetría
   deliberada del remate (ver `.cc-remate`, que lleva el «~90 arriba»). Los dos números son
   UNA decisión repartida en dos sitios del archivo, y este es el otro. */
body #brxe-sec020 { padding-bottom: clamp(96px, 11vw, 160px); }
/* ⚠️ AÑADIDA EL 2026-08-13, y el motivo es exactamente por qué existe este bloque:
   al subir los CASOS del puesto 8 al 6 quedaron pegados a SERVICIOS, y las dos eran
   superficie. Medido: **1.751px de tramo plano**, peor que los 792px que la auditoría
   del 2026-08-03 dejó como techo. Reordenar secciones invalida una alternancia
   escrita para el orden anterior, y nada avisa: el color de cada sección era
   correcto por separado.
   Casos pasa a base y la secuencia vuelve a alternar:
   base (hero+partners) · superficie (problema) · VIOLETA (giro) · superficie
   (servicios) · base (casos) · VIOLETA (números) · base (historia) · ink (cierre). */
body #brxe-sec077 { background-color: var(--cc-bg); }

/* ⚠️ LAS PÍLDORAS DE TRAYECTORIA/CAPACIDAD, AHORA SOBRE VIOLETA (2026-08-13).
   Al fusionar Respaldo dentro de Métricas, esas píldoras pasaron del suelo oscuro
   (`--cc-bg`, casi negro) a la banda violeta `#4B2FBF`. Sus colores estaban
   calibrados contra el primero, así que sobre el segundo cayeron a **2.44:1** —bajo
   el mínimo AA— y lo cazaron `a11y.mjs` y `tema.mjs` en la misma corrida.
   No es un despiste puntual, es la misma lección que ya costó el fallo de las
   bajadas del header en julio: **un token de texto no es seguro por sí mismo, lo es
   contra una superficie.** Mover contenido de fondo es cambiarle la superficie a todo
   lo que lleva dentro, y eso vale por cada color que traiga.
   Se sube el alfa dentro de la banda en vez de tocar el token global, que sigue
   siendo correcto donde nació. */
#brxe-sec061 {
  --cc-fg-muted: rgba(255, 255, 255, 0.86);
  --cc-fg-read: rgba(255, 255, 255, 0.94);
}
/* ⚠️ sec035 CAMBIÓ DE PAPEL EL 2026-08-13. Era "base" y anulaba explícitamente
   cualquier `background-image` —ahí murió el degradado de 677px de 8.37—. Ahora es
   la BANDA VIOLETA del giro: el mockup que el equipo revisó el 2026-08-10 la tiene
   así, y la sección lo necesitaba por dos razones que ya estaban anotadas. Una, era
   una de las dos con la columna muerta (820px de 1440, densidad 24.7%): una banda a
   sangre la llena por construcción. Y dos, el giro argumental merece el mismo peso
   visual que el problema que acaba de plantearse; en base plana no lo tenía.
   ⚠️ Va con especificidad de ID y no con la clase sola: `.cc-banda-violeta` es
   (0,1,0) y pierde contra el (1,0,1) de esta misma tanda de alternancia — se midió,
   la sección salía negra con la clase puesta (8.1). La clase se conserva porque es
   la que nombra la intención; la regla que pinta es esta.
   La alternancia queda: base (hero+partners) · superficie (silos) · VIOLETA (giro) ·
   superficie (servicios) · … o sea el ritmo no se pierde, gana un acento. */
/* ⚠️ LAS DOS BANDAS VIOLETAS COMPARTEN UNA SOLA DEFINICIÓN (2026-08-13). La portada tiene
   dos —el giro (`sec035`) y los números (`sec061`)— y al oscurecer solo la primera quedaron
   con tratamientos distintos en la misma página: degradado a 68,44,172→81,56,198 contra un
   plano de 75,47,191. Medido, y visible: dos violetas que no son el mismo violeta.
   Es 8.40 en su forma más simple. La banda es UN componente, así que se define una vez.
   ⚠️ El 12% de oscurecido se mezcla AQUÍ y no en `cc-tokens.css`: `--cc-violet-600` y
   `#5B3FE0` los usa todo el sitio y bajarlos allí cambiaría cada pieza violeta (§7). Lo que
   pidió Ricardo —*"bajá su luminosidad, hoy aplasta todo"*— es de estas bandas.
   `sec061` la pinta la PALETA desde la BD (8.3), así que hace falta la especificidad de id
   para ganarle; por eso van las dos por id y no por la clase. */
body #brxe-sec035.cc-banda-violeta,
body #brxe-sec061 {
  background-image: linear-gradient(100deg,
    color-mix(in srgb, var(--cc-violet-600) 88%, #0A0A0F) 0%,
    color-mix(in srgb, #5B3FE0 88%, #0A0A0F) 100%);
}

/* ── LA BANDA VIOLETA Y SU ÚNICA LÍNEA ──────────────────────────────────────
   ⚠️ HISTORIAL DE ESTA SECCIÓN, porque el estado actual es el resultado de quitar cosas y
   conviene no volver a añadirlas sin saber por qué salieron:
   · Tuvo un abanico de cuatro haces convergiendo sobre un verde (el segundo acto de los de
     S3). Salió el 2026-08-13: repetía con cuatro objetos lo que ya decía uno.
   · La línea verde cruzaba después el borde inferior, para romper la simetría de la banda
     —sus dos vecinas son el mismo color y por eso leía como banner insertado—. Ese tramo
     inferior **también salió**, por decisión de Ricardo el 2026-08-14.
   ⚠️ Consecuencia asumida: la banda vuelve a quedar encerrada entre dos superficies
   idénticas. Si eso vuelve a molestar, lo que NO sirve es subirle el alto (una banda
   encerrada más alta sigue encerrada) ni que el violeta sangre a `sec040` (obliga a
   volverla isla oscura y voltea todo su modo claro, además de tocar un fondo que pinta la
   paleta desde la BD, 8.3 + §10).
   Queda una sola línea, sobre el titular, que lo introduce y no lo toca (8.60). */
body #brxe-sec035 {
  position: relative;
  isolation: isolate;
  /* ⚠️ Si alguien pone `hidden` aquí, la línea se corta en el borde y muere el gesto
     entero de la sección sin que nada más falle. */
  overflow: visible;

  /* Las medidas de la línea viven juntas. ⚠️ Aquí decía «los DOS segmentos leen exactamente
     las mismas»: hay UN segmento, el de arriba del titular. El de abajo salió el 2026-08-14
     (ver el HISTORIAL arriba) y `--cc-linea-s2` nunca existió — comprobado con
     `git log -S` sobre toda la historia del archivo, cero coincidencias. */
  --cc-linea-x: 50%;
  --cc-linea-w: 4px;
  --cc-linea-top: clamp(40px, 5vw, 64px);    /* violeta vacío antes de que arranque */
  --cc-linea-s1: clamp(56px, 7vw, 96px);     /* largo del segmento de arriba */
  --cc-linea-aire: 56px;                     /* pausa entre el segmento 1 y el titular */

  /* ⚠️ Los segmentos son pseudo-elementos ABSOLUTOS, o sea no reservan espacio: el relleno
     superior tiene que sumar el del segmento 1 (hueco + largo + pausa). Si se toca
     `--cc-linea-s1` sin esto, el titular se sube sobre la línea. */
  padding-top: calc(var(--cc-linea-top) + var(--cc-linea-s1) + var(--cc-linea-aire));
}

/* ── HISTORIAL DE LA CONSTRUCCIÓN DE LA LÍNEA ───────────────────────────────
   ⚠️ ESTO ES HISTORIA, NO LA IMPLEMENTACIÓN. Se reescribió el 2026-08-27 porque decía lo
   contrario: aquí había DOS comentarios —uno con dos intentos y otro con tres— que
   describían en presente un gesto de **dos segmentos** y cerraban declarando una solución
   («una sola caja desde el arranque hasta fuera del borde inferior, y el hueco del medio lo
   hace un degradado de tramos duros») que **nunca se implementó**. Lo que hay debajo, y lo
   único que ha habido nunca, es un `::before` sólido de `--cc-linea-s1` sobre el titular.
   Comprobado en `HEAD` y en `8f14162`, el commit que lo introdujo.

   **Lo que pasó de verdad:** el tramo inferior se retiró por decisión de Ricardo el
   2026-08-14 (está en el HISTORIAL de la sección, arriba), o sea la solución de la caja
   única dejó de hacer falta antes de escribirse. El comentario se quedó describiéndola.
   Coste medido de esa mentira: una auditoría del 2026-08-27 la leyó como implementada,
   reportó como defecto que faltara medio gesto y calculó que la sección reservaba 216px de
   `padding-top` «para el gesto completo». **No los reserva para eso**: los 216 son
   64 de violeta vacío + los 96 de la línea que SÍ existe + 56 de pausa (medido).

   **Lo que sigue valiendo, y es el motivo de que la línea sea un pseudo-elemento DE LA
   SECCIÓN** — si algún día vuelve el segundo tramo, empezar por aquí:

   1. Segmento 1 en el FLUJO (centrado por el `flex` de `con036`, caja de 820px) y segmento
      2 con `left:50%` sobre la sección (1440px). Dos cajas distintas → dos redondeos.
   2. Los dos `position:absolute` con las mismas variables. Su bloque contenedor ya era el
      mismo (comprobado), pero el 1 pintaba en un HIJO anidado, o sea redondeaba dos veces.
   3. Los dos como `::before` y `::after` del MISMO elemento, mismas variables. **Seguían
      difiriendo**: 897..902 con 4 sólidos contra 898..902 con 5. Descartada la animación
      (idéntico con `animation:none`).

   ⚠️ Todo esto es invisible a DPR entero — 1440@1x siempre dio idéntico— y solo aparece a
   **125% de escalado de Windows**, que es el reparto por defecto de casi cualquier
   portátil. Ricardo lo vio como «desalineados 1-2px» y «quedaron en 2px de ancho»: las dos
   cosas eran el mismo fleco antialiaseado. La salida que se había pensado era quitarle la
   ocasión al redondeo con UNA sola caja; con un solo segmento el problema no se plantea,
   porque no hay nada con lo que alinearse. */
body #brxe-sec035::before {
  content: "";
  position: absolute;
  left: var(--cc-linea-x);
  translate: -50% 0;
  top: var(--cc-linea-top);
  height: var(--cc-linea-s1);
  width: var(--cc-linea-w);
  border-radius: 999px;
  background: var(--cc-green-500);
  pointer-events: none;
  transform-origin: top center;
}

/* ── El trazado, con su red de seguridad ────────────────────────────────────
   La línea se dibuja de arriba abajo al entrar la sección. Se cuelga de la clase `cc-in`
   que el observador pone en el contenedor, vía `:has()`, porque un pseudo-elemento no
   puede llevar clase propia.
   ⚠️ El estado recogido va SOLO bajo `html.cc-js`: si el JS no llega, `cc-in` no aparece
   nunca y sin esa guarda la línea se quedaría en `scaleY(0)`, o sea invisible para
   siempre. Es 8.11 literal, y en un pseudo-elemento no lo cubre la red genérica de
   `cc-motion-off`, que actúa sobre elementos reales — por eso lleva la suya. */
html.cc-js body #brxe-sec035:not(:has(.cc-stagger.cc-in))::before { transform: scaleY(0); }
html.cc-js body #brxe-sec035:has(.cc-stagger.cc-in)::before {
  animation: cc-trazar-y var(--cc-dur-reveal) var(--cc-ease-out) backwards;
}
@media (prefers-reduced-motion: reduce) {
  html.cc-js body #brxe-sec035::before { transform: none; animation: none; }
}
html.cc-motion-off body #brxe-sec035::before { transform: none; }

/* «Hay otra forma de crecer» dejaba "crecer" solo en la segunda línea. `balance` reparte
   las dos líneas en vez de llenar la primera; el `max-width` evita que en pantallas
   anchas se estire a una sola línea larguísima. */
#brxe-hea038 { max-width: 16ch; text-wrap: balance; margin-inline: auto; }

/* ── LOS PÁRRAFOS DE ENTRADA DE SECCIÓN: la misma regla que su titular ───────
   El `balance` de acá arriba se puso el 2026-08-13 para el TITULAR de S3 y se quedó ahí.
   Los cuatro párrafos de entrada de la portada seguían con reparto normal —llenar cada
   línea y dejar lo que sobre en la última—, y eso produce huérfanas. Medido el
   2026-08-29 a siete anchos, mirando qué fracción de la línea más ancha ocupa la última:

       sin balance   tex024@768   9 %      ← "y nadie te muestra qué inversión…"
                     tex044@600  11 %
                     tex039@600  20 %
                     tex131@600  20 %
       con balance   el mínimo de las 28 medidas sube a 51 %, y CERO por debajo del 25 %

   ⚠️ Ninguna cuenta de líneas cambia en ninguno de los 28 casos: `balance` reparte el
   mismo número de líneas, no añade ni quita. O sea no mueve un solo píxel de altura, que
   es lo que lo hace barato de aceptar.

   ⚠️ Va por id y no por una clase de componente A PROPÓSITO: añadir `_cssClasses` a los
   cuatro elementos obliga a tocar `home.json` y a re-importar, y el beneficio sería el
   mismo. Lo que evita que la lista se quede corta no es el selector, es el chequeo: el de
   `home.mjs` DERIVA los párrafos de entrada del DOM, así que un quinto que aparezca sin
   `balance` sale en rojo aunque nadie se acuerde de esta regla.

   ⚠️ Y NO se unificó la medida de lectura, que es lo que pedía el punto V4 de la
   auditoría: `--cc-measure` (700px) es la columna de LECTURA del blog —16px, alineada a
   la izquierda, texto largo— y estos son entradillas de 20px centradas. Son dos trabajos,
   no dos formas de decir lo mismo. Se probó igual, y el render lo desmintió: con 700px
   `tex039` pasa de 2 líneas parejas a 3 con una huérfana del 14 %. El detalle, en 8.125. */
#brxe-tex024,
#brxe-tex039,
#brxe-tex044,
#brxe-tex131 { text-wrap: balance; }

/* ── EL CORTE ENTRE S3 Y S4: BORDE LIMPIO, y hubo un fundido en medio ────────
   El 2026-08-13 se probó unir las dos secciones con un degradado, a pedido de Ricardo
   (*"¿no se pueden combinar los slides como un degradado?"*). **Se retiró el mismo día**
   y las dos razones valen más que el efecto:

   1. ⚠️ **La medición que lo justificaba estaba mal hecha.** Se dijo que había "327px de
      nada entre el final de un acto y el principio del otro", midiendo del último haz de
      S3 al primero de S4. Pero en medio está el REMATE («¿Ventas atribuidas? / Nadie
      responde esta pregunta»), que ocupa 102 de esos píxeles, y el resto es el relleno
      de sección de las dos —112px cada una, el valor del sistema—. O sea no había un
      vacío que tapar: había dos secciones con su aire normal.
   2. **Y el remedio se veía peor que el supuesto problema.** Un degradado vertical de un
      violeta saturado sobre un suelo casi negro no se lee como transición sino como fuga
      de luz o artefacto de render. Ricardo: *"el degradado es muy feo"*.

   Un borde limpio entre una sección oscura y una banda de color es lo normal y se lee
   como decisión. Si algún día se quiere suavizar el corte, la salida NO es difuminar el
   borde: es que un objeto lo cruce (los haces, una figura), que es transición por
   composición y no por desenfoque.
   Queda el chequeo de contraste del remate en `home.mjs` (8.57): mide el píxel
   renderizado, así que sigue siendo la red si alguien vuelve a poner una capa ahí. */

/* ⚠️ ESTE BLOQUE TAMBIÉN SE PERDIÓ Y SE RESTAURÓ EL 2026-08-13, y es el segundo del día.
   El mismo reemplazo por rangos que se llevó la alternancia se llevó esto, que **ni
   siquiera es de la portada**: es el POST DESTACADO del índice del blog. Sin estas reglas
   la primera entrada de /blog/ deja de ocupar el ancho completo y vuelve a ser una card
   igual a las demás, o sea desaparece la jerarquía entera de la página.
   ⚠️ Y `blog.mjs` pasó en verde con el bloque ausente: no mide el layout del destacado.
   La lección es la de 8.61 y vale repetirla: tras una reescritura larga, comparar la LISTA
   DE SELECTORES contra `git show <commit>:<archivo>`. Los dos daños del día estaban a
   cientos de líneas de lo que se editaba. */

/* ==========================================================================
   ÍNDICE DEL BLOG — el post más reciente, destacado (2026-07-30)
   El índice eran nueve cards idénticas: sin jerarquía, todas diciendo "soy
   igual de importante". El primero pasa a ocupar el ancho completo con la foto
   a un lado, que es el punto de entrada que faltaba.

   Se apunta a `:first-child` y no a un id de post: el destacado es "el más
   reciente", así que la regla tiene que seguir a quien ocupe ese lugar. Con
   scroll infinito los lotes siguientes se añaden DESPUÉS, así que el primero
   sigue siendo el primero.

   El markup lo emite Bricks y no se inventa (gotcha 8.9). El real es:
     li > a > .bricks-layout-inner > figure.image-wrapper + .content-wrapper
   ========================================================================== */

.cc-postgrid .bricks-layout-wrapper > li:first-child { grid-column: 1 / -1; }

.cc-postgrid .bricks-layout-wrapper > li:first-child .bricks-layout-inner {
  display: grid;
  /* `min(...)` y no un mínimo rígido: un mínimo fijo en una grilla es una promesa
     de overflow (gotcha 8.7). */
  grid-template-columns: minmax(min(320px, 100%), 1.05fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(20px, 3vw, 40px);
}

/* La foto del destacado es apaisada y más alta que la de una card, pero acotada:
   sin `max-height` una imagen vertical estiraría la fila entera. */
.cc-postgrid .bricks-layout-wrapper > li:first-child .image-wrapper { height: 100%; }
.cc-postgrid .bricks-layout-wrapper > li:first-child img {
  width: 100%;
  height: 100%;
  max-height: 420px;
  object-fit: cover;
}

/* El texto respira más que en una card y se centra contra la foto. El
   `.content-wrapper` de Bricks ya es flex y ocupa el alto completo de la fila, así
   que centrar es cuestión de `justify-content`, no de márgenes. */
.cc-postgrid .bricks-layout-wrapper > li:first-child .content-wrapper {
  justify-content: center;
  padding: clamp(20px, 2.4vw, 34px) clamp(20px, 2.4vw, 34px) clamp(20px, 2.4vw, 34px) 0;
}

/* ⚠️ El titular necesita el ID para ganar (gotcha 8.1). Bricks emite el tamaño del
   título del loop como `#brxe-uqjcws .repeater-item [data-field-id="…"]`, que es
   (1,2,0) y le gana a cualquier clase. Con `:first-child` + el elemento queda en
   (1,2,1) y gana por un pelo, sin `!important` y sin depender del orden de carga.
   Se usa `#brxe-uqjcws` y NO el `data-field-id`: ese id lo regenera Bricks al tocar
   el elemento, mientras que `uqjcws` viaja en page-blog-index.json y es estable.
   Que el titular suba un paso es lo que hace que se lea como destacado y no como
   una card estirada.

   El tamaño NO es un paso de display. La primera versión usó `--cc-fs-d3` (52px) y
   quedaba MÁS GRANDE que el H1 de la página, que mide 44: un título de tarjeta no
   puede pesar más que el título del sitio donde vive. 26px es el mismo tamaño que
   los títulos de tarjeta de la home, o sea "tarjeta destacada" pasa a ser un valor
   del sistema y no uno de esta página. El interlineado 1.15 ya existe aquí (lo usa
   el H1), así que no suma un valor nuevo a la escala. */
#brxe-uqjcws .repeater-item:first-child h2 {
  font-size: 26px;
  line-height: 1.15;
  letter-spacing: -0.015em;
}

/* Bajo el breakpoint del nav (767px) no hay ancho para dos columnas: vuelve a
   apilarse y se comporta como una card normal, solo que con la foto más alta. */
@media (max-width: 767px) {
  .cc-postgrid .bricks-layout-wrapper > li:first-child .bricks-layout-inner {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .cc-postgrid .bricks-layout-wrapper > li:first-child .content-wrapper {
    padding: clamp(16px, 4vw, 24px);
  }
}

/* ==========================================================================
   CARRUSEL DE MARCAS (2026-08-03)
   Propuesto por el equipo en la revisión del 2026-07-30 y priorizado por Ricardo.
   Sustituye al roster estático de 5 nombres, que ya era el activo de prueba social
   más fuerte de la página y se leía como una línea de texto suelta.

   TRES DECISIONES QUE SON EL COMPONENTE:

   1. **Es CSS puro, sin una línea de JS.** El contenido lo rinde el servidor y el
      movimiento es una `animation`. Si el JS falla, se cae o está desactivado, las
      marcas siguen ahí y siguen moviéndose. Es la lección de 8.11 llevada al caso
      fácil: si algo se puede hacer sin JS, no debe depender de JS.

   2. **El control de pausa también es CSS puro**, y ese es el punto donde este
      componente se aparta de 8.22 a propósito. Allá el botón de pausa se inyecta
      por JS porque sin JS no hay rotación que pausar. Acá la rotación la hace el
      CSS, así que existe SIN JS — y entonces un botón inyectado faltaría
      justamente cuando sigue habiendo movimiento que detener. La salida es un
      `<input type="checkbox">` con su `<label>`: server-rendered, operable con
      teclado, y su estado lo anuncia el lector de pantalla solo.
      WCAG 2.2.2 (nivel A) exige un mecanismo de pausa para movimiento automático
      que dure más de 5s y conviva con otro contenido. Esto lo es.

   3. **Solo la PRIMERA pista es contenido real**; las otras tres son repeticiones
      con `aria-hidden`. Sin eso un lector de pantalla anunciaría las marcas cuatro
      veces y Google leería contenido duplicado. Es el mismo criterio que la trust
      bar: lo visual se repite, el contenido no.

   ARITMÉTICA DEL BUCLE, que es lo que decide si se ve un hueco:
   con N pistas de ancho T, la cinta mide N·T y se anima hasta −(100/N)%, o sea
   exactamente una pista. En el peor momento del ciclo la ventana visible V necesita
   V ≤ (N−1)·T. Con 5 marcas T mide ~880px, así que **una sola copia no alcanza**
   para un viewport de 1440 y se vería el vacío al final de cada vuelta. Con N=4 el
   tope es 2.640px, que cubre hasta 2K de ancho.
   Si algún día se añaden marcas, T crece y el margen sube: nunca hay que tocar esto.

   CÓMO PASAR UNA MARCA DE TEXTO A LOGO (cuando lleguen los archivos):
   hoy las cinco van como wordmark de texto porque **no existe un solo logo de
   cliente** ni en el repo ni en los uploads locales — se buscó. La biblioteca de
   prod sí tiene seis (`logo-triumph`, `logo-palmers`, `logo-abkupfer`,
   `logo-dimak`, `logo-elbodegon`, `logo-mk`, todos en `uploads/2025/04/`, usados en
   la página VTEX Partner), pero ninguno corresponde a estas cinco.
   El camino es el mismo que ya usa la trust bar y por la misma razón de
   portabilidad (§7): el archivo va a `assets/img/marcas/<slug>.svg` —versionado, no
   un id de attachment que no sobrevive un dump— y el `<li>` pasa a llevar una clase
   con su `aspect-ratio` real y `background-image`, más `.cc-vh` en el texto para que
   el nombre siga siendo contenido indexable. **No se deja el CSS escrito de
   antemano**: sin el archivo serían reglas que no matchean nada, o sea CSS muerto.
   ========================================================================== */

.cc-marcas {
  position: relative;
  display: flex;
  align-items: center;
  /* 20px y no 14: con menos, el nombre que se está desvaneciendo en el borde
     derecho queda pegado al icono de pausa y los dos se leen como una sola cosa. */
  gap: 20px;
  width: 100%;
}

/* La máscara recorta la cinta Y difumina los dos bordes. El difuminado no es
   adorno: sin él las marcas aparecen y desaparecen de golpe contra el borde, que es
   lo que hace que un marquee se vea barato. Va con `mask` y no con un degradado de
   color encima porque así funciona igual en los dos temas — no hay que saber de qué
   color es el fondo. */
.cc-marcas__mask {
  flex: 1;
  min-width: 0;              /* un mínimo rígido en un flex es una promesa de overflow (8.7) */
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
}

/* ⚠️ EL HUECO ENTRE PISTAS VIVE EN LA PISTA, NO EN LA CINTA, y las dos razones son
   aritméticas (2026-08-04, auditoría visual).

   1. **El bucle no cerraba.** Con `gap: G` en la cinta, su ancho es `N·T + (N−1)·G`,
      pero el ciclo tiene que avanzar exactamente `T + G`. `translateX(-100/N%)` avanza
      `T + G·(N−1)/N`, o sea se queda corto en `G/N`: con G=56 eso son **14px de salto
      en cada vuelta**, cada 28 segundos. Se ve como un tirón y no hay forma de
      arreglarlo desde el CSS sin saber cuánto mide T. Con el hueco DENTRO de la pista
      (su `padding-right`), el ancho exterior de cada pista es `P = T + G`, la cinta es
      `N·P` exacto y `-100/N%` es exactamente un P. Cierra por construcción, sin que
      importe cuánto midan los items ni el gap.
   2. **Se veía la misma marca dos veces.** La desigualdad de 8.28 (`V ≤ (N−1)·T`)
      evita el VACÍO al final de la vuelta, pero no el duplicado: para eso hace falta
      `P ≥ V`, porque el patrón se repite cada P. Medido antes: V=1112 y P=788 a 1440,
      o sea 324px de contenido repetido en pantalla — y con 5 marcas eso significaba
      "Falabella … Cencosud … Falabella … Cencosud" a la vista, que se lee como un bug
      del sitio y no como un carrusel. El hueco sube a `clamp(40px, 11vw, 160px)`, que
      es el mínimo que cumple `P ≥ V` en los 5 anchos medidos (1920, 1440, 1024, 768 y
      390) con margen. Con cinco marcas la cinta queda airosa, que es como se ve un
      marquee de logos de verdad. */
/* ⚠️ El hueco BAJÓ de `clamp(40px, 11vw, 160px)` el 2026-08-22, y no es un cambio de
   gusto: aquel valor era el mínimo que cumplía `P ≥ V` con **cinco nombres de texto**,
   donde la pista medía 788px a 1440 y hacía falta un hueco enorme para que el patrón no
   se repitiera en pantalla. Con **16 logos** la pista mide ~2.900px, o sea las dos
   desigualdades de arriba se cumplen con muchísimo margen y un hueco de 160px solo
   servía para dejar la cinta medio vacía. Los dos valores están comprobados por medición
   en `home.mjs`, que los recalcula en 5 anchos en vez de confiar en este comentario. */
/* La CELDA es lo que da el ritmo, no el hueco. Con hueco fijo entre logos de anchos
   dispares —44px Emasa, 175px Tienda Copec— la distancia ENTRE MARCAS sale distinta en
   cada pareja y la fila se lee desordenada aunque el hueco sea idéntico. Con una celda
   de ancho fijo y el logo centrado dentro, el ritmo horizontal es exacto y lo que varía
   es el aire interno, que el ojo no cuenta. Por eso `--cc-marcas-hueco` es 0: el aire
   vive DENTRO de la celda. */
.cc-marcas { --cc-marcas-hueco: 0px; --cc-marcas-celda: clamp(132px, 14vw, 208px); --cc-marcas-escala: 1; }

.cc-marcas__cinta {
  display: flex;
  width: max-content;
  gap: 0;
  /* ⚠️ 90s, no los 28s de la cinta de texto. La duración es de la VUELTA COMPLETA, o sea
     la velocidad real sale de dividir la pista por ella: con 5 nombres la pista medía 788px
     y 28s daban 28px/s; con 16 logos mide 3.557px y esos mismos 28s habrían dado 127px/s
     —cuatro veces y media más rápido— y los logos pasan volando sin que se lean. 90s
     devuelve la velocidad a 40px/s a 1440 y 33px/s a 390. */
  animation: cc-marcas-correr 90s linear infinite;
  /* `will-change` NO se declara: el navegador ya promueve una capa para una
     animación de transform en curso, y dejarlo fijo reserva memoria de vídeo para
     siempre en una página que además tiene otras seis animaciones. */
}

.cc-marcas__pista {
  display: flex;
  align-items: center;
  gap: var(--cc-marcas-hueco);
  margin: 0;
  /* El hueco de la derecha es parte de la pista: es lo que hace exacto el −100/N. */
  padding: 0 var(--cc-marcas-hueco) 0 0;
  list-style: none;
}

.cc-marcas__item {
  flex: 0 0 var(--cc-marcas-celda);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* --- Los logos de la cinta (2026-08-22) ------------------------------------
   Hasta hoy la cinta llevaba los NOMBRES en texto —«Copec · Falabella · Cencosud ·
   Walmart · Mercado Libre»—, y de esos cinco solo Copec era cliente: los otros cuatro
   entraron con el primer import del rediseño como texto de maqueta y contradecían al
   propio `/portafolio/` del sitio, que nombra Tecno Fast, AGUNSA, Corona y Sencillito.
   Las 16 marcas de ahora salen del ERP: facturación en los últimos 3 meses y archivo
   de logo sin problemas anotados.

   TRES decisiones de sistema, las tres medidas y las tres explicadas en 8.83:

   1. **El ritmo lo pone la CELDA, no el hueco.** Los logos miden de 44 a 175px de
      ancho; con un hueco fijo, la distancia ENTRE MARCAS sale distinta en cada pareja
      y la fila se lee desordenada aunque el hueco sea idéntico. Con celda de ancho fijo
      y el logo centrado dentro, el ritmo horizontal es exacto.
   2. **Los archivos van RECORTADOS al borde del dibujo.** Kupfer y workit traían un
      45% de caja transparente, Metra un 20%, Corona un 39% de alto. Eso pinta la marca
      más chica que sus vecinas Y le suma aire que el hueco no controla. Los rasters se
      recortaron por su caja de alfa; a los SVG se les reescribió el `viewBox` al
      `getBBox()` real.
   3. **El tamaño sale de la TINTA, con el alto acotado.** `--cc-h` es el alto pintado de
      cada logo, calculado para que todos pongan ~900 px² de tinta —`h = √(T/(densidad·
      proporción))`— y luego acotado a 22–48px. Sin acotar, la tinta queda perfecta
      (1,01x) pero los altos van de 14 a 72px y la fila se ve descuadrada. Acotado, la
      tinta queda en 3,15x y los altos en 15–48. **Es un intercambio deliberado**:
      coherencia de altura por balance de tinta, porque lo primero se ve.
   ⚠️ Si se cambia o se añade un archivo hay que RECALCULAR su `--cc-h`: el número sale
   de la densidad de tinta del arte, no del nombre. `home.mjs` mide la razón y falla si
   se dispara.

   **Monocromo, y por eso los archivos pesan lo que pesan.** El logo se pinta siempre en
   un solo color —blanco sobre oscuro, negro sobre claro—, o sea el color de origen es
   información que se tira. Los rasters se guardan como alfa sobre blanco en WebP sin
   pérdida: los 10 bajaron de 183 KB a 38 KB sin perder un píxel de forma.
   ⚠️ `metra` llegó como `.svg` de 20 KB y era **98% un PNG incrustado, cero paths**
   (8.46): convertido a WebP pesa 2,7 KB.
   ⚠️ **mK quedó FUERA** aunque es el nº 5 por facturación: su logo es un «mK» en blanco
   calado sobre una placa oscura, o sea la marca ES el hueco. Bajo cualquier filtro
   monocromo se convierte en un rectángulo relleno. Hace falta pedirle a la marca una
   versión monocroma con fondo transparente. */
.cc-marcas__logo {
  --cc-h: 32;
  display: block;
  width: auto;
  height: calc(var(--cc-h) * var(--cc-marcas-escala) * 1px);
  max-width: 100%;
  filter: brightness(0) invert(1);
  opacity: .68;
  transition: opacity var(--cc-dur-base) var(--cc-ease-out);
}

/* El nombre en TEXTO viaja en el markup y está oculto: el logo es lo que se pinta en
   la portada. Existe para que las variantes de texto del laboratorio sean un cambio de
   CSS y no de plantilla, y va `display: none` —no `visibility` ni `clip`— porque el
   nombre ya lo anuncia el `alt` del logo: mostrarlo a un lector de pantalla lo diría dos
   veces. Lleva además `aria-hidden` por si alguna variante lo saca de `display: none`
   sin quitar el `alt`. */
.cc-marcas__nombre { display: none; }

/* El hover de la cinta va en el ITEM y no en el logo porque el logo mide justo lo que
   mide su arte: en un `--cc-k` de 0.72 el blanco de Feltrex ocupa 108x29 y apuntar ahí
   con el puntero mientras la cinta corre es imposible. */
.cc-marcas__item:hover .cc-marcas__logo { opacity: 1; }

/* En claro el logo se tiñe de NEGRO, igual que los badges de la trust bar (8.68). */
html[data-theme="light"] .cc-marcas__logo { filter: brightness(0); opacity: .7; }
html[data-theme="light"] .cc-marcas__item:hover .cc-marcas__logo { opacity: 1; }

.cc-marcas__logo--corona { --cc-h: 22; }   /* 167x22 · tinta 1221 px² */
/* Newman entró el 2026-08-25. Su altura se MIDIÓ igual que las demás —rasterizando el SVG
   y contando píxeles opacos hasta acercarse a los ~900 px² de tinta con los que se
   normaliza la cinta—, no se eligió a ojo: a 24px da 870, y las alturas vecinas dan 607
   (20px) y 1142 (28px). El archivo ya estaba en el theme: lo usa /servicios/agencia-seo/
   en su tabla de casos, así que no hubo que pedirlo. */
.cc-marcas__logo--newman { --cc-h: 24; }   /* 62x24 · tinta 870 px² */
.cc-marcas__logo--tecno-fast { --cc-h: 42; }   /* 97x42 · tinta 900 px² */
.cc-marcas__logo--metra { --cc-h: 48; }   /* 68x48 · tinta 399 px² */
.cc-marcas__logo--workit { --cc-h: 30; }   /* 79x30 · tinta 900 px² */
.cc-marcas__logo--theoduloz { --cc-h: 30; }   /* 133x30 · tinta 900 px² */
.cc-marcas__logo--copec { --cc-h: 15; }   /* 175x15 · tinta 988 px² */
.cc-marcas__logo--victorinox { --cc-h: 48; }   /* 110x48 · tinta 854 px² */
.cc-marcas__logo--kupfer { --cc-h: 48; }   /* 61x48 · tinta 411 px² */
.cc-marcas__logo--feltrex { --cc-h: 27; }   /* 102x27 · tinta 900 px² */
.cc-marcas__logo--byp { --cc-h: 48; }   /* 66x48 · tinta 501 px² */
.cc-marcas__logo--sencillito { --cc-h: 22; }   /* 114x22 · tinta 1020 px² */
.cc-marcas__logo--animal-care { --cc-h: 28; }   /* 146x28 · tinta 900 px² */
.cc-marcas__logo--open-english { --cc-h: 36; }   /* 97x36 · tinta 900 px² */
.cc-marcas__logo--adflip { --cc-h: 28; }   /* 126x28 · tinta 900 px² */
.cc-marcas__logo--emasa { --cc-h: 45; }   /* 44x45 · tinta 900 px² */
.cc-marcas__logo--maxtec { --cc-h: 22; }   /* 124x22 · tinta 1251 px² */

@media (max-width: 640px) {
  /* La celda ya encoge por su `clamp`, pero el LOGO no: a 390px un Tienda Copec de 175px
     no cabe en una celda de 132. Se baja la escala del arte, no la del hueco. */
  .cc-marcas { --cc-marcas-escala: .82; }
}

@keyframes cc-marcas-correr {
  to { transform: translateX(-25%); }   /* −100/N con N=4 pistas */
}

/* --- Pausa ---------------------------------------------------------------- */
/* El input vive fuera de pantalla pero SIGUE siendo enfocable: el visible es el
   label. No se usa `display:none`, que lo sacaría del orden de tabulación. */
.cc-marcas__check {
  /* Bricks da a los inputs una `transition: .2s` de formulario, y en un elemento
     invisible eso no significa nada — pero sí ensucia la escala de duraciones que
     `ux.mjs` vigila. Lo cazó el trinquete, no una revisión. */
  transition: none;
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.cc-marcas__check:checked ~ .cc-marcas__mask .cc-marcas__cinta {
  animation-play-state: paused;
}

/* 28×28 como el pausa del hero: pasa el mínimo de 24×24 de WCAG 2.5.8 por su
   propia caja, sin depender del `::after` de 8.17 — que aquí no serviría porque los
   pseudo-elementos están dibujando el icono. */
.cc-marcas__pausa {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 28px; height: 28px;
  border-radius: var(--cc-radius-pill);
  cursor: pointer;
  /* 0.55 de alfa da 3:1 sobre el fondo base, el mínimo de WCAG 1.4.11 para un
     componente de interfaz. En claro manda el token, que da 6.9:1. */
  color: rgba(255, 255, 255, 0.55);
  transition: color var(--cc-dur-fast) var(--cc-ease-out);
}
html[data-theme="light"] .cc-marcas__pausa { color: var(--cc-fg-muted); }
.cc-marcas__pausa:hover { color: var(--cc-fg-accent); }

.cc-marcas__pausa::before,
.cc-marcas__pausa::after {
  content: "";
  width: 3px; height: 11px;
  background: currentColor;
  border-radius: 1px;
}
/* Pausado: el icono pasa a "play", porque un control muestra la ACCIÓN que ejecuta
   y no el estado en que está (8.22). */
.cc-marcas__check:checked ~ .cc-marcas__pausa::after { display: none; }
.cc-marcas__check:checked ~ .cc-marcas__pausa::before {
  width: 10px;
  border-radius: 0;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
/* El foco del teclado se ve en el label, que es lo visible. */
.cc-marcas__check:focus-visible ~ .cc-marcas__pausa {
  outline: 3px solid var(--cc-fg-accent);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px rgba(10, 10, 15, 0.55);
}

/* Quien pidió menos movimiento ve la fila quieta. Y el control de pausa deja de
   tener sentido, así que se oculta: un botón que no hace nada es peor que ninguno
   (8.19). Las repeticiones sobrantes las recorta la máscara. */
@media (prefers-reduced-motion: reduce) {
  .cc-marcas__cinta { animation: none; }
  .cc-marcas__pausa { display: none; }
}

/* ==========================================================================
   SERVICIOS (/servicios/) — 2026-08-03
   Rediseño de la página madre de servicios. La consigna de Ricardo fue explícita:
   "fiel al tema que hemos hecho, que toda la página sea igual, no tenga distintos
   formatos". Así que la pieza NO trae vocabulario nuevo: reusa el hero, las
   tarjetas, la banda y el cierre de la home, y las cuatro unidades Canal.* con las
   MISMAS descripciones que ya usa `home.json`.
   Lo único que se añade acá son las tres clases que faltaban para que la tarjeta de
   servicio sea UNA sola definición en vez de un `_cssCustom` por tarjeta. Es 8.18 al
   revés: en la home cada card escribía su propio relleno, borde y radio (y por eso el
   modo claro necesitó una lista de 7 ids); acá el formato vive en la clase, así que
   las nueve tarjetas de la página son idénticas por construcción y el tema claro se
   resuelve con una línea que ya está más arriba (`.cc-svc-card` entra en las dos
   reglas del modo claro, la del fondo y la de la elevación).
   ========================================================================== */

/* La grilla de tarjetas de una unidad. El mínimo va con `min()` y no pelado: un
   mínimo rígido en una grilla es una promesa de overflow (8.7, tercera aparición). */
/* El mínimo subió de 280 a 320px el 2026-08-09, al entrar la primera unidad con
   CINCO tarjetas (`agencia-marketing`). A 280px y 1180 de ancho caben CUATRO
   columnas exactas (4×280 + 3×20 = 1180), así que esa página salía 4 + 1 huérfana
   y con las tarjetas a 280px de ancho: medido, 906px de alto y ~30 caracteres por
   línea, o sea por debajo del único bloque del sitio que `docs/pendientes.md` ya
   tenía marcado como fuera del rango cómodo de lectura (34).
   Con 320 el tope son TRES columnas (3×320 + 2×20 = 1000 ≤ 1180; con cuatro serían
   1340), así que esa página queda 3 + 2 y las tarjetas a ~380px.
   No cambia nada de lo que ya existía: las unidades de 2 y de 3 tarjetas de la
   página madre siguen midiendo 580 y 380px, porque `auto-fit` colapsa las pistas
   vacías y reparte el sobrante igual. Verificado antes y después. */
.cc-svc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 20px;
}
/* `align-items: stretch` NO es redundante, y es el bug que Ricardo leyó como "todo
   muy descuadrado" (2026-08-04). El elemento `block` de Bricks es un flex column con
   `align-items: flex-start` (clase `.brxe-block`), y ese valor SOBREVIVE al pasar la
   caja a `display: grid`: se aplica igual al contexto de grid, donde significa "cada
   tarjeta con su altura natural" en vez del `stretch` que trae un grid por defecto.
   Medido: 227 vs 202px en Canal.Commerce y 253/227/227 en Canal.Brand, o sea las
   filas quedaban con un escalón de hasta 26px según cuánto midiera cada descripción.
   Es la trampa de 8.1 en su forma más silenciosa: `display` sí se pisó desde la
   clase, así que la regla PARECE haber entrado del todo, y lo que quedó atrás es una
   propiedad que nadie declaró en esta hoja.
   El selector doble es a propósito (8.1): con (0,2,0) le gana a `.brxe-block` sin
   depender de que `cc-components.css` cargue después. */
.brxe-block.cc-svc-grid { align-items: stretch; }

/* La tarjeta. Mismo relleno (blanco al 6%), mismo hairline y mismo radio que las de
   la home tras la auditoría del 2026-08-03: en oscuro la elevación la da la
   superficie más clara, no una sombra. */
.cc-svc-card {
  background: rgba(255, 255, 255, 0.06);
  /* El borde se TIÑE con el color de la unidad en vez de llevarlo pleno: a fuerza
     completa cuatro bordes de color saturado convierten la página en un semáforo y
     compiten con el titular. Fuera de una unidad `--cc-unidad` no existe y el borde
     vuelve al de siempre, así que las tarjetas del blog y de la home no se enteran.

     Estaba al 34% mientras la marca superior llevaba el color a pleno. Retirada la
     marca (ver el bloque "dónde vive el color" más abajo), en las 4 hijas de
     `/servicios/` este borde quedó como ÚNICO canal —esas tarjetas no llevan número
     ni enlace— y al 34% sobre `rgba(255,255,255,.06)` no se distingue del hairline
     blanco: eso es el *"o no hay color"* del reporte del 2026-08-09. Al 55% se lee
     como borde de color y sigue sin gritar; el hover va a pleno (100%) en vez del 78%
     anterior, porque ahora es él quien tiene que marcar TODO el cambio de estado. */
  border: 1px solid var(--cc-border-on-dark);
  border-color: color-mix(in srgb, var(--cc-unidad, transparent) 55%, var(--cc-border-on-dark));
  border-radius: var(--cc-radius-lg);
  padding: 34px 30px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  transition: border-color var(--cc-dur-base) var(--cc-ease-out);
}
/* Al hover la tarjeta afirma su unidad: el borde sube a pleno. Es un cambio de estado
   real y medible, no solo un color más bonito (8.18: un control que no responde al
   puntero no se lee como control). */
.cc-svc-card:hover {
  border-color: color-mix(in srgb, var(--cc-unidad, transparent) 100%, var(--cc-border-on-dark));
}
/* El elemento `text` de Bricks envuelve su contenido en un <p> (8.9), y ese <p>
   trae el margen del navegador: sin esto el `gap` del flex no manda y las tarjetas
   quedan con espaciados distintos entre sí según qué elementos tengan. */
.cc-svc-card :is(p, h3) { margin: 0; }
/* El enlace se pega abajo, así que las flechas de una fila quedan alineadas aunque
   las descripciones midan distinto. `auto` en el margen superior de un hijo flex es
   lo que hace ese trabajo sin fijar alturas.
   Pero el `auto` tiene que ir en el HIJO DIRECTO del flex, y el enlace NO lo es: el
   elemento `text` de Bricks lo envuelve en un `<p>` dentro de su propio div (8.9, en
   dos capas), así que la cadena real es `.cc-svc-card > div.brxe-text > p > a`. Sobre
   el `<a>` el margen no hacía nada.
   Esto estuvo TAPADO por el bug del `align-items` de más arriba (8.24: un bug tapado
   por otro no está arreglado, está esperando): mientras cada tarjeta tomaba su altura
   natural no había espacio sobrante que repartir, así que el enlace caía al fondo por
   su cuenta y el chequeo de "las flechas quedan alineadas" pasaba. Al estirar las
   tarjetas apareció el sobrante y las flechas se separaron 26px — el chequeo lo
   reportó en la primera corrida.
   `:has()` en vez de una clase en el template a propósito: así el arreglo viaja en
   git y no hay que re-exportar el JSON ni escribir en la BD (§10). Si un navegador no
   lo soporta, degrada al comportamiento anterior en vez de romper nada. */
.cc-svc-card > .brxe-text:has(.cc-cardlink) { margin-top: auto; }
.cc-svc-card > .cc-cardlink { margin-top: auto; }
.cc-svc-card .cc-cardlink { margin-top: 0; }

.cc-svc-card__title {
  font-family: var(--cc-font-sans);
  font-weight: 700;
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--cc-fg-strong);
}
/* ⚠️ El color pasó de `--cc-fg-muted` a `--cc-fg-read` el 2026-08-10, y es un fallo AA
   que llevaba meses. `--cc-fg-muted` (#898989) está calibrado contra el fondo de SECCIÓN,
   pero esta tarjeta se pinta sobre `rgba(255,255,255,.06)`, o sea una superficie un 6% más
   clara: compuesto da (42,40,55) y el ratio cae a **4.14:1**, bajo el 4.5 de WCAG 1.4.3.

   Estuvo tapado porque el medidor de contraste **se saltaba las capas con alfa < 0.85** y
   medía contra la sección directamente. Al componerlas de verdad —cambio del mismo día,
   forzado por bajar el header al 72%— apareció solo. Es 8.25.8 otra vez en su forma
   general: **un token de texto no es seguro por sí mismo, lo es contra una superficie**, y
   cambiar la superficie sin re-medir el texto es donde se cuelan estos.

   `--cc-fg-read` es además el token que corresponde: esto es la descripción del servicio,
   o sea texto de lectura, no un metadato. Queda en 8.52:1. */
.cc-svc-card__texto {
  font-family: var(--cc-font-sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.6;
  color: var(--cc-fg-read);
}

/* ==========================================================================
   LAS 4 HIJAS: EL SERVICIO COMO REGISTRO (2026-08-23)

   Ricardo, sobre las tarjetas de estas cuatro páginas: *«las tarjetas así ordenadas
   con texto y la imagen es muy IA, muy feo sin cuerpo ni diseño»* y *«mucho del color
   de cada sección»*. Las dos quejas eran ciertas y tenían causas SEPARADAS, medidas:

     · el color es el duotono COCIDO en los `.webp` por `tools/img/tarjetas-duotono.py`,
       que cubre el **92–98%** de cada imagen con saturación >0,45. La foto deja de ser
       foto y pasa a ser un rectángulo de color con textura. Aislado: bajando solo el
       tinte, el croma de la sección cae del **18,3% al 0,8%** sin mover el layout.
     · lo «muy IA» es la REPETICIÓN de la misma pila —imagen 16:9 arriba, título,
       párrafo— en las trece entradas de las cuatro páginas.

   ⚠️ Y LA ESTRUCTURA LA DECIDE EL CONTENIDO, NO LA PÁGINA. Las cuatro no tienen el
   mismo número de servicios: Performance Marketing **5**, Creatividad **3**, Diseño
   **3**, Desarrollo **2**. Un índice existe para HOJEAR: con cinco entradas hojear
   gana, con dos no hay nada que hojear y plegar la mitad de la página detrás de un
   hover es perder contenido, no ganar diseño. Así que el umbral va aquí, con `:has()`,
   y no en una lista de páginas:

       4 o más entradas  ->  ÍNDICE  (una línea por servicio, se abre al apuntarla)
       3 o menos         ->  FICHA   (entrada completa, con su banda de imagen)

   Con eso sigue habiendo UNA definición con una condición dentro —no dos que se
   desincronizan— y si Creatividad pasa mañana de 3 a 5 servicios se convierte en
   índice sola. Cae de lado seguro: la rama por defecto es la FICHA, que muestra todo,
   así que si `:has()` fallara nadie pierde contenido.

   ⚠️ EL ALCANCE ES `body.cc-servicios.page-child:not(.cc-maqueta-propia)`, y no es
   cosmético: `.cc-svc-card` también vive en **home.json** (las 4 tarjetas de sec040) y en
   **page-servicios.json** (30 tarjetas en 4 rejillas). Sin acotar, esto reestructuraría la
   portada y la página madre, que nadie pidió.

   ⚠️ Y EL `:not()` SE AÑADIÓ EL 2026-08-25 PORQUE ESTE COMENTARIO SE VOLVIÓ FALSO. Decía:
   «las cuatro hijas son las únicas con `cc-servicios` Y `page-child`». Lo eran hasta que se
   rediseñó `agencia-seo`: al entrar en `ccd_paths_servicios_redisenados()` ganó las dos
   clases y **estas 25 reglas se le aplicaron encima**. Sus tarjetas de SXO/GEO/ESO/AEO
   pasaron a entradas de registro numeradas, las imágenes colapsaron a 0px de alto y las
   descripciones desaparecieron — otro diseño, aplicado a la pieza equivocada, y **ninguna
   suite lo cazó porque no era un error, era un diseño distinto**.
   `page-child` nunca significó «lleva registro de servicios»: era una coincidencia de las
   4 hijas que dejó de serlo en cuanto hubo una quinta. La lista vive en
   `ccd_paths_maqueta_propia()` del child theme, y su nombre dice lo que el alcance quiere
   decir. Es la misma lección de 8.42: un alcance que se apoya en una coincidencia falla en
   silencio cuando la coincidencia se rompe.

   Las variantes que perdieron viven en `assets/css/lab/svc-*.css` con lo que midió
   cada una: `svc-tinte-suave`, `svc-lado`, `svc-lado-suave`, `svc-ficha`,
   `svc-sin-foto`, `svc-indice`, `svc-columna`.
   ========================================================================== */

/* ── EJE 1 · la cabecera deja de irse con el scroll. En las cuatro. ──────────
   Hoy `«CANAL.MEDIA»` desaparece en los primeros 400px de una sección de 1.550, así
   que el visitante lee cinco servicios sin ver de qué unidad son.
   Las dos condiciones del `sticky` se comprueban, no se suponen (8.26): el contenedor
   pasa a `grid` con `align-items: start` —si estirara el ítem no tendría por dónde
   deslizarse— y no recorta. El `top` sale del alto real del header, 85px (8.19). */
@media (min-width: 1000px) {
  body.cc-servicios.page-child:not(.cc-maqueta-propia) #brx-content .cc-stagger:has(> .cc-svc-head) {
    display: grid;
    grid-template-columns: minmax(280px, 34%) minmax(0, 1fr);
    column-gap: clamp(40px, 5vw, 80px);
    align-items: start;
  }
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-head {
    position: sticky;
    top: calc(var(--cc-header-h) + var(--cc-space-6, 32px));
    align-self: start;
  }
}

/* ── EJE 2 · las entradas. Una columna, hairline en vez de caja. ───────────── */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid {
  grid-template-columns: 1fr;
  counter-reset: svc;
  gap: 0;
}

body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card {
  counter-increment: svc;
  position: relative;
  background: none;
  border: 0;
  /* ⚠️ El hairline va por TOKEN y no por rgba literal, y esto costó el reporte de
     Ricardo del 2026-08-24 (*«los hijos de servicio en modo blanco no se ven muy
     bien»*): en blanco al 10% sobre el `#EEEEF1` de la sección da **1.015:1**, o sea
     los trece separadores del registro desaparecen y las entradas quedan flotando
     sin estructura. `--cc-rule-on-dark` vale EXACTAMENTE este mismo rgba en oscuro
     —así que ahí no cambia nada— y voltea a `rgba(32,30,44,0.16)` en claro. 8.96. */
  border-top: 1px solid var(--cc-rule-on-dark);
  border-radius: 0;
  /* ⚠️ Y SIN SOMBRA, en los dos modos. La regla de elevación del modo claro
     (`html[data-theme="light"] … .cc-svc-card`, más arriba en esta hoja) se escribió
     cuando esto ERA una tarjeta; al pasar a registro (8.93) quedó heredada, y una
     sombra sobre algo sin fondo ni radio dibuja un RECTÁNGULO FANTASMA: en oscuro no
     se ve, en claro cada entrada volvía a leerse como una caja y el número quedaba
     pegado a ese borde. Lo reportó Ricardo el 2026-08-24 (*«los cuadrados se notan
     mucho, y se ve el número pegado»*).
     Es exactamente lo que avisa el comentario de `.cc-caso` unas líneas más arriba:
     un componente que cambia de papel tiene que SALIR de las reglas del papel
     anterior. El `:hover` de abajo ya lo ponía a `none`, o sea la intención estaba
     escrita solo para un estado. 8.97. */
  box-shadow: none;
  padding: 24px 0 24px 56px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: border-top-color var(--cc-dur-base) var(--cc-ease-out);
}
/* ⚠️ El hairline de cierre va en la REJILLA y no en `:last-child`, y la razón es un
   chequeo: `servicios.mjs` exige que las tarjetas de una página sean UNA definición, y
   con el borde en la última entrada esa entrada difería de las demás. La regla existe
   desde el 2026-08-09 y tenía razón — el arreglo es no crear la excepción. */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid { border-bottom: 1px solid var(--cc-rule-on-dark); }
/* Sin caja no hay elevación: el gesto se resuelve en el hairline. */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card:hover {
  transform: none;
  box-shadow: none;
  border-top-color: var(--cc-unidad, var(--cc-fg-accent));
}

/* El número es ESTRUCTURA: lo genera un `counter`, así que numera lo que hay. Si
   entra un servicio se renumera solo. Tabular para que las cifras no bailen. */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card::before {
  content: counter(svc, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 33px;
  font-family: var(--cc-font-sans);
  font-weight: 800;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: var(--cc-unidad, var(--cc-fg-accent));
}

/* La imagen: banda baja y ancha AL FINAL de la entrada, con `order`. Deja de
   llevarse 325 de los 623px de alto de la tarjeta y de competir por el ancho.
   ⚠️ Se resuelve con `flex` + `order` y NO con rejilla: dos intentos con `grid`
   —por `grid-template-areas` y por número de línea— dejaron la banda en una fila
   de 64px siendo de 92, pintándose sobre la última línea del párrafo. La tarjeta
   ya es flex en columna por la regla de arriba, así que mandarla al final es una
   propiedad. Menos piezas y nada que resolver. */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card .cc-svc-card__media {
  order: 9;
  width: 100%;
  height: 92px;
  margin-top: 6px;
  border-radius: 3px;
  object-fit: cover;
  object-position: center 40%;
  /* Monocroma en reposo: a 92px de alto el duotono no aporta motivo, solo tinte, y
     el color de la unidad ya lo declara el número. */
  /* ⚠️ La atenuación va en el FILTRO y no en `opacity`, y esto lo forzó un chequeo:
     `servicios.mjs` exige que nada visible quede bajo 0.9 de opacidad tras recorrer
     (8.24), porque así se detecta un reveal que no disparó. Un `opacity: .55`
     deliberado es indistinguible de ese bug para el instrumento — y encima sobre
     fondo oscuro mezcla la imagen hacia el fondo en vez de controlarla. Con
     `brightness` el resultado es el mismo a la vista y la invariante sigue con
     dientes. */
  filter: grayscale(1) contrast(1.08) brightness(0.72);
  transition: filter var(--cc-dur-base) var(--cc-ease-out);
}
/* Al apuntar la entrada la banda recupera color. Es la ÚNICA vez que el color
   aparece en la imagen, y significa «esta es la que estás leyendo». */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card:hover .cc-svc-card__media,
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card:focus-within .cc-svc-card__media {
  filter: saturate(0.5) contrast(1.06) brightness(1);
}

/* Los chips dejan de ser píldoras. ⚠️ Y solo existen en `/desarrollo/`: once de las
   trece entradas no tienen ni uno (medido), así que esto afecta a una página. */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card .cc-chip {
  border: 0;
  padding: 0;
  background: none;
  font-size: 13px;
  letter-spacing: 0.02em;
}
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-card .cc-chip:not(:last-child)::after {
  content: " · ";
  color: rgba(255, 255, 255, 0.28);
}

/* ── EJE 2b · con 4 entradas o más, la entrada se PLIEGA ────────────────────
   ⚠️ El revelado va DEBAJO, no al lado, y la razón es el eje 1: con la cabecera
   fija ocupando el 34% la entrada baja de 1.180 a ~780px, y abrir la foto en una
   columna del 34% DE ESOS 780 solapaba el texto en las cinco entradas (medido).
   Como banda inferior las dos ramas comparten el objeto y solo cambia CUÁNDO
   aparece. */
/* ⚠️ La rama índice NO cambia el formato de la entrada: mismo relleno, mismo tamaño
   de título, mismo hairline. Lo único que cambia es si el detalle está plegado — un
   ESTADO, no un formato. Tenerlo así es lo que permite que el chequeo «las 4 páginas
   comparten UNA definición» siga en pie, y ese chequeo es la escritura de 8.40. */
body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card .cc-svc-card__media { filter: saturate(0.35) contrast(1.06) brightness(1); }

/* ⚠️ EL PLEGADO SOLO DONDE HAY PUNTERO. En táctil no hay hover, así que ahí la
   entrada se muestra ENTERA desde el principio: un acordeón que solo abre con
   puntero es contenido inaccesible en teléfono, no un efecto (8.92). */
@media (hover: hover) and (min-width: 1000px) {
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card .cc-svc-card__texto,
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card .cc-chips,
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card .cc-svc-card__media {
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    margin-top: 0;
    transition:
      max-height var(--cc-dur-slow) var(--cc-ease-out),
      opacity var(--cc-dur-base) var(--cc-ease-out),
      margin-top var(--cc-dur-slow) var(--cc-ease-out);
  }
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card:hover .cc-svc-card__texto,
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card:focus-within .cc-svc-card__texto { max-height: 12em; opacity: 1; margin-top: 10px; }
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card:hover .cc-chips,
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card:focus-within .cc-chips { max-height: 6em; opacity: 1; margin-top: 10px; }
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card:hover .cc-svc-card__media,
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card:focus-within .cc-svc-card__media { max-height: 92px; opacity: 1; margin-top: 12px; }
}

/* Quien pidió menos movimiento no pliega nada: no hay nada que desplegar (8.23). */
@media (prefers-reduced-motion: reduce) {
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card .cc-svc-card__texto,
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card .cc-chips,
  body.cc-servicios.page-child:not(.cc-maqueta-propia) .cc-svc-grid:has(> .cc-svc-card:nth-child(4)) > .cc-svc-card .cc-svc-card__media {
    max-height: none;
    opacity: 1;
    transition: none;
  }
}

/* Área de golpeo del correo del footer (WCAG 2.5.8, 8.17). Al dejar de ser botón se
   quedó en 211x20: los 24px de alto los daba el relleno del botón, no el texto. */
.cc-foot__mail a {
  display: inline-block;
  padding-block: 4px;
}

/* El marcador de orden (01–04). Va por token y no por el verde de marca directo: en
   claro `--cc-fg-accent` ya es el verde de tinta (5.34:1), así que no hace falta sumar
   este id a la lista A2 del modo claro. Y desde el 2026-08-04 toma primero el color de
   la unidad, así que el 01 de Media es verde y el 04 de Brand magenta. */
.cc-svc-num {
  font-family: var(--cc-font-sans);
  font-weight: 700;
  font-size: 13px;
  line-height: 1.6;
  color: var(--cc-unidad, var(--cc-fg-accent));
}
.cc-svc-num p { margin: 0; }

/* ==========================================================================
   COLOR POR UNIDAD DE NEGOCIO (2026-08-04)
   Pedido por Ricardo: *"encuentro todo muy genérico, sin identidad, muy IA"*, con
   `infracommerce.lat` como referencia. Lo que esa página hace y que aquí se podía
   copiar SIN plagiar nada es que sus sub-marcas (`infra.digital`, `.tech`, `.data`,
   `.pay`, `.log`) llevan un punto de color distinto cada una. Canal Cero ya tiene esa
   misma estructura —Canal.Media / .Commerce / .Data / .Brand, con el punto marcado en
   el markup— y la pintaba toda del mismo verde. O sea la identidad ya estaba escrita y
   no se estaba usando: es la mejora de carácter más barata que había en el sistema.

   MECANISMO: no hay una lista de ids ni una regla por unidad. Cada isla redefine UNA
   custom property, `--cc-unidad`, y como las custom properties heredan, todo lo que
   hay dentro cambia junto: el punto del nombre (`.cc-acento`), el número de orden
   (`.cc-svc-num`), el borde de las tarjetas (`.cc-svc-card`) y el color de los enlaces
   con flecha (`.cc-cardlink`). Ninguna de esas cuatro reglas sabe en qué unidad está,
   y un elemento que se agregue mañana dentro de una isla entra solo. Es el patrón de
   8.25.5 —arreglar por ALCANCE y no por enumeración— aplicado a la identidad en vez de
   a un bug.

   Y por eso cada regla lleva `var(--cc-unidad, <lo de antes>)`: fuera de una isla el
   token no existe y todo se comporta exactamente como antes del cambio. Las tarjetas
   del blog, las de la home que no son de unidad y los enlaces del footer no se enteran.

   Los ocho valores viven en `cc-tokens.css` con su tabla de contraste medido. Los
   cuatro pasan AA sobre el fondo, la superficie y el violeta en oscuro, y sobre blanco
   y paper en claro.
   ========================================================================== */
.cc-u-media    { --cc-unidad: var(--cc-u-media);    --cc-unidad-ink: var(--cc-u-media-ink); }
.cc-u-commerce { --cc-unidad: var(--cc-u-commerce); --cc-unidad-ink: var(--cc-u-commerce-ink); }
.cc-u-data     { --cc-unidad: var(--cc-u-data);     --cc-unidad-ink: var(--cc-u-data-ink); }
.cc-u-brand    { --cc-unidad: var(--cc-u-brand);    --cc-unidad-ink: var(--cc-u-brand-ink); }

/* El modo claro se resuelve con UNA regla para las cuatro, no con cuatro overrides:
   cada isla ya declaró su par, así que acá solo se dice "usa el otro". Si mañana hay
   una quinta unidad, basta añadirla a la lista de arriba y esta regla la cubre.
   Va con `:is()` para que la especificidad sea la del selector más pesado (1,1,0) y no
   crezca con cada unidad añadida. */
html[data-theme="light"] :is(.cc-u-media, .cc-u-commerce, .cc-u-data, .cc-u-brand) {
  --cc-unidad: var(--cc-unidad-ink);
}

/* ⚠️ Las islas oscuras del modo claro (8.25.5 y 8.25.8 — banda violeta, cierre, footer,
   caja CTA de la nota) vuelven a fondo OSCURO, así que dentro de ellas el color de
   unidad tiene que ser el BRILLANTE otra vez, no la tinta. Sin esto, una tarjeta de
   unidad colocada dentro del cierre en modo claro quedaría con su color de tinta sobre
   fondo casi negro. Hoy no hay ninguna ahí; la regla existe porque el día que la haya
   el fallo sería silencioso y del tipo que ninguna medición de texto ve (8.25.8). */
html[data-theme="light"] :is(#brxe-sec061, #brxe-sec127, #brxe-ftr001, .cc-cta, .cc-isla-oscura, .cc-proyectos)
  :is(.cc-u-media, .cc-u-commerce, .cc-u-data, .cc-u-brand) {
  --cc-unidad: var(--cc-u-media);
}
html[data-theme="light"] :is(#brxe-sec061, #brxe-sec127, #brxe-ftr001, .cc-cta, .cc-isla-oscura, .cc-proyectos) .cc-u-commerce { --cc-unidad: var(--cc-u-commerce); }
html[data-theme="light"] :is(#brxe-sec061, #brxe-sec127, #brxe-ftr001, .cc-cta, .cc-isla-oscura, .cc-proyectos) .cc-u-data     { --cc-unidad: var(--cc-u-data); }
html[data-theme="light"] :is(#brxe-sec061, #brxe-sec127, #brxe-ftr001, .cc-cta, .cc-isla-oscura, .cc-proyectos) .cc-u-brand    { --cc-unidad: var(--cc-u-brand); }

/* El nombre de la unidad a 320px: "Canal.Commerce" es un token de 14 caracteres y a
   `--cc-fs-d2` (mínimo 34px) mide 303px, o sea MÁS que los 272 disponibles. Un flex
   item no baja de su min-content, así que el titular arrastraba a su bloque y con él
   a la bajada: 3 nodos fuera de caja y scroll horizontal a 320 (medido, lo cazó
   responsive.mjs). El punto de quiebre va en el template como `<wbr>` justo después
   del punto de acento, que es el único lugar donde partir "Canal.Commerce" se lee
   bien. Esto es el cinturón para el próximo nombre que sea más largo todavía. */
.cc-svc-head { min-width: 0; }
.cc-svc-head > * { min-width: 0; }
.cc-svc-head :is(h2, p) { overflow-wrap: break-word; }

/* ==========================================================================
   CARÁCTER: LO QUE EL COLOR POR UNIDAD PROMETÍA Y NO ESTABA LLEGANDO
   (2026-08-04, segunda tanda)

   Ricardo, sobre tres capturas: *"esto son cosas que no me gustan, se ven muy IA
   y feos"* — los silos de S2, las tarjetas de unidad de S5 y los chips de
   trayectoria/capacidad de S10.

   Lo medido explica las tres de una sola vez: son la MISMA FORMA repetida 15
   veces en tres secciones seguidas —rectángulo redondeado, hairline blanco al
   8–14%, relleno al 6%, texto gris y todos los hermanos idénticos—. Sin
   jerarquía interna y sin color propio, un grupo de cajas iguales es exactamente
   lo que produce un generador: de ahí la lectura de "IA".

   Y el color por unidad de 8.34 estaba a medio llegar. Medido en las 4 tarjetas
   de la home: el número 01–04 salía VERDE en las cuatro (hex fijo `#3dedb4` en el
   `_typography` del template), el borde era `rgba(255,255,255,0.08)` en las
   cuatro (regla de ID desde `_cssCustom`, que le gana a la clase — 8.1), y el
   `:hover` de `.cc-card-hover-violet` los pintaba VIOLETA a los cuatro con
   `!important`. O sea de los cuatro canales que 8.34 declara —punto, número,
   borde y flecha— en la home solo funcionaban dos, y el hover contradecía la
   identidad que la tarjeta acababa de afirmar.

   Es la lección de 8.18 en su forma más caramente literal: **el sistema existía
   y la pieza se lo estaba saltando**, así que el arreglo no es diseño nuevo, es
   dejar de pisar el que hay. Nada de esto añade un color, un radio, una duración
   ni un tamaño de fuente fuera de la escala: los tamaños nuevos (20px) y los
   colores nuevos (ninguno) ya estaban en el inventario de la home, que está
   justo en su trinquete de 12 tamaños y 11 interlineados (ux.mjs).
   ========================================================================== */

/* --- 1. La tarjeta de unidad: dónde vive el color ----------------------------
   El punto del nombre mide 6px y la flecha es un renglón: sumado, el color de
   unidad ocupaba ~0.4% de una tarjeta de 380×267. Por eso las cuatro se leían
   iguales de lejos, que es la distancia a la que se mira una grilla.

   ⚠️ HISTORIA (2026-08-09, no repetir): eso se resolvió con una MARCA SUPERIOR
   —56px × 3px pegada al borde de arriba, que crecía a 92px al hover—. Ricardo la
   rechazó explícitamente: *"no me gusta la linea arriba de las tarjetas, esa que
   aumenta al poner el cursor"*. Se retiró entera, con su hover y su fallback de
   `prefers-reduced-motion`. No volver a introducir una regla decorativa sobre el
   borde de la tarjeta: el problema que resolvía es real, pero esa forma NO es la
   solución aceptada.

   El color se sostiene ahora en los canales que YA declaraba 8.34 y que no son
   decoración añadida: el borde teñido (abajo), el punto del nombre de la unidad,
   el número de orden y la flecha del enlace. Lo único que cambió es que el borde
   pasó a teñirse lo suficiente para leerse solo, porque al quitar la marca era el
   único canal que quedaba en las tarjetas de las 4 hijas —que no llevan ni número
   ni enlace—, y al 34% no se veía: es literalmente el *"o no hay color"* del
   mismo reporte. */
.cc-svc-card { position: relative; }

/* La elevación al hover, que en la home la daba `.cc-card-hover-violet` pintando
   las cuatro tarjetas de violeta con `!important`. Ahora la sombra sale del color
   de la unidad, así que el estado hovereado AFIRMA la identidad en vez de
   borrarla. `color-mix` y no un rgba fijo: son cuatro colores y no queremos
   cuatro sombras escritas a mano (8.18).

   ⚠️ DECISIÓN DE RICARDO (2026-08-09) — antes esto estaba gateado a
   `:has(.cc-cardlink)`, o sea solo animaban las tarjetas CON enlace, por 8.18: un lift
   en algo que no lleva a ninguna parte es una promesa falsa. Lo que ese razonamiento no
   miró es lo que producía en pantalla, y medido es feo: en `/servicios/` animaban **7 de
   10** —las 3 de Canal.Data no, porque son las únicas sin página hija— y en las **4
   hijas no animaba NINGUNA**, porque ninguna de sus tarjetas lleva enlace. O sea la misma
   tarjeta, en la misma grilla, se comportaba de dos maneras sin que el visitante tuviera
   forma de saber por qué. Reportado así: *"las tarjetas de .data en servicios no tienen
   animación y luces, como las otras cartas"*.

   Se quitó el gate: **todas** las `.cc-svc-card` elevan y encienden su halo. El riesgo de
   8.18 sigue siendo real y no se niega — la salida de fondo es darle a Canal.Data una
   página propia, que es lo que la haría clicable de verdad (anotado en
   `docs/pendientes.md`). Entre "todas iguales sin destino" y "unas sí y otras no", la
   decisión fue la primera. */
.cc-svc-card {
  transition:
    transform    var(--cc-dur-base) var(--cc-ease-out),
    border-color var(--cc-dur-base) var(--cc-ease-out),
    box-shadow   var(--cc-dur-base) var(--cc-ease-out);
}
.cc-svc-card:hover {
  transform: translateY(-6px);
  box-shadow:
    var(--cc-shadow-card-dark),
    0 18px 40px -22px color-mix(in srgb, var(--cc-unidad, transparent) 60%, transparent);
}
@media (prefers-reduced-motion: reduce) {
  /* Sin desplazamiento, pero el feedback se conserva: la sombra negra casi no se
     ve sobre #0A0A0F, así que el fallback estático es un anillo del color de la
     unidad. Mismo criterio que el de `.cc-card-hover-violet` de arriba. */
  .cc-svc-card:hover {
    transform: none;
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--cc-unidad, transparent) 70%, transparent),
      0 0 26px -6px color-mix(in srgb, var(--cc-unidad, transparent) 45%, transparent);
  }
}

/* El número de orden. Sube de 13 a 20px —los dos ya estaban en la home, así que
   el trinquete de tamaños no se mueve— y toma el color de la unidad desde el
   template (el `_typography` de Bricks emite regla de ID, así que el valor tiene
   que vivir ahí, no acá: 8.1). Lo que sí va acá es la marca tabular, para que
   01/02/03/04 ocupen exactamente lo mismo y la columna de números quede a plomo
   entre tarjetas. */
.cc-u-num p,
.cc-u-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* --- 2. Los silos: cada uno lleva el color de la unidad que lo REEMPLAZA ------
   Los cuatro proveedores de S2 mapean 1 a 1 con las cuatro unidades de S5:
   agencia de medios → Canal.Media, desarrollador ecommerce → Canal.Commerce,
   consultora de datos → Canal.Data, productora de contenido → Canal.Brand. Esa
   correspondencia estaba en la copia y no en el diseño.

   Con el color puesto, S2 y S5 pasan a ser la misma historia contada dos veces:
   lo que en S2 está disperso, apagado y sin conectar, en S5 aparece ordenado, a
   pleno color y bajo un mismo techo. No hace falta explicarlo con una flecha ni
   con un "antes/después": es el mismo vocabulario cromático en dos estados.

   El color va DEBILITADO acá a propósito (punto al 55%, borde teñido al 22%): el
   silo tiene que leerse como el problema, no como la marca. Si estuviera a pleno
   las dos secciones dirían lo mismo con la misma fuerza y el contraste narrativo
   —que es todo el punto— se perdería. */
.cc-silo {
  position: relative;
  /* El `padding-left: 40px` existía para dejarle hueco al punto de color, y el punto se
     retiró el 2026-08-13 (ver abajo). Quedó un relleno asimétrico de 40 contra 22 que se
     lee como descuadre: las cuatro tarjetas van ahora con el mismo aire por los cuatro
     lados. */
  padding: clamp(18px, 1.8vw, 24px);
  /* Borde neutro: el teñido por unidad salió con el resto del color (ver el aviso del
     icono, arriba). Un solo valor para las cuatro. */
  border-color: rgba(255, 255, 255, 0.10);
  /* Sobre la superficie nueva la caja está a 1.18 de contraste y sin sombra
     (medido 2026-08-04), así que no se separaba del fondo: en oscuro la
     elevación la da la sombra tanto como en claro (8.27.3). */
  /* Sombra propia y no `--cc-shadow-card-dark`: ese token desplaza 14px hacia abajo, que
     con la caja recta deja un borde oscuro asomando bajo la tarjeta. Aquí basta un halo
     casi centrado — profundidad sin segunda caja. */
  box-shadow: 0 6px 22px -12px rgba(0, 0, 0, 0.7);
}
/* (El punto de color vivía aquí y se retiró el 2026-08-13 — el porqué está unas
   líneas más abajo, junto al del conector: en la tarjeta nueva el color de unidad ya
   lo lleva el icono y el punto lo repetía.) */

/* ⚠️ AQUÍ VIVÍAN EL PUNTO (`::before`) Y EL CONECTOR (`::after`), Y LOS DOS SE
   RETIRARON EL 2026-08-13 al pasar los silos de apilado vertical a rejilla de 4.

   El conector era una línea de 1px que salía a `left: 100%` y se desvanecía "sin
   llegar a nada", para que la desconexión la dijera el dibujo y no solo el texto.
   Traía una garantía escrita y MEDIDA: *"34px no alcanza a ningún vecino en ningún
   ancho"*, cierta mientras los `margin-left` escalonados separaban los silos entre
   10 y 150px. En la rejilla el hueco es `clamp(10px, 1.4vw, 18px)`: **el conector se
   pasa 16px y aterriza sobre la tarjeta de al lado.** Ricardo lo vio como "un rayo
   en el lado derecho de cada tarjeta, se ve feo", y medido era peor que feo — un
   hilo que une dos silos dice justo lo contrario de lo que la sección argumenta.

   > Una invariante geométrica no sobrevive a un cambio de maqueta por sí sola.
   > Estaba medida, estaba documentada, y cambiar el layout la volvió falsa sin que
   > nada fallara: ninguna prueba miraba la distancia entre el conector y su vecino.

   El punto se va por otra razón: en la tarjeta nueva el color de unidad ya lo lleva
   el ICONO, arriba a la izquierda. Un punto del mismo color a media altura lo repite
   y, sin la etiqueta de una línea a la que servía de viñeta, se lee como suciedad.
   Cada significado con un solo portador: el color lo dice el icono, la desconexión
   la dicen los haces —que además ahora se ve, porque se apagan sin juntarse—. */

/* Modo claro: el silo va a papel blanco (regla A2 de más arriba), así que el
   borde teñido tiene que repetir el `var(--cc-unidad, …)` sobre el hairline
   oscuro. Sin esto el teñido funcionaría en oscuro y en claro volvería al gris
   neutro en los cuatro — que es exactamente 8.32, tercera vez. */
html[data-theme="light"] .cc-silo {
  border-color: rgba(32, 30, 44, 0.16);
}

/* --- 3. Trayectoria y capacidad: dos cosas distintas, dos formatos -----------
   Los 7 chips de S10 eran la misma pill siete veces: 1px de borde, fondo
   transparente, 15px regular, radio 999. Leídos así son etiquetas de un CMS, y
   sobre todo esconden que las dos filas dicen cosas de naturaleza distinta:

     · TRAYECTORIA son DATOS (15 años, +200 marcas, 6 países) — hechos medibles.
     · CAPACIDAD son CREDENCIALES (Google Premier Partner, VTEX partner oficial)
       — cosas que alguien de afuera certifica.

   Un dato no se mete en una cápsula: se muestra. Una credencial sí, porque una
   cápsula con borde de color ES el lenguaje de un sello. Separar los formatos le
   da a la sección la jerarquía interna que no tenía, y de paso resuelve que el
   activo más fuerte de la banda estuviera al mismo tamaño que su etiqueta.

   Los datos suben a 20px/700 —tamaño que ya existía en la home— y se separan con
   hairlines en vez de bordes cerrados: es la forma de una ficha técnica, no de
   una nube de tags. */
.cc-especs {
  --cc-espec-sep: rgba(255, 255, 255, 0.12);
}
/* ⚠️ El separador solo existe cuando los datos van EN UNA LÍNEA, y esto lo cazó una
   captura a 390px: al envolverse la fila, cada item queda en su propio renglón y el
   `border-left` se convierte en **un palito vertical suelto a la izquierda de tres de los
   cuatro** —del primero no, porque es el que no lleva la regla—. Un separador que no
   separa nada se lee como un resto de maquetación.
   768px es donde los cuatro caben en una línea (medido: suman 508px de texto y hay 652 de
   ventana). Bajo eso se apilan, y el `min-width: 100%` del rótulo hace que arranquen en su
   propio renglón en vez de dejar al primero colgado al lado de "TRAYECTORIA".
   El rótulo lleva `!important` porque su `min-width: 110px` sale de un `_cssCustom`, o sea
   de una regla de ID: es el único caso en el que una utilidad puede ganarle (8.1). */
/* `.cc-cred` entra en la misma regla desde el 2026-08-06: la fila de CAPACIDAD dejó
   de ser tres cápsulas y ahora comparte el lenguaje de ficha de datos con la de
   TRAYECTORIA, o sea también su separador. */
@media (min-width: 768px) {
  .cc-especs > :is(.cc-dato, .cc-cred) + :is(.cc-dato, .cc-cred) {
    border-left: 1px solid var(--cc-espec-sep);
    padding-left: 26px;
  }
}
/* ⚠️ Desde el 2026-08-13 las dos filas son una REJILLA y no dos flex independientes,
   y el motivo se midió: con `flex wrap` la posición de cada item depende del ancho del
   texto que tiene delante, así que TRAYECTORIA arrancaba sus datos en x=266/384/556 y
   CAPACIDAD los suyos en x=266/500/703. Sumadas a las columnas de las cifras de arriba
   (130/542/954) eran **tres rejillas distintas apiladas** y ninguna alineada con otra:
   eso es lo que se ve como "acoplado", y no el espaciado.
   Con `110px repeat(3,1fr)` las dos filas comparten la columna del rótulo y las tres de
   los datos, así que se alinean entre sí por construcción. */
@media (max-width: 767px) {
  /* El `!important` es el mismo caso de 8.1 que ya documentaba la regla de abajo: la
     plantilla declara la rejilla desde un `_cssCustom`, o sea una regla de ID, y una
     utilidad de clase no puede ganarle sin esto. */
  .cc-especs { grid-template-columns: 1fr !important; gap: 14px !important; }
}
/* ⚠️ El separador NO se voltea con el tema, y es un caso de 8.24 que estuvo a punto de
   colarse: `#brxe-sec061` es una **isla oscura** (está en las PERMITIDAS de `tema.mjs`),
   o sea sigue siendo violeta en modo claro. Un `--cc-espec-sep` en tinta pintaría
   hairlines oscuros sobre fondo violeta — correcto para una sección que se aclara,
   equivocado para una que no. La banda mantiene el separador blanco en los dos temas. */

/* La unidad de medida del dato ("años", "marcas", "países") baja a texto de
   apoyo: sin esto "15 años" son dos palabras del mismo peso y el número no
   destaca, que era el problema. El `<span>` lo pone el template, así que el dato
   sigue siendo UNA cadena traducible y no dos elementos que alguien pueda
   desalinear en el builder. */
.cc-dato__u {
  font-size: var(--cc-fs-sm);
  font-weight: var(--cc-fw-medium);
  color: var(--cc-fg-muted);
}

/* La credencial, como dato y no como etiqueta. Sin relleno, sin borde y sin
   radio: el peso lo da el texto (600 sobre `--cc-fg-strong`) y la separación, el
   hairline que comparte con `.cc-dato`. Ver el aviso de más arriba.

   Efecto colateral bueno: al no haber relleno hecho del mismo token que el texto,
   desaparece de raíz el problema que 8.35 tuvo que medir y compensar en modo claro
   —el lavado al 8% de `--cc-fg-accent` bajo un texto de `--cc-fg-accent`, que daba
   4.48 y no pasaba AA—. El tema claro ya no necesita ninguna excepción para esta
   fila: es la lección de 8.27.6 (si el template usa el token, el tema no necesita
   overrides), llegando por la vía de quitar decoración. */
.cc-cred {
  font-weight: var(--cc-fw-semibold);
}

/* El rótulo de cada fila (TRAYECTORIA, CAPACIDAD). Estaba a 12px/700 SIN
   letter-spacing, que es lo que hace que un texto en mayúsculas se lea como
   grito en vez de como rótulo: en caps, el tracking es lo que separa un
   eyebrow de una palabra escrita a los gritos. Es tipografía, no color, así que
   no toca ningún trinquete. */
/* El rótulo de fila de `sec061` (TRAYECTORIA, CAPACIDAD). Pedido de Ricardo el 2026-08-17
   (R-01): «más tamaño». Estaban a 12px CLAVADOS en el JSON —la clase solo ponía el
   interletrado— junto a un contenido de 15-16px, y por eso se leían como una nota al pie
   en vez de como el rótulo de su fila.

   ⚠️ NO se suben los otros rótulos de 12px de la portada, y es a propósito: los de
   `sec020` (`cc-silo__rot`: INVERSIÓN, SESIONES…) viven DENTRO de tarjetas pequeñas, donde
   12px es proporcionado. Mismo aspecto, distinto papel; igualarlos habría sido tratar como
   una sola cosa dos que no lo son.

   14px es el siguiente peldaño de la escala (`--cc-fs-xs`) y **ya se usa en la portada**,
   así que el trinquete de `ux.mjs` no se mueve: sigue habiendo 12 tamaños distintos, que es
   justo el techo. Subirlo a un valor nuevo habría necesitado decisión de equipo (§7).
   El valor vive aquí y no en el JSON por 8.18: si hay que volver a tocarlo, es una línea. */
/* La rejilla de las dos filas de especificaciones de `sec061`. Vivía DUPLICADA en el JSON,
   como `_cssCustom` con selector de id, una copia por fila — o sea dos sitios que tenían que
   decir lo mismo y ninguna forma de notar que dejaran de hacerlo (§5). Aquí es una.

   ⚠️ LA COLUMNA DEL RÓTULO ES LO QUE SE ROMPIÓ AL SUBIR EL TAMAÑO (R-01, 2026-08-17).
   Estaba fijada en 110px, y a 12px «TRAYECTORIA» medía ~108: cabía justo. A 14px mide
   **125,6px**, o sea se salía 15,6 de su columna y se comía el hueco: quedaban **10px**
   hasta «15 años», mientras «CAPACIDAD» —108,6px, que sí cabe— conservaba los suyos.
   Ricardo lo vio en el acto («se ve muy pegado»), y lo que lo delataba no era el hueco
   pequeño sino que las DOS FILAS no lo tuvieran igual: la corta parecía bien y la larga
   parecía rota.

   ⚠️ Y lo primero que medí NO lo vio: comparé la CAJA del rótulo con el inicio del valor y
   me dio 26px, el `gap` declarado, en los 8 anchos. La caja acaba en el borde de la
   columna; el texto se sale de ella con `overflow: visible`, así que la caja miente. Hay
   que medir la TINTA (un `Range` sobre el nodo, o el ancho del texto en un span suelto).
   Es 8.15 con otra cara: el instrumento devolvía un número correcto de algo que no era la
   pregunta.

   El ancho sale de la medida del rótulo más largo, no de un número redondo: 125,6 + holgura.
   Que las dos filas compartan valor es lo que mantiene alineada la columna de los valores. */
.cc-especs {
  display: grid;
  grid-template-columns: var(--cc-especs-rotulo, 136px) repeat(3, 1fr);
  align-items: baseline;
  gap: 26px;
}
.cc-especs > * { width: auto; min-width: 0; }

.cc-rotulo {
  letter-spacing: 0.14em;
  font-size: var(--cc-fs-xs);
}

/* ==========================================================================
   LAS 4 HIJAS VIEJAS DE /servicios/ (2026-08-09) — gotcha 8.40

   `agencia-marketing`, `desarrollo-web-proyectos-tecnologicos`, `creatividad` y
   `diseno` compartían estructura entre sí en el diseño anterior, así que se
   rediseñaron como UNA sola definición: las cuatro salen del mismo generador y
   tienen las mismas 4 secciones (hero, qué hacemos, proyectos asociados, cierre).
   Lo que cambia es el contenido y el color de unidad, nada más.

   Este bloque es TODO el CSS que hizo falta: tres clases. El resto —el hero con
   su aurora, las tarjetas, la grilla, la banda, los enlaces con flecha, el color
   por unidad— ya existía y se reusó tal cual, que era la prueba de que el sistema
   estaba terminado. Si esta sección hubiera crecido, la conclusión habría sido la
   contraria.
   ========================================================================== */

/* El rótulo "SERVICIOS" del hero, que es además el enlace de vuelta a la página
   madre. El diseño viejo hacía ese retorno con un cuadrado verde flotante
   (`.arrow-back`, un elemento `code` con `#59F0BA` escrito a mano — ni siquiera el
   verde vigente) posicionado sobre el contenido. Ahora es una migaja de pan en el
   sitio donde se busca, y el retorno completo a las 6 hermanas lo da el
   desplegable del header (8.32), que existe en todas las páginas. */
.cc-migaja a {
  color: inherit;
  text-decoration: none;
  transition: color var(--cc-dur-base) var(--cc-ease-out);
}
.cc-migaja a:hover { color: var(--cc-unidad, var(--cc-fg-accent)); }

/* Fila de chips dentro de una tarjeta. La página de Desarrollo escribía estas
   listas como prosa con pipes ("VTEX | PrestaShop | Shopify | …"), que es una
   tabla disfrazada de frase: se lee como si fuera una oración y no lo es.
   El `<p>` lo pone Bricks (8.9), así que el margen entre chips va en el chip y no
   en un `gap` — un `<p>` no es un flex container y `gap` ahí no haría nada. */
.cc-chips { line-height: 1; }
.cc-chips .cc-chip { margin: 0 6px 8px 0; }

/* `.cc-chip` nació en el blog con el verde de marca escrito en la regla. Al entrar
   en una isla de unidad tiene que tomar el color de esa unidad, o la tarjeta de
   Commerce muestra chips verdes dentro de un borde violeta.
   Se arregla por ALCANCE y no enumerando (8.25.5): el fallback es EXACTAMENTE el
   valor anterior, así que el chip del blog —que no está en ninguna isla— no se
   entera de este cambio. */
.cc-chip {
  border-color: color-mix(in srgb, var(--cc-unidad, var(--cc-green-500)) 50%, transparent);
  color: var(--cc-unidad, var(--cc-green-500));
}

/* El carrusel de proyectos asociados. Es contenido DINÁMICO heredado (consulta el
   CPT de la disciplina por su término de `portafolio_categoria`), así que la
   consulta se conservó intacta: es la única fuente de esos enlaces internos y en
   un sitio posicionado eso no se tira. Lo que se cambió fue el vestido y tres
   defectos del original, documentados en el JSON.
   Las flechas se acotan a 44px por el área de toque (WCAG 2.5.8, la misma medida
   que el resto del sitio desde la auditoría del 2026-07-29). */
.cc-proyectos .bricks-layout-item img {
  border-radius: var(--cc-radius-md);
  object-fit: cover;
}
/* Las flechas. El nombre de clase es `.swiper-button` + `.bricks-swiper-button-prev`
   / `-next` (verificado en `includes/elements/post-navigation.php` del theme padre,
   no supuesto): un selector contra markup inventado es 8.10, y en un carrusel de
   terceros es la trampa más fácil de pisar. */
.cc-proyectos .swiper-button {
  min-width: 44px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 10, 15, 0.72);
  border: 1px solid var(--cc-border-on-dark);
  border-radius: var(--cc-radius-pill);
  color: var(--cc-fg-strong);
  transition: border-color var(--cc-dur-base) var(--cc-ease-out);
}
.cc-proyectos .swiper-button:hover {
  border-color: var(--cc-unidad, var(--cc-fg-accent));
}

/* ── El vídeo de marca en la home (2026-08-09) ─────────────────────────────────
   Va en "NUESTRA HISTORIA" y no en "Somos parte de tu equipo", donde primero se probó
   con una foto de la oficina: Ricardo la rechazó —*"no tiene sentido una foto de la
   oficina ahí"*— y tenía razón, un despacho vacío no ilustra cercanía ni delegación.
   El vídeo, en cambio, ES la marca contando quién es, y el titular de esa sección dice
   lo mismo con palabras.

   Con póster y CONTROLES, nunca de fondo: es una pieza narrada de 60s con texto quemado
   y locución; muda y en bucle pelearía con el titular y no se entendería.
   `object-fit: contain` y no `cover`: el póster es el logo centrado, y `cover` lo recorta
   hasta dejar un rectángulo violeta plano en pantallas anchas (medido a 1920).
   ⚠️ DORMIDA DESDE EL 2026-08-17: el vídeo salió de la portada por decisión de Ricardo
   («ya es antiguo», R-04), así que hoy no la usa nadie —ni la portada ni la variante de
   laboratorio—. NO se borra porque lo que vale de este bloque no son las 8 declaraciones
   sino las tres medidas de arriba, que costaron una sesión y se perderían con él. Si el
   vídeo vuelve, funciona tal cual; si se decide que no vuelve, esto se va y el póster
   `cc-video-poster.webp` con él (ojo: el póster SÍ lo sigue usando `tools/lab/`).
   ========================================================================== */
.cc-home-video {
  width: 100%;
  border-radius: var(--cc-radius-lg);
  overflow: hidden;
  border: 1px solid var(--cc-border-on-dark);
}
.cc-home-video video {
  /* Sin `max-height`: forzaba una caja más ancha que 16:9 y `contain` dejaba dos franjas
     oscuras a los lados que se leían como un error de encuadre. Dentro del contenedor de
     1180 la proporción sola da 662px de alto, que cabe de sobra en una pantalla. */
  display: block; width: 100%; height: auto;
  aspect-ratio: 16 / 9;
  object-fit: contain; background: #0a0a0f;
}

/* ==========================================================================
   ARTE DE CABECERA DE LA FAMILIA /servicios/ (2026-08-10) — gotcha 8.50

   Pedido de Ricardo: *"en el original, cada sección tenía imágenes, ¿qué opinas si las
   colocamos? para que no sean simples tarjetas con información"*.

   Se fue a ver QUÉ imágenes eran —producción todavía sirve el diseño anterior de estas
   mismas páginas, así que el inventario sale con un GET— y eran dos familias distintas:

     · **Las de tarjeta no volvieron.** Once de doce eran banco genérico y varias
       claramente generadas por IA (pasillos de datacenter, tableros que brillan, ondas de
       partículas), en magenta y cian, o sea fuera de la paleta. Tres eran CAPTURAS DE
       PANTALLA con el texto quemado dentro: no se selecciona, no lo lee un lector de
       pantalla y no cambia en modo claro. Y pesaban: las 5 de `agencia-marketing` suman
       3,08 MB contra los 0,19 MB que pesa la página entera. Traerlas habría sido restaurar
       exactamente el *"muy genérico, muy IA"* del que salió el rediseño.

     · **Las 4 de cabecera sí.** Un recorte de figura por página —astronauta, futbolista,
       Van Gogh, stormtrooper— en duotono. Eso no era relleno: era un SISTEMA de una imagen
       y un color por página, que es la mitad visual del `--cc-u-*` que el rediseño
       reconstruyó en CSS el 2026-08-04 y dejó sin imagen.

   Se recuperó el mecanismo, no los archivos: retiñidas al color de SU unidad, sin el grano
   de escaneo y en WebP. De 665-1015 KB a **15-28 KB** (−97%).

   ⚠️ SEGUNDA VUELTA, y es la lección. La primera versión colocaba el arte como un recorte
   a la derecha con los bordes fundidos en el propio archivo. Ricardo: *"no me gustó mucho
   como se ven las imágenes, como un cuadro difuminado"*. Tenía razón y el diagnóstico es
   exacto: un rectángulo con los bordes en alfa **sigue leyéndose como un rectángulo**, y
   encima difuminado. Fundir los bordes no hace que una pieza pertenezca a la composición;
   solo hace que se note menos que no pertenece.

   Lo que sí pertenece es lo que propuso él: **la imagen ocupa la sección entera y se apaga
   al llegar al texto**. Eso cambia tres cosas de la implementación:

     1. **El fotograma completo, espejado.** El original trae la figura a la izquierda y el
        aire a la derecha, que es donde iba el titular en el diseño anterior. Aquí el texto
        va a la izquierda, así que la imagen se espeja y la figura cae en el lado libre.
     2. **El desvanecido pasa del ARCHIVO al CSS.** Antes iba en el alfa del WebP porque el
        degradado de la sección es un `background-image` y no puede apagar a un hijo. Ahora
        tiene que ser una **máscara**, y por una razón que el archivo no puede resolver:
        dónde acaba el texto depende del ancho de la pantalla, y un alfa horneado no se
        entera. La máscara se declara en `%` del propio elemento, así que acompaña.
     3. **La aurora se apaga en estas 4 páginas.** El `::before` violeta al 55% se pintaba
        ENCIMA del arte —los pseudos van sobre el fondo de la sección— y enturbiaba el
        color de unidad: un stormtrooper verde bajo un velo violeta no es ni verde ni
        violeta. La aurora existía para que el hero no fuera un rectángulo plano; con arte
        ya no hace falta y solo ensucia.
   ========================================================================== */
.cc-hero__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* `contain` y no `cover`, que fue el primer intento: la imagen es 2.66:1 y el hero
     ronda 2:1, así que `cover` la ampliaba un 20% y recortaba los lados — la figura salía
     enorme y descabezada. Con `contain` se ve entera y quedan dos bandas arriba y abajo,
     que **no se ven**: la sombra del duotono ES `--cc-ink-900`, el mismo color que el
     fondo de la sección, así que la banda y el fondo son el mismo píxel. Es la misma
     propiedad que hace que la imagen no necesite marco. */
  object-fit: contain;
  object-position: right center;
  z-index: 0;                    /* los hijos de .cc-aurora van a z-index 1 */
  pointer-events: none;          /* decorativa: no roba el clic al CTA */
  /* El apagado. Los porcentajes son del elemento, o sea del ancho de la pantalla: a 1440
     el texto acaba en el 66% y a 1920 en el 62%, así que un corte fijo en px se quedaría
     corto en una y largo en la otra. 0-30% transparente cubre la columna de lectura con
     margen; 30-72% es la transición; del 72% en adelante la imagen va entera. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, transparent 30%, rgba(0,0,0,.55) 52%, #000 72%);
          mask-image: linear-gradient(90deg, transparent 0%, transparent 30%, rgba(0,0,0,.55) 52%, #000 72%);
}

/* Sin soporte de máscara la imagen quedaría entera bajo el titular. No se deja: el texto
   es blanco sobre un duotono con luces del color de unidad y el contraste no está medido
   ahí. Es la misma regla que 8.23 — si la red de seguridad no puede garantizar el estado
   bueno, que retire el efecto en vez de dejar algo a medias. */
@supports not ((-webkit-mask-image: linear-gradient(#000, #000)) or (mask-image: linear-gradient(#000, #000))) {
  .cc-hero__art { display: none; }
}

/* Con arte, la aurora sobra: se pintaba encima y enturbiaba el color de unidad. */
.cc-hero-art-on::before,
.cc-hero-art-on::after { display: none; }

/* Bajo 992px el hero es una sola columna y el texto ocupa TODO el ancho, así que una
   máscara horizontal no tiene dónde apagarse: dejaría la figura detrás del titular. Se
   cambia el eje —la imagen se apaga hacia ARRIBA, donde está el texto, y se ve entera
   abajo— y la sección gana alto para que quepan las dos cosas.
   No se oculta: esconderla en móvil dejaría a la mitad del tráfico con la página que
   Ricardo pidió cambiar. */
@media (max-width: 991px) {
  .cc-hero__art {
    object-position: center bottom;
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, transparent 42%, rgba(0,0,0,.5) 62%, #000 82%);
            mask-image: linear-gradient(180deg, transparent 0%, transparent 42%, rgba(0,0,0,.5) 62%, #000 82%);
  }
  .cc-hero-art-on { padding-bottom: calc(var(--cc-sec-md) + 120px); }
}

/* ⚠️ MODO CLARO (8.24). El duotono está construido sobre `--cc-ink-900`, que es constante:
   no cambia con el tema. Eso en oscuro es justo lo que se quiere —las sombras de la imagen
   SON el fondo de la sección—, pero en claro la mitad derecha del hero se volvería una
   mancha oscura contra el papel.

   La salida no inventa nada: el sistema ya decidió el 2026-08-03 que ciertas piezas siguen
   oscuras en modo claro (la banda violeta, el cierre, el footer, la caja CTA de la nota —
   8.25.5). Aquí el hero entero es esa isla: el fondo de la sección se queda en tinta y el
   arte sigue a sangre encima, así que la transición imagen→fondo vuelve a ser invisible,
   que es lo que la hacía funcionar en oscuro.

   Y el texto no hace falta arreglarlo a mano: la sección lleva además `cc-isla-oscura`,
   que es el mecanismo que ya existe desde el 2026-08-03 y devuelve de una vez los tokens
   de texto Y los de superficie a sus valores sobre oscuro. Escribir aquí un `color:` por
   elemento sería reimplementar eso peor — y es exactamente el bug de 8.25.8, donde la
   primera versión de la isla devolvía los colores de texto pero no los de superficie. */
/* ⚠️ Y los ids no son decoración, son ESPECIFICIDAD. `html[data-theme="light"]
   .cc-hero-art-on` pesa (0,2,0) y el fondo de la sección lo pone Bricks desde el ajuste
   `_background` como `#brxe-sds001{background-color:var(--bricks-color-xyartc)}`, que pesa
   (1,0,0) — y esa variable de paleta SÍ la voltea el modo claro, así que la sección se iba
   a blanco con el texto ya en blanco: titular invisible. Es 8.25.8 otra vez, la isla
   devolviendo los colores de TEXTO y no los de SUPERFICIE, y es 8.3: la paleta de Bricks
   vive en la BD y gana por id.
   Un `:is()` toma la especificidad de su argumento más específico, así que meter los 4 ids
   sube esta regla a (1,2,0) y pasa por encima sin `!important`. Es el mismo recurso que ya
   usa la lista de islas oscuras de más arriba. Si alguien renombra una sección esto deja de
   aplicar — por eso `servicios.mjs` lo mide en claro, para que no sea silencioso (8.42). */
html[data-theme="light"] :is(#brxe-smk001, #brxe-sdv001, #brxe-scr001, #brxe-sds001) {
  background-color: var(--cc-ink-900);
}

/* ── Imagen de tarjeta de servicio (2026-08-10) ───────────────────────────────
   Segunda mitad del pedido de Ricardo: *"quiero que pongas igualmente las imágenes,
   aunque sean IA o recortes. Me gusta como se ve en la página original, pero nosotros
   démosle el toque más moderno"*.

   O sea vuelven las 11 de tarjeta que la primera pasada había descartado por genéricas,
   y la decisión de qué hacer con ellas se mueve de "cuáles" a "cómo". El "toque moderno"
   son cuatro cosas medibles, ninguna de gusto:

     1. **Duotono al color de su unidad**, igual que el arte de cabecera. Es lo que
        convierte una colección de stock de bancos distintos —magenta, cian, beige,
        fucsia— en una serie. Con una rampa más suave que la del hero: ahí la escena
        estorba y se hunde, aquí la escena ES el motivo.
     2. **Exposición nivelada**. Tres de los trece están fotografiados sobre blanco y
        pesaban el doble que sus vecinas en la grilla. Teñir del mismo color no iguala lo
        que el ojo lee primero, que es el brillo.
     3. **Todas a 16:9**. Venían en 1.89:1, 4:3, 1.76:1 y 1.33:1.
     4. **A sangre en la tarjeta**, no encajadas dentro del relleno. Una imagen con aire
        alrededor dentro de una caja con borde se lee como dos cajas; pegada a los cantos
        superiores es la tarjeta la que tiene una imagen.

   Peso: las 13 juntas ocupan **375 KB**. Los originales PNG de las mismas 13 sumaban
   **6,1 MB**, y `campanas-en-meta` sola pesaba 1,3 MB por una caja de 370px.

   El radio resta 1px al del contenedor: el borde de la tarjeta ocupa ese pixel y sin la
   resta asoma una uña de fondo entre la imagen y el borde en las dos esquinas de arriba. */
.cc-svc-card__media {
  display: block;
  /* Cancela el relleno de la tarjeta (34px 30px) para llegar a los cantos.
     ⚠️ El `max-width: none` no es opcional: el reset del theme lleva `img{max-width:100%}`
     y ese 100% es el ancho de CONTENIDO de la tarjeta (318px), así que recortaba los 378
     de vuelta a 318 — los márgenes negativos sí aplicaban y la imagen quedaba pegada a la
     izquierda y a 60px del canto derecho. Se ve en una captura y no en el CSS. */
  width: calc(100% + 60px);
  max-width: none;
  margin: -34px -30px 6px;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  /* ⚠️ EL RADIO VUELVE A LA IMAGEN, Y NO SE RECORTA DESDE LA TARJETA. Costó tres intentos
     (Ricardo, 2026-08-25: *«las esquinas de las imágenes se ven raras»*), y los dos que
     fallaron enseñan más que el que funcionó:

     · Intento 1 — subir el radio al pleno. FALSO: el margen negativo de arriba cancela el
       RELLENO, no el borde, así que la imagen queda 1px DENTRO por cada lado (medido:
       tarjeta 130.0→410.0, imagen 131.0→409.0). Con radio 18 en un hueco de 17 se recorta
       de más y asoma el fondo de la tarjeta: en claro, un halo blanco sobre foto oscura.
     · Intento 2 — `overflow: hidden` en la tarjeta y radio 0 en la imagen, para tener UNA
       sola curva. Suena bien y **se ve peor**: el recorte redondeado de un elemento
       compuesto —una imagen con `object-fit`— Chromium lo resuelve con una máscara SIN
       SUAVIZAR, mientras el borde sí va suavizado. Entre la curva limpia del borde y la
       dentada del recorte aparece el fondo en escalones. Comparado a dpr 5 sobre las
       cuatro tarjetas: el escalón estaba en las cuatro, idéntico — que es lo que delató
       que no era la foto, porque cuatro fotos distintas no comparten artefacto.

     La forma que sí funciona es la original: el radio en la PROPIA imagen, que Chromium sí
     suaviza, y `calc(… - 1px)` porque la imagen vive en la caja de relleno. Que quede
     escrito: aquí el instinto de «pon overflow hidden y olvídate» produce un defecto
     visible, y solo se ve mirando a 5× — a tamaño natural pasa por «se ve raro». */
  border-radius: calc(var(--cc-radius-lg) - 1px) calc(var(--cc-radius-lg) - 1px) 0 0;
}

/* ==========================================================================
   EL CONTROL DE PAUSA, EN REPOSO (2026-08-10)

   Ricardo: *"quita los símbolos de pause y reanudar del homepage: carrusel y el texto
   que cambia en el principio, que quede en bucle nomás"*. Y visualmente tiene razón: dos
   glifos de "⏸" sueltos en una portada son ruido de interfaz, no diseño — nadie los busca
   y ahí están, compitiendo con el CTA del hero y con los logos de marca.

   ⚠️ AHORA ES UNO SOLO: «el texto que cambia en el principio» dejó de cambiar el
   2026-08-23 —el hero pasó a un mensaje fijo— así que su pausa se fue con él y aquí queda
   la del carrusel de marcas. La regla se mantiene igual de necesaria para esa.

   ⚠️ Pero **borrarlo no se puede**, y conviene que quede escrito por qué. El
   carrusel se mueve solo y dura más de 5 segundos, así que cae bajo **WCAG 2.2.2
   (Pause, Stop, Hide), nivel A** — el mismo nivel que "tener texto alternativo". El
   criterio no pide un botón bonito: pide que exista **un mecanismo**. Y las pausas por
   `mouseenter` no cuentan, porque quien navega con teclado o con lector de pantalla nunca
   pasa el puntero por encima.

   La salida es la que usan los sitios grandes con carruseles: **el control existe siempre
   y solo se VE cuando hace falta**. Aparece al pasar el puntero por su carrusel o al
   entrar el foco con Tab, y el resto del tiempo es invisible.

   Lo que NO se hace, y es la trampa fácil: `display:none` o `visibility:hidden`. Las dos
   sacan el botón del orden de tabulación, o sea eliminan el mecanismo de verdad y
   dejarían el sitio incumpliendo el criterio con la apariencia de cumplirlo. Aquí solo
   baja la opacidad, que no quita presencia (es 8.14 al derecho: `opacity:0` NO saca del
   tab, y eso, que allí era el bug, aquí es justo la propiedad que se necesita).

   El `:focus-visible` propio del botón está en la lista a propósito: quien llega con Tab
   necesita verlo en cuanto lo enfoca, aunque el puntero esté en la otra punta. */
.cc-hero__pausa,
.cc-marcas__pausa {
  opacity: 0;
  transition: opacity var(--cc-dur-base) var(--cc-ease-out);
}
/* El ancestro es el REAL, comprobado en el DOM y no supuesto: `.cc-marcas`, que es un
   contenedor propio. Escribirlo contra un `.cc-marcas-wrap` que no existe habría dejado
   el botón invisible para siempre — mecanismo eliminado de hecho, que es peor que no
   haber tocado nada.
   (Aquí acompañaba el del hero, cuyo ancestro tenía que ser su SECCIÓN `#brxe-sec001`
   porque el JS lo inyectaba dentro de la fila de dots. Se fue con el carrusel el
   2026-08-23.) */
#brxe-sec001:hover .cc-hero__pausa,
#brxe-sec001:focus-within .cc-hero__pausa,
.cc-marcas:hover .cc-marcas__pausa,
.cc-marcas:focus-within .cc-marcas__pausa {
  opacity: 1;
}
/* Quien pidió menos movimiento tiene los carruseles ya detenidos, así que el control no
   tiene nada que pausar: se queda visible para poder REANUDAR si quiere. */
@media (prefers-reduced-motion: reduce) {
  .cc-hero__pausa, .cc-marcas__pausa { opacity: 1; }
}

/* ==========================================================================
   FOOTER — LA RETÍCULA (2026-08-10)

   Ricardo: *"el footer no sé si me gusta mucho, está como todo muy acoplado"*.
   "Acoplado" es exacto y era medible. Lo que había:

     · **Tres columnas de `auto-fit` en 1180px**, o sea el ancho lo repartía el
       contenido: la de marca amontonaba cuatro cosas distintas —logo, claim, redes y
       el sello de Google Premier— en 272px de alto, mientras a la derecha de la
       columna de navegación quedaban ~260px vacíos. Todo el peso a la izquierda y aire
       muerto a la derecha.
     · **Una columna sin rótulo.** "NAVEGACIÓN" iba en versalitas; la dirección, el
       teléfono y el email no llevaban ninguno, así que esa columna se leía como texto
       suelto caído en medio del footer.
     · **Dos focos peleando.** El email era un botón violeta saturado —el único color
       pleno del footer— a 40px del sello blanco de Google. Dos centros de atención en
       la misma banda, ninguno gana.
     · **Entropía sin motivo:** 4 tamaños, **5 interlineados**, 4 pesos y 3 colores para
       14 enlaces.

   Ahora son **cuatro columnas con rótulo** y una fila de cierre:

     marca (1.5fr) · Servicios · Compañía · Contacto (1.2fr)

   Tres decisiones que no son de gusto:

   1. **La columna de Servicios es nueva y es la que llena el ancho.** No es relleno:
      pone **7 enlaces internos a las páginas comerciales en todas las páginas del
      sitio**, que es de lo más barato que se puede hacer por el posicionamiento de un
      sitio que ya rankea. El hueco muerto se cierra con contenido útil, no con aire.
   2. **El sello de Google baja a la fila de cierre.** Es una credencial, no navegación:
      arriba obligaba a la columna de marca a medir el doble que sus vecinas y era lo
      primero que veías del footer. Abajo, junto al copyright, está donde se buscan las
      credenciales — y de paso la columna de marca queda con su altura natural.
   3. **El email deja de ser botón.** Como enlace de texto la columna se lee igual que
      las otras tres y el footer deja de tener dos centros. El correo sigue siendo
      `mailto:` y sigue siendo el más destacado de su columna, por color, no por caja.

   La primera fila NO usa `auto-fit`: con `auto-fit` el ancho de cada columna depende de
   cuánto texto tenga, que es la versión de grilla del `space-between` que 8.27 ya sacó
   de aquí. Cuatro fracciones explícitas es lo que hace que la retícula sea una decisión.
   ========================================================================== */
.cc-foot__grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1.2fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}
/* Las 3 columnas de enlaces comparten UNA definición: un solo tamaño, un solo
   interlineado y un solo color. Es el trinquete de entropía de `ux.mjs` aplicado a
   mano — cuatro tamaños y cinco interlineados en 14 enlaces no los distingue nadie,
   solo hacen que el bloque se lea sucio. */
.cc-foot__grid > .brxe-block > .brxe-text:not(.cc-social):not(.cc-badge-google-wrap):not(.cc-foot__titulo),
.cc-foot__grid > .brxe-block > .brxe-text:not(.cc-social) > p {
  /* UN tamaño y UN interlineado para las tres columnas de enlaces y la de contacto.
     La versión anterior tenía 4 tamaños y 5 interlineados para 14 enlaces: nadie
     distingue 14 de 15px, solo hacen que el bloque se lea sucio. El interlineado va en
     ems y no en `2` a secas para que no lo multiplique cada tamaño heredado — así salió
     el primer intento, con 6 interlineados en vez de uno. */
  font-size: var(--cc-fs-sm);
  /* El interlineado es 1.6 y NO un valor propio, y las dos vueltas que costó valen como
     regla. Primero fue `1.9em`: como el footer es global y cada nodo heredaba su tamaño,
     salían seis valores computados distintos y los trinquetes de entropía de `ux.mjs`
     subieron en las TRES páginas a la vez (home 11→12, blog 4→5, servicios 7→8). Después
     `30px`: un solo valor, sí, pero **uno NUEVO** — y el trinquete no cuenta cuántos usa
     el footer, cuenta cuántos hay en la página.
     1.6 es el que ya usan 67 nodos de la home. Con el tamaño forzado arriba da un único
     computado y, sobre todo, **no estrena ninguno**. La lección: en un componente global,
     un valor tipográfico nuevo no cuesta lo que ocupa, cuesta en todas las páginas. */
  line-height: 1.6;
  margin: 0;
}

/* El correo: el elemento con más peso de su columna, con color en vez de caja. */
.cc-foot__mail a {
  color: var(--cc-fg-accent);
  font-weight: var(--cc-fw-semibold);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--cc-fg-accent) 40%, transparent);
  transition: border-color var(--cc-dur-base) var(--cc-ease-out);
}
.cc-foot__mail a:hover { border-bottom-color: var(--cc-fg-accent); }

/* La fila de cierre: copyright a la izquierda, credencial a la derecha. */
.cc-foot__cierre {
  display: flex;
  /* `row` explícito: el elemento `block` de Bricks trae `flex-direction: column` y
     sobrevive a que esta clase declare `display:flex` — el copyright y el sello salían
     apilados y centrados. Es el primo del 8.33 que acaba de morder en la grilla de
     unidades: heredar la dirección del componente base y creer que la clase la manda. */
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.cc-foot__cierre .cc-badge-google-wrap { margin-top: 0; }

@media (max-width: 991px) {
  .cc-foot__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 575px) {
  .cc-foot__grid { grid-template-columns: 1fr; }
  .cc-foot__cierre { justify-content: flex-start; }
}

/* ==========================================================================
   FORMULARIOS (2026-08-10)
   La sección de cierre de la home sirve el formulario de contacto real —el mismo
   `[gravityform id="1"]` de /contacto/— desde un elemento `shortcode` de Bricks.
   Antes tenía una caja falsa y una nota de desarrollo escritas dentro de home.json,
   o sea versionadas; el detalle está en el encabezado de `functions.php`.

   ⚠️ LO QUE FALTA POR VER, y conviene que esté escrito: Gravity Forms es de licencia
   y NO está en el entorno local, así que el aspecto del formulario dentro de esta
   sección **no se ha podido verificar midiendo**. Lo de abajo es lo defendible sin
   verlo —heredar los colores de la isla y las escalas del sistema— y necesita una
   pasada visual en cuanto exista staging (docs/pendientes.md).
   ========================================================================== */

/* El marcador que se ve SOLO en local, donde el plugin no está. Deliberadamente sobrio
   y sin aire de componente: tiene que leerse como "aquí falta una pieza del entorno" y
   no como parte del diseño. */
.cc-form-ausente {
  margin: 0;
  padding: 18px 20px;
  border: 1px dashed var(--cc-border-soft);
  border-radius: var(--cc-radius-md);
  color: var(--cc-fg-muted);
  font-size: 14px;
  line-height: 1.6;
}

/* ⚠️ AQUÍ HABÍA 40 LÍNEAS QUE VESTÍAN ESTE FORMULARIO A MANO, y se retiraron el
   2026-08-27 al meter la portada en el alcance de 8.113 (`.cc-home`, al final de este
   archivo). No estaban mal escritas: estaban escritas A CIEGAS —su propio comentario de
   arriba lo decía— y por eso les faltaba justo lo que no se puede adivinar sin ver el
   formulario real. Lo medido en el staging antes de retirarlas:

     input[type="url"]  no estaba en la lista  →  38px de alto contra 48 del resto
     el botón            107x38 y menta #ADEFDB  →  bajo el mínimo táctil de 44
     las casillas        20x20                   →  bajo el 24x24 de WCAG 2.5.8
     el fondo de campo   lo pintaba la BD, no esto: (0,3,1) pierde contra (1,0,0)

   Mantenerlas además de 8.113 sería tener DOS sitios donde vive la misma decisión, que es
   lo que §5 pide no hacer. La única que sobrevive es la de abajo, y no describe el
   formulario sino su ISLA: `#brxe-sec127` es oscura en los dos temas, así que lo que 8.113
   voltea con el tema tiene que quedarse quieto aquí. Se escribe SOLO lo que la isla no
   cubría ya (bloque del modo claro, arriba en este archivo). */
.cc-form-cierre .cc-form-ausente { color: var(--cc-fg-on-dark); }

/* ==========================================================================
   RED DE ANCHO PARA LAS PÁGINAS DEL DISEÑO ANTERIOR (A-03, 2026-08-10)
   `/agencia-vtex-partner/` desbordaba 332px a 768 y 22px a 390. La auditoría
   localizó los dos orígenes midiendo, y ninguno era el carrusel —su `splide__track`
   ya recorta—: eran dos anchos escritos en PÍXELES que no caben en pantallas
   estrechas.

     · un `.brxe-container` con `width: 1100px`  → 1100 − 768 = los 332px exactos.
       Es el gotcha 8.7 en su forma literal, sobreviviendo en una página que el
       rediseño no tocó.
     · el `h3.title` de un acordeón con `width: 363px` dentro de un padre de 310.

   Se arregla con `max-width` y no tocando el `width`, a propósito: son propiedades
   distintas, así que esto NO pelea especificidad con la regla por id que emite Bricks
   —le ganaría un `#brxe-xxx` cualquiera— y encima es inofensivo donde el ancho ya
   cabe. Y va en CSS versionado y no en el builder porque esas páginas son del diseño
   viejo: el equipo las sigue editando y un cambio suyo en la BD se perdería.

   ⚠️ Importa más desde el 2026-08-10: el mega-menú convirtió Agencia VTEX en uno de
   los siete destinos del header, o sea que ahora se llega a esa página desde las 32.
   ========================================================================== */
/* ⚠️ Dos correcciones que costó llegar a ellas, y las dos importan:

   1. `#brx-content` no es decorativo. Bricks emite `#brxe-jrjcds { max-width: 1133px }`,
      una regla por ID (1,0,0) que le gana a cualquier clase suelta, así que la primera
      versión —`.brxe-container` a secas— no movió ni un píxel. Con el contenedor de
      contenido delante queda (1,1,0) y manda. Es 8.1 otra vez.

   2. Y va acotada a ESTA PÁGINA, no a todo el sitio. La segunda versión aplicaba
      `max-width: 100%` a los containers de todas las páginas, y al ganarle también a los
      max-width legítimos ensanchó un contenedor de la PORTADA: el carrusel de marcas pasó
      a medir 1324px de ventana contra una pista de 1300 y **empezó a mostrar una marca
      dos veces**. Lo cazó `home.mjs` en la misma corrida. Una red de seguridad que se
      aplica donde no hace falta deja de ser una red y pasa a ser un cambio de diseño.
      El id de página es estable entre dumps —los ids vienen de producción, ver el paso 7
      de `post-import.php`—, así que `page-id-7962` identifica a /agencia-vtex-partner/ en
      cualquier entorno. Si mañana otra página legacy desborda, se añade su id aquí. */
body.page-id-7962 #brx-content .brxe-container { max-width: 100%; }
#brx-content .brxe-accordion .accordion-title .title {
  max-width: 100%;
  /* ⚠️ `max-width` NO basta, y por poco se da esto por arreglado: ciñe la CAJA pero no
     el texto. El título "¿Qué integraciones cubren (ERP/OMS/pagos…" no tiene dónde
     partir —las barras no son puntos de corte—, así que su min-content seguía midiendo
     240px en un hueco de 161 y el desborde se mantenía intacto con la caja "correcta".
     Es 8.31, y la lección de instrumento es que medir el ancho del elemento decía que sí
     mientras la página seguía moviéndose de lado: hay que medir el `scrollWidth` del
     documento, que es lo que ve quien la usa. */
  overflow-wrap: anywhere;
}

/* ==========================================================================
   DOS DESBORDES SUELTOS DEL DISEÑO ANTERIOR (A-08, 2026-08-10)
   Acotados por id de página por la lección de A-03: una red de ancho aplicada donde
   no hace falta deja de ser una red y pasa a ser un cambio de diseño. Los ids de
   página vienen de producción y son estables entre dumps (paso 7 de post-import.php).
   ========================================================================== */

/* /auditoria-gratis/ (8316). El primer diagnóstico —"una imagen más ancha que su
   hueco"— era FALSO, y `max-width: 100%` no movió nada: la imagen mide exactamente lo
   que su padre. Lo que la saca de la página es un DESPLAZAMIENTO: lleva
   `position: relative; left: 54%` con `transform: translateX(-50%)`, el truco clásico
   para centrar algo más ancho que su contenedor. Mientras la imagen sobresale por los
   dos lados el 4% de diferencia no se nota; cuando deja de sobresalir —en móvil— ese 4%
   se convierte en 16px de desborde limpio (medido: left=210.59, transform=-195).
   Se anula el desplazamiento solo por debajo de 768, donde ya no hay nada que centrar.
   Arriba se deja intacto: ahí el encuadre es deliberado y funciona. */
body.page-id-8316 #brx-content img { max-width: 100%; height: auto; }
@media (max-width: 767px) {
  body.page-id-8316 #brx-content .brxe-image { position: static; transform: none; }
}

/* /politicas-de-privacidad/ (7904): dos enlaces cuyo texto ES una URL larga. Una URL no
   tiene espacios, así que su min-content es toda la cadena y empuja 49px fuera a 390.
   Va sobre los enlaces y no sobre todo el texto a propósito: `anywhere` en un párrafo
   normal parte palabras que sí tenían dónde cortar y se lee peor. */
body.page-id-7904 #brx-content a { overflow-wrap: anywhere; }

/* ==========================================================================
   EL SUELO DE LA PÁGINA ES DEL TEMA, TAMBIÉN EN LAS PÁGINAS SIN REDISEÑAR
   (2026-08-10, pedido de Ricardo: "el navbar se ve mal en Agencia SEO e Influencer
   Marketing" + "estandarizar esas páginas al tema")

   El header del rediseño es translúcido al 72% con `backdrop-filter`, así que NO tiene
   color propio: toma el de lo que hay detrás. En 7 de las 12 páginas medidas el suelo ya
   es `rgb(10,10,15)` y el header se lee oscuro; en 5 el `body` sigue en BLANCO —herencia
   del diseño anterior— y ahí el mismo header se compone a un gris lavado. No es un fallo
   del header: es que debajo hay otro sitio.

   Medido el 2026-08-10 · suelo blanco en: /servicios/agencia-seo/,
   /servicios/influencer-marketing/, /newsletters/, /ecommerce-hub-2026/, /geo-aeo/.

   Se declara con el TOKEN y no con un hex, así que el modo claro —donde `--cc-bg` es
   papel— sigue funcionando sin una sola excepción. Y va sobre `html` y `body` a la vez
   porque el reparto varía por página: unas pintan en `html` y otras en `body`. */
/* ⚠️ EL SUELO Y EL TEXTO SON UN PAR, Y HASTA EL 2026-08-17 SOLO SE PINTABA EL SUELO.
   La regla de abajo pintó de oscuro las 30 páginas, incluidas las 4 LEGALES —cookies,
   aviso legal, descargo, y el índice de privacidad—, que NO están construidas con
   Bricks: su contenido sale del editor, así que su texto se quedó en el `#363636` por
   defecto del theme padre. Fondo #0A0A0F con texto #363636 es **1.63:1**, o sea la
   política de cookies entera dejó de leerse (56 nodos), y el aviso legal con ella.

   Nadie lo vio durante una semana por la razón de siempre: ninguna suite mira esas
   páginas. `a11y.mjs` recorría 5 y `tema.mjs` las 9 rediseñadas; las legales no están
   en ninguna lista, así que su contraste no falló — no se medía (8.53). Es además un
   fallo que NO se puede ver en producción, porque allí `assets/css/` no existe: vive
   solo en lo que este repo está a punto de desplegar. Gotcha 8.67.

   El arreglo es la otra mitad de la misma decisión, no una excepción: quien fija el
   fondo de la página fija su color de texto. Va con el TOKEN, así que el modo claro
   sigue saliendo gratis. Y hereda, o sea solo alcanza a lo que no declara color
   propio: los elementos de Bricks traen el suyo desde la BD y no se tocan. Medido en
   las 24 páginas: arregla 61 nodos y no empeora ni uno. */
html,
body.wp-singular,
body.home { background-color: var(--cc-bg); color: var(--cc-fg); }

/* Los términos de la auditoría (8596) son la excepción, y por eso van aparte: sí están
   hechos con Bricks y su negro está ESCRITO EN EL BUILDER, o sea vive en la BD y la
   herencia de arriba no lo alcanza. Son exactamente DOS elementos —el `h1` y el bloque
   de texto— y de ellos hereda el resto de la página: 37 nodos a 1.06:1 con dos
   selectores. Se anula desde el theme y no desde el builder por §10 (un arreglo en la
   BD se pierde con el próximo volcado), y queda anotado en la tabla de
   `pendientes.md` de decisiones del builder que el theme está anulando: lo correcto
   es quitar ese color desde Bricks, y mientras no se haga, los dos dicen cosas
   distintas. */
body.page-id-8596 #brxe-uhnihz,
body.page-id-8596 #brxe-vfrafq { color: var(--cc-fg); }
/* Y sus DOS enlaces, que declaran el negro por su cuenta y por tanto tampoco heredan:
   la URL de las bases y el correo de contacto. NO se igualan al texto —eso los dejaría
   indistinguibles, que es WCAG 1.4.1 nivel A y el mismo fallo que el blog ya corrigió
   (`.cc-post__content a[href]`)—: van al token de enlace, que voltea con el tema, y
   conservan el subrayado como distintivo. Los cazó el chequeo nuevo después de arreglar
   el bloque de texto: 2 nodos a 1.06:1 que el arreglo del párrafo no alcanzaba. */
body.page-id-8596 #brxe-vfrafq a[href] {
  color: var(--cc-fg-link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* Dos etiquetas heredadas que quedaban por debajo de AA, destapadas al poner el suelo
   del tema (2026-08-10). Las dos son el MISMO error y el sistema ya tiene su regla:
   sobre un acento claro el texto va en TINTA, no en blanco.
     · /servicios/influencer-marketing/ — blanco sobre el violeta viejo #6C6CFF: 4.02:1
     · /portafolio/ — el filtro activo, blanco sobre el verde #08AA7A: 2.98:1
   No se toca el color de fondo: cambiarlo sería rediseñar la pieza, y estas dos páginas
   no están rediseñadas. Se cambia solo el color del texto, que es lo que falla. */
body.page-id-24 #brx-content .brxe-posts li.active { color: var(--cc-ink-900); }
/* Los botones violetas de /servicios/influencer-marketing/ (8763): blanco sobre #6C6CFF
   da 4.02:1 y el mínimo es 4.5. Se tiñe el BOTÓN y no todos los `strong` de la página —
   la primera versión hizo eso y volteó también los que van sobre fondo oscuro, pasando
   de 4 fallos a 12. Sobre un acento claro, tinta; es la misma regla del sistema. */
body.page-id-8763 #brx-content .bricks-button.bricks-background-primary,
body.page-id-8763 #brxe-fzyonm { color: var(--cc-ink-900); }
/* ⚠️ Y NO `.bricks-button` a secas: la página tiene además un botón sobre fondo
   oscuro, y teñirlo de tinta lo dejaba en 1:1 — cambiar un fallo de 4.02 por uno de
   1.0 es empeorar midiendo mal. Se listan los que llevan el fondo claro. */

/* ==========================================================================
   TEMA ESTÁNDAR EN LAS 5 PÁGINAS QUE NO SE VAN A REDISEÑAR (2026-08-10)
   Pedido de Ricardo. La lista de páginas vive en `ccd_paths_tema_estandar()` y llega
   aquí como la clase `.cc-tema-estandar` en el body — una sola lista, ver 8.42.

   La regla del encargo es "estandarizar, NO rediseñar": se toca la capa de sistema de
   diseño —familia, escala de radios, escala de duraciones, forma de los botones— y NO
   la maqueta, la copia, las imágenes ni el relleno de los componentes. Por eso abajo no
   hay ni un `padding`, ni un `margin`, ni un `width`: nada que mueva una caja de sitio.

   El punto de partida, medido el 2026-08-10 contra la home (que es el tema):
     · 4 de 5 páginas caían a `-apple-system` en parte de su texto
     · agencia-seo: 14 tamaños y 21 interlineados (la home: 12 y 11)
     · radios fuera de escala: 19px, 20px, 25px, 13px, 12.6px, 18.6px
     · duraciones fuera de escala: 0.2s, 0.3s, 0.5s
     · botones: 2 definiciones por página, contra UNA en la home
   ========================================================================== */

/* 1. LA TIPOGRAFÍA. Es lo que más dice "esto es de otro sitio", y el fallo es el mismo
   que ya se documentó para el footer en style.css: el child theme no declaraba familia,
   así que todo elemento de Bricks que no la fijara en sus settings caía al font del
   navegador. Se declara una vez en la raíz y lo que ya dice Montserrat no cambia. */
body.cc-tema-estandar { font-family: var(--cc-font-sans); }

/* 1b. EL SUELO, y SOLO en el 404 y en la búsqueda (2026-08-26, gotcha 8.105).
   En las páginas de la lista el fondo oscuro ya lo pinta Bricks desde sus estilos
   globales, así que aquí no hacía falta y por eso no estaba. El 404 y la búsqueda
   NO son páginas —son estados de la consulta—, ese estilo global no las alcanza y
   se quedaban con el `body` en blanco sobre el `html` de #0A0A0F: se veía una banda
   gris arriba, un bloque claro en medio y el pie negro debajo, en la misma pantalla.
   Se pintan con los TOKENS y no con el hex, que es lo que hace que el modo claro
   siga funcionando aquí sin una segunda regla: `--cc-bg` y `--cc-fg` ya se voltean.
   Va acotado con `:is()` a esos dos estados para no tocar el resto de la lista. */
body.cc-tema-estandar:is(.error404, .search) {
  background-color: var(--cc-bg);
  color: var(--cc-fg);
}

/* 1c. LA BÚSQUEDA SIN RESULTADOS, que era un callejón sin salida (2026-08-26, 8.105).
   Bricks pinta ahí un `<h3>No se encontró nada.</h3>` y nada más: `#brx-content` medía
   67,8px, sin un enlace ni un campo. Y es justo donde lleva el buscador del 404 —la
   única salida del 404—, así que quien se perdía terminaba en una página vacía.
   El bloque lo emite `search.php` del child theme; esto lo viste. Todo sale de los
   tokens, así que el modo claro se voltea solo y no hace falta una segunda regla. */
.cc-sin-resultados {
  max-width: 720px;
  margin-inline: auto;
  padding-block: var(--cc-space-11) var(--cc-space-12);
  /* A 390px `#brx-content` no trae relleno propio (medido: 0px), así que sin esto el
     campo de búsqueda va de borde a borde de la pantalla. */
  padding-inline: var(--cc-space-5);
  text-align: center;
}
.cc-sin-resultados__titulo {
  font-family: var(--cc-font-display);
  font-size: var(--cc-fs-d3);
  font-weight: var(--cc-fw-bold);
  line-height: var(--cc-lh-snug);
  letter-spacing: var(--cc-tracking-tight);
  color: var(--cc-fg-strong);
  margin: 0 0 var(--cc-space-5);
  /* El término lo escribe el visitante: una palabra larga sin espacios no puede
     empujar la caja y sacar la página de cuadro (8.7). */
  overflow-wrap: anywhere;
}
.cc-sin-resultados__texto {
  font-size: var(--cc-fs-lead);
  line-height: var(--cc-lh-lead);
  color: var(--cc-fg-read);
  margin: 0 0 var(--cc-space-8);
}
.cc-sin-resultados__form {
  display: flex;
  /* ⚠️ `width:100%` NO es decorativo: `.brxe-container` es un flex column con
     `align-items:center`, así que sin esto el formulario se encoge a su contenido y
     queda descentrado respecto del titular. Es 8.101 tal cual: el ancho no lo decide
     el `max-width` del padre, lo decide el flex. Medido: 422px de 720. */
  width: 100%;
  gap: var(--cc-space-3);
  /* Se envuelve en vez de encogerse: a 320px un campo y un botón en la misma fila
     dejan el campo en ~140px, que no da para leer lo que uno escribió. */
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: var(--cc-space-9);
}
.cc-sin-resultados__form input[type="search"] {
  flex: 1 1 260px;
  min-width: 0;
  padding: var(--cc-space-3) var(--cc-space-5);
  font-family: inherit;
  font-size: var(--cc-fs-base);
  color: var(--cc-fg);
  background-color: transparent;
  border: 1px solid var(--cc-border);
  border-radius: var(--cc-radius-pill);
  transition: border-color var(--cc-dur-fast) var(--cc-ease);
}
.cc-sin-resultados__form input[type="search"]::placeholder { color: var(--cc-fg-muted); }
.cc-sin-resultados__form input[type="search"]:focus-visible {
  outline: 2px solid var(--cc-fg-accent);
  outline-offset: 2px;
  border-color: var(--cc-fg-accent);
}
.cc-sin-resultados__form button {
  /* 44x44 mínimo por WCAG 2.5.8: el relleno vertical sale de ahí, no del gusto. */
  min-height: 44px;
  padding: var(--cc-space-3) var(--cc-space-7);
  font-family: inherit;
  font-size: var(--cc-fs-sm);
  font-weight: var(--cc-fw-semibold);
  color: var(--cc-ink-900);
  background-color: var(--cc-fg-accent);
  border: 0;
  border-radius: var(--cc-radius-pill);
  cursor: pointer;
  transition: background-color var(--cc-dur-fast) var(--cc-ease);
}
.cc-sin-resultados__form button:hover { background-color: var(--cc-green-700, #08AA7A); }
.cc-sin-resultados__salidas {
  display: flex;
  /* Mismo motivo que el formulario (8.101): sin ancho propio se encoge a su contenido
     y `justify-content:center` no centra nada. Medido a 390px: 299px pegados a la
     izquierda, con el titular centrado justo encima. */
  width: 100%;
  flex-wrap: wrap;
  gap: var(--cc-space-3) var(--cc-space-6);
  justify-content: center;
}
.cc-sin-resultados__salidas a {
  /* Subrayado, no solo color: WCAG 1.4.1, la misma regla que los enlaces en prosa. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--cc-space-2);
  font-size: var(--cc-fs-base);
  font-weight: var(--cc-fw-medium);
  color: var(--cc-fg-link);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  transition: text-decoration-thickness var(--cc-dur-fast) var(--cc-ease);
}
.cc-sin-resultados__salidas a:hover { text-decoration-thickness: 2px; }

/* 2. LOS RADIOS, a la escala de cc-tokens.css. La diferencia entre 19 y 18 no la ve
   nadie de a uno; lo que sí se ve es un sitio donde cada componente redondea distinto.
   Se mapea al valor de la escala más cercano, sin inventar ninguno nuevo. */
body.cc-tema-estandar #brx-content :is(.bricks-button, .brxe-button) {
  border-radius: var(--cc-radius-pill, 999px);
}
body.cc-tema-estandar #brx-content :is(.brxe-image, .brxe-block, .brxe-div, img) {
  border-radius: inherit;
}

/* 3. LOS BOTONES. La home tiene UNA definición y estas páginas dos cada una, con
   tamaños de 17.25px que no existen en ninguna escala. Se unifican forma, tamaño y peso
   —píldora, 15px, 700, la misma transición del sistema— y NO el relleno: cambiarlo
   movería las cajas, que es la línea que este bloque no cruza.
   ⚠️ El COLOR se deja como está a propósito. En estas páginas el fondo de cada botón
   viene de una regla por id, así que el CSS no puede distinguir el CTA sólido del
   fantasma, y pintarlos todos del acento convertiría en llamada principal a cuatro
   fichas de un selector. Es una decisión de jerarquía y la toma el equipo. */
body.cc-tema-estandar #brx-content :is(.bricks-button, .brxe-button) {
  font-family: var(--cc-font-sans);
  font-size: 15px;
  font-weight: 700;
  transition-duration: 0.18s;
}

/* 4. LAS DURACIONES a la escala (0.1 · 0.18 · 0.24 · 0.4 · 0.56). Las tres de fuera
   —0.2s, 0.3s, 0.5s— son indistinguibles a ojo de su vecina de la escala, así que
   alinearlas no cambia nada de lo que se ve y sí quita tres valores del sistema. */
/* `*` y no `[class*="brxe-"]`: en /equipo/ las duraciones de 0.5s y 0.3s viven en
   elementos internos sin clase de Bricks, así que el selector acotado las dejaba fuera.
   Aquí es seguro barrer: estas páginas no participan del sistema de movimiento —ese va
   por `ccd_pieza_con_movimiento()`— o sea que no hay ninguna duración deliberada que
   pisar, solo las que nadie eligió. */
body.cc-tema-estandar #brx-content * { transition-duration: 0.24s; }
body.cc-tema-estandar #brx-content :is(a, button, .bricks-button) { transition-duration: 0.18s; }

/* ==========================================================================
   NOSOTROS Y PORTAFOLIO: LA REJILLA SE QUEDA, LA TARJETA PASA A SER LA DEL TEMA
   (2026-08-10, pedido de Ricardo: "que se vean similar a la original, me gusta así")

   La composición NO se toca: mismas fotos, mismo orden, mismas columnas, misma copia.
   Lo que cambia es el vestido de cada tarjeta, que pasa a ser el mismo de las 4 hijas
   de /servicios/ — y los valores no se eligen aquí, se copian de esa pieza, que es la
   definición canónica: fondo `rgba(255,255,255,.06)`, radio 18px, borde de 1px, y la
   imagen recortada a la caja en vez de deformada.

   ⚠️ Va en CSS y NO versionando el JSON de estas dos páginas, a propósito. Versionarlas
   dejaría al equipo sin poder editarlas en el builder (§5: el JSON manda sobre la BD, y
   `post-import.sh` revierte lo que se toque). Estas dos se editan a menudo —el equipo
   entra, cambia una foto— y ese costo no compensa cuando el resultado se consigue con
   una hoja de estilo. Si algún día se rediseñan de verdad, entonces sí se versionan.
   ========================================================================== */

/* La tarjeta, idéntica en las dos páginas: una definición, no dos (8.40).
   ⚠️ SIN FONDO Y SIN CONTORNO, y esto costó tres rondas de "se ve mal el borde".
   La primera versión copiaba entera la tarjeta de las 4 hijas de /servicios/ —fondo
   `rgba(255,255,255,.06)` y filete de 1px—, y ahí la tarjeta es una CAJA DE TEXTO: el
   fondo separa el texto de la página y el filete la delimita. Aquí no: aquí la tarjeta
   es una FOTO a sangre. Un filete de 1px sobre una foto no se lee como borde, se lee
   como una raya; y un fondo bajo una imagen que ocupa todo solo asoma cuando algo no
   encaja — que es exactamente de dónde salían las muescas y las franjas que fuimos
   persiguiendo una por una.
   La lección, y es de diseño y no de CSS: **copiar un componente no es copiar sus
   valores, es copiar la decisión que los produjo.** El mismo sistema pide cosas
   distintas para una caja de texto y para una imagen a sangre. Del tema se queda lo que
   suma sin encajonar: el radio, el recorte de la foto y la elevación al pasar el
   puntero. */
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item) {
  border-radius: var(--cc-radius-lg);
  overflow: hidden;                  /* la imagen se recorta al radio, no lo desborda */
  /* `box-shadow` y no `transform`: la elevación dejó de ser geométrica el 2026-08-18
     (8.79). La duración sigue en la escala, que es lo que exige `ux.mjs`. */
  transition: box-shadow 0.24s;
}
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item):hover {
  /* ⚠️ `!important` y no más especificidad, que es lo que se intentó primero. Bricks
     emite sobre estas fichas un `transform` propio —la matriz identidad de su animación
     de entrada, con `transition: transform .5s`— y una declaración con `!important` no
     se gana subiendo la especificidad: se gana con otra `!important`. Medido el
     2026-08-17: sin esto, `/equipo/` se quedaba en `matrix(1,0,0,1,0,0)` al pasar el
     puntero mientras `/portafolio/` sí elevaba, con LA MISMA regla — la diferencia era
     que allí Bricks no escribe transform.
     `pendientes.md` lo dejó abierto desde el 2026-08-15 diciendo que arreglarlo «pedía
     volver a medir las dos páginas». Remedidas: /equipo/ pasa a -4px y /portafolio/
     sigue en -4px, o sea la regla no le quita nada a la que ya funcionaba. */
  /* ── Por qué NO es `translateY(-4px)` (2026-08-18) ───────────────────────────
     Ricardo: *«en portafolios al pasar el mouse delante de algunos parpadea y hace la
     animación de bajar muy rápido, estilo bug»*. Y era literal.

     **La firma, medida:** con el puntero cerca del BORDE INFERIOR de una tarjeta, la
     secuencia de estado es `H____.HH____` — entra el hover, la tarjeta sube 4px, el
     cursor queda en el hueco que acaba de dejar, salta `mouseleave`, la tarjeta baja,
     el cursor vuelve a estar dentro, y otra vez. En el centro de la tarjeta la misma
     sonda da 45/45 estable: **el bug solo existe donde el movimiento cruza el puntero.**
     Y lo que se VE no es la tarjeta temblando —son 4px— sino el rótulo que se funde
     sobre la imagen reiniciando su transición en bucle, que es lo que Ricardo grabó.

     **Contraprueba:** anulando el `transform` en el navegador, 45/45 estable. Es la
     elevación, no Isotope ni el rótulo.

     ⚠️ **Y la salida obvia NO funciona acá:** extender el área de toque 4px hacia abajo
     con un `::after` —el patrón que este repo ya usa en `.cc-dot` y en el nav— queda
     RECORTADO, porque Isotope escribe `overflow: hidden` sobre la tarjeta. Medido: sigue
     parpadeando (3 cambios). Pelear con lo que escribe Isotope es 8.64.

     **Lo que sí funciona es que la caja nunca ENCOJA por ningún lado.** Con
     `transform-origin: bottom center` la tarjeta crece hacia arriba ~4px —el mismo gesto
     y la misma magnitud que antes— y el borde inferior se queda clavado, así que el
     puntero no puede quedarse fuera. Los lados crecen 3,5px dentro de un canalón de
     30px, y hacia arriba no hay nada que invadir. Medido: **45/45 estable en el borde
     inferior, el superior y el centro**, y en las dos páginas que usan esta regla.
     Escala uniforme y no `scaleY`, que estiraría las fotos un 1,3%.
     Gotcha **8.79**. */
  /* ⚠️ NINGÚN MOVIMIENTO, y esta es la tercera versión (2026-08-18). Las dos anteriores
     fallaron por la misma razón de fondo, que es la que hay que recordar:

       v1 `translateY(-4px)`  — la ficha se quitaba de debajo del cursor en el borde
                                inferior. Bucle.
       v2 `scale()` con origen abajo — arreglaba ESE borde, pero al FILTRAR el `:hover`
                                oscila igual (7 cambios medidos, y es inevitable: las
                                fichas se mueven bajo un cursor quieto). Ricardo lo vio:
                                *«hace una animación de irse para abajo repetitivamente
                                y sin texto»* — el «sin texto» era el retardo funcionando
                                y el movimiento, esta regla.

     **Mientras el hover MUEVA algo, la oscilación se ve.** Y la oscilación no se puede
     eliminar. Así que la señal de elevación no puede ser geométrica: es una SOMBRA, que
     además es lo que «elevación» significa. La caja no se mueve ni un píxel, así que da
     igual cuántas veces entre y salga el estado. */
  box-shadow: 0 12px 32px rgba(0, 0, 0, .5) !important;
  /* ── La segunda mitad de 8.79: el filtro ──────────────────────────────────
     Ricardo, después: *«al parecer pasa cuando pongo un filtro»*. Y es un mecanismo
     DISTINTO del de arriba, con su propia medida: al filtrar, Isotope recoloca las
     tarjetas y estas se deslizan **bajo un cursor quieto**, así que `mouseenter` y
     `mouseleave` se disparan solos. Medido pasando el ratón a distintos momentos:

       ·   50ms tras filtrar (Isotope moviendo) -> `.HH.HH.H…`  6 cambios
       ·  250ms (a media animación)             -> `HH.H___HH…` 4 cambios
       · 1200ms (ya asentado)                   -> 45/45 estable, 0 cambios

     Eso NO se arregla con geometría: que una caja que se mueve cruce un puntero quieto
     es inevitable, y lo hace cualquier rejilla filtrable. Lo que se puede evitar es el
     ARTEFACTO, que es lo que se ve: el rótulo `.overlay-inner` de Bricks entrando y
     saliendo a `opacity .24s` en bucle — el «parpadea y baja muy rápido» del vídeo.

     ⚠️ Y no hay estado al que engancharse: **Isotope no marca la animación en el DOM**
     —medido: ni clase ni atributo cambian en el wrapper ni en los items durante los
     1200ms—, así que un `pointer-events: none` mientras dura exigiría JavaScript propio
     escuchando sus eventos internos. No hace falta.

     La solución es INTENCIÓN DE HOVER: la entrada espera 100ms; la salida, no. Un hover
     espurio de la recolocación dura ~80ms y no llega a arrancar nada, mientras que uno
     de verdad se retrasa un valor imperceptible. El retardo va en la regla `:hover`
     —o sea gobierna la transición HACIA el estado— y el reposo lo deja en 0, que es
     como se consigue asimetría de entrada y salida con dos declaraciones. */
  transition-delay: var(--cc-dur-instant);
}

/* La salida, inmediata: sin esto el retardo también frenaría el volver a su sitio. */
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item) {
  transition-delay: 0s;
}
/* El rótulo de Bricks, el mismo trato: es LO QUE SE VE parpadear. */
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item) .overlay-inner {
  transition-delay: 0s;
}
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item):hover .overlay-inner {
  transition-delay: var(--cc-dur-instant);
}

/* La imagen, a sangre dentro de la tarjeta. `object-fit: cover` es el arreglo real: hoy
   /equipo/ sirve las 25 fotos con `fill`, o sea ESTIRADAS a la caja — las caras salen
   deformadas y eso sí se nota, aunque nadie lo hubiera nombrado. */
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item) img {
  display: block;
  width: 100%;
  object-fit: cover;
  border-radius: 0;                  /* el radio lo pone la tarjeta, no la imagen */
}

/* El texto de la tarjeta respira como en el rediseño: el relleno va DENTRO, no en la
   imagen, que por eso queda a sangre. */
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item) > :not(img):not(picture):not(a) {
  padding-left: 18px;
  padding-right: 18px;
}
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item) > :last-child:not(img) {
  padding-bottom: 18px;
}

/* ── Portafolio: el filtro y el icono de las tarjetas (2026-08-10) ───────────
   Reportado por Ricardo: *"moderniza las categorías/filtro"* y *"las tarjetas tienen
   como un ojo, pero se ve mal en la mayoría"*. */

/* EL "OJO". Es un SVG de 300×150 con `fill: #000` que el bucle pinta IGUAL en las 28
   tarjetas —no es el logo del proyecto, es decoración— y vive dentro del panel que
   aparece al pasar el puntero, o sea negro sobre un velo oscuro: un borrón que tapa la
   foto y no aporta nada. Se retira en vez de recolorearlo: un icono genérico repetido 28
   veces no gana nada por ser visible, y el panel ya dice lo que hace falta con el título
   y la bajada. El campo sigue en el bucle, así que si mañana se quiere un icono POR
   proyecto, el sitio donde ponerlo ya existe. */
body.cc-tema-estandar #brx-content .brxe-posts .dynamic > svg { display: none; }

/* EL FILTRO. Tenía borde de 3px, radio de 25px y 16px de texto: tres valores que no
   existen en ninguna escala del sistema. Pasa al lenguaje de los chips del rediseño —
   píldora, borde de 1px, 15px— y el activo toma el acento de marca en vez del verde
   viejo `#08AA7A`, con tinta encima, que es la regla del sistema sobre acento claro. */
/* ⚠️ El selector es `.bricks-isotope-filters li` y NO `.brxe-posts li:not(.repeater-item)`,
   y la diferencia tumbó la rejilla entera. Isotope mete DENTRO de su contenedor unos `li`
   de ayuda —invisibles— con los que calcula el ancho de columna; la primera versión los
   cazaba y les ponía relleno y borde, así que la columna medida creció y las 28 tarjetas
   colapsaron a UNA sola columna, apiladas en x=170. Medido: sin este bloque vuelven a
   x=170 y x=735.
   La regla que queda: en una rejilla posicionada por JS, un selector amplio no solo pinta
   de más — puede reprogramar la geometría. */
body.cc-tema-estandar #brx-content .bricks-isotope-filters li {
  border: 1px solid var(--cc-border-on-dark);
  border-radius: var(--cc-radius-pill, 999px);
  padding: 9px 18px;
  font-size: 15px;
  font-weight: 600;
  color: var(--cc-fg-read);
  background: transparent;
  transition: background-color 0.18s, border-color 0.18s, color 0.18s;
}
body.cc-tema-estandar #brx-content .bricks-isotope-filters li:hover {
  border-color: var(--cc-fg-accent);
  color: var(--cc-fg-accent);
}
body.cc-tema-estandar #brx-content .bricks-isotope-filters li.active {
  background: var(--cc-fg-accent);
  border-color: var(--cc-fg-accent);
  color: var(--cc-ink-900);
}

/* EL SALTO AL FILTRAR (2026-08-15, reportado por Ricardo: «los portafolios saltan»).
   Medido en el navegador, la causa no era la que parecía. Isotope **fija la altura del
   contenedor en UN fotograma** y anima las tarjetas después:

     t=  8 ms  alto de la grilla 4633 -> 1324  (el pie sube 3.309px de golpe)
     t=362 ms  terminan de desvanecerse las 20 que se van
     t=670 ms  las 8 que quedan acaban de colocarse

   O sea hay **650ms en que el pie ya subió y las tarjetas todavía vuelan**. Eso es lo que
   se ve como un tirón: no lo dan las tarjetas, lo da todo lo que hay DEBAJO de ellas.
   Y si estabas más abajo del nuevo alto máximo, el navegador además te arrastra al hacer
   clamp del scroll (medido: 1800 -> 1327).

   Y hay un SEGUNDO defecto encima, que Ricardo describió aparte —«como que las tarjetas
   hacen doble animación»— y que la misma traza ya contenía:

     hasta t=344 ms  cambian opacidad Y posición  (las 20 que se van, desvaneciéndose)
     de  t=368 a 660 cambia SOLO la posición      (las 7 que quedan, aún viajando)

   O sea 316ms de cola en que ya no se desvanece nada y todavía hay tarjetas moviéndose:
   eso es el segundo gesto. No lo hace Isotope por su cuenta —lo alarga la transición de
   `transform` que esta misma hoja le pone a `.repeater-item` unas líneas más arriba, para
   la elevación al pasar el puntero—. Comprobado: anulando esa transición, todo el
   movimiento se resuelve en 12ms.

   LA CURA, Y ES UNA SOLA DECISIÓN PARA LOS DOS DEFECTOS: que todo termine a la vez.
   Medido sobre cuatro combinaciones de valores del sistema —los tokens, no valores
   inventados, que `ux.mjs` tiene un trinquete sobre eso—:

     tarjetas | altura | cola  | desfase | mayor salto de altura por fotograma
     --------- -------- ------- --------- ------------------------------------
      (nada)   (nada)    282ms    —        3.309 px   <- el estado que se reportó
       100ms    240ms     29ms     45ms      617 px
       100ms    400ms     49ms    200ms      372 px
       180ms    400ms    133ms     33ms      374 px   <- elegida
       240ms    560ms    282ms    137ms      266 px

   ⚠️ El compromiso es real y conviene tenerlo escrito: 3.309px no se pueden recorrer
   rápido Y suave a la vez. Cuanto más corta la transición de altura, mayor el tirón por
   fotograma. Se elige `fast`+`slow` porque **las tarjetas y la página terminan juntas**
   (33ms de desfase): mientras queda cola de movimiento la altura sigue viajando, así que
   no se lee como dos gestos sino como uno que acaba. Las variantes de 100ms cortan más la
   cola pero dejan la página moviéndose 200ms después de que las tarjetas ya pararon, que
   es cambiar un desajuste por otro.

   ⚠️ Se apunta SOLO al contenedor y a sus hijos DIRECTOS: el bloque de arriba documenta
   que un selector amplio dentro de una rejilla posicionada por JS le reprograma la
   geometría.
   ⚠️ Y no anima al CARGAR la página —comprobado midiendo el alto durante los primeros
   1,5s—: en la primera maquetación la altura se fija antes de que esta hoja aplique.
   Gotcha 8.64, con sus chequeos en `ux.mjs`. */
body.cc-tema-estandar #brx-content ul.bricks-layout-wrapper.isotope {
  transition: height var(--cc-dur-slow) var(--cc-ease);
}
/* ⚠️ LA POSICIÓN NO SE ANIMA, Y ESTO ES LO QUE DE VERDAD SE VEÍA MAL.
   Acortar la animación no bastó: Ricardo seguía viéndolo después de bajarla a 180ms. Al
   mirar la secuencia fotograma a fotograma —en vez de fiarse de los tiempos— aparece el
   motivo, y no es la duración: **mientras las tarjetas viajan a su nueva casilla se
   SOLAPAN unas con otras**. Las que se van todavía son opacas y se cruzan con las que
   llegan, así que a mitad del gesto la rejilla es un revoltijo. Medido, contando pares de
   tarjetas visibles cuyas cajas se cortan:

     posición animada 180ms  ->  pico de 11 PARES solapados, solape presente de 27 a 326ms
     solo desvanecido        ->  0 pares, en ningún fotograma

   Por eso aquí solo se transiciona `opacity`. Las tarjetas aparecen ya en su sitio y se
   desvanecen; la altura del contenedor sigue viajando, que es lo que evita el tirón. Una
   rejilla filtrada no necesita que sus piezas viajen: necesita que el cambio se entienda.

   ⚠️ Lección de método, y es la de 8.15 otra vez: **la métrica que se eligió primero
   —cuánto dura cada fase— daba «arreglado» mientras el defecto seguía ahí.** El defecto no
   era temporal, era espacial. Cuando alguien insiste en que sigue viéndolo, lo que hay que
   cambiar es lo que se mide, no el umbral.

   ⚠️ `!important` porque Isotope escribe `transition-duration` INLINE mientras anima, y una
   hoja no le gana a un estilo inline. Es 8.1 en su versión de JavaScript. */
body.cc-tema-estandar #brx-content ul.bricks-layout-wrapper.isotope > li {
  transition: opacity var(--cc-dur-fast) linear !important;
}
/* La elevación al pasar el puntero se conserva declarando su transición EN el `:hover`:
   entra suave y sale seca. Si estuviera en la regla base volvería a animar `transform` y
   con ella el reacomodo de Isotope — que es justo lo que acabamos de quitar. */
body.cc-tema-estandar #brx-content ul.bricks-layout-wrapper.isotope > li:hover {
  transition: opacity var(--cc-dur-fast) linear,
              transform var(--cc-dur-fast) var(--cc-ease) !important;
}
@media (prefers-reduced-motion: reduce) {
  /* Sin transición se vuelve al cambio instantáneo, y es lo correcto: quien pide menos
     movimiento prefiere un salto brusco a una animación de medio segundo (8.24). */
  body.cc-tema-estandar #brx-content ul.bricks-layout-wrapper.isotope,
  body.cc-tema-estandar #brx-content ul.bricks-layout-wrapper.isotope > li { transition: none; }
}

/* Las fichas de /equipo/: nombre y cargo alineados igual. Medido, el desajuste no era una
   preferencia sino una incoherencia dentro de la MISMA ficha: el nombre es una caja de
   ancho automático que el flex centra, con el texto a la izquierda dentro —así que los
   nombres cortos PARECEN centrados (Dayana Verde a 41px del borde) y los largos no
   (Mauricio Dinamarca a 18px)— y el cargo es una caja de ancho fijo con el texto
   centrado. Dos criterios en dos líneas que se leen juntas.
   Se unifican a la IZQUIERDA porque es lo que hace el resto del sitio: las tarjetas de
   /servicios/ y los propios títulos de área de esta página ("Gerencia", "Finanzas"). */
/* ⚠️ `.card p` y no `.card > .brxe-div > p`: el cargo de varias fichas cuelga un nivel
   más abajo, así que el selector de hijo directo dejaba tres alineaciones distintas
   donde el objetivo era una. */
body.cc-tema-estandar #brx-content .brxe-div.card p {
  width: 100%;
  text-align: left;
}
/* Y un respiro entre la foto y el nombre: sin él el texto arranca pegado al borde de la
   imagen, que es lo que más se notaba en la primera ficha. */
body.cc-tema-estandar #brx-content .brxe-div.card > .brxe-div { padding-top: 14px; }
/* ⚠️ Y los CONTENEDORES del texto, no solo los `p`. El nombre vive en una caja de ancho
   automático que el flex de la ficha centra, así que ponerle `text-align: left` al
   párrafo no movía nada: el texto ya estaba a la izquierda DE SU CAJA, y la caja estaba
   centrada. Medir el desplazamiento contra el padre lo ocultaba —daba 0— y solo se ve
   midiendo contra la ficha. Es 8.15: la referencia equivocada convierte un fallo en un
   número correcto. */
body.cc-tema-estandar #brx-content .brxe-div.card > .brxe-div,
body.cc-tema-estandar #brx-content .brxe-div.card > .brxe-div > * {
  width: 100%;
  align-items: flex-start;
}

/* ⚠️ 8.33 POR CUARTA VEZ, y esta la vio Ricardo antes que ninguna prueba: las fichas de
   /equipo/ salían ESCALONADAS. El contenedor de cada área es un flex con
   `align-items: flex-start`, así que cada ficha toma la altura de su propia foto — y las
   fotos vienen en dos proporciones (1.07 y 1.00), o sea tarjetas de 257 y 270px con sus
   tops en 2488, 2489, 2491 y 2495. Seis fichas en fila y ninguna alineada con su vecina.

   Se arregla por donde corresponde y no con un alto fijo a ojo:
     · la fila ESTIRA a sus hijas (`align-items: stretch`), que es lo que 8.33 pide
       re-declarar siempre que se hereda un eje de Bricks;
     · y la foto adopta UNA proporción para todas, así el recorte lo decide el sistema y
       no el archivo que subió cada quien. Cuadrada porque es la que ya tienen: 1.00 y
       1.07 medidas, o sea nadie va a notar el ajuste y todas quedan iguales.
   Es la misma exigencia que `servicios.mjs` ya le hace a las tarjetas del rediseño: no
   basta con que compartan definición, tienen que ocupar la misma caja. */
body.cc-tema-estandar #brx-content .brxe-div.card { height: 100%; }
body.cc-tema-estandar #brx-content .brxe-block:has(> .brxe-div.card),
body.cc-tema-estandar #brx-content .brxe-div.card { align-self: stretch; }
body.cc-tema-estandar #brx-content .brxe-div:has(> .brxe-block > .brxe-div.card),
body.cc-tema-estandar #brx-content .brxe-block:has(> .brxe-div.card) { align-items: stretch; }
/* ⚠️ Y el ANCHO fijo, que es la mitad que faltaba. La ficha no declara ancho, así que
   toma el natural de su foto: medidas 7 anchuras distintas —200, 196, 193, 172, 171, 165
   y 154— con sus altos detrás. `aspect-ratio` por sí solo no lo arregla: fija la
   proporción, no el tamaño.
   ⚠️⚠️ Y la lección de instrumento, que es la cara: la primera medición dijo "25 fichas,
   todas de 200×200, uniforme" y era MENTIRA — se tomó antes de que las imágenes
   decodificaran, cuando todas ocupaban el mismo hueco reservado. Ricardo lo vio en
   pantalla mientras la prueba decía que estaba bien. Para medir una rejilla que depende
   del tamaño de sus imágenes hay que esperar a `img.decode()`, no a `networkidle` (8.15). */
body.cc-tema-estandar #brx-content .brxe-div.card { width: 200px; }
body.cc-tema-estandar #brx-content .brxe-div.card > img {
  width: 100%;
  aspect-ratio: 1 / 1;
  height: auto;
  object-fit: cover;
}
/* ⚠️ El bloque de texto NO lleva radio propio. Traía 18px en las cuatro esquinas, así que
   sus esquinas SUPERIORES se redondeaban justo donde topa con la foto y dejaban dos
   muescas —una a cada lado— con el fondo de la tarjeta asomando: es el "se ve mal el
   borde en ambos lados" que reportó Ricardo. Medido, los dos hijos llegan a los bordes
   (izq=+0, der=-0): el defecto no era una sangría sino un radio de más.
   La tarjeta ya recorta con `overflow: hidden`, o sea que el redondeo exterior está
   resuelto y cualquier radio en el hijo solo puede restar. */
body.cc-tema-estandar #brx-content :is(.brxe-div.card, .brxe-posts .repeater-item) > * {
  border-radius: 0;
}
/* ⚠️ La franja muerta del pie. En /portafolio/ la tarjeta mide 319px y la imagen 301: los
   18 que sobran son el panel de título, que va `visibility: hidden` hasta el hover pero
   SIGUE OCUPANDO SITIO. En las 28 tarjetas quedaba una banda oscura al pie que se lee
   como un borde mal rematado —es lo que reportó Ricardo— y que además rompe la
   proporción: la foto ya no llena su caja.
   Se saca del flujo con `position: absolute`, que es lo que el panel quería ser desde el
   principio: aparece ENCIMA al pasar el puntero y no reserva nada mientras no se ve. Con
   eso la imagen ocupa la tarjeta entera y el borde vuelve a ser un rectángulo limpio. */
/* Medido: todo el contenido de la tarjeta acaba en 301px y la tarjeta mide 319. Los 18
   que sobran no son un panel oculto —esa fue mi primera hipótesis y no movió nada— sino
   `padding-bottom: 18px` en el propio enlace. Con la tarjeta sin fondo no se veía; con
   fondo y borde se convierte en una banda muerta al pie de las 28. */
/* ⚠️ Con `!important`, y no por pereza. La regla que pone ese relleno la genera Bricks
   para el elemento y gana incluso a un selector de (1,3,2) como el de aquí abajo —se
   probó y el computado seguía en `0px 0px 18px`—. La alternativa era encadenar el id
   generado del bucle (`#brxe-vxzbum`), que cambia si alguien rehace la sección en el
   builder: más frágil y más difícil de encontrar dentro de seis meses. */
body.cc-tema-estandar #brx-content .brxe-posts .repeater-item > a { padding-bottom: 0 !important; }


/* ==========================================================================
   TEXTO SOBRE LA BANDA VIOLETA (2026-08-13)
   Las dos bandas violetas —el giro y los números— son islas oscuras, pero NO son
   la misma superficie que las otras islas del sitio, y eso rompe la suposición que
   el bloque de islas venía haciendo. El cierre, el footer y los velos de carrusel
   son casi negros (#0a0a0f); el violeta es `#4B2FBF`, bastante más claro. El mismo
   token da resultados opuestos:

     --cc-ink-meta (#898989)   sobre #0a0a0f → 5.4:1  ✅
     --cc-ink-meta (#898989)   sobre #4B2FBF → 2.44:1 ❌

   Saltó al fusionar Respaldo dentro de Métricas: las píldoras de trayectoria vivían
   sobre el suelo oscuro y pasaron al violeta con sus colores intactos. En oscuro se
   arregló subiendo el alfa en `#brxe-sec061`, pero en CLARO la regla de islas gana
   por especificidad (1,1,1 contra 1,0,0) y volvía a bajarlos. De ahí que `a11y.mjs`
   pasara y `tema.mjs` no: **el mismo fallo tapado en un tema y visible en el otro**,
   que es exactamente lo que 8.24 dice que hay que medir en los dos.

   Va con la MISMA especificidad que la regla de islas y DESPUÉS en el archivo, que es
   lo que decide un empate. Y cubre las dos bandas, no solo la que falló: la otra
   (el giro) tiene hoy poco texto atenuado y mañana puede tenerlo.
   ========================================================================== */
:is(#brxe-sec035, #brxe-sec061),
html[data-theme="light"] :is(#brxe-sec035, #brxe-sec061) {
  --cc-fg-muted: rgba(255, 255, 255, 0.86);
  --cc-fg-read:  rgba(255, 255, 255, 0.94);
}

/* ⚠️ Y un color de FONDO sólido bajo el degradado de la banda del giro. No es
   decorativo y no lo pinta nadie: el degradado es un `background-image`, así que
   cualquiera que suba por el DOM buscando un fondo sólido —el medidor de contraste
   de las suites, un lector, un navegador que no pinte el degradado— no encuentra
   ninguno y cae al fondo de PÁGINA. En claro eso daba "texto claro sobre fondo
   claro, 1.7:1" para una bajada que en pantalla está a ~5:1 sobre violeta: un fallo
   inventado por el instrumento.
   La salida no es enseñarle al medidor a leer degradados sino no dejar el hueco:
   un degradado debería declarar siempre su color de base. */
body #brxe-sec035.cc-banda-violeta { background-color: var(--cc-violet-600); }

/* =============================================================================
   MAQUETA DEL FORMULARIO — solo local (2026-08-15)
   =============================================================================
   Acompaña a `ccd_form_maqueta()` en `functions.php`. Existe para poder DISEÑAR y MEDIR
   `/contacto/` en local: sin ella el formulario ocupa 1.374px contra los 1.067 que mide la
   página en producción, y cualquier decisión de layout se toma sobre una página que no
   existe. Es lo que llevó a la auditoría a apuntar «la mitad vacíos» (ver la corrección en
   `pendientes.md`).

   ⚠️ Todo cuelga de `.cc-form-maqueta`, una clase que **solo emite la maqueta**. En
   producción Gravity Forms registra su propio shortcode, la maqueta no llega a existir y
   estas reglas no encuentran a nadie: no pueden filtrarse. Es la misma guarda de dos capas
   que ya tiene el PHP, pero en CSS.

   No pretende ser el formulario final: pretende ocupar lo que ocupa el real, para que las
   medidas de la página signifiquen algo. ========================================= */
.cc-form-maqueta { --mq-linea: color-mix(in srgb, var(--cc-fg-read) 28%, transparent); }
.cc-form-maqueta .cc-form-ausente {
  border: 1px dashed var(--mq-linea);
  border-radius: var(--cc-radius-md);
  padding: 10px 14px;
  margin: 0 0 var(--cc-space-6);
  font-size: 13px;
  /* ⚠️ Era `--cc-fg-muted` y lo tumbó `a11y.mjs` el 2026-08-27 con **3.94:1**, al entrar
     la maqueta en la tarjeta de 8.113: el mismo #898989 daba 4.68:1 sobre el fondo de
     sección y 3.94 sobre la tarjeta. No cambió el color: cambió lo que tiene detrás, que
     es el modo de fallo que describe 8.37 al revés — la regla siguió correcta y su
     contexto no. Se sube al token de lectura, que está medido en los dos temas (9.64:1
     sobre sección) y sigue siendo más quieto que el cuerpo. */
  color: var(--cc-fg-read);
}
.cc-form-maqueta .gform_fields,
.cc-form-maqueta .gfield_checkbox { list-style: none; margin: 0; padding: 0; }
.cc-form-maqueta .gform_fields { display: grid; gap: var(--cc-space-6); }
.cc-form-maqueta .gfield_label {
  display: block;
  margin-bottom: 6px;
  font-size: 15px;
  font-weight: 700;
  color: var(--cc-fg-read);
}
.cc-form-maqueta .gfield_required { color: var(--cc-fg-accent); }
/* Los dos campos por fila del formulario real (nombre+apellido, email+teléfono). */
.cc-form-maqueta .ginput_complex { display: grid; grid-template-columns: 1fr 1fr; gap: var(--cc-space-4); }
.cc-form-maqueta input,
.cc-form-maqueta textarea {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--mq-linea);
  padding: 8px 2px;
  color: var(--cc-fg-read);
  font: inherit;
  font-size: 15px;
}
.cc-form-maqueta textarea { resize: none; }
/* ⚠️ 24x24 y no el tamaño nativo (13px). Lo pide WCAG 2.5.8 y lo mide `a11y.mjs`, que
   cubre esta página. En producción las casillas de Gravity Forms miden 13px, así que la
   maqueta es aquí a propósito MÁS estricta que el original: sirve de recordatorio de lo
   que el formulario real debería cumplir cuando se pueda auditar en staging (§12). */
.cc-form-maqueta input[type="checkbox"] {
  width: 24px; height: 24px;
  flex: 0 0 auto;
  border: 1px solid var(--mq-linea);
}
/* Duración explícita y del sistema: sin ella los campos heredan un `0.2s` que no está en
   ninguna escala, y el trinquete de entropía de `ux.mjs` lo caza. */
.cc-form-maqueta input,
.cc-form-maqueta textarea,
.cc-form-maqueta .gform_button { transition-duration: var(--cc-dur-fast); }
/* Las 8 casillas van en flujo, como en producción: no una por línea. */
.cc-form-maqueta .gfield_checkbox { display: flex; flex-wrap: wrap; gap: 10px var(--cc-space-4); }
.cc-form-maqueta .gfield_checkbox li { display: flex; align-items: center; gap: 8px; }
.cc-form-maqueta .gfield_checkbox label { font-size: 14px; color: var(--cc-fg-read); }
.cc-form-maqueta .charleft { display: block; margin-top: 6px; font-size: 12px; color: var(--cc-fg-muted); }
.cc-form-maqueta .gform_footer { margin-top: var(--cc-space-6); display: flex; justify-content: flex-end; }
.cc-form-maqueta .gform_button {
  border: 0;
  border-radius: var(--cc-radius-pill, 999px);
  padding: 12px 32px;
  background: var(--cc-fg-accent);
  color: var(--cc-ink-900);
  font: inherit;
  font-weight: 700;
}
@media (max-width: 767px) {
  .cc-form-maqueta .ginput_complex { grid-template-columns: 1fr; }
}

/* =============================================================================
   /equipo/ — fichas: nombres cortados y dos tarjetas moradas (2026-08-15)
   =============================================================================
   Reportado por Ricardo: *«las tarjetas se ven feas, los nombres se ven raros… hay
   algunas con un borde morado no sé por qué. En móvil algunos nombres se cortan»*. Las
   tres cosas son reales y las tres salen del MISMO sitio: valores fijos escritos a mano
   en el builder, que viven en la BD y no aguantan un texto más largo que el que había el
   día que se escribieron.

   1. EL RECORTE. El bloque de texto de cada ficha lleva `height: 70px` fijo
      (`#brxe-xsiwel` en el CSS de la página). A 1440 sobra, porque los nombres caben en
      una línea. A 390 la caja se estrecha, «José Andrés Pinto» pasa a dos líneas (54px) y
      «Auxiliar Administrativo Contable» a tres (42px): **96px de contenido en 70px de
      caja**, y `overflow: hidden` —que esta hoja pone para que la foto se recorte al
      radio— se come los 11px que sobresalen del borde de la ficha.
      Se pasa a `height: auto` con el 70 como MÍNIMO: las fichas de nombre corto quedan
      exactamente igual que hoy y solo crecen las que lo necesitan.

   ⚠️ No se toca el `overflow: hidden` de la ficha: es lo que redondea la foto. El
   problema nunca fue el recorte, era la caja demasiado baja.

   2. EL MORADO. Dos fichas —Dayana Verde y José Andrés Pinto, las dos de Finanzas— llevan
      `background: var(--bricks-color-kwgdoe)`, o sea `#4B2FBF`, puesto **desde el
      builder** sobre su id. Las otras 24 llevan la superficie de siempre. No hay ninguna
      regla que lo explique —ni el área ni el cargo— así que se lee como un descuido, no
      como una jerarquía.
      Se normaliza al mismo fondo que el resto. ⚠️ Y ojo: **esto es una decisión de diseño
      tomada en la BD y aquí se está anulando desde el theme.** Si el violeta fuera
      deliberado —destacar Finanzas— la vía correcta es al revés: quitarlo del builder y
      declarar la variante en el sistema. Queda anotado en `pendientes.md`.
   ========================================================================== */
body.cc-tema-estandar #brx-content .brxe-div.card > .brxe-div {
  height: auto;
  min-height: 70px;
}
/* ⚠️ Y la segunda mitad del arreglo, sin la cual el primero EMPEORA lo que venía a
   arreglar. Al dejar crecer el bloque de texto, cada ficha pasó a medir lo que pedía su
   contenido: se acabó el recorte, pero **6 de 15 filas quedaron con tarjetas de distinta
   altura** — que es justo el «se ven feas» del que salió todo.
   `height: 100%` no sirve para igualarlas: se resuelve contra un padre de altura
   indefinida, así que cada una acaba con la suya. Lo que sí iguala es dejar que el flex
   las estire — `height: auto` y `align-self: stretch`—: todas las de una fila toman la
   altura de la más alta.
   Medido en las dos: recorte 0px y **0 filas desparejas de 17 a 390px y de 11 a 1440**. */
/* ⚠️ `!important` en `align-self`, y no por comodidad: Bricks emite `#brxe-tjpttd
   { align-self: flex-start !important }` desde el builder. La especificidad de esta regla
   YA le gana (1 id + 3 clases contra 1 id), así que sin el `!important` parecía correcta
   y no aplicaba — se vio leyendo el computado, no el CSS. Es 8.1 literal. */
body.cc-tema-estandar #brx-content .brxe-div.card {
  height: auto !important;
  align-self: stretch !important;
  background-color: var(--cc-surface-1, rgb(28, 26, 42));
}

/* =============================================================================
   /portafolio/ — los filtros también en móvil (2026-08-15)
   =============================================================================
   Pedido de Ricardo: *«quiero que se vean y funcionen los filtros en la versión móvil»*.
   No estaban rotos: la página tiene **DOS rejillas** y el builder alterna entre ellas.

     #brxe-vxzbum   la buena: 28 proyectos CON filtros    -> `display:none` bajo 767px
     #brxe-bnvyqj   una copia: la mitad de proyectos, SIN filtros -> la que se ve en móvil

   O sea en un teléfono no solo faltaban los filtros: se veían **la mitad de los
   proyectos**. Se invierte la alternancia y el móvil pasa a usar la rejilla buena.

   Medido a 390 y 414px con la regla puesta: los 7 filtros se reparten en 3 filas, cada uno
   de 46px de alto —por encima de los 24 que pide WCAG 2.5.8—, la página NO desborda
   (`scrollWidth` = ancho del viewport) y **el filtrado funciona**: 28 tarjetas pasan a 8 al
   pulsar «Creatividad». Isotope no necesita nada más; la rejilla cae a una columna sola.

   ⚠️ Esto ANULA desde el theme una decisión tomada en el builder, y por eso va aquí y no
   en la BD: un arreglo en la BD se pierde con el próximo volcado de producción (§10). La
   copia `#brxe-bnvyqj` sigue existiendo en la página — lo suyo es borrarla desde el
   builder, y mientras no se haga, esta regla la mantiene oculta en todos los anchos.
   Queda anotado en `pendientes.md`. ============================================ */
@media (max-width: 767px) {
  body.cc-tema-estandar #brx-content #brxe-vxzbum { display: block !important; }
  body.cc-tema-estandar #brx-content #brxe-bnvyqj { display: none !important; }
}

/* =============================================================================
   /portafolio/ — la costura bajo la cabecera (2026-08-15)
   =============================================================================
   Ricardo: *«el fondo de Portafolio no me gusta»*. Medido, hay una firma concreta y no es
   una cuestión de gusto: la banda del titular usa `#0D0D0D` sobre una página de `#0A0A0F`.
   Los dos son tokens del sistema —`--cc-bg-deep` y `--cc-bg`— pero **no son el mismo
   negro**: uno es gris neutro y el otro lleva el tinte azul del fondo principal. Tres
   puntos de diferencia por canal, lo justo para que se vea un rectángulo mal recortado
   bajo la cabecera y no para que parezca deliberado.

   Y lo que lo cierra: `/portafolio/` es la **única** página del sitio que hace esto.
   Medida la primera sección de las seis principales:

     /portafolio/   rgb(13,13,13)   sobre rgb(10,10,15)   <- costura
     /equipo/       transparente
     /blog/         transparente
     /contacto/     transparente
     /servicios/    rgb(10,10,15)   = el fondo
     /              rgb(10,10,15)   = el fondo

   Se pasa a transparente, que es lo que hacen las otras. Una banda de título solo se
   justifica si CONTRASTA de verdad; a Δ3 no separa nada, solo ensucia.

   ⚠️ El color venía del builder, así que esto vuelve a anular una decisión de la BD — con
   el mismo criterio que las otras tres de hoy: en el theme para que sobreviva al volcado, y
   anotado en `pendientes.md` para resolverlo en su origen. */
/* ⚠️ Se apunta al ID y no a `section:first-of-type`. La primera versión usaba el
   selector genérico y habría tocado la primera sección de TODAS las páginas de tema
   estándar —`/brands/` tiene ahí un `rgb(25,25,25)` que sí es suyo—. El defecto es de una
   página; el arreglo también. Es el mismo criterio que el resto de parches heredados de
   esta hoja, que van por id. */
body.cc-tema-estandar #brx-content #brxe-xskses {
  background-color: transparent;
}

/* ══════════════════════════════════════════════════════════════════════════
   /contacto/ — datos y formulario (2026-08-16)
   ══════════════════════════════════════════════════════════════════════════
   El defecto que arregla esta rejilla está medido en `pendientes.md`: «la columna
   izquierda muere mucho antes que la derecha —la foto y la caja de dirección acaban
   donde el formulario aún tiene 150px por delante— y debajo queda una banda muerta».

   ⚠️ Y el diagnóstico ORIGINAL de esa página era falso, lo que conviene recordar antes
   de tocar nada aquí: la auditoría del 2026-08-09 le atribuyó «1.391px de alto, la mitad
   vacíos» midiendo en LOCAL, donde Gravity Forms no está montado. Contra producción la
   página mide 1.067 y el formulario ocupa 689×868 (8.62). O sea la mitad derecha nunca
   estuvo vacía; la que se queda corta es la IZQUIERDA, y es la que esto llena.

   La cura no es estirar la columna: es darle contenido real. Hasta hoy solo tenía la
   dirección. Ahora lleva los tres datos que el sitio YA publica en su pie —dirección,
   teléfono y correo— más la vía que no es el formulario. Ninguno es copia nueva.
   ────────────────────────────────────────────────────────────────────────── */

.cc-contacto-grid {
  display: grid;
  /* `1fr 1.15fr` y no `1fr 1fr`: el formulario son 16 campos y la columna de datos
     son cuatro líneas. Partir por la mitad le da al lado ligero el mismo peso que al
     pesado, que es justo el desequilibrio del que venimos. */
  grid-template-columns: 1fr 1.15fr;
  gap: var(--cc-space-10);
  align-items: start;
}

/* Un solo breakpoint, y es el mismo que usa el resto del sistema (8.19): bajo 767 el
   header ya apila, así que apilar aquí a otro ancho crearía un tercer punto de quiebre
   que nadie recuerda. */
@media (max-width: 767px) {
  .cc-contacto-grid {
    grid-template-columns: 1fr;
    gap: var(--cc-space-8);
  }
}

.cc-contacto-datos {
  display: flex;
  flex-direction: column;
  gap: var(--cc-space-5);
}

/* La foto de la oficina (2026-08-16). Va PRIMERA en la columna y no de adorno: sitúa, y
   la dirección que viene justo debajo la nombra. Es además el primer material PROPIO que
   entra en una pieza rediseñada — hasta hoy las imágenes del sistema eran stock, por bien
   tratado que estuviera, y el hallazgo 5 de la auditoría del 2026-08-09 lleva desde
   entonces pidiendo exactamente esto.

   ⚠️ Se sirve desde el THEME y no desde la biblioteca de medios, por la misma razón que
   los logos de partners: los ids de adjunto no sobreviven a un volcado (§10), así que un
   JSON versionado que cite uno se rompe solo. */
/* ⚠️ La clase va sobre el `<img>` MISMO, no sobre un envoltorio: el elemento `image` de
   Bricks emite la etiqueta directa cuando no hay enlace ni pie de foto. La primera
   versión de esta regla era `.cc-contacto-foto img { … }` y no aplicó NUNCA — 8.9
   literal, un selector escrito contra markup que no existe. Se veía bien igual, por pura
   suerte: la fuente ya viene a 16:9, así que el `aspect-ratio` que no llegaba a aplicarse
   no hacía falta. Con otra foto habría salido deformada y nadie habría entendido por qué.
   Se cubren las DOS formas por si un día lleva enlace, que es cuando Bricks sí envuelve. */
.cc-contacto-foto,
.cc-contacto-foto img {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 0 var(--cc-space-2);
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--cc-radius-md);
}

/* ⚠️ Y aquí está la mitad del arreglo que la rejilla NO resuelve, medida y no supuesta.
   Con los tres datos puestos, la columna izquierda mide 283px contra los 1.014 del
   formulario: quedaban **731px de banda muerta** a 1440. Darle más contenido sería
   escribir copia, que es decisión del equipo (§7), así que la salida es que el panel
   ACOMPAÑE en vez de quedarse atrás — el hueco deja de leerse como abandono porque el
   ojo nunca lo tiene delante.

   `top` sale de la altura real del header pegajoso (85px, medida de 320 a 1440 el
   2026-07-29, gotcha 8.19) más aire. Si el header cambia de alto, este número se
   queda corto y el panel se mete debajo: por eso va con el número explicado y no suelto.

   ⚠️ Depende de que el contenedor NO recorte y de `align-items: start` en la rejilla —
   sin eso el ítem se estira a la altura de la fila y un `sticky` dentro de una caja de
   su mismo alto no tiene por dónde deslizarse, que es 8.26 literal: el header declaraba
   `position: sticky` desde que se construyó y nunca se pegó porque su padre lo ceñía. */
@media (min-width: 768px) {
  .cc-contacto-datos {
    position: sticky;
    top: calc(85px + var(--cc-space-6));
  }
}

/* ==========================================================================
   EL CIERRE DE LA PORTADA ACOMPAÑA A SU FORMULARIO (2026-08-17)
   Ricardo: *"este texto del homepage ¿puedes hacer que baje con el formulario, como en
   contacto?"*. Es el MISMO caso y por eso reusa el mismo mecanismo, no uno nuevo: el
   titular y su bajada miden 291px al lado de un formulario de 1.077, así que al
   desplazarse el texto desaparecía por arriba y quedaban 786px de columna vacía junto a
   los campos — exactamente lo que el panel de datos de /contacto/ vino a resolver.

   ⚠️ Las dos condiciones que un `sticky` necesita ya se cumplían aquí, y conviene decir
   cuáles son porque son la mitad de 8.26: el contenedor es `grid` con `align-items:
   start` —si estirara el ítem a la altura de la fila, no tendría por dónde deslizarse— y
   no recorta (`overflow: visible`). Medido antes de tocar nada: 291px de contenido en una
   fila de 1.077 dejan 786px de recorrido.

   El `top` sale de la altura real del header pegajoso (85px, 8.19) más aire, igual que en
   /contacto/. Si el header cambia de alto, este número se queda corto: por eso va
   explicado y no suelto.
   ========================================================================== */
@media (min-width: 768px) {
  .cc-cierre-texto {
    position: sticky;
    top: calc(85px + var(--cc-space-6));
  }
}

/* Sin movimiento pedido, nada se mueve: un panel que persigue al usuario es
   justamente lo que `prefers-reduced-motion` viene a apagar (8.23). */
@media (prefers-reduced-motion: reduce) {
  .cc-contacto-datos,
  .cc-cierre-texto { position: static; }
}

.cc-contacto-titulo {
  margin: 0 0 var(--cc-space-2);
  color: var(--cc-fg-strong);
  font-family: var(--cc-font-sans);
  font-weight: 700;
  font-size: var(--cc-fs-h3);
  line-height: 1.2;
}

/* Cada dato es rótulo arriba y valor abajo. Se usa `grid` y no dos párrafos para que
   el rótulo y su valor no se puedan separar al reflowear.

   ⚠️ Y va sobre el div Y sobre su `<p>` interior, que no es redundancia: el elemento
   `text` de Bricks ENVUELVE su contenido en un `<p>`. Con la regla solo en el div, la
   rejilla se aplicaba a un contenedor de un único hijo y los `span` seguían en línea:
   en pantalla salía «DIRECCIÓNAv. Andrés Bello 2687», pegado y sin salto.
   Es 8.9 —un selector contra markup que no existe— y esta vez NO lo cazó ninguna
   medición: lo vio Ricardo en la captura. Las sondas miraban la caja del panel y su
   altura, que eran correctas; nadie le había preguntado a la página si el rótulo y su
   valor caían en líneas distintas. 8.53 otra vez. */
.cc-contacto-dato,
.cc-contacto-dato > p {
  display: grid;
  gap: 4px;
  margin: 0;
}

.cc-contacto-rotulo {
  color: var(--cc-fg-muted);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.cc-contacto-valor {
  color: var(--cc-fg-read);
  font-size: var(--cc-fs-base);
  line-height: 1.5;
}

/* ⚠️ El teléfono y el correo son enlaces, así que NO pueden distinguirse solo por
   color: es WCAG 1.4.1 nivel A, y es exactamente el fallo que `ux.mjs` cazó en el
   cuerpo de las notas el 2026-07-29. Van subrayados desde el principio. */
/* ⚠️ `inline-flex` + `min-height` y NO `padding`: el teléfono y el correo son enlaces, o
   sea objetivos táctiles, y a su alto natural median **22px** contra los 24 que pide
   WCAG 2.5.8. Lo cazó `a11y.mjs` en la primera corrida de esta página, que es
   exactamente para lo que está.
   Se agranda el ÁREA sin tocar el tamaño visible del texto — el mismo criterio con el
   que se resolvieron los puntos del carrusel y las flechas (8.17). Con `padding` se
   habría movido la línea base respecto del rótulo de arriba. */
a.cc-contacto-valor {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  width: fit-content;
  color: var(--cc-fg-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: color var(--cc-dur-fast) var(--cc-ease),
              text-decoration-thickness var(--cc-dur-fast) var(--cc-ease);
}

a.cc-contacto-valor:hover,
a.cc-contacto-valor:focus-visible {
  color: var(--cc-fg-accent);
  text-decoration-thickness: 2px;
}

/* La vía alternativa al formulario. Existe porque una página de conversión no debería
   tener UNA sola puerta: quien no quiere escribir un formulario hoy se va sin dejar
   rastro. Son enlaces de navegación, no copia nueva. */
.cc-contacto-alt,
.cc-contacto-alt > p {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cc-space-5);
  margin: 0;
}
.cc-contacto-alt { margin-top: var(--cc-space-3); }

/* El formulario ocupa su columna entera: si se deja a su ancho natural, Gravity Forms
   colapsa los campos a ~420px y reaparece el hueco que esta rejilla vino a cerrar. */
.cc-contacto-form { width: 100%; }
.cc-contacto-form .gform_wrapper { width: 100%; }

/* ⚠️ NO se reusa `.cc-form-cierre`, y la tentación era grande porque hace justo esto.
   Esa clase está escrita para `#brxe-sec127`, que es ISLA OSCURA EN LOS DOS TEMAS, así
   que pinta con `--cc-fg-on-dark`: blanco fijo. La sección de /contacto/ SÍ voltea con
   el tema, o sea heredarla daría blanco sobre fondo claro — que es exactamente el fallo
   A-02 de la auditoría del 2026-08-10, en otra pieza.

   Y no es teórico: la primera versión de esta página usaba `--cc-fg-on-dark` en sus
   títulos y enlaces, y `tema.mjs` la tumbó con **1.16:1** en modo claro. Las mismas
   medidas de caja, con tokens que voltean.

   ⚠️ El formulario REAL no se puede ver en local (Gravity Forms es de licencia, 8.62),
   así que esto se verifica contra la maqueta de 16 campos que emite el theme y queda
   pendiente de confirmar contra staging cuando exista (§12). */
.cc-contacto-form .gform_wrapper,
.cc-contacto-form .cc-form-ausente { color: var(--cc-fg-read); }

.cc-contacto-form .gform_wrapper .gfield_label {
  color: var(--cc-fg-strong);
  font-size: 15px;
  font-weight: 600;
}
.cc-contacto-form .gform_wrapper .gfield_description { color: var(--cc-fg-muted); }

.cc-contacto-form .gform_wrapper input[type="text"],
.cc-contacto-form .gform_wrapper input[type="email"],
.cc-contacto-form .gform_wrapper input[type="tel"],
.cc-contacto-form .gform_wrapper textarea,
.cc-contacto-form .gform_wrapper select {
  min-height: 48px;                    /* área de toque cómoda (8.17) */
  padding: 12px 14px;
  background: var(--cc-bg-surface);
  border: 1px solid var(--cc-border-soft);
  border-radius: var(--cc-radius-sm);
  color: var(--cc-fg-strong);
  font-family: inherit;
  font-size: 16px;                     /* 16px evita el zoom automático de iOS al enfocar */
}
.cc-contacto-form .gform_wrapper :is(input, textarea, select):focus-visible {
  outline: 2px solid var(--cc-fg-accent);
  outline-offset: 2px;
}
/* ⚠️ El fondo sale de `--cc-green-500` y NO de `--cc-fg-accent`, y la diferencia la
   encontró `tema.mjs` tumbando esta página con **3.7:1** en modo claro. `--cc-fg-accent`
   es un token de TEXTO: en claro voltea a `--cc-green-ink` (#067A57, verde oscuro) para
   poder leerse sobre papel. Usado como FONDO hace justo lo contrario — un botón oscuro
   con tinta encima.
   El emparejamiento correcto es el que ya usa el CTA de la portada: verde de marca fijo
   en los dos temas con tinta encima. `--cc-fg-accent` se queda solo en el anillo de foco,
   donde su papel de primer plano sí es el correcto. */
.cc-contacto-form .gform_wrapper .gform_footer input[type="submit"],
.cc-contacto-form .gform_wrapper .gform_footer button {
  min-height: 48px;
  padding: 0 26px;
  background: var(--cc-green-500);
  border: 0;
  border-radius: var(--cc-radius-pill, 999px);
  color: var(--cc-ink-900);
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

/* ══════════════════════════════════════════════════════════════════════════
   /portafolio/ en TELÉFONO: el título del proyecto no cabe y lo recorta la tarjeta
   ══════════════════════════════════════════════════════════════════════════
   Reportado por Ricardo el 2026-08-16 como «las tarjetas muy anchas». La firma medida
   a 390px es otra y es peor que ancho:

     título   "Tecno Fast | Estrategia Crecimiento Acelerado"   31,5px → 4 líneas, 176px
     tarjeta  li.repeater-item                                  219px, overflow: hidden

   El alto de la tarjeta lo fija **Isotope** (la rejilla con filtros, 8.64), así que no
   crece con el contenido: lo que no cabe se recorta. En pantalla el título aparece
   cortado por la mitad —«Estrategia» partida— y en la tarjeta siguiente desaparece
   entero, porque cada una hereda el mismo alto.

   ⚠️ La cura NO es quitar el `overflow` ni tocar el alto: los dos los escribe Isotope
   por JS mientras maqueta, así que una regla nuestra o pierde o le desordena la
   rejilla — que es exactamente lo que costó el gotcha 8.64. Lo que sí es nuestro es
   el TAMAÑO del título, y a 31,5px estaba escalado para escritorio.

   Se baja solo bajo 767px y solo dentro de la rejilla de proyectos. La escala sale de
   los tokens del sistema, no de un número inventado. */
@media (max-width: 767px) {
  #brx-content .repeater-item :is(h1, h2, h3, h4, h5, .brxe-heading) {
    font-size: var(--cc-fs-lg);
    line-height: 1.22;
  }
}

/* ==========================================================================
   NO HAY REGLAS DE MODO CLARO PARA `.cc-tema-estandar`, Y ES DELIBERADO (2026-08-17)
   Se escribieron y se retiraron el mismo día. Llegaron a dejar `agencia-seo` e
   `influencer-marketing` con CERO nodos bajo AA en claro volcando superficie y texto al
   token a la vez — la mitad de la vuelta (solo superficie) las empeoraba de 27 a 35
   nodos, que es 8.67 en el otro sentido y vale para cualquier página legacy.

   Se retiraron porque el resultado no valía: 14 de sus superficies grandes son FOTOS
   oscuras, así que la página en claro se ve casi igual que en oscuro. Dar el conmutador
   ahí es el «control que no cambia nada» de 8.68. Sin esas dos páginas en la lista del
   conmutador, estas reglas no se activarían nunca: código muerto.

   El detalle está en `ccd_paths_tema_claro_medido()` del child theme. Si algún día se
   rediseñan, esto se rehace en media hora; lo que no se puede rehacer es el tiempo de
   quien lea una hoja llena de reglas que no aplican.
   ========================================================================== */

/* ==========================================================================
   DOS DEFECTOS DE LOS FORMULARIOS QUE NO SON DEL MODO CLARO (2026-08-17)
   Los dos estaban en el tema OSCURO, o sea a la vista de cualquiera, y salieron al
   barrer las páginas legacy. Van juntos porque los dos los pinta Gravity Forms.
   ========================================================================== */

/* 3.a. El botón de envío daba 1:1 y 1.06:1 — literalmente invisible. Es un botón de
   contorno: texto en tinta (#0A0A0F), relleno transparente y borde verde, sobre una
   sección negra. En `/auditoria-seo/` y `/auditoria-gratis/` el MISMO botón sí viene
   relleno de verde y por eso allí se lee: es la misma pieza con dos tratamientos.
   Se unifica al relleno, que es el que ya funciona y el del sistema.

   ⚠️ Y de paso: ese borde venía en `#0BE5A2`, el verde ANTERIOR al cambio de paleta
   del 2026-07-23. Llevaba ahí desde entonces porque nada mira el color de un borde.
   Se reconoce el botón por su clase de Gravity Forms y no por su color, por lo mismo
   que en 8.66: una excepción definida por el color que quiere permitir no protege nada. */
/* ⚠️ El selector incluye `.cc-seo-form` desde el 2026-08-17: al re-esqueletar
   `/servicios/agencia-seo/`, la página salió de `.cc-tema-estandar` —ya no es una página
   «estandarizada», es una pieza del sistema— y con ella se fue este arreglo, dejando el
   botón otra vez ilegible. Un arreglo colgado de la clase de un ESTADO transitorio se
   pierde el día que ese estado cambia; el formulario, en cambio, sigue siendo el mismo. */
body.cc-tema-estandar #brx-content .gform_button,
.cc-seo-form .gform_button {
  background-color: var(--cc-green-500);
  border-color: var(--cc-green-500);
  color: var(--cc-ink-900);
}
body.cc-tema-estandar #brx-content .gform_button:hover,
.cc-seo-form .gform_button:hover {
  background-color: var(--cc-green-700, #12b98a);
  border-color: var(--cc-green-700, #12b98a);
}

/* 3.b. El contador de caracteres (`.charleft`) iba en gris #898989 sobre la banda
   violeta: 2.95:1, bajo el mínimo de 4.5. Hereda del texto de su banda en vez de
   traer un gris propio — el gris de un formulario está pensado para fondo blanco, y
   aquí no hay ninguno. */
body.cc-tema-estandar #brx-content .charleft,
body.cc-tema-estandar #brx-content .ginput_counter,
.cc-seo-form :is(.charleft, .ginput_counter) {
  color: inherit;
  opacity: .78;
}

/* ==========================================================================
   LOS RÓTULOS DE MÉTRICA DE agencia-seo: 9px y solapados (M-02 + M-03, 2026-08-17)
   La auditoría móvil del 2026-08-16 los anotó como DOS hallazgos —«texto bajo 11px»
   y «13 solapes de texto»— y son UNO: los 18 rótulos («Impresiones», «Sesiones»,
   «Conversiones») van a 9px con `margin-top: -5px`, así que se meten 5px dentro de la
   cifra que tienen encima. Remedido con el instrumento corregido: 32 solapes, no 13.
   La primera cuenta subía a 49 porque contaba padre e hijo como si se pisaran.

   `pendientes.md` los dejó sin tocar «porque no tienen clase común y sería una lista
   de ids que se pudre». No hacía falta: son el SEGUNDO `.brxe-text-basic` de su div,
   o sea el que va debajo de la cifra, y eso sí es un selector estructural. Medido:
   caza los 18 de esta página y CERO en influencer-marketing, /portafolio/, /equipo/ y
   la de VTEX, que son las otras cuatro con tema estándar.

   Se sube a `--cc-fs-xs`, que es el valor de la escala; no se estrena ninguno nuevo
   (§7). Y se quita el margen negativo, que es lo que producía el solape. */
body.cc-tema-estandar #brx-content .brxe-div > .brxe-text-basic + .brxe-text-basic {
  margin-top: 0;
  font-size: var(--cc-fs-xs);
  line-height: 1.35;
}

/* ==========================================================================
   /servicios/agencia-seo/ — LOS COMPONENTES QUE EL SISTEMA NO TENÍA (2026-08-17)
   Re-esqueletado de la página: misma copia, mismas imágenes de contenido, mismo orden
   de secciones, reconstruida con el vocabulario del sistema. Casi todo reutiliza lo que
   ya había (`cc-svc-head`, `cc-svc-grid`, `cc-svc-card`, `cc-svc-num`, `cc-band`,
   `cc-isla-oscura`, `cc-btn-marca`). Lo de abajo es lo que faltaba.

   El componente de verdad nuevo es UNO: la tarjeta de caso —logo de cliente más tres
   métricas—, que no existe en ninguna otra pieza. Las demás clases son su familia y el
   vestido de dos elementos NATIVOS de Bricks (las pestañas y el acordeón), que se
   re-visten en vez de reconstruirse: funcionan, son accesibles por teclado y el
   acordeón además emite el `faqSchema`.

   NINGÚN TOKEN NUEVO: todo sale de las escalas existentes (§7).
   ========================================================================== */

/* ── LA TABLA DE CASOS ─────────────────────────────────────────────────────
   Ricardo, 2026-08-17: *"¿no hay una forma más bonita de presentar los casos de éxito
   SEO?"*. La había, y el motivo de que no lo pareciera era medible: la sección repetía
   los tres rótulos DIECIOCHO veces y el periodo cinco, con las 18 cifras al mismo tamaño.
   Eso no es una colección de tarjetas, es una tabla comparativa dibujada como si no lo
   fuera — y perdía justo lo que un caso de éxito tiene que permitir: comparar.

   El sistema ya presenta cifras en la portada, y al revés: UNA que manda a 64px y las de
   apoyo a 20px, con rótulos en versalitas. Aquí se aplica esa jerarquía a una matriz.
   ────────────────────────────────────────────────────────────────────────── */

.cc-casos { display: flex; flex-direction: column; }

.cc-casos__cab,
.cc-caso {
  display: grid;
  grid-template-columns: minmax(150px, 1.1fr) repeat(3, minmax(0, 1fr));
  gap: 16px;
  align-items: center;
}

.cc-casos__cab {
  padding-bottom: 10px;
  border-bottom: 1px solid color-mix(in srgb, var(--cc-fg) 20%, transparent);
}
.cc-casos__col {
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
/* La primera columna es la del cliente y su rótulo no aporta —el logo lo dice—, así que
   se reserva el espacio sin escribirlo. Se deja en el DOM para el lector de pantalla. */
.cc-casos__cab > .cc-casos__col:first-child { visibility: hidden; }

.cc-caso {
  padding: 18px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--cc-fg) 12%, transparent);
}
.cc-caso:last-child { border-bottom: 0; }

.cc-caso__quien { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }

/* El logo se normaliza por ALTURA y no por ancho: son seis marcas con proporciones muy
   distintas —un wordmark ancho y un monograma cuadrado en la misma columna— y un tope de
   ancho recortaría a las anchas. Es la lección de la trust bar de la portada. */
img.cc-caso__logo { height: 26px; width: auto; max-width: 100%; }

.cc-caso__periodo { letter-spacing: 0.06em; text-transform: uppercase; }

.cc-caso__cifra { font-variant-numeric: tabular-nums; }

/* ⚠️ El rótulo de cada celda se oculta VISUALMENTE en escritorio, no con `display:none`.
   La cabecera de columna ya lo dice al ojo, pero un lector de pantalla no asocia una celda
   con su columna cuando la «tabla» son divs: sin esto, cada cifra se leería suelta y sin
   decir de qué es. La técnica de recorte lo saca de la vista y lo deja en el árbol. */
@media (min-width: 768px) {
  .cc-caso__rotulo {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* Bajo 767px la matriz no cabe: la cabecera se retira, cada caso pasa a ser una ficha y
   los rótulos vuelven a verse. El contenido es el mismo; cambia quién lo etiqueta. */
@media (max-width: 767px) {
  .cc-casos__cab { display: none; }
  .cc-caso {
    grid-template-columns: 1fr 1fr;
    gap: 14px 20px;
    padding: 20px 0;
  }
  .cc-caso__quien { grid-column: 1 / -1; }
}
/* ⚠️ Y UNA sola columna bajo 480px. Con dos, a 320px cada celda mide 126px y la cifra más
   larga —«14.119.000», 8 dígitos a 29px— pide 134: desbordaba la página 34px. Lo cazó
   `responsive.mjs` a 320, que es el ancho que existe justo para esto (8.7).
   Se parte la rejilla en vez de encoger la cifra: el número es lo que la sección viene a
   enseñar, y achicarlo para que quepa es resolver el síntoma. */
@media (max-width: 479px) {
  .cc-caso { grid-template-columns: 1fr; }
}

/* ── Las pestañas, re-vestidas ─────────────────────────────────────────── */
.cc-seo-pane {
  display: grid;
  grid-template-columns: minmax(min(280px, 100%), 349px) minmax(0, 1fr);
  gap: clamp(20px, 3vw, 40px);
  /* ⚠️ `stretch` y no `start`, y esto es la mitad del «se ve desordenado» que reportó
     Ricardo: con `start` cada columna medía lo suyo —436px la de la imagen y 246 la de la
     lista— así que la línea divisoria, que es el borde de la segunda, MORÍA a media altura
     y dejaba un escalón. Estirando, la divisoria recorre la fila entera y las dos columnas
     se leen como dos mitades de lo mismo. */
  align-items: stretch;
}
.cc-seo-pane > h3 { grid-column: 1 / -1; }
/* 349px es el `_widthMax` que traía esa columna en el original: es lo que pone la
   divisoria cerca de la ilustración en vez de perdida a media página. */
.cc-seo-pane__izq {
  display: flex;
  flex-direction: column;
  gap: 20px;
  align-items: flex-start;
  max-width: 349px;
}

/* ⚠️ LA LÍNEA DIVISORIA entre las dos columnas. El original la traía como un elemento
   `divider` de Bricks —vertical, 1px, 315px de alto, blanco, oculto en móvil— y se perdió
   al re-esqueletar. La echó de menos Ricardo, y tenía razón: sin ella las dos columnas se
   leen como dos bloques sueltos y no como las dos mitades de una misma explicación.
   Vuelve como BORDE de la segunda columna y no como elemento: hace lo mismo, voltea sola
   con el tema —un `#ffffff` fijo se vería como una raya negra en claro— y ahorra un nodo
   por pestaña. Se retira bajo 767px, igual que hacía el original. */
.cc-seo-pane__der {
  border-left: 1px solid color-mix(in srgb, var(--cc-fg) 22%, transparent);
  padding-left: clamp(20px, 3vw, 35px);
}
/* La lista es texto de lectura y la columna daba 755px: por encima de `--cc-measure`, que
   es el ancho con el que se lee el resto del sitio. Lo cazó `ux.mjs` en cuanto la página
   entró en esa prueba — el mismo chequeo que corrigió las bajadas de las otras piezas. */
.cc-seo-pane__der > .brxe-text { max-width: var(--cc-measure); }
@media (max-width: 767px) {
  .cc-seo-pane__der { border-left: 0; padding-left: 0; }
}
/* La ilustración es la misma en las 4 pestañas y no aporta información: se acota para que
   no gobierne el alto de la fila, que es lo que hacía con su tamaño natural. */
/* 202px es el ancho al que la servía la página anterior (medido sobre su render). A 320
   la ilustración —que es la MISMA en las cuatro pestañas y no aporta información— dominaba
   la columna y empujaba la descripción lejos del texto al que acompaña. */
img.cc-seo-pane__art { width: 100%; max-width: 202px; height: auto; }
.cc-seo-pane__lista ul { margin: 0; padding-left: 1.1em; display: grid; gap: 10px; }
@media (max-width: 767px) {
  .cc-seo-pane { grid-template-columns: 1fr; }
}

/* ── El formulario de auditoría, a dos columnas ────────────────────────── */
.cc-seo-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: clamp(24px, 4vw, 48px);
  align-items: center;
}
.cc-seo-form__art img { width: 100%; height: auto; border-radius: var(--cc-radius-lg); }

/* ── Los 7 pasos numerados ─────────────────────────────────────────────── */
.cc-seo-pasos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: 20px;
}
.brxe-block.cc-seo-pasos { align-items: stretch; }   /* misma trampa que cc-svc-grid */
.cc-seo-paso {
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* ⚠️ ESTO ES UN PASO DEL MÉTODO, NO UNA PESTAÑA. El 2026-08-25 se bajó este
     `padding-top` de 16 a 8 creyendo que era la marca de la pestaña activa, y se le dejó
     un comentario que hablaba de pestañas. No lo era: la barra de la pestaña vive en
     `.cc-seo-tabs .tab-title`. Restaurado a 16, que es el aire con el que se diseñó la
     rejilla de siete pasos. Buscar por el VALOR y no por el componente es cómo se edita
     la regla equivocada. */
  padding-top: 16px;
  border-top: 2px solid var(--cc-fg-accent);
}

/* ── Ritmo vertical y arreglos de la revisión visual de agencia-seo (2026-08-17) ──
   Los cuatro salieron de MIRAR la página después de que las 10 suites dieran verde. Es
   el mismo patrón de siempre: lo que no se le pregunta a una suite, no falla. */

/* 1. El aire entre el titular de sección y su contenido. El contenedor de Bricks es un
   flex column con `gap: normal` —o sea cero—, así que los 9 titulares quedaban pegados a
   su grilla. El valor sigue la escala de la portada. */
.cc-seo-cont { gap: clamp(22px, 3vw, 36px); }

/* Y la MEDIDA DE LECTURA. Los párrafos sueltos de una sección —la banda del 40 %, la
   bajada del hero— heredaban el ancho del contenedor (1180px) y quedaban muy por encima
   de `--cc-measure`, que es el valor con el que se lee el resto del sitio. Solo alcanza a
   los hijos DIRECTOS: dentro de una tarjeta la columna ya acota sola. */
.cc-seo-cont > .brxe-text { max-width: var(--cc-measure); }

/* 2. La maqueta del panel de pestaña. ⚠️ La rejilla NO va en `.tab-pane` sino en un
   bloque hijo, y el motivo es de cascada moderna: Bricks emite
   `.brxe-tabs-nested .tab-pane.brx-open{display:block!important}` DENTRO de
   `@layer bricks`, y una `!important` dentro de una capa le gana a una `!important` sin
   capa por mucha especificidad que tenga — la prioridad de capas se INVIERTE para las
   importantes. Se probó con (0,2,0), con (0,4,0) y con `!important`: las tres pierden.
   El panel se queda con su `display` y la maqueta vive en un hijo nuestro. */
/* Las 4 tarjetas de «Más allá del SEO» caben en UNA fila: con el mínimo de 320px de
   `cc-svc-grid` entraban tres y la cuarta quedaba sola, que se lee como un descuadre y no
   como una decisión. Son cuatro conceptos hermanos (SXO, GEO, ESO, AEO), así que la fila
   de cuatro también es lo correcto semánticamente. */
.cc-seo-cuatro { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }

/* ⚠️ Los rótulos de pestaña, TODOS a la misma altura. Medidos: 66, 66, 66 y 96px —el
   cuarto trae un `<br>` en la copia y ocupa dos líneas—, así que la fila quedaba dentada y
   los cuatro indicadores de pestaña activa aparecían a alturas distintas. Es la otra mitad
   del «desordenado». Se estiran todos al más alto en vez de recortar el que sobra: el
   salto de línea está en el contenido y ese no se toca (§5). */
.cc-seo-tabs .tab-menu { align-items: stretch; }
.cc-seo-tabs .tab-title { display: flex; align-items: flex-start; }

/* ── LA FILA DE PESTAÑAS PARECE UNA FILA DE PESTAÑAS (2026-08-25) ──────────
   Ricardo: *«no sé, se ve simple/feo/desordenado»* sobre esta sección. Mirado a tamaño
   real, eran cuatro cosas concretas y ninguna es de contenido:

   1. NO HABÍA FILA. Cuatro textos sueltos flotando bajo el H2, sin nada que los agrupe ni
      línea base común: no se leían como un control, se leían como una lista suelta. Ahora
      una regla recorre el ancho y las pestañas se apoyan en ella — que es lo que convierte
      cuatro palabras en un selector.
   2. LA MARCA DE LA ACTIVA ESTABA ARRIBA Y DESPEGADA. Con `border-top` quedaba a media
      altura entre el titular y el rótulo, así que se leía como una raya suelta bajo el H2
      y no como «esta es la pestaña abierta». Pasa ABAJO, montada sobre la regla: es el
      patrón que todo el mundo reconoce y de paso desaparece el hueco.
   3. LA SEGUNDA LÍNEA DEL CUARTO RÓTULO IBA INDENTADA. «(Linkbuilding)» aparecía centrada
      respecto de su primera línea, que es lo que más descuadraba la fila. El `<br>` está
      en la copia y no se toca (§5); lo que se corrige es la alineación del texto.
   4. LA CITA FLOTABA. Es la voz del cliente y lo único distinto entre las cuatro pestañas,
      pero iba pegada al borde y con peso de titular, compitiendo con el H2 de la sección.
   ────────────────────────────────────────────────────────────────────────── */
.cc-seo-tabs .tab-menu {
  gap: clamp(18px, 2.4vw, 34px);
  border-bottom: 1px solid color-mix(in srgb, var(--cc-fg) 16%, transparent);
}
.cc-seo-tabs .tab-title {
  /* La marca de la activa vive abajo: se reserva su grosor en todas para que ninguna
     salte de posición al abrirse. */
  padding: 0 0 12px;
  border-top: 0;
  border-bottom: 2px solid transparent;
  text-align: left;
  transition: color var(--cc-dur-fast, .18s) var(--cc-ease-out, ease),
              border-color var(--cc-dur-fast, .18s) var(--cc-ease-out, ease);
}
.cc-seo-tabs .tab-title.brx-open { border-bottom-color: var(--cc-fg-accent); }
/* El rótulo inactivo se apaga un punto más, para que la activa gane sin subir de tamaño. */
.cc-seo-tabs .tab-title:not(.brx-open) { color: var(--cc-fg-muted); }
.cc-seo-tabs .tab-title:hover { color: var(--cc-fg); }

/* La cita: se acerca a su panel, pierde el peso de titular y gana una barra de acento que
   la marca como voz del cliente. No cambia una palabra. */
.cc-seo-pane > h3 {
  margin: 4px 0 6px;
  padding-left: 14px;
  border-left: 3px solid var(--cc-fg-accent);
  font-weight: 600;
}

/* 3. ⚠️ Aquí había un `white-space: nowrap` para que «Enlaces externos (Linkbuilding)»
   no cayera en dos líneas. Se retiró: el salto NO era del ancho, está EN LA COPIA — el
   rótulo trae un `<br>` literal desde la página anterior. La regla no hacía nada y
   además intentaba discutirle a un contenido que esta tanda se comprometió a respetar
   verbatim. Si algún día molesta, se quita el `<br>` del contenido, no con CSS. */

/* 4. La FAQ no parecía una FAQ: cinco titulares sueltos, sin separadores y sin ninguna
   señal de que se abren. Un acordeón que no se anuncia como tal no lo usa nadie. */
.cc-seo-faq > .brxe-block { border-bottom: 1px solid rgba(255, 255, 255, 0.12); }
.cc-seo-faq .accordion-title-wrapper { padding: 6px 0; }
.cc-seo-faq .accordion-title-wrapper::after {
  content: "";
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  margin-left: 16px;
  border-right: 2px solid var(--cc-fg-accent);
  border-bottom: 2px solid var(--cc-fg-accent);
  transform: rotate(45deg);
  transition: transform var(--cc-dur-fast, .18s) var(--cc-ease-out, ease);
}
.cc-seo-faq > .brx-open .accordion-title-wrapper::after { transform: rotate(-135deg); }
html[data-theme="light"] .cc-seo-faq > .brxe-block { border-bottom-color: rgba(32, 30, 44, 0.16); }

/* ── DOS SECCIONES A FORMATO REGISTRO (2026-08-25) ─────────────────────────
   Ricardo, al ver la página con las reglas de las 4 hijas aplicadas por accidente:
   *«no me disgustaba cómo se veían unas partes con el tema de los otros servicios»* —
   «Más allá del SEO» y «Somos Agencia Full Service»—. Tenía razón, así que se hace **a
   propósito** y solo en esas dos, en vez de dejar que 25 reglas ajenas se apliquen enteras.

   Por qué encajan y las demás no: son grupos de hermanos sin jerarquía interna —cuatro
   disciplinas, cinco capacidades— y sin imagen que aporte. Es la forma para la que se
   diseñó la entrada de registro en las hijas (8.93). La apertura y la matriz de casos NO
   se tocan: son lo único que solo puede ser de esta página.

   ⚠️ UNA DIFERENCIA DELIBERADA CON EL REGISTRO DE LAS HIJAS: allí la descripción se oculta
   y aparece al hover, y allí funciona porque cada entrada lleva a una página de servicio.
   Aquí la descripción ES el contenido —«SXO (Search Experience Optimization)» sin su línea
   no dice nada— y en táctil no hay hover. Se queda visible siempre. Copiar un patrón
   incluye decidir qué parte de él NO aplica.
   ────────────────────────────────────────────────────────────────────────── */

.cc-seo-registro {
  grid-template-columns: 1fr;
  gap: 0;
  border-bottom: 1px solid var(--cc-rule-on-dark);
}
.cc-seo-registro > .cc-svc-card {
  position: relative;
  background: none;
  border: 0;
  /* El mismo hairline del registro de las hijas: `--cc-rule-on-dark` voltea solo con el
     tema, que es lo que 8.96 dejó resuelto. */
  border-top: 1px solid var(--cc-rule-on-dark);
  border-radius: 0;
  /* Sin fondo ni radio, una sombra dibuja el contorno de una caja que no existe: es 8.97,
     y se apaga aquí en vez de esperar a que la herede otro estado. */
  box-shadow: none;
  padding: 22px 0 22px 38px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  transition: border-top-color var(--cc-dur-base) var(--cc-ease-out);
}
.cc-seo-registro > .cc-svc-card:hover {
  transform: none;
  box-shadow: none;
  /* El acento de la fila apuntada sale de SU unidad cuando la tiene —las 5 de Full Service
     llevan una cada una— y del verde de marca cuando no. */
  border-top-color: var(--cc-unidad, var(--cc-fg-accent));
}

/* ⚠️ Y LO MISMO OTRA VEZ PARA EL MODO CLARO, que es 8.32 y no un descuido (2026-08-26,
   gotcha 8.110). Las dos reglas de arriba son (0,2,0); las de tarjeta del modo claro
   —`html[data-theme="light"] .cc-svc-card`, con su fondo blanco, su borde teñido de
   unidad y su sombra de elevación— son (0,2,1). En oscuro nadie compite y el registro
   se ve como debe; **al voltear a claro ganaban ellas** y cada fila volvía a ser una
   caja blanca con sombra y una línea azul de reposo que se lee como un hover pegado.
   Lo reportó Ricardo el 2026-08-26 con dos capturas: «se mantiene ese color azul sin
   tener el mouse encima» y «claramente se ve el rectángulo con la sombra».

   ⚠️ Y NO es que la regla del registro estuviera mal: es que el registro de las hijas
   sí está protegido —su selector lleva `body.cc-servicios…:not(.cc-maqueta-propia)`, que
   suma especificidad de sobra— y `agencia-seo` queda FUERA de ese `:not()` por llevar
   `cc-maqueta-propia`. O sea el arreglo de 8.97 nunca alcanzó a esta página, y el bug
   que Ricardo reportó el 2026-08-24 seguía vivo aquí, en otra pieza. */
html[data-theme="light"] .cc-seo-registro > .cc-svc-card {
  background: none;
  border: 0;
  border-top: 1px solid var(--cc-rule-on-dark);
  border-radius: 0;
  box-shadow: none;
}
html[data-theme="light"] .cc-seo-registro > .cc-svc-card:hover {
  box-shadow: none;
  border-top-color: var(--cc-unidad, var(--cc-fg-accent));
}
/* ⚠️ ESTAS ENTRADAS NO VAN NUMERADAS, y quitarlo fue una corrección de Ricardo el mismo
   día: *«¿no está mal o no se ve raro que todo esté enumerado?»*. Tiene razón, y el
   argumento es de página, no de sitio:

   Con el registro puesto, esta página tenía TRES listas numeradas y **solo una está
   ordenada de verdad** — «Nuestro método SEO paso a paso», que es una secuencia y donde el
   `01–07` dice algo cierto. Las otras dos son grupos de hermanos: cuatro disciplinas
   paralelas y cinco capacidades. Numerarlas insinúa un orden que no existe y se lee como
   ranking (¿SXO es la primera y AEO la cuarta?).

   Y el daño de fondo es el otro: usar el mismo recurso para las tres lo VACÍA. Cuando la
   única lista que sí es una secuencia comparte marcador con dos que no lo son, el lector
   deja de leer los números como información. Un recurso estructural tiene que codificar
   algo verdadero; si no, es decoración que además miente.

   (Las 4 hijas viejas SÍ numeran sus servicios, que tampoco son secuencia. Ahí no colisiona
   con nada, y su patrón no se toca: la decisión de aquí es de esta página.)

   En su lugar queda una marca de UNIDAD: conserva el color del área —que es taxonomía real
   de la casa— sin afirmar un orden. Y en «Más allá del SEO» el rótulo ya es el marcador:
   `SXO`, `GEO`, `ESO`, `AEO` no necesitan nada delante. */
.cc-seo-registro > .cc-svc-card::before {
  content: "";
  position: absolute;
  left: 0;
  top: 32px;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--cc-unidad, var(--cc-fg-accent));
  opacity: .9;
}
/* El título de la entrada deja de ser un titular de tarjeta y pasa a ser el rótulo de una
   fila: mismo tamaño de lectura, sin la escala grande que pedía la caja. */
.cc-seo-registro .cc-svc-card__title { font-size: var(--cc-fs-lg); line-height: var(--cc-lh-snug); }
.cc-seo-registro .cc-svc-card__texto { max-width: var(--cc-measure); }

@media (min-width: 900px) {
  /* En pantalla ancha la fila se abre en dos: el rótulo a la izquierda y su descripción a
     la derecha. Es lo que Ricardo señaló como «mostrándose a la derecha», y de paso acorta
     la línea de lectura a la mitad del ancho disponible. */
  .cc-seo-registro > .cc-svc-card {
    display: grid;
    grid-template-columns: minmax(0, 30ch) minmax(0, 1fr);
    column-gap: clamp(28px, 4vw, 64px);
    align-items: baseline;
  }
  /* ⚠️ PERO NO TODAS LAS ENTRADAS TIENEN RÓTULO. Las cinco de «Full Service» son solo
     texto —su copia ya empieza por el nombre del área: «Performance Marketing: …»—, así
     que sin esto su párrafo caía en la columna del rótulo, encajonado a 30ch, con la
     segunda columna vacía a la derecha. Se detecta la ausencia con `:has()` en vez de
     inventar una clase: la estructura ya lo dice. */
  .cc-seo-registro > .cc-svc-card:not(:has(> .cc-svc-card__title)) > .cc-svc-card__texto {
    grid-column: 1 / -1;
  }
}

/* ── LA APERTURA QUE ARGUMENTA CON LA EVIDENCIA (2026-08-25) ───────────────
   Pedido de Ricardo: *«que sea único y siga el tema de la página»*. Lo segundo lo daba ya
   el vocabulario del sistema; lo primero no, y la medición lo decía: la página compartía
   TODO con `/servicios/diseno/` y no tenía un solo rasgo que solo pudiera ser suyo.

   Lo único que ninguna plantilla puede copiar de esta página son sus **18 cifras medidas
   de 6 clientes con nombre**. Estaban en la segunda sección, después de una ilustración de
   stock de un navegador. Ahora el titular y la matriz son UNA sola apertura, así que sobre
   el pliegue hay resultados con nombre de cliente en vez de un mockup genérico.

   ⚠️ UNA sección y no dos con el mismo fondo, y no es un detalle de implementación: el
   trinquete de `ux.mjs` exige que ninguna pareja de secciones consecutivas comparta fondo
   —el umbral salió medido de las 7 piezas—, y dos oscuras seguidas se ven exactamente como
   el tramo plano que Ricardo llamó «sosa». Una sección no tiene pareja.
   ────────────────────────────────────────────────────────────────────────── */

.cc-seo-apertura .cc-seo-evidencia {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--cc-space-md, 24px);
  /* La regla separa el argumento de su prueba. Es el mismo hairline de la matriz, así que
     el bloque entero se lee como una sola pieza de lectura y no como dos cosas pegadas. */
  margin-top: var(--cc-space-lg, 40px);
  padding-top: var(--cc-space-lg, 40px);
  border-top: 1px solid color-mix(in srgb, var(--cc-fg) 12%, transparent);
}

/* ⚠️ El H1 se mide en CARACTERES, no en píxeles, y el defecto era de caja y no de copia:
   medido a 1440 antes de esto, «Agencia SEO en Chile / SEO Técnico, Contenidos & /
   eCommerce» caía en CUATRO líneas con la última colgando sola. `balance` reparte, y la
   medida evita que en pantallas anchas se estire a una línea larguísima. */
.cc-seo-h1 {
  max-width: 19ch;
  text-wrap: balance;
}

/* Los titulares de sección, con el mismo criterio que el H1: medida en caracteres y
   equilibrio. Sin esto «Servicios SEO completos para tu negocio» deja «negocio» solo en la
   segunda línea, que es el mismo defecto de caja que tenía el H1. */
.cc-seo-cont .cc-svc-head > .brxe-heading {
  max-width: 26ch;
  text-wrap: balance;
}

/* Las cifras de la matriz mandan sobre el resto de la apertura: es lo que la sección viene
   a demostrar. No se estrena tamaño — se sube un escalón de la escala existente. */
.cc-seo-apertura .cc-caso__cifra { font-size: var(--cc-fs-h3); }

/* ── LA MATRIZ DEJA DE SER UNA PLANILLA (2026-08-25) ───────────────────────
   Ricardo: *«toda esta parte no me gusta»*, sobre los casos y las pestañas. Medido, la
   matriz tenía cuatro defectos y ninguno es de contenido — el contenido se deja intacto:

   1. LAS TRES CIFRAS AL MISMO PESO. Una fila mezcla un ABSOLUTO (impresiones) con dos
      PORCENTAJES, y los tres iban al mismo tamaño: gana el número más largo, no el que
      argumenta. `2.782.000` pesaba más que `121,80%` por tener más dígitos.
      El sistema ya resuelve esto en la portada —UNA cifra manda y las de apoyo bajan—, y
      el propio comentario de `.cc-casos` decía que aplicaba esa jerarquía… pero no estaba
      aplicada: las tres celdas salían con el mismo tamaño.
   2. QUÉ COLUMNA MANDA. Sesiones, y es una regla UNIFORME, no elegir la mejor cifra de
      cada cliente: sesiones es tráfico orgánico, que es lo que una agencia SEO vende. Con
      una regla por fila la tabla dejaría de ser comparable, que es su única razón de ser.
   3. SEIS FILAS SIN RITMO. Nada ayudaba a seguir una fila de izquierda a derecha en 1180px.
   4. EL PERIODO COMPITIENDO con el logo del cliente en la primera columna.

   ⚠️ Lo que NO se toca, porque es contenido y no presentación: que Palmers y MK repitan
   `121,80%` y `59%` exactos (REVISAR-DATO abierto desde el 2026-08-17), que los
   porcentajes no digan si son variación, y el logo de MK, que es un cuadrado gris
   ilegible por el archivo. Los tres están en `pendientes.md`.
   ────────────────────────────────────────────────────────────────────────── */

/* ⚠️ EL TAMAÑO Y EL COLOR DE LAS CIFRAS NO ESTÁN AQUÍ, y el intento fallido queda escrito
   para que nadie lo repita: Bricks emite la tipografía por elemento como
   `#brxe-<id>{font-size:...}`, un selector de ID (1,0,0), así que
   `.cc-caso > .cc-caso__celda:nth-child(3) .cc-caso__cifra` —(0,3,0)— NO aplica (8.1).
   Se declara en `gen-agencia-seo-v3.py`, que es donde ya se declara el tamaño. Aquí solo
   queda lo que el ID no gobierna: el interletrado de la cifra que manda. */
.cc-caso__cifra--manda { letter-spacing: -0.02em; }

/* ⚠️ AQUÍ HUBO UNA CEBRA Y SE RETIRÓ EL MISMO DÍA (2026-08-25), por dos razones y las dos
   se vieron mirando, no midiendo:
   1. CAÍA EN LAS FILAS EQUIVOCADAS. La regla era `:nth-child(even)` con el comentario «así
      la primera queda limpia»… pero `.cc-casos__cab` es el primer hijo del contenedor, así
      que desplaza la paridad y el teñido caía en la 1ª, 3ª y 5ª — exactamente lo contrario
      de lo escrito. Un comentario que afirma lo que la regla no hace es peor que no tener
      comentario.
   2. Y AUNQUE CAYERA BIEN, SOBRA. Con la cifra de sesiones en acento verde, la fila ya
      tiene un ancla para el ojo; el teñido solo añadía barras flotantes que no alineaban
      con la cabecera. Los hairlines que ya tenía la tabla hacen el trabajo.
   Se deja escrito para que nadie la reponga «para dar ritmo». */

/* Las columnas numéricas se alinean a la DERECHA, cabecera incluida. Es lo que hace
   comparables tres columnas de cifras de largo distinto —`2.782.000` contra `470.700`— y
   de paso recoge el vacío que quedaba entre la última cifra y el borde de la tabla: los
   números terminaban donde acababa su texto, no donde acaba su columna. Con
   `tabular-nums`, que la tabla ya usa, las unidades quedan una debajo de otra. */
/* ⚠️ HACE FALTA `align-items`, NO BASTA `text-align`, y la primera versión se quedó a
   medias: la cabecera se alineó y las cifras no. Los bloques de Bricks son FLEX EN COLUMNA,
   así que cada hijo se encoge a su contenido y queda pegado al inicio del eje — `text-align`
   se aplica dentro de una caja que ya mide lo mismo que su texto, o sea no hace nada. Lo
   que mueve la caja es el alineamiento del flex. */
.cc-caso > .cc-caso__celda {
  align-items: flex-end;
  text-align: right;
}
.cc-casos__cab > .cc-casos__col:not(:first-child) { text-align: right; }
@media (max-width: 767px) {
  /* En fichas ya no hay columnas que comparar: vuelve a la izquierda, con su rótulo. */
  .cc-caso > .cc-caso__celda { align-items: flex-start; text-align: left; }
}

/* El periodo deja de competir con el logo: se separa de él y baja de peso. Es dato de
   contexto, no identidad del cliente. */
.cc-caso__periodo { opacity: .72; }
.cc-caso__quien { gap: 12px; }

@media (max-width: 767px) {
  /* En fichas, la cifra que manda no puede ser la mitad de alta que la ficha: se acota. */
  .cc-caso > .cc-caso__celda:nth-child(3) .cc-caso__cifra { font-size: var(--cc-fs-h3); }
  .cc-caso { padding-inline: 0; background: none; }
  .cc-caso:nth-child(even) { background: none; }
}

/* ── LAS PESTAÑAS: LA COLUMNA IZQUIERDA DEJA DE ESTAR VACÍA (2026-08-25) ───
   Medido: la ilustración ocupa 202×202 px en una columna de 349, con la descripción caída
   al fondo — la mitad izquierda se lee como un hueco con dos cosas en los extremos,
   mientras la derecha lleva cuatro viñetas largas. Y la ilustración es **la misma en las
   cuatro pestañas** (medido: `imgs-pestanas-seo.webp` ×4), así que no informa de nada:
   si sirve igual para «SEO técnico» que para «Linkbuilding», no dice nada de ninguno.

   No se retira —es contenido— pero deja de gobernar la columna: baja a 132px y se agrupa
   con la descripción a la que acompaña, en vez de flotar arriba con el texto al fondo.

   ⚠️ Y queda dicho lo que NO arregla el CSS: la ilustración está FUERA DE PALETA (lilas y
   azules claros que no son ningún token). Las 4 imágenes de tarjeta de esta misma página
   se retiñeron el 2026-08-17 con `tools/img/seo-tarjetas-duotono.py`; esta no, porque es
   un icono plano y la receta es para foto. Hace falta una variante, y eso es asset.
   ────────────────────────────────────────────────────────────────────────── */

/* ⚠️ `img.cc-seo-pane__art` sigue aquí y en el rediseño YA NO SE USA: la ilustración se
   retiró del panel el 2026-08-25. La regla se conserva porque `/lab-agencia-seo/` —la copia
   del re-esqueleto de agosto, que está para comparar— sí la lleva. Si esa copia se retira,
   esta regla se va con ella. */
img.cc-seo-pane__art { max-width: 132px; }

/* ── LA SECCIÓN DE PESTAÑAS DEJA DE VERSE DESORDENADA (2026-08-25) ─────────
   Ricardo: *«lo de "Servicios SEO completos para tu negocio" lo encuentro muy desordenado,
   además la imagen la encuentro innecesaria»*. Medido, eran tres cosas distintas:

   1. LA ILUSTRACIÓN. Retirada en el generador — era la misma en las 4 pestañas.
   2. LA COLUMNA IZQUIERDA quedaba con la descripción caída al fondo, porque el grupo se
      repartía a los extremos de una columna estirada. Ahora arranca ARRIBA, a la misma
      altura que la primera viñeta de su derecha: las dos mitades empiezan juntas, que es
      lo que hace que se lean como una sola explicación.
   3. LA BARRA DE LA PESTAÑA ACTIVA vivía a 16px de su propio rótulo, así que no se leía
      como suya sino como una raya suelta encima de la fila. A 8px es del rótulo.
   ────────────────────────────────────────────────────────────────────────── */
/* ── EL PANEL SE APILA: CITA · DESCRIPCIÓN · LISTA A DOS COLUMNAS (2026-08-25) ──
   Ricardo, sobre esta sección: *«se ve simple/feo/desordenado»*, y tras arreglar la fila de
   pestañas seguía sobrando algo — **aire**. Medido: la columna izquierda llevaba cuatro
   líneas de descripción y luego ~200px de hueco, porque su alto lo mandaba la lista de la
   derecha. Dos columnas de peso muy distinto no son dos columnas: son una columna y un
   margen ocupado.

   Ahora las tres piezas se apilan y cada una usa el ancho que le corresponde: la cita
   abre, la descripción la sigue como entradilla acotada a la medida de lectura, y la lista
   —que es lo que más pesa— baja a todo el ancho en DOS columnas. La sección encoge y no
   queda ni un hueco sin función.

   ⚠️ La divisoria vertical se retira con la rejilla: era el borde de la segunda columna, y
   sin dos columnas no separa nada. Vuelve a ser lo que ya avisaba su comentario original —
   un elemento que existe para explicar una relación— y esa relación ahora la explica el
   orden. */
.cc-seo-pane {
  grid-template-columns: 1fr;
  gap: clamp(14px, 1.6vw, 22px);
}
.cc-seo-pane__izq {
  justify-content: flex-start;
  gap: 16px;
  /* Entradilla: texto de lectura, acotado como el resto del sitio. */
  max-width: var(--cc-measure);
}
.cc-seo-pane__der {
  border-left: 0;
  padding-left: 0;
}
/* La lista a dos columnas. `auto-fit` con un mínimo de 340px: a 1180 entran dos, y en
   cuanto no caben vuelve sola a una sin necesidad de otra consulta de medios. El hueco
   entre columnas es mayor que el de filas para que no se lean como una sola lista. */
.cc-seo-pane__lista ul {
  /* ⚠️ DOS COLUMNAS FIJAS, no `auto-fit`. Con `auto-fit` y un mínimo de 340px a 1180 de
     ancho entran TRES, y las listas de estas pestañas tienen CUATRO puntos: la cuarta caía
     sola en una segunda fila. Es exactamente el descuadre 3+1 que ya se corrigió en las
     tarjetas de «Más allá del SEO» unas líneas más arriba — cuando el número de elementos
     es conocido y pequeño, la rejilla se declara, no se negocia. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 48px;
}
@media (max-width: 900px) {
  .cc-seo-pane__lista ul { grid-template-columns: 1fr; }
}
/* ⚠️ Y HAY QUE SOLTAR LA MEDIDA DEL CONTENEDOR, o las dos columnas no caben. La regla
   `.cc-seo-pane__der > .brxe-text { max-width: var(--cc-measure) }` se puso el 2026-08-17
   porque `ux.mjs` cazó la lista a 755px, por encima del ancho con el que se lee el resto
   del sitio. Sigue siendo correcta para UNA columna y deja de serlo para dos: la medida de
   lectura es una propiedad de la LÍNEA, no del bloque, y aquí cada columna mide ~566px —
   por debajo del límite— mientras el bloque mide 1180. Cap al bloque y la rejilla no puede
   partir; cap por columna y se lee igual de bien en la mitad de alto. */
/* ⚠️ La especificidad importa aquí: quien pone el tope es `.cc-seo-pane__der > .brxe-text`
   (0,2,0), así que `.cc-seo-pane__lista` a secas (0,1,0) NO lo levanta — medido, la lista
   seguía a 700px. Se encadenan las dos clases sobre el mismo elemento, que es lo mínimo
   para ganar sin inventar un `!important`. */
.cc-seo-pane__der > .brxe-text.cc-seo-pane__lista {
  max-width: none;
  /* ⚠️ Y NO BASTA CON QUITAR EL TOPE: hay que estirar. Es la MISMA trampa que en la tabla
     de casos — los bloques de Bricks son flex, así que el hijo se encoge a su contenido y
     se queda en 700px aunque su `max-width` sea `none`. Medido en el árbol: contenedor
     1180, lista 700, `max-width: none`. Dos veces el mismo tropiezo en la misma tanda:
     en este theme, ante «no ocupa el ancho», mirar el FLEX antes que el `max-width`. */
  width: 100%;
  align-self: stretch;
}
.cc-seo-pane__lista li { max-width: var(--cc-measure); }

/* La cita del cliente deja de competir con el H2 de la sección. Sigue siendo el elemento
   que abre el panel —es lo único distinto entre las cuatro pestañas y por eso se queda
   arriba— pero baja de peso y gana aire por debajo. */
.cc-seo-pane > h3 { margin-bottom: 4px; }

/* Las viñetas eran los puntos por defecto del navegador. Pasan al sistema: un guion de
   acento, que es lo que usa el resto del sitio en sus listas. No cambia una palabra. */
.cc-seo-pane__lista ul { list-style: none; padding-left: 0; }
.cc-seo-pane__lista li { position: relative; padding-left: 22px; }
.cc-seo-pane__lista li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.68em;
  width: 10px;
  height: 2px;
  background: var(--cc-fg-accent);
  border-radius: 2px;
}


@media (max-width: 767px) {
  /* En móvil la matriz se convierte en fichas (ver `.cc-caso` arriba) y el bloque crece
     mucho: la separación se acorta para que el CTA no quede a dos pantallas del titular. */
  .cc-seo-apertura .cc-seo-evidencia {
    margin-top: var(--cc-space-md, 24px);
    padding-top: var(--cc-space-md, 24px);
  }
  .cc-seo-apertura .cc-caso__cifra { font-size: var(--cc-fs-h4); }
}

/* ==========================================================================
   LA CAPA DE TEMA ESTÁNDAR, AMPLIADA A BOTONES, FAQ Y TARJETAS (2026-08-17)

   Ricardo, tras revisar el re-esqueleto de `/servicios/agencia-seo/`: *«deja SEO como
   estaba, pero ¿no se puede dejar de tal manera con el tema de la página? ejemplo
   botones, preguntas frecuentes, tarjetas»*. La respuesta no es un mecanismo nuevo: es
   exactamente lo que `.cc-tema-estandar` viene a hacer desde el 2026-08-10 —«estandarizar,
   NO rediseñar»— solo que la capa cubría familia tipográfica, radios y duraciones, y se
   quedaba corta en esos tres componentes. Medido antes de escribir:

     · los botones tenían TRES tratamientos en la misma página: relleno verde con radio
       de píldora, contorno con el verde VIEJO #0BE5A2, y el de Gravity Forms con radio
       19px y 17,25px de letra, escapándose de la capa entera;
     · el acordeón de la FAQ no tenía NADA: cinco titulares sueltos, sin separadores y sin
       ninguna señal de que se abren;
     · las tarjetas de «Más allá del SEO» no tenían superficie ni radio, y una traía un
       borde azul #0081C6 de la paleta anterior.

   ⚠️ Se toca la capa de SISTEMA y no la maqueta: ni un padding, ni un margin, ni un width
   que mueva una caja de sitio. Es la regla del encargo original de esta clase, y es lo
   que hace que esto valga para las 5 páginas de la lista y no solo para SEO.
   ========================================================================== */

/* 1. LOS BOTONES, uno solo. El de contorno pasa al verde vigente —el #0BE5A2 es de antes
   del 2026-07-23 y llevaba ahí desde entonces porque nada mira el color de un borde— y el
   de Gravity Forms entra en la misma forma que los demás en vez de traer la suya. */
body.cc-tema-estandar #brx-content :is(.bricks-button, .brxe-button, .gform_button) {
  border-radius: var(--cc-radius-pill, 999px);
  font-family: var(--cc-font-sans);
  font-weight: 700;
}
body.cc-tema-estandar #brx-content :is(.bricks-button, .brxe-button):not([class*="bricks-background"]) {
  border-color: var(--cc-fg-accent);
}
body.cc-tema-estandar #brx-content .gform_button {
  font-size: var(--cc-fs-sm);
  padding: 12px 28px;
}

/* 2. LA FAQ. Aquí la capa NO dibuja nada: alinea y colorea lo que la página ya trae.

   ⚠️ ESTA REGLA EMPEZÓ DIBUJANDO UN CHEVRON Y ESE ERA EL BUG (Ricardo, 2026-08-17: *«en seo
   el preguntas frecuentes se ve mal»*). Se veían dos por fila: el `i.ion-ios-arrow-down`
   nativo —gris y pegado al final del texto, o sea a una x distinta en cada fila, porque
   sigue el ancho del titular— y el verde que añadía un `::after` nuestro, al borde derecho.

   El origen tiene nombre: la afirmación «el acordeón no tenía NADA» se midió sobre la
   página RE-ESQUELETADA, que se revirtió horas después. Medir una versión y escribir la
   regla para otra es 8.62 con otro disfraz.

   ⚠️ Y EL PRIMER ARREGLO TAMPOCO BASTÓ, que es la parte que vale la pena recordar. Se hizo
   condicional —dibujar solo si no hay `> .brxe-icon`— y el inventario decía que
   `/agencia-vtex-partner/` no tenía icono, así que la rama de dibujo parecía necesaria.
   **El inventario estaba mal: preguntaba solo por HIJOS DIRECTOS.** VTEX usa el acordeón
   clásico de Bricks, con otra estructura —`.accordion-title-wrapper > .accordion-title >
   h3 + i.expanded + i`—, o sea dos iconos anidados un nivel más abajo. Sí tenía chevron; el
   detector no lo veía. Y como ahí el envoltorio es `display: block` y no `flex`, el
   `::after` no se iba a la derecha: caía a la línea siguiente, contra el margen izquierdo,
   como una marca verde debajo de cada pregunta.
   Lo delata que la medición daba VERDE: contaba «0 dobles» porque su idea de «nativo» era
   la equivocada. Un chequeo solo prueba la pregunta que sabe hacer (8.53).

   Remedido con la estructura real, las 3 páginas con FAQ de esta lista YA TRAEN chevron:
   `agencia-seo` 5/5, `influencer-marketing` 4/4 y `agencia-vtex-partner` 7/7. O sea la rama
   de dibujo no la necesitaba nadie: era código especulativo, y encima rompía. Se borró.
   **La capa estandariza lo que hay; cuando cree que falta algo, conviene mirar otra vez.** */
body.cc-tema-estandar #brx-content .brxe-accordion-nested > .brxe-block {
  border-bottom: 1px solid color-mix(in srgb, var(--cc-fg) 12%, transparent);
}
body.cc-tema-estandar #brx-content .accordion-title-wrapper { padding: 6px 0; }

/* El chevron se va al borde derecho y toma el acento. `margin-left: auto` es el arreglo de
   verdad y no el color: sin él el icono queda donde termine el texto, así que cada fila lo
   tiene a una x distinta y la columna no existe.
   ⚠️ NO se le pone `transform`: Bricks ya lo rota al abrir —medido, pasa a
   `matrix(-1,0,0,-1,0,0)`, o sea 180°— y una rotación nuestra encima se sumaría a la suya.
   Los dos selectores son las dos estructuras de acordeón que Bricks emite, no una
   duplicación: `> .brxe-icon` es el anidado (SEO, influencer) y `.accordion-title .icon`
   el clásico (VTEX), donde además hay dos iconos —abierto y cerrado— y por eso se colorean
   los dos. VTEX ya los alineaba solo, así que ahí esto es solo color. */
body.cc-tema-estandar #brx-content .accordion-title-wrapper > .brxe-icon {
  margin-left: auto;
  color: var(--cc-fg-accent);
}
body.cc-tema-estandar #brx-content .accordion-title .icon {
  color: var(--cc-fg-accent);
}

/* 3. LAS TARJETAS con imagen reciben la superficie del sistema. Se reconocen por
   ESTRUCTURA —un bloque cuyo hijo directo es una imagen— y no por una lista de ids, que es
   lo que `pendientes.md` lleva advirtiendo que se pudre. `:has()` ya se usa en esta hoja
   (`body.blog .brxe-section:has(.cc-postgrid)`), así que no estrena técnica.

   ⚠️ Y pide imagen **Y TEXTO** como hijos directos, no solo imagen. La primera versión
   pedía solo la imagen y Ricardo lo cazó en el acto —*«se ve mal SEO»*—: encajonaba la
   ilustración del hero y la del formulario, que son envoltorios de una imagen sola, no
   tarjetas. Medido: de 7 coincidencias a 5 en agencia-seo, y las dos que se van son
   justamente las de 1100px y 550px. Una tarjeta es una imagen CON algo que decir al lado;
   un bloque con una imagen dentro es cualquier cosa.
   ⚠️ Y el borde sale del token: una de estas tarjetas traía #0081C6, azul de la paleta
   anterior, que sobre el fondo actual no pertenece a nada.

   ⚠️⚠️ TERCERA CONDICIÓN, Y ES LA QUE FALTABA: la tarjeta tiene que ser MIEMBRO DE UN
   CONJUNTO. «Imagen + texto» seguía encajonando cosas que no son tarjetas, y Ricardo cazó
   dos el 2026-08-17: el `<h1>` de `/portafolio/` —«Portafolio CC» con el punto verde de
   marca al lado, que es una imagen de 20x15— y la ilustración con pie del panel «SEO
   técnico», que convive con una lista de viñetas y no con otras tarjetas.

   Se midieron los 18 candidatos de las 5 páginas contra varios discriminadores —tamaño de
   la imagen, orden imagen/texto, número de hijos, presencia de titular— y el único que
   separa sin excepciones es CUÁNTOS HERMANOS COMPARTEN SU MISMA FORMA:

     hermanos = 1  ->  los 6 falsos positivos, sin excepción
                       (`Portafolio CC`, la ilustración de SEO y sus 3 gemelas de pestaña
                        oculta, y el `<h3>` «Recibe una propuesta» de influencer)
     hermanos >= 3 ->  los 12 verdaderos, sin excepción
                       (SXO/GEO/ESO/AEO, y los dos grupos de influencer)

   Y no es una correlación que salió de los datos: es la definición. **Una tarjeta es una
   unidad que se repite.** Un bloque con imagen y texto que está SOLO en su contenedor es
   una maqueta —una ilustración con su pie, un titular con su adorno—, y ponerle superficie
   lo convierte en un componente que no existe.

   ⚠️ CÓMO SE ESCRIBE «TIENE HERMANA», Y POR QUÉ NO COMO PARECE. Lo natural sería preguntar
   por el padre —«aquí dentro hay dos o más»— pero eso obliga a meter la forma DENTRO de un
   `:has()`, y la forma ya contiene `:has()`. **`:has()` no se puede anidar dentro de otro
   `:has()`**: el selector no es inválido a medias, el navegador descarta la regla ENTERA y
   en silencio. Escrito así estuvo un rato, y el síntoma engañaba: `agencia-seo` parecía
   correcta —sus 4 tarjetas seguían con borde, pero era el borde propio de Bricks, no este—
   mientras influencer-marketing perdía los suyos. Comprobado con `querySelectorAll`, que
   lanza excepción con el selector anidado y es la forma barata de no confiar en la vista.

   Así que la pregunta se hace de lado, con el hermano siguiente, que no necesita anidar:
     · `FORMA + FORMA`  -> todas las del conjunto menos la primera
     · `FORMA:has(+ bloque > imagen)` -> la primera, mirando hacia adelante
   La unión es «pertenece a una serie». Contrastado contra los 18 candidatos de las 5
   páginas: **18/18**, sin un solo falso positivo ni negativo. */
body.cc-tema-estandar #brx-content
  :is(.brxe-block, .brxe-div):has(> .brxe-image):has(> :is(.brxe-text, .brxe-heading, .brxe-text-basic))
  + :is(.brxe-block, .brxe-div):has(> .brxe-image):has(> :is(.brxe-text, .brxe-heading, .brxe-text-basic)),
body.cc-tema-estandar #brx-content
  :is(.brxe-block, .brxe-div):has(> .brxe-image):has(> :is(.brxe-text, .brxe-heading, .brxe-text-basic)):has(+ :is(.brxe-block, .brxe-div) > .brxe-image) {
  border-radius: var(--cc-radius-lg);
  border: 1px solid color-mix(in srgb, var(--cc-fg) 10%, transparent);
  background: color-mix(in srgb, var(--cc-fg) 5%, transparent);
  overflow: hidden;
  /* Las del conjunto miden lo mismo aunque su texto no. Sin esto, la tarjeta de AEO —la de
     pie más largo— bajaba 22px respecto a sus tres hermanas, y con el borde puesto ese
     desnivel se ve: la superficie es lo que vuelve visible una desalineación que antes no
     lo era. O sea lo arregla quien lo causó. */
  align-self: stretch;
  transition: transform var(--cc-dur-fast, .18s) var(--cc-ease-out, ease),
              border-color var(--cc-dur-fast, .18s) var(--cc-ease-out, ease);
}
body.cc-tema-estandar #brx-content
  :is(.brxe-block, .brxe-div):has(> .brxe-image):has(> :is(.brxe-text, .brxe-heading, .brxe-text-basic))
  + :is(.brxe-block, .brxe-div):has(> .brxe-image):has(> :is(.brxe-text, .brxe-heading, .brxe-text-basic)):hover,
body.cc-tema-estandar #brx-content
  :is(.brxe-block, .brxe-div):has(> .brxe-image):has(> :is(.brxe-text, .brxe-heading, .brxe-text-basic)):has(+ :is(.brxe-block, .brxe-div) > .brxe-image):hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--cc-fg-accent) 45%, transparent);
}

/* ==========================================================================
   ACORDEÓN EN MÓVIL: EL CHEVRON NO SE VA A LA LÍNEA DE ABAJO (2026-08-26, 8.112)
   ==========================================================================
   Reportado por Ricardo desde su teléfono: *«las preguntas frecuentes también se ven
   mal»*. Medido a 390px en `/servicios/agencia-seo/`:

     .accordion-title-wrapper   342x56   flex-wrap: WRAP   padding: 0
       └ h3                     342x46   ← ocupa el ancho ENTERO
       └ ::after (el chevron)    10x10   ← no le queda sitio: cae a la 2ª línea

   O sea la marca de desplegar aparecía DEBAJO de la pregunta, pegada a la línea
   separadora, y con `min-height:56px` el contenido (46+10) llenaba la fila al
   milímetro: cero aire arriba y abajo. Una pregunta de una sola línea se veía bien, y
   por eso pasaba desapercibido en el escritorio, donde ninguna llega a dos líneas.

   ⚠️ NO es un defecto del componente: la página VTEX usa el mismo acordeón y ahí está
   bien (`nowrap`, `padding: 6px 0`, la fila crece a 60px con títulos de dos líneas).
   Es la configuración de ESTA página, que Bricks escribe como `#brxe-seo211
   .accordion-title-wrapper` — (1,1,0), así que hace falta un ID para ganarle y
   `#brx-content` es el único genérico que existe en todas las páginas de Bricks.
   `body` delante lo deja en (1,2,0), que gana pase lo que pase con el orden de carga.

   Se acota a ≤767px a propósito: sobre ese ancho el `flex-wrap` ya vale `nowrap` y los
   títulos caben en una línea, así que tocarlo ahí sería cambiar algo que funciona. */
@media (max-width: 767px) {
  body #brx-content .accordion-title-wrapper {
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--cc-space-4);
    /* Deja respirar las preguntas de dos líneas. La fila crece porque Bricks fija
       `min-height` y no `height`. */
    padding-block: var(--cc-space-3);
  }
  /* Sin esto, `nowrap` no encoge el título —un hijo flex no baja de su contenido— y el
     chevron se sale de la caja en vez de bajar de línea: el mismo bug con otro síntoma.
     Es 8.101 desde el otro lado. */
  body #brx-content .accordion-title-wrapper > * { min-width: 0; }
  /* Y el chevron no puede encogerse a cero al apretar. */
  body #brx-content .accordion-title-wrapper::after { flex: 0 0 auto; }
}

/* =============================================================================
   FORMULARIOS DE GRAVITY FORMS (2026-08-26, gotcha 8.113)
   =============================================================================
   Ricardo, comparando `/contacto/` con la landing del E-Commerce Blend: *«es más
   decente del que tenemos»*. Tenía razón, y era medible:

     campo URL 38px de alto contra 48 del resto   ·   borde VERDE en todos los campos
     botón de enviar 38px  ← por debajo del mínimo de 44 de WCAG 2.5.8
     botón en menta pálido #ADEFDB, que no es ningún valor de la paleta

   ⚠️ Y la causa no es descuido: **nadie lo había visto nunca**. Gravity Forms es de
   licencia y en local no está, así que el theme pinta una MAQUETA (`ccd_form_maqueta()`),
   cuyo propio comentario dice que «no pretende ser el formulario final: pretende ocupar
   lo que ocupa el real». Resolvió lo suyo —el layout de la página— y dejó este hueco, que
   solo se ve donde hay GF de verdad: el staging, que existe desde el 2026-08-25. Lo que se
   veía era el tema `orbital` de Gravity Forms **sin tematizar**, con sus valores de
   fábrica (azul #204ce5, fondo blanco, borde gris).

   POR QUÉ SE TOCAN VARIABLES Y NO REGLAS. El tema `orbital` está enteramente dirigido por
   una familia `--gf-color-*` de la que deriva todo lo demás. Sobrescribir esas variables
   —que es la vía que el propio framework documenta— es robusto: no pelea con la
   especificidad de GF, no se rompe cuando actualicen sus hojas, y hace falta una sola
   declaración por decisión en vez de una por selector.

   POR QUÉ NO SE COPIA EL CSS DE LA LANDING. El suyo vive en los ajustes de Bricks de
   `/ecommerce-blend/`, o sea EN LA BD (§10): no viaja en git y habría que rehacerlo en cada
   entorno y en cada página. Aquí cubre los 10 formularios del sitio de una vez.

   ⚠️ EL ALCANCE EXCLUYE LAS LANDINGS DE EVENTO, y no por casualidad: su `<body>` no lleva
   NINGUNA clase `cc-`, así que acotar a las páginas que el theme gobierna las deja fuera
   sola. Se comprobó midiendo las dos: `/ecommerce-blend/` es `page-id-9590` y nada más.
   Eso importa porque esas landings ya se ven bien y son la referencia, no el problema.

   ⚠️ Y EXCLUÍA TAMBIÉN LA PORTADA, que no es lo mismo: ahí no había landing ajena que
   respetar sino la pieza más trabajada del sitio sirviendo EL MISMO formulario 1 desde
   `#brxe-sec127`. Su `<body>` tampoco llevaba clase `cc-`, así que «acotar a las páginas
   que el theme gobierna» la dejó fuera junto con las landings — el criterio era correcto y
   el marcador no existía. Se añade `.cc-home`, que emite `body_class` por `is_front_page()`.
   Lo que se veía mientras tanto está medido en 8.114: los selectores de id que la BD
   incrusta DENTRO del formulario ganaban por un punto de especificidad.

   Y NO ES UNA COPIA, ES UNA TRADUCCIÓN. La landing es de tema oscuro fijo; `/contacto/`
   tiene conmutador, así que todo sale de tokens que ya voltean (`--cc-fg`, `--cc-border`,
   `--cc-fg-read`) y solo la superficie del campo necesita su propio valor por tema. Y el
   verde es el de la escala (`--cc-green-500`), no el `#0BE5A2` de la landing: estrenar un
   valor fuera de la paleta es 8.18. ========================================== */

body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper {
  /* La superficie del campo: en oscuro es un vidrio sobre el fondo y en claro es papel
     hundido. El valor de dark vive en `cc-tokens.css` desde el 2026-08-27, porque una isla
     oscura necesita recuperarlo en modo claro sin volver a escribir el rgba (8.114). */
  --cc-form-superficie: var(--cc-form-superficie-on-dark);

  /* La familia raíz de GF. De estas seis deriva el tema entero. */
  --gf-color-primary: var(--cc-green-500);
  --gf-color-primary-contrast: var(--cc-ink-900);
  --gf-color-primary-darker: var(--cc-green-700);
  --gf-color-in-ctrl: var(--cc-form-superficie);
  --gf-color-in-ctrl-contrast: var(--cc-fg);
  --gf-color-in-ctrl-light: var(--cc-border);
  --gf-color-in-ctrl-light-darker: var(--cc-border);
  --gf-color-out-ctrl-dark: var(--cc-fg-read);
  --gf-color-out-ctrl-light: var(--cc-border-soft);
  --gf-ctrl-radius: var(--cc-radius-sm);
}
html[data-theme="light"] body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper {
  --cc-form-superficie: var(--cc-bg-surface);
}

/* Los controles. Las variables no cubren el ALTO ni el tamaño de letra, así que van aquí.
   La especificidad (1,3,1) le gana al `#gform_wrapper_N[data-form-index]` que GF inyecta
   en línea, que es (1,1,0) — sin esto no aplica nada de lo de abajo. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(input[type="text"], input[type="email"], input[type="tel"], input[type="url"],
      input[type="number"], select, textarea) {
  /* 48px, no 44: el mínimo táctil es 44 y un campo de texto se toca peor que un botón
     porque hay que acertar dentro para colocar el cursor. Es además el alto que ya tenían
     los campos buenos de esta página, así que no cambia el layout que se diseñó. */
  min-height: 48px;
  padding: 0 var(--cc-space-3);
  /* ⚠️ 16px NO ES ESTÉTICO: bajo 16px, iOS hace ZOOM al enfocar un campo y deja al
     visitante con la página descuadrada y sin forma obvia de volver. La landing usa 14px
     y por eso le pasa. Es el motivo por el que este valor no se «mapea a la escala» hacia
     abajo. */
  font-family: var(--cc-font-sans);
  font-size: var(--cc-fs-sm);
  color: var(--cc-fg);
  background-color: var(--cc-form-superficie);
  border: 1px solid var(--cc-border);
  border-radius: var(--cc-radius-sm);
  transition: border-color var(--cc-dur-fast) var(--cc-ease);
}
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper textarea {
  min-height: 120px;
  padding: var(--cc-space-3);
  line-height: var(--cc-lh-relaxed);
}
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(input, select, textarea):focus-visible {
  outline: 2px solid var(--cc-fg-accent);
  outline-offset: 2px;
  border-color: var(--cc-fg-accent);
}
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper ::placeholder {
  color: var(--cc-fg-muted);
  opacity: 1;   /* Firefox lo atenúa por su cuenta y el marcador queda ilegible. */
}

/* La etiqueta. Va por debajo del cuerpo pero NO por debajo de 12px, que es donde a11y
   empieza a marcar. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(.gfield_label, legend.gfield_label) {
  font-family: var(--cc-font-sans);
  font-size: var(--cc-fs-xs);
  font-weight: var(--cc-fw-semibold);
  color: var(--cc-fg-read);
  margin-bottom: var(--cc-space-2);
}

/* El botón. Era lo peor del formulario: 38px de alto —bajo el mínimo táctil— y un menta
   pálido que no está en la paleta. Se alinea con el CTA flotante, que es el mismo gesto:
   verde de marca, tinta casi negra encima (13.14:1) y forma de píldora. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(input[type="submit"], button[type="submit"], .gform_button) {
  min-height: 48px;
  padding: var(--cc-space-3) var(--cc-space-8);
  font-family: var(--cc-font-sans);
  font-size: var(--cc-fs-sm);
  font-weight: var(--cc-fw-bold);
  color: var(--cc-ink-900);
  background-color: var(--cc-green-500);
  border: 0;
  border-radius: var(--cc-radius-pill);
  cursor: pointer;
  transition: background-color var(--cc-dur-fast) var(--cc-ease);
  /* A TODO EL ANCHO, como en la landing del Blend. No es capricho de tamaño: en un
     formulario largo el envío es el único paso siguiente, y un botón pequeño alineado a la
     izquierda compite con el resto de la columna en vez de cerrarla. Ocupar la línea es lo
     que lo convierte en el final del recorrido. */
  width: 100%;
}
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(input[type="submit"], button[type="submit"], .gform_button):hover {
  background-color: var(--cc-green-700);
}

/* El área de texto. ⚠️ `min-height` NO basta: GF le pone la clase `textarea small`, que
   fija `height`, y una altura explícita gana a un mínimo. Medido antes de esto: 50px con
   `rows='10'` en el markup, o sea el atributo tampoco mandaba. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  textarea:is(.small, .medium, .large, [class]) {
  height: auto;
  min-height: 140px;
  padding: var(--cc-space-3);
  line-height: var(--cc-lh-relaxed);
  resize: vertical;
}

/* Casillas y radios. Medido en el staging: 20x21 px, por debajo del mínimo de 24x24 de
   WCAG 2.5.8 — y `a11y.mjs` ya lo venía marcando contra el servidor. Aquí no se puede
   crecer «un poco»: o pasa el mínimo o no. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(input[type="checkbox"], input[type="radio"]) {
  width: 24px;
  height: 24px;
  accent-color: var(--cc-green-500);
  cursor: pointer;
}

/* EL SELECTOR DE PAÍS del teléfono (`phoneFormat: formatted` + `defaultCountry: cl`).
   ⚠️ NO es un `<select>` —eso costó una medición—: GF lo renderiza como un `<button>`
   con `aria-haspopup="listbox"`, así que un selector escrito contra `select` no encuentra
   nada y el chequeo diría que está bien. Va emparejado con el campo: son un solo control
   partido en dos, y con el radio completo en los dos lados se leen como dos cosas. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  .gform-phone__country-selector {
  min-height: 48px;
  padding: 0 var(--cc-space-3);
  font-family: var(--cc-font-sans);
  font-size: var(--cc-fs-sm);
  color: var(--cc-fg);
  background-color: var(--cc-form-superficie);
  border: 1px solid var(--cc-border);
  border-right: 0;
  border-radius: var(--cc-radius-sm) 0 0 var(--cc-radius-sm);
  cursor: pointer;
}
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  .gform-phone:has(.gform-phone__country-selector) input[type="tel"] {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
/* La etiqueta «País» del selector es una sub-etiqueta y no debe competir con la del campo. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  .gform-field-label--type-sub {
  font-size: var(--cc-fs-xxs);
  color: var(--cc-fg-muted);
}

/* ⚠️ NEUTRALIZAR LOS BLANCOS FIJOS QUE SIRVE LA BD (2026-08-26, gotcha 8.113).
   Medido en el staging, modo claro: las etiquetas del formulario salían en BLANCO PURO
   sobre el papel `#EEEEF1` — **1.16:1**, ilegible. No lo causaba el theme: lo encontró
   `CSS.getMatchedStylesForNode` (CDP), que da la lista autoritativa de reglas aplicadas y
   es lo único que lo habría encontrado (8.102):

     .gfield_label { color: white !important }        ← <style> incrustado en la PÁGINA
     #input_1_6 label { color: white }                ← estilo de elemento de Bricks

   Los dos viven en la BD, o sea no viajan en git y no se pueden corregir desde aquí sin
   entrar al builder — y ahí se perderían con el próximo `post-import` si la pieza estuviera
   versionada. Un blanco fijo es correcto mientras solo existe el tema oscuro y deja de
   serlo el día que hay conmutador: es exactamente el caso que §10 pide resolver en CSS.

   Se usa `!important` a regañadientes y SOLO en el color, que es lo que falla: para ganar a
   un `!important` no basta con especificidad. El tamaño y el peso que fija la página se
   respetan — añadir más `!important` del necesario es cómo una hoja se vuelve imposible de
   razonar. En oscuro `--cc-fg-strong` ES blanco, así que ahí no cambia nada. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(.gfield_label, legend.gfield_label) {
  color: var(--cc-fg-strong) !important;
}
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(.gfield_checkbox label, .gfield_radio label, .gfield_consent label) {
  color: var(--cc-fg-read) !important;
}

/* La lista de opciones, en REJILLA y no en flujo (2026-08-26, 8.113). Reportado por Ricardo
   mirando el staging: *«se ve horrible»*. Medido: `.gfield_checkbox` es `flex-wrap: wrap`, o
   sea cada opción ocupa lo que mide SU TEXTO y las siguientes se acomodan donde caben —
   «Diseño y UX» y «Desarrollo web» compartían fila y el resto no, así que las columnas salían
   dentadas y la lista se leía como un amontonamiento.
   Con `auto-fit` la rejilla decide el nº de columnas por el ancho disponible y no por el largo
   de la copia: 2 en escritorio, 1 bajo ~520px. `.gchoice` se deja como está —GF ya lo alinea
   con su propia rejilla— porque tocar la alineación interna no era el problema. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper .gfield_checkbox,
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper .gfield_radio {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--cc-space-3) var(--cc-space-6);
  align-items: start;
}

/* LA TARJETA DE CRISTAL (2026-08-26, 8.113). Ricardo: *«me refería al efecto cristal o
   blur»*. Medido en la landing del Blend, y el matiz importa: el cristal NO lo hace el
   desenfoque. Su `.bl-formcard` lleva `backdrop-filter: blur(18px)`, pero detrás tiene un
   color PLANO (`rgb(5,2,3)`), así que ese blur no desenfoca nada — lo que se ve es la
   superficie blanca translúcida al 6% con un borde al 16%. Copiar solo el `blur` no habría
   dado el efecto, y es el tipo de cosa que se descubre midiendo en vez de leyendo el CSS.

   Se conserva el `backdrop-filter` igualmente: cuesta cero sobre un fondo plano y paga solo
   el día que detrás haya un degradado o una foto — que es justo lo que puede pasar aquí.

   ⚠️ TRAMPA COMPROBADA ANTES DE PONERLO (8.85): un `backdrop-filter` crea bloque contenedor
   para los `position:fixed` que tenga dentro, y eso ya mordió una vez en este repo. Medido:
   cero elementos fijos dentro de la sección del formulario en las dos páginas.

   ⚠️ Y la superficie se declara en LOS DOS TEMAS a propósito. Un `rgba(255,255,255,.06)`
   sobre papel es invisible, así que en claro la tarjeta se quedaría sin caja — y además el
   chequeo de 8.110 fallaría con razón: un componente no puede cambiar de forma al voltear el
   tema. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper {
  --cc-form-tarjeta: var(--cc-form-tarjeta-on-dark);
  --cc-form-tarjeta-borde: var(--cc-form-tarjeta-borde-on-dark);

  padding: var(--cc-space-7);
  background-color: var(--cc-form-tarjeta);
  border: 1px solid var(--cc-form-tarjeta-borde);
  border-radius: var(--cc-radius-md);
  backdrop-filter: blur(18px);
}
html[data-theme="light"] body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper {
  --cc-form-tarjeta: var(--cc-white);
  --cc-form-tarjeta-borde: var(--cc-border);
}

/* ⚠️ EL RELLENO DE LA TARJETA CEDE EN MÓVIL (2026-08-26, 8.113). Los 32px que la landing usa
   en escritorio provocaban **10px de desborde horizontal a 320px** —`scrollW=330`, con 14
   elementos fuera de caja— y lo cazó `responsive.mjs` en la misma tanda en que se puso la
   tarjeta. A ese ancho el relleno compite con el contenido, que es lo único que importa.
   No se quita: se reduce, porque sin nada de aire la tarjeta deja de leerse como una caja. */
@media (max-width: 520px) {
  body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper {
    padding: var(--cc-space-5);
  }
}

/* El contador de caracteres. Al meter el formulario en la tarjeta pasó a leerse sobre esa
   superficie en vez de sobre el fondo de sección, y su gris de fábrica cayó a **4.14:1** —bajo
   el 4.5 de AA— con 12px de letra. Lo cazó `a11y.mjs`. Sale del token de lectura, que es el
   que ya está medido en los dos temas.
   ⚠️ Y de paso queda dicho: **en local esta cadena SÍ está traducida** («0 de 600 caracteres
   máximos») y en el staging sale en INGLÉS. O sea no falta código: falta el fichero de idioma
   de Gravity Forms en ese entorno. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(.charleft, .ginput_counter, .gform_validation_errors + *) {
  color: var(--cc-fg-read);
}

/* ⚠️ EL PUNTO VERDE QUE FLOTA BAJO EL BOTÓN (2026-08-27, gotcha 8.114). Lo sirve la BD,
   en el mismo `<style>` incrustado que pintaba los campos de negro:

     #gform_submit_button_1::after { content:""; width:8px; height:8px; background:#0BE5A2;
                                     position:relative; bottom:-35px; right:0 }

   `bottom:-35px` lo empuja 35px POR DEBAJO de su propio botón, así que no es el remate de
   nada: es un punto suelto al final del formulario. Y el punto verde SÍ es motivo de marca
   —es el mismo gesto de `.cc-acento`—, lo que lo hace peor de leer: parece un adorno
   colocado mal, no un adorno de más.
   Se neutraliza el pseudo-elemento ENTERO y no solo su desplazamiento: reposicionar un
   adorno que la BD coloca a ojo deja las dos capas discutiendo dónde va, y la BD gana en
   el próximo entorno. Lo correcto es retirarlo del campo HTML del formulario 1 desde el
   editor de Gravity Forms; mientras eso no pase, esto lo tapa. Revertir = borrar esta regla.
   ⚠️ El `#0BE5A2` de ese punto tampoco es de la paleta: es el verde viejo, el que se
   cambió por `#3DEDB4` el 2026-07-23 (8.18). */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(input[type="submit"], button[type="submit"], .gform_button)::after {
  content: none;
}

/* ⚠️ EL FORMULARIO DE LA PORTADA VIVE EN UNA ISLA OSCURA, Y 8.113 VOLTEA CON EL TEMA
   (2026-08-27, gotcha 8.114). `#brxe-sec127` se queda en `#201E2C` en los DOS temas —está
   en la lista de islas del bloque de modo claro, arriba en este archivo—, así que meter su
   formulario en el alcance de 8.113 le trajo también el modo claro de 8.113, que está
   escrito para una sección que SÍ voltea. Medido en el staging antes de esto:

     tarjeta   rgb(255,255,255)        ← una tarjeta de papel dentro de una banda #201E2C
     borde     rgba(32,30,44,0.18)     ← canto oscuro sobre oscuro: la caja desaparecía
     campo     borde rgba(32,30,44,.18) ← los campos se quedaban sin canto

   La isla ya devolvía los tokens de TEXTO y `--cc-bg-surface` (por eso las etiquetas daban
   16.36:1 y los campos salían oscuros); lo que no podía devolver eran la tarjeta y el borde
   de control, que no existían como tokens cuando se escribió. Ahora sí, y esto solo los
   nombra: cero hex repetidos.
   ⚠️ La especificidad es deliberada. La regla de 8.113 a la que gana es
   `html[data-theme="light"] body:is(…) #brx-content .gform_wrapper`, o sea (1,3,1); con
   `body` delante esta queda en (1,4,1) y no depende del orden del archivo — que es lo que
   se rompe en cuanto alguien mueve un bloque. */
html[data-theme="light"] body #brx-content .cc-form-cierre .gform_wrapper {
  --cc-form-tarjeta:       var(--cc-form-tarjeta-on-dark);
  --cc-form-tarjeta-borde: var(--cc-form-tarjeta-borde-on-dark);
  --cc-form-superficie:    var(--cc-form-superficie-on-dark);
  --cc-border:             var(--cc-border-ctrl-on-dark);
  --cc-border-soft:        var(--cc-hairline-on-dark);
}

/* ⚠️ EL TELÉFONO NO COMPARTE FILA (2026-08-27, gotcha 8.114). Reportado por Ricardo mirando
   el formulario ya tematizado: *«se ve muy descuadrado todo»*, y comparándolo con el de
   `/ecommerce-blend/`, donde el Celular va SOLO en su línea.
   Medido a 1440 sobre el staging, con los dos en `gfield--width-half`:

     email     796,6037  232x104   control a 48px de alto
     teléfono 1045,6037  232x104   control partido en País + Nº, con DOS sub-etiquetas encima

   O sea las dos cajas miden lo mismo y aun así los controles no arrancan a la misma altura:
   el campo de teléfono gasta una línea de texto que su vecino no gasta. No es un fallo de
   alineación que se arregle con `align-items` — es que ese campo NO es del mismo alto que
   los demás, por construcción, así que ponerlo al lado de cualquier otro descuadra la fila.
   Y con el selector de país mide además 232px para tres cosas (bandera, prefijo y número).

   Va con `grid-column` y no tocando el ancho del campo en Gravity Forms a propósito: el
   ancho es config de FORMULARIO, o sea vive en la BD (§10) y habría que repetirlo en cada
   entorno y en cada uno de los formularios que use un teléfono. Aquí es una regla.
   El email entra en la misma regla porque era su pareja de fila: dejarlo a media caña con
   medio renglón vacío al lado es peor que la fila que se quería arreglar. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  :is(.gfield--type-phone, .gfield--type-email) {
  grid-column: 1 / -1;
}

/* ⚠️ UN PANEL FLOTANTE NO PUEDE SER TRANSLÚCIDO (2026-08-27, gotcha 8.114). Reportado por
   Ricardo abriendo el selector de país: *«al seleccionar el país se ve mal»* — y lo que se
   veía era la lista de casillas del formulario ATRAVESANDO la lista de países.

   Medido con el desplegable abierto, en el staging:

     #gform_phone_dropdown_… .gform-phone__dropdown
        background-color: rgba(255,255,255,0.07)   ← translúcido
        position: absolute · z-index: 1010          ← o sea flota sobre el contenido

   La causa es una consecuencia directa de cómo está tematizado 8.113, y por eso vive aquí y
   no en su bloque: `--gf-color-in-ctrl` alimenta **dos cosas a la vez** — la superficie de un
   campo y la de este panel—. Un vidrio al 7% es correcto para un campo, que está apoyado
   sobre un fondo sólido y quiere dejarlo ver; es incorrecto para algo que se despliega ENCIMA
   de otro contenido, donde lo de detrás no es fondo: es texto. La variable no distingue los
   dos papeles, así que hay que decirlo aquí.
   ⚠️ Sale de `--cc-bg-surface` y no de un rgba nuevo porque ese token **lo re-declara la isla
   oscura** del modo claro, así que el panel se queda opaco también cuando el visitante
   voltea el tema en la portada — que es donde vive esta isla. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper
  .gform-phone__dropdown {
  background-color: var(--cc-bg-surface);
  border: 1px solid var(--cc-border);
  border-radius: var(--cc-radius-sm);
}

/* ⚠️ Y DOS COSAS MÁS DEL SELECTOR DE PAÍS, que salieron midiendo el desplegable (8.114).
   Ninguna se veía sin abrirlo, y las dos son del mismo sitio: **es un `<button>`, no un
   `<select>`**, así que se cae de las reglas escritas para campos.

     con el teclado (Tab)   outline: 0px          ← un control operable SIN foco visible
     con el panel abierto   outline: 3px rgba(32,76,229,0.65)

   Ese `#204ce5` es **el azul de fábrica de Gravity Forms**, el mismo que 8.113 dio por
   sustituido — y no lo estaba: `--gf-color-primary` sí se cambió, pero el contorno sale de
   OTRA familia (`--gf-ctrl-outline-color`) que nadie había tocado. Es 8.37 en su versión de
   variables: la decisión estaba tomada y una rama del árbol no se enteró.
   El anillo sale de `--cc-fg-accent`, que es el token de PRIMER PLANO y voltea con el tema
   (en la isla oscura de la portada se queda en verde de marca, que es lo correcto). */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper {
  --gf-ctrl-outline-color: var(--cc-fg-accent);
  /* ⚠️ Y EL TRIPLETE, que es lo que de verdad pintaba el azul. Con solo el hex cambiado, el
     contorno del estado abierto seguía dando `rgba(32,76,229,0.65)`: esa rama de GF no usa
     `--gf-color-primary` sino `rgba(var(--gf-color-primary-rgb), .65)`, y el `-rgb` seguía
     siendo el de fábrica. Medido antes y después sobre el mismo nodo, con el panel abierto.
     ⚠️ **Aquí NO va una regla de `:focus-visible`**, y se probó a ponerla: el child theme ya
     da un anillo verde de 3px a todo `button` —`:where(a, button, input, …):focus-visible`—
     así que una regla propia solo habría creado un segundo sitio donde vive la misma
     decisión, y encima con otro grosor. Lo que faltaba era el color, no el anillo. */
  --gf-color-primary-rgb: var(--cc-green-500-rgb);
  --gf-color-in-ctrl-primary-rgb: var(--cc-green-500-rgb);
}

/* ⚠️ LA OPCIÓN RESALTADA DEL DESPLEGABLE, EN BLANCO SOBRE BLANCO (2026-08-27, 8.114).
   Ricardo, con el selector abierto: *«al seleccionar se ve en blanco»*. Medido:

     .gform-phone__country-item--focused   background rgb(245,245,245)  color rgb(255,255,255)
     .gform-phone__country-item--active    idem                          idem          → 1.06:1

   `#F5F5F5` es `--gf-color-secondary-darker`, de la familia `--gf-color-secondary-*` que
   8.113 **no tocó**: tematizó `primary` y `in-ctrl` —lo que se ve sin interactuar— y esta
   familia solo aparece al abrir un desplegable, o sea nunca salió en una captura. Es el mismo
   modo de fallo que el azul del contorno: la decisión estaba tomada y una rama del árbol no
   se enteró (8.37 en versión de variables).

   Se tematiza la familia entera y NO solo la fila resaltada, que es la vía de 8.113: de
   `secondary` cuelgan también el hover del botón terciario y los menús que traiga GF mañana.
   El resalte sale de `color-mix` sobre `--cc-fg` en vez de un hex: en oscuro `--cc-fg` es
   blanco y levanta la fila, en claro es tinta y la hunde — el mismo gesto con el signo que
   toque, sin dos valores que mantener. */
body:is(.cc-servicios, .cc-tema-estandar, .cc-home) #brx-content .gform_wrapper {
  --gf-color-secondary: var(--cc-bg-surface);
  --gf-color-secondary-contrast: var(--cc-fg);
  --gf-color-secondary-darker: color-mix(in srgb, var(--cc-fg) 14%, transparent);
  --gf-color-secondary-lighter: var(--cc-bg-surface);
  --gf-color-in-ctrl-light-lighter: color-mix(in srgb, var(--cc-fg) 14%, transparent);
  --gf-ctrl-btn-bg-color-hover-tertiary: color-mix(in srgb, var(--cc-fg) 14%, transparent);
}

/* ⚠️ LA MARCA «PRÓXIMAMENTE» DE CANAL.DATA (2026-08-27, gotcha 8.118). El mega-menú ya
   distinguía los tres ítems de esa unidad —van en `<span class="cc-mega__proximo">`, en
   gris atenuado y sin cursor de enlace: medido, **6:1** de contraste contra el **12.37:1**
   de los enlaces de al lado—, así que la decisión estaba tomada y aplicada.
   Lo que faltaba es que se dijera. La distinción vivía en el NOMBRE DE LA CLASE y en el
   tono, y eso no es una afirmación: lo destapó una auditoría hecha leyendo el DOM, que dio
   por rotos tres ítems sin `href`. Si un lector humano del markup lo lee así, un visitante
   que ve tres líneas grises apagadas también.

   Va en la CABECERA de la columna y no en cada ítem: los tres son la misma unidad, y
   repetir la palabra tres veces convierte una aclaración en ruido.
   ⚠️ Y va en el MARKUP, no en un `::after`. Es la decisión opuesta a la del guion de los
   silos (`.cc-silo__vacio::after`) y por la misma regla: aquello es SEÑALIZACIÓN —no se
   lee en voz alta ni se traduce— y esto es CONTENIDO, que sí.

   No estrena ningún valor: 14px es `--cc-fs-xs`, que ya usa la portada, y el color es el
   mismo `--cc-fg-muted` de los tres ítems. Y no mueve el trinquete tipográfico ni aunque
   lo estrenara: el panel va `hidden`, así que `ux.mjs` —que filtra por `offsetParent`— no
   lo cuenta. */
.cc-mega__pronto {
  margin-left: 8px;
  font-size: var(--cc-fs-xs);
  font-weight: var(--cc-fw-medium);
  color: var(--cc-fg-muted);
  white-space: nowrap;
}


/* ==========================================================================
   /geo-aeo/ — H3. La maqueta propia, con la capa de sistema (2026-08-31)
   ==========================================================================

   DE DÓNDE VIENE ESTE BLOQUE. Hasta hoy el diseño entero de `/geo-aeo/` eran **8.736
   caracteres de CSS a nivel de PÁGINA** guardados en `_bricks_page_settings.customCss`,
   o sea **en la BD** (§10), que Bricks compilaba a `uploads/bricks/css/post-9485.min.css`.
   Ninguna otra pieza rediseñada tiene eso: medido el 2026-08-31, `/servicios/`,
   `/contacto/`, `servicios/creatividad` y `servicios/agencia-seo` tienen ese campo
   **vacío** y su estilo vive acá. `/geo-aeo/` era la excepción, y esto la deshace.

   POR QUÉ IMPORTABA. Ese CSS clavaba siete colores a mano, y **cuatro son valores que el
   sistema RETIRÓ el 2026-07-23**:

       #0BE5A2 ×12  → var(--cc-green-500)   (#3DEDB4)
       #191919 ×5   → var(--cc-bg)          (#0A0A0F)
       #1E1E1E ×4   → var(--cc-bg-band)     (#101016)
       #442ABA ×2   → var(--cc-bg-impulse)  (#4B2FBF)

   O sea la página era una foto de la paleta anterior al cambio, y por eso `tema.mjs`
   decía que **responde al tema un 0 %**: sus fondos no eran tokens, así que el
   conmutador ahí era un control que no cambiaba nada.

   ⚠️ EL ALCANCE `body.cc-geo` NO ES COSMÉTICO, ES LA RED. De las 61 clases de esta
   página, **11 reusan nombres del sistema con otros valores** —`.cc-btn`, `.cc-card`,
   `.cc-hero`, `.cc-dot`, `.cc-badge`, `.cc-tabs`, `.cc-tab`, `.cc-cta`, `.cc-dato`,
   `.cc-servicios`, `.cc-bt`—. Traerlas acá sin acotar las haría **globales** y rompería
   el sitio entero. Cada regla va prefijada, y las dos únicas que no lo están son los
   `@keyframes`, que no tienen alcance.

   ⚠️ Y ESO DEJA UNA DEUDA ESCRITA, a propósito: mientras esas 11 existan, hay dos
   definiciones del mismo nombre en el repo y solo el prefijo las separa. Reconciliarlas
   —que la página use el `.cc-btn` del sistema— cambia su aspecto, y el pedido del
   2026-08-31 fue explícito: «hazlo parecido a como está». Se deja para cuando esa
   página se rediseñe de verdad, no ahora.

   LO QUE SÍ SE CAMBIÓ ADEMÁS DE LOS COLORES:
     · los rellenos de sección a mano (104/108, 100/100, 110/110, 96/96) → `--cc-sec-*`;
     · los blancos con alfa ALTOS (.62–.86), que son TEXTO, → `--cc-fg-read` y
       `--cc-fg-muted`, porque en claro un blanco sobre papel es invisible;
     · los BAJOS (.06–.16), que son filetes y velos, → `--cc-hairline-on-dark` y
       `--cc-rule-on-dark`, que voltean con el tema;
     · la guarda de `prefers-reduced-motion`, que el original **no tenía** (ver abajo).

   Los tres `rgba(0,0,0,…)` que quedan son canales de MÁSCARA, no colores de diseño: ahí
   el negro significa «opaco» y sustituirlo por un token sería un error.
   ========================================================================== */
/* ⚠️ LOS DOS ERRORES DE MAPEO QUE CAZÓ `tema.mjs` AL ESTRENAR ESTE BLOQUE, y la regla que
   los explica: en este sistema hay tokens de PALETA y tokens de ROL, y **solo los de rol
   voltean con el tema**.

   (a) Verde de paleta como color de TEXTO. `--cc-green-500` es paleta: en claro sigue
       siendo #3DEDB4 y sobre papel blanco da **1.5:1**. Los 6 casos —`.cc-eyebrow`,
       `.cc-badge`, `.cc-check`, `.cc-card-tag`, `.cc-step-num`, `.cc-faq-sign`— pasan a
       `--cc-fg-accent`, que es el token de ROL y voltea a `--cc-green-ink` (#067A57).

   (b) Texto SOBRE el verde. Al revés: el FONDO es paleta y no voltea, así que el texto
       tiene que ser un oscuro **estable**. Llevaban `--cc-bg-deep`, que sí voltea, o sea
       en claro quedaba papel claro sobre verde: **1.43:1**. Pasan a `--cc-ink-900`.

   La forma general: al portar CSS clavado, un hex de fondo y un hex de texto **no se
   traducen al mismo tipo de token**, aunque el hex sea idéntico. Fondo → rol; texto sobre
   un color de marca → paleta. Sustituir por hex, como hizo la primera pasada de este
   porte, produce exactamente estos dos fallos y ninguna otra suite los ve: el contraste en
   OSCURO era correcto en los diez casos. */
body.cc-geo .cc-sec {font-family:'Montserrat',-apple-system,'Helvetica Neue',Arial,sans-serif;-webkit-font-smoothing:antialiased;color:var(--cc-fg);display:block;}
body.cc-geo .cc-sec,
body.cc-geo .cc-sec *,
body.cc-geo .cc-sec *::before,
body.cc-geo .cc-sec *::after {box-sizing:border-box;}
body.cc-geo .cc-wrap {width:100%;max-width:1240px;margin:0 auto;padding:0 48px;}
body.cc-geo .cc-dot {display:inline-block;width:16px;height:16px;border-radius:50%;background:var(--cc-green-500);margin-left:10px;vertical-align:baseline;}
body.cc-geo .cc-btn {display:inline-flex;align-items:center;gap:10px;padding:17px 34px;border-radius:999px;background:var(--cc-green-500);color:var(--cc-ink-900);font-weight:700;font-size:17px;text-decoration:none;transition:transform .18s,background-color .18s;font-family:inherit;border:none;cursor:pointer;}
body.cc-geo .cc-btn:hover {background:var(--cc-green-400);transform:translateY(-2px);color:var(--cc-ink-900);}
body.cc-geo .cc-arrow {font-size:18px;line-height:1;}
body.cc-geo .cc-eyebrow {color:var(--cc-fg-accent);font-weight:600;font-size:13px;letter-spacing:.14em;text-transform:uppercase;margin-bottom:16px;}
body.cc-geo .cc-h2 {font-weight:800;font-size:42px;line-height:1.1;margin:0;color:var(--cc-fg);letter-spacing:-.01em;}
body.cc-geo .cc-head-center {text-align:center;margin-bottom:52px;}
body.cc-geo .cc-head-center .cc-h2 {max-width:720px;margin:0 auto;}
body.cc-geo .cc-head-left {max-width:760px;margin-bottom:56px;}
/* HERO */
body.cc-geo .cc-hero{position:relative;overflow:hidden;background:linear-gradient(180deg,var(--cc-bg-deep) 55%,var(--cc-bg) 100%);}
body.cc-geo .cc-hero::before {content:"";position:absolute;top:-55%;left:50%;width:1700px;height:1700px;transform:translateX(-50%);background:repeating-conic-gradient(from 0deg at 50% 50%, color-mix(in srgb, var(--cc-sky-500) calc(0 * 100%), transparent) 0deg, color-mix(in srgb, var(--cc-sky-500) calc(.32 * 100%), transparent) .55deg, color-mix(in srgb, var(--cc-green-500) calc(.22 * 100%), transparent) 1.5deg, rgba(0,0,0,0) 2.9deg);-webkit-mask:radial-gradient(circle at 50% 50%, #000 0%, rgba(0,0,0,.6) 34%, transparent 60%);mask:radial-gradient(circle at 50% 50%, #000 0%, rgba(0,0,0,.6) 34%, transparent 60%);animation:ccSpin 45s linear infinite;opacity:.55;pointer-events:none;z-index:0;}
body.cc-geo .cc-hero::after {content:"";position:absolute;inset:0;background-image:radial-gradient(var(--cc-fg-muted) 1px,transparent 1px);background-size:44px 44px;-webkit-mask:radial-gradient(circle at 50% 40%,#000 0%,transparent 55%);mask:radial-gradient(circle at 50% 40%,#000 0%,transparent 55%);opacity:.12;pointer-events:none;z-index:0;}
@keyframes ccSpin{to{transform:translateX(-50%) rotate(360deg);}
}
@keyframes ccPulse{0%,100%{opacity:.85}
50% {opacity:.35}
}
body.cc-geo .cc-hero-wrap {position:relative;max-width:1000px;padding:118px 48px 140px;text-align:center;z-index:1;}
body.cc-geo .cc-badge {display:inline-flex;align-items:center;gap:9px;padding:8px 16px;border:1px solid color-mix(in srgb, var(--cc-green-500) calc(.4 * 100%), transparent);border-radius:999px;font-size:12.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--cc-fg-accent);margin-bottom:30px;}
body.cc-geo .cc-badge-dot {width:7px;height:7px;border-radius:50%;background:var(--cc-green-500);animation:ccPulse 2.4s ease-in-out infinite;flex:none;}
body.cc-geo .cc-hero-h1 {font-weight:800;font-size:66px;line-height:1.02;letter-spacing:-.02em;margin:0 auto 26px;max-width:900px;color:var(--cc-fg);}
body.cc-geo .cc-hero-h1 .cc-dot {width:16px;height:16px;}
body.cc-geo .cc-hero-sub {font-size:20px;line-height:1.55;color:var(--cc-fg-read);max-width:660px;margin:0 auto 40px;}
body.cc-geo .cc-hero-sub strong {color:var(--cc-fg);font-weight:700;}
/* SERVICIOS */
body.cc-geo .cc-servicios{background:var(--cc-bg);padding: var(--cc-sec-md) 0 108px;}
body.cc-geo .cc-tabs {display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-bottom:48px;}
body.cc-geo .cc-tab {background:transparent;border:1px solid var(--cc-rule-on-dark);border-radius:999px;padding:12px 22px;font-family:inherit;font-weight:600;font-size:14.5px;color:var(--cc-fg-read);cursor:pointer;transition:all .18s;}
body.cc-geo .cc-tab:hover {border-color:var(--cc-fg-muted);color:var(--cc-fg);}
body.cc-geo .cc-tab.is-active {background:var(--cc-green-500);border-color:var(--cc-green-500);color:var(--cc-ink-900);}
body.cc-geo .cc-panel {display:none;background:var(--cc-bg-band);border:1px solid var(--cc-hairline-on-dark);border-radius:24px;padding:56px 56px 52px;}
body.cc-geo .cc-panel.is-active {display:block;}
body.cc-geo .cc-panel-head {margin-bottom:40px;max-width:820px;}
body.cc-geo .cc-quote {font-weight:700;font-size:30px;line-height:1.22;margin:0 0 14px;color:var(--cc-fg);}
body.cc-geo .cc-panel-desc {font-size:18px;line-height:1.6;color:var(--cc-fg-read);margin:0;}
body.cc-geo .cc-bullets {display:grid;grid-template-columns:1fr 1fr;gap:2px;background:var(--cc-hairline-on-dark);border-radius:16px;overflow:hidden;}
body.cc-geo .cc-bullet {background:var(--cc-bg-band);padding:26px 28px;display:flex;gap:16px;align-items:flex-start;}
body.cc-geo .cc-check {flex:none;width:26px;height:26px;border-radius:8px;background:color-mix(in srgb, var(--cc-green-500) calc(.14 * 100%), transparent);color:var(--cc-fg-accent);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:800;margin-top:2px;}
body.cc-geo .cc-bt {font-weight:700;font-size:16px;color:var(--cc-fg);margin-bottom:5px;}
body.cc-geo .cc-bd {font-size:15px;line-height:1.55;color:var(--cc-fg-read);}
/* CONCEPTS */
body.cc-geo .cc-concepts{background:var(--cc-bg-deep);padding: var(--cc-sec-md) 0 110px;}
body.cc-geo .cc-cards {display:grid;grid-template-columns:repeat(4,1fr);gap:20px;}
body.cc-geo .cc-card {background:var(--cc-bg-band);border:1px solid var(--cc-hairline-on-dark);border-radius:18px;padding:32px 28px;}
body.cc-geo .cc-card-sov {border-color:color-mix(in srgb, var(--cc-violet-500) 35%, transparent);}
body.cc-geo .cc-card-tag {display:inline-block;font-weight:800;font-size:15px;letter-spacing:.02em;color:var(--cc-fg-accent);margin-bottom:14px;}
body.cc-geo .cc-tag-violet {color:var(--cc-violet-100);}
body.cc-geo .cc-card-title {font-weight:700;font-size:16px;color:var(--cc-fg);margin-bottom:12px;line-height:1.3;}
body.cc-geo .cc-card-desc {font-size:15px;line-height:1.6;color:var(--cc-fg-read);margin:0;}
/* DATO */
body.cc-geo .cc-dato{background:var(--cc-bg-impulse);padding: var(--cc-sec-md) 0;}
body.cc-geo .cc-dato-grid {max-width:1080px;display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:start;}
body.cc-geo .cc-dato-h {font-weight:800;font-size:44px;line-height:1.12;margin:0;color:var(--cc-fg);}
body.cc-geo .cc-dato-text {display:flex;flex-direction:column;gap:22px;}
body.cc-geo .cc-dato-text p {font-size:18px;line-height:1.65;color:var(--cc-fg-read);margin:0;}
/* METODO */
body.cc-geo .cc-metodo{background:var(--cc-bg);padding: var(--cc-sec-md) 0 108px;}
body.cc-geo .cc-steps {display:grid;grid-template-columns:1fr 1fr;gap:2px;background:var(--cc-hairline-on-dark);border:1px solid var(--cc-hairline-on-dark);border-radius:20px;overflow:hidden;}
body.cc-geo .cc-step {background:var(--cc-bg);padding:34px 36px;display:flex;gap:22px;align-items:flex-start;}
body.cc-geo .cc-step-num {flex:none;font-weight:800;font-size:34px;color:var(--cc-fg-accent);line-height:1;width:44px;}
body.cc-geo .cc-step-t {font-weight:700;font-size:19px;color:var(--cc-fg);margin-bottom:8px;}
body.cc-geo .cc-step-d {font-size:15.5px;line-height:1.6;color:var(--cc-fg-read);margin:0;}
/* SOMOS */
body.cc-geo .cc-somos{background:var(--cc-bg-deep);padding: var(--cc-sec-md) 0;}
body.cc-geo .cc-somos-wrap {max-width:900px;text-align:center;}
body.cc-geo .cc-somos-h {font-size:40px;line-height:1.16;margin:0 0 24px;}
body.cc-geo .cc-somos-p {font-size:19px;line-height:1.6;color:var(--cc-fg-read);margin:0 auto;max-width:680px;}
/* FAQ */
body.cc-geo .cc-faq{position:relative;background:var(--cc-bg);padding: var(--cc-sec-md) 0 104px;overflow:hidden;}
body.cc-geo .cc-faq::before {content:"";position:absolute;inset:0;background:repeating-linear-gradient(115deg,color-mix(in srgb, var(--cc-green-500) calc(.05 * 100%), transparent) 0px,color-mix(in srgb, var(--cc-green-500) calc(.05 * 100%), transparent) 2px,transparent 2px,transparent 26px);pointer-events:none;z-index:0;}
body.cc-geo .cc-faq-wrap {position:relative;max-width:920px;z-index:1;}
body.cc-geo .cc-faqs {display:flex;flex-direction:column;gap:12px;}
body.cc-geo .cc-faq-item {background:var(--cc-bg-band);border:1px solid var(--cc-hairline-on-dark);border-radius:14px;overflow:hidden;}
body.cc-geo .cc-faq-q {width:100%;display:flex;align-items:center;justify-content:space-between;gap:20px;text-align:left;background:transparent;border:none;padding:22px 26px;cursor:pointer;font-family:inherit;}
body.cc-geo .cc-faq-q>span:first-child {font-weight:600;font-size:17px;color:var(--cc-fg);}
body.cc-geo .cc-faq-sign {flex:none;width:28px;height:28px;border-radius:50%;background:var(--cc-hairline-on-dark);color:var(--cc-fg-accent);display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:600;line-height:1;}
body.cc-geo .cc-faq-item.is-open .cc-faq-sign {background:var(--cc-green-500);color:var(--cc-ink-900);}
body.cc-geo .cc-faq-a {display:none;padding:0 26px 24px;font-size:16px;line-height:1.65;color:var(--cc-fg-read);}
body.cc-geo .cc-faq-item.is-open .cc-faq-a {display:block;}
/* CTA */
body.cc-geo .cc-cta{background:var(--cc-bg-impulse);padding: var(--cc-sec-sm) 0;position:relative;}
body.cc-geo .cc-cta-wrap {max-width:900px;text-align:center;position:relative;}
body.cc-geo .cc-cta-h {font-weight:800;font-size:40px;line-height:1.12;margin:0 0 18px;color:var(--cc-fg);}
body.cc-geo .cc-cta-p {font-size:18px;line-height:1.6;color:var(--cc-fg-read);margin:0 auto 38px;max-width:560px;}
body.cc-geo .cc-anchor {position:absolute;top:-110px;left:0;}
/* RESPONSIVE */
@media(max-width:1000px){body.cc-geo .cc-hero-h1{font-size:52px;}body.cc-geo .cc-h2,
  body.cc-geo .cc-dato-h{font-size:34px;}body.cc-geo .cc-dato-grid{grid-template-columns:1fr;gap:28px;}body.cc-geo .cc-cards{grid-template-columns:1fr 1fr;}
}
@media(max-width:768px){body.cc-geo .cc-wrap{padding:0 24px;}body.cc-geo .cc-hero-wrap{padding:80px 24px 96px;}body.cc-geo .cc-hero-h1{font-size:40px;}body.cc-geo .cc-hero-sub{font-size:18px;}body.cc-geo .cc-h2,
  body.cc-geo .cc-dato-h,
  body.cc-geo .cc-somos-h,
  body.cc-geo .cc-cta-h{font-size:30px;}body.cc-geo .cc-bullets,
  body.cc-geo .cc-steps{grid-template-columns:1fr;}body.cc-geo .cc-panel{padding:32px 24px;}body.cc-geo .cc-quote{font-size:24px;}
}
@media(max-width:560px){body.cc-geo .cc-cards{grid-template-columns:1fr;}body.cc-geo .cc-hero-h1{font-size:34px;}
}

/* ⚠️ LA GUARDA QUE EL ORIGINAL NO TENÍA. Las dos animaciones de esta página —el giro del
   resplandor del hero y el latido del punto de la insignia— corrían SIN condición. El
   sistema de movimiento del sitio respeta `prefers-reduced-motion` en todo lo demás
   (8.11–8.13, 8.24), así que esta página era el único sitio donde un usuario que pide
   menos movimiento lo recibía igual. No es estética: es la red de seguridad del grupo. */
@media (prefers-reduced-motion: reduce) {body.cc-geo .cc-hero::before,
  body.cc-geo .cc-badge-dot{ animation: none !important; }
}


/* ⚠️ LAS ISLAS VIOLETAS NO VOLTEAN, Y POR ESO SU TEXTO TAMPOCO PUEDE.
   Tercera capa del mismo error, y la que costó dos vueltas de `tema.mjs` verlo. `.cc-dato`
   y `.cc-cta` llevan `background: var(--cc-bg-impulse)` — un color de MARCA, estable en los
   dos temas. Dentro de ellas, los tokens de ROL sí voltean, así que en claro pasaba esto:

       1.14 : 1   «Las marcas que no aparecen en IA…»   --cc-fg      → gris oscuro sobre violeta
       1.14 : 1   «Cada vez más usuarios resuelven…»    --cc-fg-read → gris sobre violeta
       1.76 : 1   «SHARE OF VOICE»                      --cc-fg-accent → verde ink sobre violeta

   O sea el arreglo anterior —pasar los verdes de texto a `--cc-fg-accent`— era correcto en
   general y **equivocado dentro de una isla de marca**, donde lo que hace falta es lo
   contrario: valores estables. La regla, y es la que hay que llevarse: **el tipo de token
   del texto lo decide si su FONDO voltea o no, no el tema de la página.**

   Se resuelve con una sola regla acotada a las dos islas en vez de tocando cada clase: así
   una sección violeta nueva hereda el arreglo sin que nadie se acuerde. */
body.cc-geo :is(.cc-dato, .cc-cta) :is(h1, h2, h3, h4, p, li, span, strong, .cc-h2) {
	color: var(--cc-white);
}
body.cc-geo :is(.cc-dato, .cc-cta) :is(.cc-eyebrow, .cc-card-tag, .cc-check, .cc-step-num) {
	/* Menta de PALETA, no `--cc-fg-accent`: sobre el violeta da 6,5:1 en los dos temas. */
	color: var(--cc-fg-mint);
}

/* ⚠️ EL ÚLTIMO, y es el caso que NO se resuelve eligiendo mejor el token: `.cc-tag-violet`
   —la etiqueta «SHARE OF VOICE», que va violeta a propósito para distinguirse de las tres
   verdes— llevaba `--cc-violet-100` (#BAAEE7). Es un lila CLARO, pensado para leerse sobre
   fondo oscuro: ahí da 9:1, y sobre la tarjeta clara del modo claro **1.76:1**.

   Y acá no sirve cambiar de token, porque **no existe un token de ROL para texto violeta**
   —el sistema tiene `--cc-fg-accent` para el verde y nada equivalente en violeta—. Las dos
   salidas eran estrenar un token de rol nuevo, que es decisión del equipo (§7), o declarar
   los dos valores acá con el mismo mecanismo que usa `cc-tokens.css`. Se elige lo segundo:
   es la solución más pequeña y no añade vocabulario al sistema por una etiqueta.

   El claro va a `--cc-violet-600` (#4B2FBF): 7,4:1 sobre la tarjeta. El oscuro se queda
   como está. Y el selector es `html[data-theme="light"]`, que es exactamente el que usa el
   bloque claro de los tokens — no un inventado. */
html[data-theme="light"] body.cc-geo .cc-tag-violet {
	color: var(--cc-violet-600);
}


/* ==========================================================================
   /geo-aeo/ — pasada VISUAL sobre el feedback de Ricardo (2026-08-31, tarde)
   ==========================================================================
   Cuatro puntos, con sus palabras, y cada arreglo alineado con lo que el sistema YA hace en
   otras páginas en vez de estrenar tratamiento (§7).
   ========================================================================== */

/* (1) «Esta parte no me gusta» — la banda violeta del cierre.
   El diagnóstico: la banda se leía como una TARJETA flotando entre dos zonas blancas, no
   como una banda del sitio. El sistema tiene su propio tratamiento y es plano y a sangre:
   `.cc-banda-violeta { background-color: var(--cc-violet-600) }`, sin radio. Acá se le
   quita el radio y se fuerza el ancho completo para que ancle con el resto de la página. */
body.cc-geo :is(.cc-dato, .cc-cta) {
	border-radius: 0;
	margin-inline: 0;
	width: 100%;
	max-width: none;
}

/* (2) «Las preguntas frecuentes se ven distintas a las otras páginas» — cierto, y medido:
   `/servicios/agencia-seo/` usa el acordeón nativo de Bricks con `.cc-seo-faq`, cuyo
   tratamiento es **un filete inferior y nada más** —sin tarjeta, sin relleno, sin radio—:

       .cc-seo-faq > .brxe-block { border-bottom: 1px solid rgba(255,255,255,.12) }
       html[data-theme="light"] … { border-bottom-color: rgba(32,30,44,.16) }

   Esos dos valores son exactamente `--cc-rule-on-dark` en cada tema, así que acá se usa el
   token y no los literales. El `+` pierde su círculo verde relleno —que era la mitad del
   «se ve distinto»— y queda como glifo de acento. */
body.cc-geo .cc-faq-item {
	background: transparent;
	border: 0;
	border-bottom: 1px solid var(--cc-rule-on-dark);
	border-radius: 0;
}
body.cc-geo .cc-faq-item:first-child { border-top: 1px solid var(--cc-rule-on-dark); }
body.cc-geo .cc-faq-q { padding-inline: 4px; }
body.cc-geo .cc-faq-sign {
	background: transparent;
	width: auto;
	height: auto;
	border-radius: 0;
	font-size: 22px;
	color: var(--cc-fg-accent);
}
body.cc-geo .cc-faq-item.is-open .cc-faq-sign {
	background: transparent;
	color: var(--cc-fg-accent);
}
body.cc-geo .cc-faq-a { padding-inline: 4px; }

/* (3) «Esa cápsula es muy de IA» — la píldora con borde verde y punto latiendo del hero.
   Y tiene razón: es la firma visual de un hero generado. La portada, que es la pieza de
   referencia, **no usa píldora**: su antetítulo es texto en versalitas con interletraje.
   Así que la cápsula se disuelve en el antetítulo que ya existe en esta misma página
   (`.cc-eyebrow`), y el punto latiendo se va con ella — era el otro tic del mismo estilo. */
body.cc-geo .cc-badge {
	padding: 0;
	border: 0;
	border-radius: 0;
	background: transparent;
	gap: 0;
	color: var(--cc-fg-accent);
	font-weight: 600;
	font-size: 13px;
	letter-spacing: .14em;
	text-transform: uppercase;
}
body.cc-geo .cc-badge-dot { display: none; }

/* (4) «En modo blanco hay detalles que no se ven y se ve muy plano todo».
   Medido, y son dos causas distintas:

   · Los PANELES se pintaban con relleno (`--cc-bg-band`), que en claro es un gris plano
     #EEEEF1: en oscuro un relleno da volumen, en claro da una mancha. Pasan a fondo
     transparente con filete, o sea la estructura la dibuja la línea y no el bloque.
   · Los DIVISORES de la rejilla de pasos y de las tarjetas usaban
     `--cc-hairline-on-dark`, que en claro vale 0.14 de alfa y desaparece. Pasan a
     `--cc-rule-on-dark` (0.16), que es el token de REGLAS y no el de filetes de tarjeta —
     la diferencia entre los dos existe justo para esto. */
body.cc-geo :is(.cc-panel, .cc-card, .cc-step) {
	background: transparent;
	border-color: var(--cc-rule-on-dark);
}
body.cc-geo .cc-steps {
	background: var(--cc-rule-on-dark);
	border-color: var(--cc-rule-on-dark);
}
body.cc-geo .cc-step { background: var(--cc-bg); }
body.cc-geo .cc-tab { border-color: var(--cc-rule-on-dark); }

/* ⚠️ Y el relieve que en oscuro daba el relleno hay que devolverlo por otro lado, o el
   arreglo de arriba deja el claro correcto y el oscuro más pobre que antes: en oscuro los
   paneles y tarjetas recuperan una superficie muy leve, que sobre el suelo #0A0A0F sí se
   percibe y sobre papel blanco no existiría. */
html:not([data-theme="light"]) body.cc-geo :is(.cc-panel, .cc-card) {
	background: color-mix(in srgb, var(--cc-white) 3%, transparent);
}


/* --------------------------------------------------------------------------
   /geo-aeo/ — el acordeón, igualado A LA REFERENCIA valor por valor (2026-08-31)
   --------------------------------------------------------------------------
   Segunda pasada. La primera copió el filete de `.cc-seo-faq` y Ricardo dijo, con razón,
   «sigue distinto que el Agencia-seo». En vez de seguir a ojo se DIFERENCIARON las
   propiedades calculadas de los dos acordeones renderizados, y las que no coincidían eran
   siete —no una—:

       propiedad          agencia-seo (referencia)      geo-aeo (antes)
       ancho                       1180 px                    824 px
       borde del item        0 arriba / 1 abajo         1 arriba / 1 abajo
       hueco entre items          0 (normal)                   12 px
       tamaño de pregunta          20 px                       17 px
       relleno del título      0 (va en el wrapper)        22px 4px
       indicador           chevron ::after 10px         glifo «+» de 22 px
       etiqueta                      H3                       SPAN

   Las seis primeras se igualan acá. **La séptima no**: la pregunta es un `<span>` dentro de
   un `<button>` y la referencia usa `<h3>`; cambiar eso es tocar el CONTENIDO de la página
   —el JSON, no el CSS— y además tiene consecuencias de jerarquía de encabezados y de SEO
   que no son de esta tanda. Queda anotado en `pendientes.md`, no arreglado a medias.

   ⚠️ El chevron se copia con los valores literales de la referencia (10px, 2px, 45deg /
   -135deg, `--cc-fg-accent`) en vez de aproximarlo: «se ve igual» es el requisito, así que
   la fuente de verdad es esa regla y no mi criterio. */

/* El ancho: TOPADO como la referencia, no libre.
   ⚠️ La primera versión ponía `max-width: none` porque midiendo a 1280 px la referencia daba
   1180 y parecía que llenaba el contenedor. **Eran cosas distintas**: 1180 es su tope FIJO
   (`max-width: 1180px` en su contenedor), no el ancho de la ventana. A 1920 la referencia
   sigue en 1180 y la mía se iba a **1824**, con el chevron a un metro de la pregunta — que es
   justo lo que Ricardo vio. Medir a UN solo ancho no distingue «llena el contenedor» de
   «está topado»: hacen falta dos.
   1276 = 1180 del tope + los 48 px de relleno de `.cc-wrap` a cada lado. */
body.cc-geo .cc-faq-wrap { max-width: 1276px; }

/* Lista continua: sin hueco entre filas y con filete SOLO abajo. */
body.cc-geo .cc-faqs { gap: 0; }
/* El alfa del filete se copia LITERAL de la referencia (.12 en oscuro) en vez de usar
   `--cc-rule-on-dark`, que vale .10: son 2 centésimas invisibles, pero el requisito de esta
   tanda es «que se vea igual que agencia-seo» y la fuente de verdad es esa regla. En CLARO
   el token ya coincide con la referencia (`rgba(32,30,44,.16)`), así que ahí sí se usa. */
body.cc-geo .cc-faq-item {
	border-top: 0;
	border-bottom: 1px solid rgba(255, 255, 255, .12);
}
html[data-theme="light"] body.cc-geo .cc-faq-item {
	border-bottom-color: var(--cc-rule-on-dark);
}
body.cc-geo .cc-faq-item:first-child { border-top: 0; }

/* La pregunta, al tamaño de la referencia; el relleno vertical baja al del wrapper. */
/* 16,5px y no 18: medido, la fila cerrada de la referencia mide **57 px** y con 18 daba 60.
   El número sale de igualar la altura renderizada, no de copiar el relleno — la referencia
   reparte el suyo entre el item y un `.accordion-title-wrapper` que este markup no tiene. */
body.cc-geo .cc-faq-q { padding: 16.5px 0; }
body.cc-geo .cc-faq-q > span:first-child { font-size: 20px; }
body.cc-geo .cc-faq-a { padding: 0 0 22px; }

/* El indicador: fuera el glifo de texto, dentro el chevron de la referencia. */
body.cc-geo .cc-faq-sign {
	font-size: 0;              /* oculta el «+» / «−» sin tocar el HTML */
	width: auto;
	height: auto;
	margin-left: 16px;
	background: transparent;
	position: relative;
	display: inline-flex;
	align-items: center;
}
body.cc-geo .cc-faq-sign::after {
	content: "";
	flex: 0 0 auto;
	width: 10px;
	height: 10px;
	border-right: 2px solid var(--cc-fg-accent);
	border-bottom: 2px solid var(--cc-fg-accent);
	transform: rotate(45deg);
	transition: transform var(--cc-dur-fast, .18s) var(--cc-ease-out, ease);
}
body.cc-geo .cc-faq-item.is-open .cc-faq-sign::after { transform: rotate(-135deg); }

/* ⚠️ EL CHEVRON SALÍA CORTADO, y son dos cosas que se suman (2026-08-31, tarde):

   · el `::after` mide 10×10 pero **rotado 45° su caja pasa a ~14,1 px** (10·√2), así que
     desborda a su padre de 10×10;
   · y `.cc-faq-item` heredaba `overflow: hidden` del CSS original —estaba ahí para recortar
     las esquinas redondeadas de la tarjeta, que ya no existe—, así que ese desborde se
     RECORTA. Lo que Ricardo veía como «flechas cortadas» y como una línea verde bajo la
     pregunta abierta era el mismo chevron a medias.

   La geometría de un cuadrado rotado es la trampa reutilizable: reservar la caja del lado
   no basta, hay que reservar la DIAGONAL. */
body.cc-geo .cc-faq-item { overflow: visible; }
body.cc-geo .cc-faq-sign {
	width: 16px;
	height: 16px;
	justify-content: center;
	overflow: visible;
}
