/**
 * cms-sections.css — la COMPOSICIÓN de las secciones del CMS.
 *
 * POR QUÉ EXISTE (2026-09-27). `cms-content.css` viste el texto que sale del
 * editor; esta hoja viste cómo se COLOCAN las secciones entre sí. Son cosas
 * distintas y por eso no comparten fichero: aquélla está acotada a `.cms-body`
 * —contenido, nunca chrome— y ésta habla de la caja de la sección.
 *
 * Lo que la hizo falta, medido sirviendo `kita.onlainez.net/de/konzept`: ocho
 * secciones, las ocho con la misma forma —cabecera arriba, cuerpo a ancho
 * completo, mismo fondo—. No había un solo defecto que arreglar y aun así la
 * página se leía plana, porque **el ritmo no estaba construido**: el core no
 * tenía forma de expresar ni «esta sección va en dos columnas» ni «esta va
 * sobre otra franja». Un tema que quisiera ritmo tenía que clavarlo en su CSS
 * por posición, que es el anti-patrón que `contrato-base.md` §14 persigue.
 *
 * REPARTO (§14). De la PIEZA —y por tanto del core— son la proporción de las
 * columnas, el ancho al que se apilan y el `sticky`. Del TEMA es el color: todo
 * sale de tokens `--cms-*` con el valor de hoy como respaldo, así que un tema
 * que no haga nada la ve correcta, y uno que mapee sus doce variables la ve en
 * su paleta. Ni un color literal, fuera de los respaldos.
 *
 * Referencia de la composición: las páginas de producto de Stripe, Linear y
 * Vercel alternan bloque-a-ancho / par-de-dos-columnas, y cambian la franja de
 * fondo para marcar el cambio de tema. Es ritmo, no decoración.
 */

/* Autocontención: la hoja no da por hecho el reset del tema. Los dos temas
   vivos declaran `box-sizing:border-box` —medido—, así que hoy sobra; está
   porque «funciona porque el de al lado lo arregla» no es una pieza del core,
   y el día que nazca un tema sin reset el padding se sumaría al 100 %.

   ⚠️ Y una advertencia de MÉTODO, que es lo que costó aquí: el
   desbordamiento que motivó estas líneas NO existía. Lo fabricaba el arnés
   —`--window-size` de Chrome no baja de un layout de 500px, y sin
   `<meta viewport>` la página se compone a 980— así que la foto de un móvil
   enseñaba contenido cortado POR LA HERRAMIENTA. Lo dice el docblock de
   `tests/visual/mobile.php`, que es el banco que sí mide y que se niega a
   medir cuando la emulación no ha tomado. Para esta pieza da
   `viewport 393px · content 393px`, OK. Cualquier juicio de ancho sobre una
   captura de `--window-size` es papel mojado. */
.cms-split,
.cms-split *,
.cms-split *::before,
.cms-split *::after { box-sizing: border-box; }

/* ── 1. La variante `split`: cabecera a un lado, lista al otro ───────────── */

.cms-split {
    padding: 4rem 1.5rem;
    background: var(--cms-surface, #fff);
}

.cms-split__in {
    max-width: 1100px;
    margin: 0 auto;
    display: grid;
    gap: 2rem;
    /* Una sola columna por defecto: el móvil es el caso base y no necesita
       media query para ser correcto. El par aparece cuando hay sitio (abajo). */
    grid-template-columns: 1fr;
}

.cms-split__head {
    min-width: 0;   /* un item de grid no encoge por debajo de su contenido sin esto */
    /* El copy no se estira a 1100px: una columna de texto legible ronda los
       60-70 caracteres, y ésta es la mitad estrecha del par a propósito. */
    max-width: 34rem;
}

.cms-split__title {
    margin: 0 0 .75rem;
    font-size: 1.875rem;
    font-weight: 800;
    line-height: 1.2;
    color: var(--cms-heading, #0f172a);
}

.cms-split__sub {
    margin: 0;
    font-size: 1.0625rem;
    line-height: 1.65;
    color: var(--cms-muted, #64748b);
}

.cms-split__list {
    /* Es una LISTA de verdad, no divs: un lector de pantalla anuncia cuántos
       elementos hay antes de leerlos, que es justo la información que da el
       formato en columna. `list-style:none` no le quita la semántica en los
       navegadores que importan aquí (`reglas/suelo-de-navegadores.md`). */
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 1rem;
}

.cms-split__item {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
    padding: 1.25rem 1.375rem;
    border-radius: .75rem;
    background: var(--cms-surface-alt, #f8fafc);
}

.cms-split__ico {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    line-height: 1;
    font-size: 1.125rem;
    color: var(--cms-heading, #0f172a);
}

.cms-split__ico svg {
    width: 1.125rem;
    height: 1.125rem;
}

.cms-split__body { min-width: 0; }

.cms-split__item-title {
    margin: 0 0 .25rem;
    font-size: 1.0625rem;
    font-weight: 700;
    color: var(--cms-heading, #0f172a);
}

.cms-split__text {
    margin: 0;
    font-size: .9375rem;
    line-height: 1.6;
    color: var(--cms-muted, #64748b);
}

/* El título enlazado: hereda el tamaño y el peso del titular que envuelve — es
   el mismo texto, no un enlace añadido —, y sólo cambia de color al apuntarlo. */
.cms-split__link {
    color: inherit;
    text-decoration: none;
}
.cms-split__link:hover,
.cms-split__link:focus-visible {
    color: var(--cms-link, #2563eb);
    text-decoration: underline;
}

/* El par, cuando hay sitio para dos columnas legibles.
   860px sale de sumar: 34rem de copy + 1rem de aire + una lista que no baje de
   los ~380px en los que una tarjeta con icono deja de caber en una línea. Por
   debajo de eso el par no mejora nada: apila, que es el caso base de arriba. */
@media (min-width: 860px) {
    .cms-split__in {
        grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
        gap: 3.5rem;
        align-items: start;
    }

    .cms-split__head {
        /* Se queda a la vista mientras la lista avanza: el lector no pierde de
           qué trata lo que está leyendo. El `top` deja hueco a una cabecera
           pegajosa; si el tema no la tiene, sigue siendo correcto. */
        position: sticky;
        top: calc(var(--app-top-inset, 0px) + var(--cms-sticky-top, 0px) + 2rem);
    }

    /* SIN CABECERA NO HAY PAR, y esto no es un detalle de estilo.
       La cabecera sólo se emite si la sección tiene título o subtítulo, así que
       una sin texto deja la lista SOLA en la primera columna: media página
       ocupada y la otra media en blanco. Medido el 2026-09-27 sirviendo
       `kita.onlainez.net/de/konzept` — la sección de privacidad no lleva
       titular propio, porque el suyo vive en la sección de tags de al lado, y
       sus cuatro tarjetas salieron en una columna estrecha a la izquierda.
       Si no hay con quién emparejar, la lista ocupa el ancho entero. */
    .cms-split__list:only-child { grid-column: 1 / -1; }
}

/* Quien pide menos movimiento no quiere que un bloque le siga por la pantalla. */
@media (prefers-reduced-motion: reduce) {
    .cms-split__head { position: static; }
}

/* ── 2. El TONO: la franja sobre la que se pinta una sección ─────────────── */

/*
   El ritmo de una página se construye alternando la franja — es lo que separa
   «una lista de bloques» de «una página compuesta». La clase la pone el
   coordinador (`SectionRenderer::withTone`), no la plantilla, así que vale para
   las 23 variantes y para las que aporte un módulo mañana.

   NO PINTA: redefine los tokens `--cms-*` EN EL PROPIO `<section>`. Como las
   plantillas ya escriben `background: var(--cms-surface, #fff)` y
   `color: var(--cms-heading, …)`, heredan el tono sin cambiar un byte. Es el
   `data-theme` de los sistemas de diseño modernos, acotado a una sección.
   Funciona porque el `style` inline de la plantilla declara el USO
   (`background`), no la variable: una clase puede redefinir la variable que ese
   inline lee, aunque no podría ganarle la propiedad.

   REPARTO (§14). Los cuatro nombres y qué token mueve cada uno son del CORE —
   son el vocabulario con el que una página declara su ritmo—. El COLOR es del
   tema: los respaldos de abajo son grises de fábrica para que un tema que no
   haga nada siga siendo legible, y cualquier tema los sustituye mapeando sus
   `--cms-tone-*`. Ni un color de marca escrito aquí.

   `neutral` no tiene regla: es la ausencia de tono, y no emite clase.
*/

/* Una sección con tono cubre el ancho de la franja aunque su plantilla no lo
   diga: media franja no es una franja. Las plantillas del core ya centran su
   contenido con un `max-width` interno, así que esto no ensancha nada.

   Y el COLOR HEREDADO, que no es un detalle: las plantillas del core declaran
   su fondo (`background: var(--cms-surface, …)`) pero no su tinta, así que todo
   lo que no fije `color` explícitamente —los iconos de `grid/3col`, el primero
   que lo destapó— se quedaba en el negro por defecto del navegador. Mientras
   todas las franjas fueron claras nadie lo vio; sobre `contrast` el icono
   desaparece. Medido el 2026-09-27 renderizando `grid/3col` en los cuatro
   tonos. Va aquí y no en cada plantilla por lo mismo que la clase: son 23. */
.cms-tone {
    /* 🔴 NO se toca el `display`, y esto costó un defecto en vivo.
       La primera versión ponía `display:block` «para que la franja cubra el
       ancho» — innecesario, porque un `<section>` ya es de bloque— y el día que
       el tono se aplicó a una sección que era `grid`, la aplastó: las tres
       tarjetas de garantías de la home de `kita` pasaron de tres columnas a una
       pila, medido sirviendo el 2026-09-27. Una pieza transversal no puede
       decidir el layout de las 23 secciones a las que se aplica: sólo el color. */
    color: var(--cms-text, #475569);

    /* Y el FONDO, que es lo que hace visible la franja. Parece redundante
       —las plantillas del core ya escriben `background: var(--cms-surface, …)`
       en su propio `<section>`— y no lo es: una plantilla de TEMA puede no
       declarar fondo ninguno y dejar ver el del `body`. Medido el 2026-09-27
       en el tema de `kita`: `.kz-steps`, `.kz-badges`, `.kz-cta` y `.kz-prose`
       sólo ponen padding, así que el tono no las habría teñido y el ritmo se
       habría roto justo en las secciones que el tema viste — las que más
       importan.

       No pelea con el estilo en línea del core: ahí el inline gana, pero
       calcula EL MISMO valor, porque los dos leen la variable que el tono
       redefine. Uno u otro, el resultado es el mismo color. */
    background: var(--cms-surface, #fff);
}

/* 🔴 `--cms-tone-soft-muted` valía `#64748b` y da **4.34** sobre este mismo
   fondo: por debajo del 4.5 que pide WCAG 2.1 AA para texto normal. Era el
   ÚNICO respaldo del juego que no pasaba —los otros ocho van de 5.75 a 16.30—,
   así que toda instalación que no sustituyera ese token servía su texto
   secundario ilegible sin que nada lo dijera. Medido el 2026-09-28 con
   `tests/visual/a11y.php`, el primer barrido de contraste de este proyecto.

   `#586475` da 5.49 aquí y 6.01 sobre la tarjeta blanca, y sigue siendo más
   claro que `--cms-text` (6.92): la jerarquía visual se conserva, que es lo que
   un «muted» tiene que hacer. No se elige al ras del 4.5 a propósito — un tema
   que aclare un poco su fondo no puede tumbar el cumplimiento.
   Lo vigila `ToneContrastTest`. */
.cms-tone--soft {
    --cms-surface:     var(--cms-tone-soft-bg,     #f1f5f9);
    --cms-surface-alt: var(--cms-tone-soft-card,   #ffffff);
    --cms-heading:     var(--cms-tone-soft-head,   #0f172a);
    --cms-text:        var(--cms-tone-soft-text,   #475569);
    --cms-muted:       var(--cms-tone-soft-muted,  #586475);
    --cms-border:      var(--cms-tone-soft-border, #dbe3ec);
}

.cms-tone--accent {
    --cms-surface:     var(--cms-tone-accent-bg,     #ecfdf5);
    --cms-surface-alt: var(--cms-tone-accent-card,   #ffffff);
    --cms-heading:     var(--cms-tone-accent-head,   #064e3b);
    --cms-text:        var(--cms-tone-accent-text,   #1f4d3f);
    --cms-muted:       var(--cms-tone-accent-muted,  #3f6b5c);
    --cms-border:      var(--cms-tone-accent-border, #c7ece0);
}

/* La franja invertida. Su `--cms-surface-alt` es una veladura sobre el propio
   fondo y no un gris: una tarjeta clara sobre franja oscura rompe la franja, que
   es justo lo que la propuesta de referencia hace bien. */
.cms-tone--contrast {
    --cms-surface:     var(--cms-tone-contrast-bg,     #14342b);
    --cms-surface-alt: var(--cms-tone-contrast-card,   rgba(255, 255, 255, .07));
    --cms-heading:     var(--cms-tone-contrast-head,   #ffffff);
    --cms-text:        var(--cms-tone-contrast-text,   rgba(255, 255, 255, .86));
    --cms-muted:       var(--cms-tone-contrast-muted,  rgba(255, 255, 255, .72));
    --cms-border:      var(--cms-tone-contrast-border, rgba(255, 255, 255, .18));
    --cms-link:        var(--cms-tone-contrast-link,   #ffffff);
}

/* ── 3. La barra de anclas: navegación DENTRO de una página larga ────────── */

/*
   Va pegajosa porque su trabajo es seguir disponible mientras se lee: quien
   entra a resolver una duda concreta no quiere volver arriba para cambiar de
   sección. Es el `on this page` de Stripe, MDN o las docs de Symfony.

   El desbordamiento horizontal es DELIBERADO y es la decisión de diseño de la
   pieza: con seis etiquetas en un móvil, la alternativa es envolver a tres
   líneas y comerse media pantalla. Se desplaza, con el gesto que ya espera
   cualquiera que haya usado una barra de pestañas en un teléfono.
*/

.cms-anchors {
    position: sticky;
    /*
       DÓNDE se pega, que es el reparto §14 en una línea: que la barra sea
       pegajosa es de la PIEZA —del core—; cuánto ocupa lo que hay encima es del
       TEMA, porque sólo él sabe cuánto mide su cabecera.

       🔴 Medido el 2026-09-27 antes de desplegar: el tema de `kita` tiene
       `.kt-head { position:sticky; top:0; z-index:200 }`, así que una barra a
       `top:0` se pega EXACTAMENTE DEBAJO de ella y desaparece — se sirve, no da
       error, y simplemente no se ve. Con dos elementos pegados al mismo `top`
       gana el z-index, y el de una cabecera siempre será mayor.

       Los dos sumandos valen 0 por defecto, así que un tema que no declare nada
       ve el comportamiento de siempre:
         · `--app-top-inset`  lo publica el CORE cuando pinta una franja fija
           arriba. Ya existía — lo resolvió el tema del vertical el 2026-09-22
           (`fix(theme): la barra pegajosa lee el inset del core`), y se reusa
           en vez de inventar un segundo nombre para lo mismo.
         · `--cms-sticky-top` lo declara el TEMA: la altura de su cabecera.
    */
    top: calc(var(--app-top-inset, 0px) + var(--cms-sticky-top, 0px));
    z-index: 20;   /* por encima del contenido y por DEBAJO de la cabecera del tema */
    background: var(--cms-surface, #fff);
    border-bottom: 1px solid var(--cms-border, #e2e8f0);
    box-sizing: border-box;
}

.cms-anchors__in {
    max-width: 1100px;
    margin: 0 auto;
    padding: .75rem 1.5rem;
    display: flex;
    gap: 1.5rem;
    overflow-x: auto;
    /* Sin barra visible: es una fila de enlaces, no un panel con scroll. El
       desplazamiento sigue estando, con dedo, rueda y teclado. */
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.cms-anchors__in::-webkit-scrollbar { display: none; }

.cms-anchors__link {
    flex: 0 0 auto;
    font-size: .875rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--cms-muted, #64748b);
    text-decoration: none;
    padding: .25rem 0;
    border-bottom: 2px solid transparent;
}

.cms-anchors__link:hover,
.cms-anchors__link:focus-visible {
    color: var(--cms-heading, #0f172a);
    border-bottom-color: var(--cms-heading, #0f172a);
}

/* Una barra que se queda arriba no puede tapar el titular al que acaba de
   saltar. `scroll-margin-top` es la respuesta del propio navegador a eso, y
   sale gratis: se aplica al destino, no al que salta. */
.cms-split,
[data-section] {
    scroll-margin-top: calc(
        var(--app-top-inset, 0px) + var(--cms-sticky-top, 0px) + 3.5rem
    );
}

/* El interruptor del panel, que si no estuviera cableado sería un control que
   miente: `cms_sections.php` lo ofrece, así que tiene que hacer algo. */
.cms-anchors--static { position: static; }

/* Quien pide menos movimiento tampoco quiere una barra persiguiéndole. */
@media (prefers-reduced-motion: reduce) {
    .cms-anchors { position: static; }
}
