﻿/* ============================================================
   landing.css — secciones de la portada
   Orden: hero · enlaces · novedades · footer
   ============================================================ */

/* ============ HERO ============ */
.hero {
    position: relative;
    padding-block: 22px 0;
    overflow: hidden;
}

.hero__head {
    position: relative;
    z-index: 3;
    text-align: center;
}

/* El logo vectorizado: una sola pieza con el katakana arriba y el nombre
   abajo, el .club en rojo ya venía dibujado. Nada de filtro #wobble encima —
   el trazo del logo ya está hecho a mano y torcerlo dos veces lo emborrona. */
.hero__title {
    margin: 0;
    line-height: 0;
}

/* 430 y no más: el nombre queda del porte del título de 46px que reemplaza.
   El logo trae 1900x430 de viewBox, así que el alto sale solo. El margin
   auto no sobra: el reset pone las imágenes en display:block y el
   text-align del .hero__head no las alcanza. */
.hero__title img {
    width: 430px;
    margin-inline: auto;
}

/* --- Destellos --- */
/* Sin #wobble, como el logo: son SVG dibujados a mano en el markup y torcerlos
   otra vez no agrega nada. Y era el peor sitio para un filtro: el destello flota
   con una animación INFINITA, así que el ruido se recalculaba sin parar —y se
   regeneraba al terminar el aparecer de la escena, que en el móvil se nota—. */
.hero__spark {
    position: absolute;
    z-index: 1;
    color: var(--trazo); /* son dibujo, no texto */
    animation: drift 14s ease-in-out infinite alternate;
}

.hero__spark--1 { top: 6%; left: 3%; width: 26px; }
.hero__spark--2 { top: 4%; right: 4%; width: 22px; animation-delay: -5s; }
.hero__spark--3 { top: 44%; left: 2%; width: 17px; animation-delay: -9s; }
.hero__spark--4 { top: 30%; right: 2%; width: 15px; animation-delay: -2s; }

@keyframes drift {
    from { translate: 0 0; rotate: 0deg; }
    to { translate: 14px -10px; rotate: 18deg; }
}

/* --- Banner: la banda tocando ---
   El arte va ENTERO y sin recortar: el webp conserva la proporción del original
   (1983x793) y la banda mide lo que mida la imagen a lo ancho del shell —926 de
   ancho por 370 de alto—. Acá no hay encuadre que hacer ni alto que fijar; el
   archivo lo genera e2e/comprimir.mjs. */
.hero__scene {
    position: relative;
    z-index: 2;
    margin-top: 12px;
    /* El papel del sitio ES el del arte, así que mientras carga no se nota el
       hueco: la banda ya está del color que va a tener la ilustración. */
    background: var(--paper);
}

/* Sin marco y con los cantos desvanecidos: el papel del arte y el de la hoja son
   el mismo tono, así que lo único que delataba el rectángulo era su textura
   cortada en seco. La máscara la disuelve.
   Son dos degradados que se cruzan —uno horizontal y otro vertical— y hay que
   quedarse con lo que los dos dejan opaco: eso es mask-composite: intersect.
   El tramo es corto (22px a los lados, 14 arriba y abajo) porque los personajes
   llegan casi al filo; con más, al azul de la derecha le comería el brazo. */
.hero__art {
    -webkit-mask-image:
        linear-gradient(to right, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%),
        linear-gradient(to bottom, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to right, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%),
        linear-gradient(to bottom, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
    mask-composite: intersect;
    line-height: 0; /* mata el hueco que el inline deja bajo la imagen */
}

.hero__art { width: 100%; height: auto; }

/* ============ ENLACES (linktree) ============ */
.links {
    /* 56 y no --section-gap: el rótulo circular (74px) sobresale 37px por
       encima del panel y se comía el borde del banner. */
    padding-block: 56px 0;
}

.links__panel {
    position: relative;
    /* 42 = medio rótulo (74px) + aire. Sin aire lateral: lo pone cada fila,
       así el hover puede pintar de borde a borde del panel. */
    padding: 42px 0 0;
    background: var(--paper);
}

/* Las columnas viven en la LISTA, no en cada fila: si cada fila fuera su propia
   grilla, la primera columna se ajustaría al ancho de su propio badge y los
   títulos arrancarían en tres sitios distintos. Con subgrid todas comparten el
   mismo ancho de columna, calculado sobre el badge más ancho. */
.links__list {
    display: grid;
    grid-template-columns: auto 1fr auto;
    column-gap: 12px;
}

.links__list > li {
    position: relative;
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
}

.links__list > li + li { border-top: var(--stroke-thin) dashed rgba(23, 19, 15, 0.35); }

.links__row {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
    align-items: center;
    padding: 7px 14px;
    transition: background-color 0.15s ease;
}

/* El badge, centrado en su columna: no todos miden lo mismo */
.links__row .tag { justify-self: center; }

.links__row:hover { background: rgba(255, 210, 48, 0.28); }

/* Título en la display: es lo que le da el pulso dibujado a la fila */
.links__text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.links__title {
    font-family: var(--font-display);
    font-size: 14px;
    font-weight: 700;
    letter-spacing: -0.01em;
    /* Sin subrayado, nunca: el aviso de que la fila es clickeable lo da el
       fondo amarillo del hover, no el título. */
    text-decoration: none;
}

.links__desc {
    font-family: var(--font-body);
    font-size: 11px;
    color: var(--ink-soft);
}


/* Franja inferior: redes sociales, donde antes estaba el «View More» */
.links__social {
    position: relative; /* el trazo de la línea de arriba se mide contra esto */
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
    padding: 9px 14px;
    border-top: var(--stroke) solid var(--trazo);
}

.links__social-label {
    font-family: var(--font-display);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--ink-soft);
}

.links__pill {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 3px 12px;
    font-family: var(--font-display);
    font-size: 11px;
    color: var(--paper);
    background: var(--tag-bg, var(--ink));
    border: var(--stroke-thin) solid var(--trazo);
    transition: translate 0.15s ease;
}

.links__pill-icon {
    width: 13px;
    height: 13px;
    align-self: center;
    flex: none;
}

.links__pill strong { font-weight: 800; letter-spacing: 0.04em; }
.links__pill span { opacity: 0.85; }

.links__pill:hover { translate: 0 -2px; }

/* ============ BLOG (fichas de entrada) ============ */
.blog {
    position: relative;
    /* el rótulo «Blog» cuelga del borde superior */
    margin-top: 46px;
    padding-block: 38px 26px;
    background-color: var(--paper-warm);
    border-block: var(--stroke) solid var(--trazo);
}

/* Trama de puntos: el papel de la sección amarilla */
.blog::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: radial-gradient(rgba(240, 172, 0, 0.55) 1.4px, transparent 1.5px);
    background-size: 11px 11px;
    opacity: 0.75;
}

.blog > .shell { position: relative; z-index: 1; }
.blog > .section-badge { z-index: 6; }

.cards {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
}

.card {
    position: relative;
    /* Columna para que el botón (flex: 1) llene TODA la ficha. La grilla estira
       las fichas de una fila al alto de la más alta, pero el botón medía lo de su
       contenido: a una entrada con poco texto le quedaba abajo un tramo de papel
       que no era botón. En el teléfono, tocar ahí la enderezaba (:hover) y no
       abría nada. */
    display: flex;
    flex-direction: column;
    background: var(--paper);
    rotate: var(--tilt, 0deg);
    box-shadow: 3px 4px 0 rgba(23, 19, 15, 0.14);
    /* Ojo: NO agregar will-change, translateZ(0) ni backface-visibility. Suenan a
       "arreglo de nitidez" y hacen lo contrario: promueven la ficha a capa propia,
       que se rasteriza derecha y se rota ya pintada = texto borroso de verdad.
       Sin promover, Chromium pinta el texto ya rotado y se ve bien. */
    transition: rotate 0.15s ease, box-shadow 0.15s ease;
}

.card:hover {
    rotate: 0deg;
    box-shadow: 5px 8px 0 rgba(23, 19, 15, 0.28);
}

/* El afiche entero es el botón: hay que resetear los estilos de <button> */
.card__abrir {
    display: flex;
    flex: 1; /* todo el alto de la ficha, ver .card */
    flex-direction: column;
    gap: 5px;
    width: 100%;
    padding: 7px 7px 9px;
    font: inherit;
    color: inherit;
    text-align: left;
    background: none;
    border: 0;
    cursor: pointer;
}

.card__abrir:focus-visible { outline: var(--stroke-thin) solid var(--blue); outline-offset: 2px; }

/* Chincheta que sujeta la ficha al mural */
.card__pin {
    position: absolute;
    top: -9px;
    left: 50%;
    translate: -50% 0;
    z-index: 4;
    width: 17px;
    height: 17px;
    /* Redonda a pulso con el radio, no con #wobble-soft: la chincheta vive
       dentro de la ficha, que se anima al aparecer, y el ruido de un filtro se
       regenera en cada repintado —en el móvil se la veía rehacerse al frenar la
       ficha—. Cuatro radios distintos y queda igual de torcida, siempre igual. */
    border-radius: 54% 46% 49% 51% / 47% 53% 46% 54%;
    border: var(--stroke-thin) solid var(--trazo);
    background: var(--pin, var(--yellow));
    box-shadow: 1px 2px 0 rgba(23, 19, 15, 0.55);
    /* Es adorno y va encima del botón: sin esto, tocar la chincheta no abría la
       entrada. */
    pointer-events: none;
}

/* Afiche: vertical, como un flyer de verdad */
.card__thumb {
    position: relative;
    display: block;
    aspect-ratio: 3 / 4;
    border: var(--stroke-thin) solid var(--trazo);
    overflow: hidden;
    --fill: var(--thumb, var(--blue));
}

.card__thumb svg { position: absolute; inset: 0; width: 100%; height: 100%; }

/* La foto de portada, recortada al afiche. El object-position sale de la
   entrada: es el encuadre que se eligió arrastrando la foto en el editor. */
.card__foto {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Sin portada: color plano con la inicial. Un hueco que se nota, para que se
   llene; no un dibujo de relleno que aparente ser el contenido. */
.card__inicial {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-family: var(--font-display);
    font-size: 46px;
    font-weight: 800;
    color: var(--paper);
    opacity: 0.85;
}

.card__texto {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.card__meta {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.card__resumen {
    font-family: var(--font-body);
    font-size: 11px;
    color: var(--ink-soft);
    line-height: 1.45;
}

/* Con una sola entrada, la ficha se tumba: el afiche a la izquierda y el texto
   al lado. Una ficha suelta en una grilla de cuatro se ve como un error. */
.cards--una {
    grid-template-columns: 1fr;
    justify-items: center;
}

/* Ancho acotado: a lo ancho del marco la entrada queda con medio metro de
   papel vacío a la derecha. 620 = el mismo ancho del modal que abre. */
.cards--una .card { width: 620px; max-width: 100%; }

.cards--una .card__abrir {
    flex-direction: row;
    gap: 14px;
    padding: 9px;
}

.cards--una .card__thumb {
    flex: none;
    width: 150px;
}

.cards--una .card__texto { justify-content: center; gap: 6px; }
.cards--una .card__title { font-size: 17px; font-family: var(--font-display); }
.cards--una .card__resumen { font-size: 12px; }

.card__tag {
    min-width: 0;
    font-size: 9px;
    letter-spacing: 0.06em;
    padding: 0.15em 0.6em;
}

.card__date {
    display: block;
    font-family: var(--font-display);
    font-size: 10px;
    font-weight: 600;
    color: var(--ink-soft);
}

.card__title {
    display: block;
    font-family: var(--font-body);
    font-size: 11px;
    font-weight: 800;
    line-height: 1.35;
}

/* ============ MODAL (lo raya un crayón) ============ */
/* El papel y el marco ya están cuando abre. Lo que se anima es un crayón barriendo
   el modal en zigzag mientras el contenido se revela debajo.

   Tres cosas sincronizadas con --dibujo, sobre EL MISMO path:
     - la estela: cuatro copias con dash de largo distinto pegadas a la punta
     - el crayón: keyframes de left/top en % con los mismos vértices
     - el contenido: clip-path que baja al ritmo del barrido
   Si se toca el zigzag hay que cambiarlo aquí Y en la constante Zigzag de Index.cshtml.

   El crayón va FIJO en diagonal, como si el lector lo tuviera en la mano: dibujado
   ya inclinado y sin rotación animada. Si girara siguiendo la curva parecería una
   flecha, no un lápiz.
   Lo "humano" sale de la geometría: cada pasada del zigzag tiene otra altura y otro
   alcance, y el filtro #wobble tuerce la línea. */
.modal {
    --velo-modal: rgba(10, 8, 6, 0.78);

    position: fixed;
    inset: 0;
    z-index: 200;
    display: grid;
    /* minmax(0, 1fr) y NO una columna `auto`: la misma trampa que las fichas del
       blog. Una columna automática se estira al max-content de lo que lleva
       dentro, así que con la caja de 620px la columna medía 620 aunque la
       pantalla tuviera 390 —y el `max-width: 100%` de la caja, que se mide
       contra la columna, dejaba de topar con la pantalla y el modal se desbordaba
       hacia la derecha en el teléfono—. Con minmax(0, 1fr) la columna mide lo
       que el modal y el tope vuelve a ser la pantalla. */
    grid-template-columns: minmax(0, 1fr);
    place-items: center;
}

.modal[hidden] { display: none; }

.modal__fondo {
    position: absolute;
    inset: 0;
    background: var(--velo-modal);
    animation: lightbox-fondo 0.2s ease both;
}

/* Con el visor abierto la caja del modal SE ESCONDE: detrás de la foto queda sólo
   la página, con el velo que ya tenía. Decisión del usuario.

   Antes se tapaba con un velo propio, y no alcanzaba: la caja velada asomaba a
   los lados de la foto (arriba y abajo en el teléfono) y sus bordes se leían
   como líneas sueltas; el cartel de cerrar del teléfono, que cuelga medio afuera
   de la caja, quedaba con esa mitad en amarillo puro. Un velo a pantalla
   completa tampoco servía: oscurecía dos veces lo ya oscurecido.

   visibility y no display: la caja conserva su tamaño y el scroll del cuerpo, y
   al cerrar el visor vuelve tal cual. De golpe, sin fundido, como el velo de
   antes: con fundido se veía atrasado respecto de la foto. */
.modal--tapado .modal__caja { visibility: hidden; }

.modal__caja {
    --modal-w: 620px;
    --modal-h: 460px;
    --dibujo: 1.15s;
    --largo: 9072px; /* el recorrido del zigzag */

    position: relative;
    z-index: 1;
    display: flex; /* el cuerpo manda el alto, entre el mínimo y el máximo */
    width: var(--modal-w);
    min-height: 300px;
    max-height: min(78vh, 640px);
    /* 100% del .modal, que es fixed e inset: 0, y NO 100vw: el canon no admite
       vw fuera de las media queries, y además vw no descuenta la barra de
       scroll —con scrollbar-gutter: stable la caja se pasaba de ancho—. */
    max-width: calc(100% - 40px);
    animation: lightbox-aparecer 0.18s ease both;
}

.modal__caja:focus { outline: none; }

/* A sangre: iba con inset 6px para dejar sitio al marco de tinta, y el marco ya
   no está. */
.modal__papel {
    position: absolute;
    inset: 0;
    z-index: 0;
    background: var(--paper);
}

/* El papel del modal y el del visor llevan el grano de la hoja, pero DEBAJO de
   lo que va encima: el de la hoja (.page::before) se pintaba sobre todo y velaba
   las fotos. z-index -1 dentro de su propio contexto: sobre el color del papel y
   bajo el contenido. El visor necesita el isolation para armar ese contexto. */
.modal__papel::before,
.visor__marco::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: var(--grain);
    background-size: 240px 240px;
    opacity: 0.16;
}

.visor__marco { isolation: isolate; }

/* La estela se borra al terminar: el trazo no queda, solo el acto de dibujar */
.modal__estela {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    overflow: visible;
    animation: crayon-borra 0.4s ease var(--dibujo) both;
}

/* UN filtro para las cuatro copias, en el <g>, y no uno por path. El trazo cambia
   en cada cuadro, así que el filtro se recalcula en cada cuadro sobre toda la caja,
   y con uno por copia eran cuatro. Medido en un teléfono emulado: la mitad del
   trabajo, y se ve igual —el ruido es el mismo, en las mismas coordenadas—. */
.modal__estela g { filter: url(#wobble); }

.modal__estela path {
    fill: none;
    stroke: #e03b2e;
    stroke-width: 22;
    stroke-linecap: round;
    stroke-linejoin: round;
    /* un tramo visible de --cola, y después hueco hasta el final */
    stroke-dasharray: var(--cola) 99999px;
    animation: crayon-estela var(--dibujo) cubic-bezier(0.4, 0.03, 0.6, 0.97) both;
}

/* (Aquí vivía .modal__trazo, el marco de tinta de la caja. Se quitó: la caja va
   sin línea alrededor, como el marco del visor. Decisión del usuario.) */

/* Recorrido en PORCENTAJES, no offset-path: el alto de la caja es variable y
   offset-path solo acepta píxeles absolutos, así que el lápiz se despegaría del
   zigzag (que sí se estira con el viewBox). Los vértices salen de los mismos
   puntos que la constante Zigzag de _Modal.cshtml; si se cambia uno, el otro
   también.

   Se mueve con TRANSLATE y no con left/top: left/top recalculan la maqueta en
   cada cuadro y lo hace el hilo principal; un translate lo mueve la tarjeta
   gráfica. El truco para que el % siga siendo de la caja: el % de translate es
   del PROPIO elemento, así que la capa que se mueve mide lo mismo que la caja
   (inset: 0) y el lápiz va en su esquina de arriba a la izquierda.

   pointer-events: none no es adorno. La capa tapa la caja entera, y sin eso
   se comería todos los clics del contenido. */
.modal__crayon {
    position: absolute;
    inset: 0;
    z-index: 5;
    pointer-events: none;
    animation: crayon-recorre var(--dibujo) cubic-bezier(0.4, 0.03, 0.6, 0.97) both,
               crayon-guarda 0.25s ease var(--dibujo) both;
}

.modal__crayon svg {
    position: absolute;
    left: 0;
    top: 0;
    margin: -116px 0 0 -14px; /* la punta está en (14,116) del viewBox de 130 */
}

.modal__cuerpo {
    position: relative;
    z-index: 3;
    flex: 1;
    min-height: 0; /* si no, el flex item se niega a encogerse y reaparece el scroll */
    padding: 20px 22px;
    overflow-y: auto;
    animation: crayon-destapa var(--dibujo) cubic-bezier(0.4, 0.03, 0.6, 0.97) both;
}

@keyframes lightbox-fondo {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes lightbox-aparecer {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* La punta del tramo visible va justo donde está el crayón; la cola lo sigue */
@keyframes crayon-estela {
    from { stroke-dashoffset: var(--cola); }
    to   { stroke-dashoffset: calc(var(--cola) - var(--largo)); }
}

/* Los mismos vértices de siempre, antes left/top y ahora translate (x y) */
@keyframes crayon-recorre {
    0.000% { translate: 4.387% 7.130%; }
    6.250% { translate: 94.790% 7.130%; }
    12.500% { translate: 4.290% 12.457%; }
    18.750% { translate: 94.516% 18.609%; }
    25.000% { translate: 2.855% 24.543%; }
    31.250% { translate: 94.565% 30.130%; }
    37.500% { translate: 3.968% 35.326%; }
    43.750% { translate: 94.710% 42.152%; }
    50.000% { translate: 4.565% 46.935%; }
    56.250% { translate: 96.032% 53.761%; }
    62.500% { translate: 5.581% 58.630%; }
    68.750% { translate: 96.855% 63.804%; }
    75.000% { translate: 3.161% 69.891%; }
    81.250% { translate: 95.258% 75.326%; }
    87.500% { translate: 3.274% 82.109%; }
    93.750% { translate: 96.210% 87.457%; }
    100.000% { translate: 4.339% 92.848%; }
}

@keyframes crayon-guarda {
    to { opacity: 0; }
}

@keyframes crayon-borra {
    to { opacity: 0; }
}

@keyframes crayon-destapa {
    from { clip-path: inset(0 0 100% 0); }
    to   { clip-path: inset(0 0 0 0); }
}

.modal__kicker {
    font-family: var(--font-display);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    color: var(--ink-soft);
    margin: 0 0 2px;
}

.modal__titulo {
    font-family: var(--font-display);
    font-size: 24px;
    margin: 0;
}

.modal__afiche {
    display: grid;
    place-items: center;
    height: 130px;
    margin-bottom: 12px;
    border: var(--stroke-thin) solid var(--trazo);
    font-family: var(--font-display);
    font-size: 13px;
    font-weight: 700;
    color: var(--paper);
}

/* La portada arriba del modal: una banda recortada, no la foto entera. Decisión
   del usuario. El alto es fijo para que todas las entradas abran igual, y QUÉ se
   ve dentro de la banda lo decide el encuadre —el mismo object-position que el
   afiche del mural—, así que el editor muestra las dos formas al arrastrar. */
.modal__portada {
    display: block;
    width: 100%;
    height: 230px;
    object-fit: cover;
    margin-bottom: 12px;
    border: var(--stroke-thin) solid var(--trazo);
}

/* El cuerpo es HTML escrito en el editor: estos son los estilos de lo que
   puede salir de ahí. Densidad de 2006, igual que el resto. */
.modal__texto { font-size: 12px; line-height: 1.55; }
.modal__texto > :last-child { margin-bottom: 0; }
/* El div va junto al p: contenteditable cerraba los bloques con <div> antes de
   pedirle a Chrome que usara <p>, y lo ya escrito así tiene que verse igual. */
.modal__texto p, .modal__texto div { margin: 0 0 8px; }

.modal__texto h4 {
    font-family: var(--font-display);
    font-size: 13px;
    margin: 12px 0 5px;
}

.modal__texto ul, .modal__texto ol {
    margin: 0 0 8px;
    padding-left: 18px;
    list-style: revert;
}

.modal__texto li { padding: 1px 0; }

/* La lista numerada se ve como una lista de canciones, que es para lo que se usa.
   Antes las canciones eran una tabla aparte y se veian asi; al pasar a ser parte
   del cuerpo, el estilo se mudo con ellas. */
.modal__texto ol {
    list-style: decimal inside;
    padding-left: 0;
    font-family: var(--font-display);
    font-size: 13px;
    font-weight: 600;
}

.modal__texto ol li { padding: 2px 0; }
.modal__texto a { text-decoration: underline; }
.modal__texto figure { margin: 10px 0; }

.modal__texto img {
    display: block;
    width: 100%;
    height: auto;
    border: var(--stroke-thin) solid var(--trazo);
}

.modal__texto blockquote {
    margin: 8px 0;
    padding-left: 10px;
    border-left: 3px solid var(--yellow);
    color: var(--ink-soft);
}

.modal__sub {
    font-family: var(--font-display);
    font-size: 13px;
    margin: 14px 0 6px;
}

.modal__galeria {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
}

.modal__foto {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    background: var(--paper-deep);
    border: var(--stroke-thin) solid var(--trazo);
}

.modal__video {
    display: grid;
    place-items: center;
    height: 90px;
    background: var(--ink);
    border: var(--stroke-thin) solid var(--trazo);
    color: var(--paper);
    font-size: 22px;
}

.modal__aviso {
    padding: 8px 10px;
    background: var(--paper-warm);
    border: var(--stroke-thin) dashed var(--trazo);
    font-size: 12px;
    margin: 0;
}

/* Las fotos de la entrada se abren en el visor. El tabindex lo pone site.js al
   clonar el detalle, así que la lupa sólo sale cuando de verdad hay visor. */
.modal__cuerpo img[tabindex] { cursor: zoom-in; }
.modal__cuerpo img[data-video][tabindex] { cursor: pointer; }

/* Un video dentro de la entrada: su póster con un PLAY ▶▶ encima, y el clic lo
   abre en el visor. El rótulo va en un ::after del envoltorio y no en un <span>
   propio porque en el cuerpo el HTML lo escribe el editor, y ahí sólo viaja la
   <figure class="medio-video"> con su <img data-video>.

   El ::after no recibe el clic (pointer-events): el que lo recibe es la <img>,
   que es la que site.js conoce. */
.medio-video {
    position: relative;
    display: block;
}

.medio-video::after {
    content: "play";
    /* Centrado con inset + margin auto y no con translate: un transform apaga
       el antialiasing subpíxel del texto (ver el canon en CLAUDE.md). */
    position: absolute;
    inset: 0;
    width: fit-content;
    height: fit-content;
    margin: auto;
    padding: 3px 26px 3px 9px;
    font-family: var(--font-display);
    font-size: 11px;
    font-weight: 800;
    line-height: 1.35;
    letter-spacing: 0.12em;
    /* Caja alta por CSS, como las .tag: acá la mayúscula es la forma de la
       pieza, no el texto. */
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--ink);
    /* Las dos puntas ▶▶ son un SVG y no el carácter: en Windows ▶ sale como
       emoji de colores según la fuente que lo tome. */
    background:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M0.5 0.5 L6.5 4.5 L0.5 8.5Z M7 0.5 L13 4.5 L7 8.5Z' fill='%23e03b2e' stroke='%2317130f' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E")
        no-repeat right 8px center,
        var(--paper);
    border: var(--stroke-thin) solid var(--trazo);
    box-shadow: 2px 2px 0 rgba(23, 19, 15, 0.85);
    pointer-events: none;
}

/* El amarillo de hover de todo el sitio */
.medio-video:hover::after { background-color: var(--yellow); }

/* En el cuerpo la miniatura no va a todo el ancho: un video vertical a 576px
   mediría más de mil de alto. Se ajusta a la foto y se centra; el ancho del
   envoltorio sigue a la imagen para que el PLAY caiga en su centro. */
.modal__texto figure.medio-video {
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
}

.modal__texto .medio-video img {
    width: auto;
    max-width: 100%;
    max-height: 360px;
}
.modal__cuerpo img[tabindex]:focus { outline: none; }
.modal__cuerpo img[tabindex]:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* ============ VISOR DE FOTOS ============ */
/* Al modo del Lightbox 2 (2006): la foto ENTERA —nada de recortes, que para eso
   ya está la banda del modal— con un marco de papel que la abraza y el pie abajo
   con «foto 2 de 5». Va encima del modal y no lo cierra.

   El marco abraza la foto porque la foto manda: el ancho sale del max-width y el
   alto del max-height, y la proporción la conserva el propio <img>. El alto se
   calcula contra la pantalla porque un visor a pantalla completa no puede no
   hacerlo; es lo mismo que ya hace el max-height del modal. */
.visor {
    --aire: 28px;
    --pie: 26px;
    --boton: 0px; /* lo que ocupa el cartel de cerrar; sólo existe en el teléfono */

    position: fixed;
    inset: 0;
    z-index: 300;
    display: grid;
    /* minmax(0, 1fr) por lo mismo que el modal: con columna automática, el
       `max-width: 100%` del marco se mide contra una columna que ya creció con
       la foto y deja de topar con la pantalla. */
    grid-template-columns: minmax(0, 1fr);
    place-items: center;
    padding: var(--aire) 20px;
}

.visor[hidden] { display: none; }

/* Transparente: la página ya viene oscurecida por el fondo del modal, y lo único
   que falta tapar es la caja (.modal--tapado). Sigue ahí para cerrar con un clic. */
.visor__fondo {
    position: absolute;
    inset: 0;
}

.visor__marco {
    position: relative; /* lo daba .hand-box; el borde y el cartel se miden contra esto */
    max-width: 100%;
    margin: 0;
    padding: 10px 10px 0;
    background: var(--paper);
    animation: lightbox-aparecer 0.18s ease both;
}

.visor__marco:focus { outline: none; }

/* Al deslizar en el teléfono asoma la foto de al lado en una copia del marco (la
   «vecina», site.js). Las dos van en la MISMA celda: así la vecina queda
   centrada igual que el marco y lo único que las separa es su translate. */
.visor__marco { grid-area: 1 / 1; }
.visor__marco--vecina { pointer-events: none; }

/* Sin pie, el papel cierra abajo con el mismo aire que a los lados */
.visor__marco:has(.visor__pie[hidden]) { padding-bottom: 10px; }

/* El marco NO lleva línea: es papel y nada más, y la foto se recorta sola con
   su propio borde fino. Decisión del usuario. Tuvo borde dos veces y las dos
   estorbó: con el filtro #wobble se regeneraba a la vista en el iPhone, y
   dibujado a mano competía con la foto, que es lo que hay que mirar. */

.visor__lienzo { position: relative; }

.visor__foto {
    width: auto;
    height: auto;
    /* 20 del papel arriba y abajo, 4 del borde, el pie y el cartel de cerrar */
    max-height: calc(100vh - var(--aire) * 2 - 24px - var(--pie) - var(--boton));
    max-height: calc(100dvh - var(--aire) * 2 - 24px - var(--pie) - var(--boton));
    border: var(--stroke-thin) solid var(--trazo);
    background: var(--paper-deep);
}

.visor:not(.visor--varias) .visor__foto { cursor: zoom-out; }

/* El video toma el lugar de la foto con las mismas medidas: el tamaño lo pone su
   póster, así que uno vertical queda alto y angosto y uno horizontal ancho, sin
   nada aparte. El negro de fondo es por si el video tarda más que el póster. */
.visor__video {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: calc(100vh - var(--aire) * 2 - 24px - var(--pie) - var(--boton));
    max-height: calc(100dvh - var(--aire) * 2 - 24px - var(--pie) - var(--boton));
    border: var(--stroke-thin) solid var(--trazo);
    background: var(--ink);
}

/* El video va SIN controles nativos (ver _Modal.cshtml) y se toca para pausar,
   así que el cursor lo dice. Se probó antes esconder los controles de iOS por
   CSS —`::-webkit-media-controls-*`— y NO sirve: esos pseudo-elementos tapan el
   botón grande de arranque, pero el panel de −10 / pausa / +10 que Safari dibuja
   en medio del video no se va por ahí. La única salida era no pedir controles. */
.visor__video { cursor: pointer; }

/* Mientras el video no tiene con qué arrancar: papel blanco con el logo. Blanco y
   no oscuro a propósito —decisión del usuario—, y dentro del borde de tinta del
   video (inset 2px) para que el marco no se pierda. No recibe clics: si el
   usuario pausa mientras carga, el clic tiene que llegar al video.
   Sin #wobble sobre el logo, como en el hero: su trazo ya está hecho a mano. */
.visor__carga {
    position: absolute;
    inset: 2px;
    display: grid;
    place-items: center;
    background: var(--paper-outer);
    pointer-events: none;
}

.visor__carga img {
    width: 70%;
    max-width: 260px;
    animation: visor-latido 0.9s ease-in-out infinite alternate;
}

/* Un latido suave: un logo quieto sobre blanco se lee como colgado, no como carga */
@keyframes visor-latido {
    from { opacity: 0.4; }
    to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .visor__carga img { animation: none; }
}

/* El reset pone display: block a toda <img>, y eso le gana al [hidden] */
.visor__foto[hidden],
.visor__video[hidden],
.visor__carga[hidden] { display: none; }

/* Con un video las mitades no pueden tapar la imagen: se comerían los
   controles y el clic que pausa. Quedan sólo las flechas, a media altura. */
.visor--video .visor__paso {
    top: 50%;
    bottom: auto;
    width: auto;
    translate: 0 -50%;
}

/* Las dos mitades de la foto: la de la izquierda vuelve, la de la derecha avanza.
   La flecha va siempre a la vista y no sólo al pasar el ratón: en un teléfono no
   hay «pasar el ratón». */
.visor__paso {
    position: absolute;
    top: 0;
    bottom: 0;
    display: none;
    align-items: center;
    width: 50%;
    padding: 0 10px;
    border: 0;
    background: none;
    cursor: pointer;
}

.visor--varias .visor__paso { display: flex; }
.visor__paso--antes { left: 0; justify-content: flex-start; }
.visor__paso--despues { right: 0; justify-content: flex-end; }
.visor__paso:focus { outline: none; }

.visor__flecha {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    background: var(--paper);
    border: var(--stroke-thin) solid var(--trazo);
}

.visor__flecha path {
    fill: none;
    stroke: var(--trazo);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* El mismo amarillo del hover de las filas de música */
.visor__paso:hover .visor__flecha,
.visor__paso:focus-visible .visor__flecha { background: var(--yellow); }

/* width 0 + min-width 100%: el pie ocupa lo que mida la foto pero no la
   ensancha. Sin esto un texto largo estiraba el papel más allá de la foto. */
.visor__pie {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    width: 0;
    min-width: 100%;
    min-height: var(--pie);
    padding-top: 6px;
    font-family: var(--font-display);
    font-size: 11px;
    font-weight: 700;
    line-height: 1.55;
    color: var(--ink-soft);
}

.visor__pie[hidden] { display: none; }
.visor__cuenta { flex: none; margin-left: auto; }

/* El botón de cerrar del teléfono. En escritorio no existe —se cierra con clic
   afuera o con Escape, decisión del usuario—, y por eso lo único que va fuera de
   las media queries es esconderlo. Cómo se ve, en la de 620. */
.cerrar-movil { display: none; }

/* ============ QUIÉNES SOMOS ============ */
/* Debajo del blog, y de la misma familia que el panel de música: papel, borde a
   pulso y el rótulo colgado del canto de arriba. Se diferencia del blog en el
   fondo —el blog es la banda amarilla con puntos— para que se lean como dos
   secciones y no como una sola larga. */
.nosotros {
    /* 56 como en .links: el rótulo sobresale medio alto por encima del panel.
       Y abajo aire + línea: sin eso el panel quedaba pegado a la firma y la
       sección entera se leía como parte del pie. El blog cierra su banda igual. */
    position: relative; /* el trazo de la línea de cierre se mide contra esto */
    padding-block: 56px 30px;
    border-bottom: var(--stroke) solid var(--trazo);
}

.nosotros__panel {
    position: relative;
    /* 42 = medio rótulo + aire. Acá sí hay aire lateral: es texto, no filas que
       se pintan de borde a borde al pasar por encima. */
    padding: 42px 22px 20px;
    background: var(--paper);
}

.nosotros__texto {
    font-size: 12px;
    line-height: 1.55;
}

.nosotros__texto p { margin: 0 0 8px; }
.nosotros__texto > :last-child { margin-bottom: 0; }

.nosotros__gente {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    margin: 14px 0 0;
    padding: 0;
    list-style: none;
}

.nosotros__quien {
    display: flex;
    align-items: center;
    gap: 9px;
    min-width: 0;
    padding: 7px 9px;
    background: var(--paper-warm);
    border: var(--stroke-thin) solid var(--trazo);
}

.nosotros__cara {
    flex: none;
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    border: var(--stroke-thin) solid var(--trazo);
}

.nosotros__quien-texto { min-width: 0; }

/* Sin negrita y sin fuente de títulos: acá todo es texto normal. Decisión del
   usuario. Lo único que distingue al nombre de lo que hace es el tono.

   El nombre lleva al perfil de Instagram, pero NO se anuncia: sin subrayado y sin
   color de enlace, sólo la arroba delante y el amarillo al pasar por encima.
   Decisión del usuario: que no sea obvio, sólo para curiosos. Es el mismo criterio
   de las filas de música, donde el aviso también es el fondo del hover. */
.nosotros__nombre {
    display: block;
    width: fit-content;
    max-width: 100%;
    font-size: 12px;
    line-height: 1.3;
    color: inherit;
    /* Un usuario larguísimo corta donde sea antes que salirse de su fila: no lleva
       espacios donde partir. */
    overflow-wrap: anywhere;
}

a.nosotros__nombre {
    text-decoration: none;
}

a.nosotros__nombre:hover { background: var(--yellow); }

.nosotros__rol {
    display: block;
    font-size: 11px;
    color: var(--ink-soft);
}

/* La foto se guarda a 96 y se ve a 32: en un teléfono, que pinta el triple de
   píxeles, una de 32 se vería pastosa. */
.nosotros__foto {
    width: 32px;
    height: 32px;
    object-fit: cover;
    border: var(--stroke-thin) solid var(--trazo);
}

/* ============ FOOTER ============ */
.footer {
    padding-block: 22px;
    text-align: center;
}

.footer__mark {
    /* El reset le da a todo <p> un margin-bottom de 1em, que acá desbalanceaba
       el pie: 22px de aire arriba y 42 abajo. */
    margin: 0;
    line-height: 0;
}

/* El mismo logo del hero, recortado en la vista para dejar solo el nombre:
   el katakana a este porte seria una mancha. Ocupa la mitad de abajo del
   viewBox (y 208..424 de 430), y con cover eso cae en el 97%. */
.footer__mark img {
    width: 200px;
    height: 23px;
    margin-inline: auto;
    object-fit: cover;
    object-position: 50% 97%;
}


/* ============ RESPONSIVE ============ */
/* Los dos cortes y el porqué están en crayon.css (sección 10). Acá va sólo lo
   de cada sección. Nada de esto corre en escritorio: de 1025 para arriba el
   sitio es el mismo de siempre, al píxel. */

@media (max-width: 1024px) {
    /* La banda amarilla del blog sangra hasta las líneas del marco: el aire
       lateral lo puso .page, así que hay que devolvérselo. */
    .blog {
        margin-inline: calc(var(--gutter) * -1);
        padding-inline: var(--gutter);
    }

    /* La trama de puntos se mide contra la caja de padding, así que con ese
       aire nuevo dejaba dos franjas de amarillo liso en los bordes. */
    .blog::before { inset: 0 calc(var(--gutter) * -1); }

    /* minmax(0,·) y no 1fr a secas: el mínimo automático de una columna es el
       max-content de lo que lleva dentro, y la ficha tumbada pide 620px. En una
       hoja más angosta la columna crecía a 620 y la ficha se salía —recortada
       por el overflow-x de .page, así que ni siquiera aparecía barra: sólo
       quedaba cortada—. En escritorio da exactamente lo mismo. */
    .cards--una { grid-template-columns: minmax(0, 1fr); }

    /* El logo sigue midiendo sus 430: lo único que se le agrega es un techo
       para cuando la hoja es más angosta que eso. Un porcentaje a secas lo
       hacía CRECER —74% de 946 son 700— que es justo al revés. */
    .hero__title img { max-width: 84%; }

    /* Los dos destellos de abajo están puestos en % del alto del hero, medido
       para el de escritorio. Con el banner más bajo dejan de quedar detrás del
       arte y asoman en el papel como una mancha suelta. */
    .hero__spark--3,
    .hero__spark--4 { display: none; }

    /* Tres por fila: cuatro fichas de menos de 200px dejan el título en cuatro
       renglones y el mural deja de leerse.

       El :not() no sobra. La ficha tumbada de «una sola entrada» pisa las
       columnas desde .cards--una, y eso funcionaba sólo por orden en el
       archivo: al escribir .cards acá abajo, la única ficha volvía a caer en
       la grilla y quedaba de 178px con el texto en una palabra por renglón. */
    .cards:not(.cards--una) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 620px) {
    .hero { padding-block: 16px 0; }

    /* Dos por fila: sigue siendo un mural. Uno por fila sería un listado, que
       es justo lo que el mural no es. (El :not, igual que arriba.) */
    .cards:not(.cards--una) { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    /* La ficha tumbada (una sola entrada) mantiene la forma, con el afiche
       más chico: en columna sería una ficha normal y se pierde la excepción. */
    .cards--una .card__abrir { gap: 10px; }
    .cards--una .card__thumb { width: 104px; }
    .cards--una .card__title { font-size: 15px; }

    /* El linktree aguanta en fila —badge, texto y flecha— pero sin holgura */
    .links { padding-block: 46px 0; }
    .links__panel { padding-top: 34px; }
    .links__row { padding: 7px 9px; }
    .links__list { column-gap: 9px; }

    /* Tres columnas de 100px dejan el nombre en tres renglones: van apiladas */
    /* Y sin línea de cierre: acá el aire de abajo ya es 0, así que la línea
       queda pegada al panel y no separa nada de nada —sobra, y se ve—. En
       escritorio sí hace falta, que ahí cuelgan 30px de papel entre el panel y
       la firma. Decisión del usuario. */
    .nosotros {
        padding-block: 46px 0;
        border-bottom-color: transparent;
    }

    .nosotros > .trazo { display: none; }
    .nosotros__panel { padding: 38px 14px 16px; }
    .nosotros__gente { grid-template-columns: 1fr; }

    .modal__cuerpo { padding: 14px 15px; }
    .modal__titulo { font-size: 20px; }
    .modal__portada { height: 150px; }
    .modal__galeria { grid-template-columns: repeat(2, 1fr); }

    /* En el teléfono cada píxel de pantalla es foto */
    .visor { --aire: 14px; padding-inline: 8px; }
    .visor__marco { padding: 6px 6px 0; }
    .visor__marco:has(.visor__pie[hidden]) { padding-bottom: 6px; }
    .visor__foto,
    .visor__video { max-height: calc(100dvh - var(--aire) * 2 - 16px - var(--pie) - var(--boton)); }

    /* El cartel de cerrar: colgado del borde de abajo y centrado, como el rótulo
       de sección cuelga del de arriba. Mitad sobre el papel, mitad afuera.

       44px de alto, el mínimo cómodo para un dedo. Centrado con margin auto y
       no con translate: un transform apaga el antialiasing subpíxel del texto.
       Al presionarlo baja y se come la sombra: se siente como apretar un botón. */
    .cerrar-movil {
        position: absolute;
        left: 0;
        right: 0;
        bottom: -22px;
        z-index: 6; /* sobre el crayón del modal (5) */
        display: flex;
        align-items: center;
        gap: 6px;
        width: fit-content;
        min-height: 44px;
        margin-inline: auto;
        padding: 0 20px;
        font-family: var(--font-display);
        font-size: 15px;
        font-weight: 800;
        line-height: 1;
        letter-spacing: 0.04em;
        color: var(--ink);
        background: var(--yellow);
        border: var(--stroke) solid var(--trazo);
        box-shadow: 3px 4px 0 rgba(23, 19, 15, 0.9);
        cursor: pointer;
        touch-action: manipulation; /* sin la espera del doble toque */
        -webkit-tap-highlight-color: transparent;
    }

    .cerrar-movil:active {
        bottom: -25px;
        box-shadow: 0 1px 0 rgba(23, 19, 15, 0.9);
    }

    /* La mitad de arriba del cartel cae sobre el papel: lo que va al final del
       cuerpo no puede quedar escondido debajo. */
    .modal__cuerpo { padding-bottom: 34px; }

    /* En el visor el cartel cuelga del marco: el papel se alarga para que no pise
       el «foto 2 de 5», y la foto se achica para dejarle lugar abajo. */
    .visor { --boton: 46px; padding-bottom: calc(var(--aire) + 22px); }
    .visor__marco,
    .visor__marco:has(.visor__pie[hidden]) { padding-bottom: 30px; }
}

/* ============ PANTALLAS TÁCTILES: el crayón liviano ============ */
/* En el teléfono el dibujado del crayón se saltaba cuadros. Medido con una traza
   de Chrome en un teléfono emulado (390px @3x), lo caro era el filtro #wobble de
   la estela: se recalcula en cada cuadro sobre toda la caja, y a densidad ×3.
   Bajarle octavas al ruido no ahorra NADA —se probó—: lo que cuesta es pasar la
   caja por un filtro, no calcular el ruido.

   Por tipo de pantalla y no por ancho: una ventana angosta de PC puede con esto,
   y una tablet no. En el PC el crayón queda EXACTAMENTE igual.

   - La estela, sin filtro. Lo único que se ve distinto, y apenas: el borde de la
     cola sale liso en vez de ondulado, y casi toda queda bajo el contenido.
   - El contenido en su propia capa, para que el destape (clip-path) no lo mande a
     repintar en cada cuadro. En el PC eso está prohibido —apaga el antialiasing
     subpíxel del texto, ver el canon—, pero un teléfono no lo usa: no pierde nada. */
@media (hover: none) and (pointer: coarse) {
    .modal__estela g { filter: none; }
    .modal__cuerpo { will-change: transform; }
}

/* ============ PANTALLAS TÁCTILES: el visor se desliza ============ */
/* Con el dedo se pasa de foto deslizando (site.js), así que las flechas sobran.
   Decisión del usuario. El «foto 2 de 5» del pie es el que avisa que hay más.
   Por tipo de pantalla y no por ancho, igual que el crayón: una ventana angosta
   de PC no tiene dedo con que deslizar y necesita sus flechas.

   pan-y: lo de lado lo maneja el script; pinch-zoom sigue dejando ampliar. */
@media (hover: none) and (pointer: coarse) {
    .visor--varias .visor__paso { display: none; }
    .visor--varias .visor__lienzo { touch-action: pan-y pinch-zoom; }
}
