/* ============================================================
   LAYOUT — estructura espacial. Todo parametrizado por tokens.
   ============================================================ */

/* --- FOLIACIÓN LATERAL (01–95, como los márgenes del TFM) --- */
/* Maquetación en BLOQUE, no flex.
   Safari no parsea var() dentro del atajo `flex`, ignoraba el
   flex-shrink:0 y aplastaba los números hasta solaparlos.
   En bloque no hay nada que pueda encogerse. */
.foliation {
  position: fixed;
  top: 0;
  height: 100vh;
  height: 100lvh;      /* altura de ventana GRANDE: no cambia cuando la
                          barra de Safari se retrae. Sin esto, en iOS el
                          alto varía al hacer scroll y los números
                          aparecen y desaparecen. */
  width: var(--col-fol);
  display: block;
  /* Arranca por DEBAJO de la banda de la cabecera, en todos los tamaños.
     Antes esto vivía sólo en la media query de móvil y en escritorio los
     primeros números se montaban sobre la banda. --band-h se deriva de
     --band-pad, así que si la banda engorda o adelgaza, la foliación se
     recoloca sola. */
  padding-top: calc(var(--band-h) + var(--gutter) / 2);
  padding-bottom: var(--gutter);
  z-index: 40;
  pointer-events: none;
  font-family: var(--f-mono);
  font-size: var(--fs-micro);
  letter-spacing: .02em;
  color: var(--fol-dim);
  font-variant-numeric: tabular-nums;
  overflow: hidden;          /* si sobra un número, se corta; no desborda */

  /* MODO NEGATIVO. Los números se restan de lo que tengan debajo, así que
     se invierten solos: claros sobre la foto oscura, negros sobre blanco.
     Requiere que la foliación y la foto compartan contexto de apilamiento
     —ambas cuelgan de <body>—. Si algún ancestro llegara a tener opacity,
     transform, filter o isolation, se crearía un contexto nuevo y la
     mezcla dejaría de ver la foto: los números volverían a ser blancos. */
  mix-blend-mode: var(--fol-blend, difference);
  color: #fff;               /* blanco puro = inversión total del fondo */
}
.foliation--left  { left: var(--gutter);  text-align: left;  }
.foliation--right { right: var(--gutter); text-align: right; }

.foliation span {
  display: block;
  height: var(--fol-row);
  min-height: var(--fol-row);
  line-height: var(--fol-row);
  transition: color var(--dur-fast) var(--ease);
}
.foliation span.is-active { color: var(--fol-active); font-weight: 700; }

/* En móvil la foliación se queda, sólo se estrecha y se pega al borde. */
@media (max-width: 720px) {
  .foliation {
    --col-fol: 26px;
    font-size: 0.625rem;
    /* El padding-top que la baja bajo la banda vive ahora en la regla
       base, común a móvil y escritorio. Aquí sólo se estrecha. */
  }
  .foliation--left  { left:  calc(var(--gutter) / 2); }
  .foliation--right { right: calc(var(--gutter) / 2); }
}

/* --- PÁGINA / PANTALLAS ------------------------------------- */
.page {
  position: relative;
  z-index: 1;          /* el contenido va por delante de la foto fija */
  padding-left:  calc(var(--gutter) * 2 + var(--col-fol));
  padding-right: calc(var(--gutter) * 2 + var(--col-fol));
}
@media (max-width: 720px) {
  /* Deja pasar la columna de foliación: arranca en gutter/2 y mide
     --col-fol de ancho. El contenido empieza después, con su propio
     gutter de aire, para que los números no se solapen con el texto. */
  .page { padding-inline: calc(var(--gutter) / 2 + var(--col-fol) + var(--gutter)); }
}

.screen {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding-block: var(--sp-7);
}
.screen--full { min-height: 100svh; }

/* Retícula de 12 columnas disponible dentro de cualquier .screen */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), 1fr);
  gap: var(--gutter);
}
@media (max-width: 720px) { .grid { grid-template-columns: repeat(4, 1fr); } }

/* --- LANDING ------------------------------------------------ */
/* La foto ocupa la ventana entera, sangrando por debajo de los
   márgenes de la retícula. Siempre centrada, recorte por cover.  */
.landing {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;   /* sigue la barra del navegador en móvil */
  margin-inline: calc(-1 * (var(--gutter) * 2 + var(--col-fol)));
}
@media (max-width: 720px) {
  .landing { margin-inline: calc(-1 * var(--gutter)); }
}

/* ESCRITORIO / HORIZONTAL: capa fija a la ventana. Un elemento fijo se
   mide contra el layout viewport, así que se pinta detrás de la barra de
   estado desde el primer fotograma y no encoge cuando el navegador
   retrae sus barras.
   En móvil vertical pasa a `relative` (ver más abajo) para que la foto
   haga scroll en vez de quedarse pegada. Es una decisión de diseño, no
   un rodeo a ningún bug. */
.landing__media {
  position: fixed;
  inset: 0;             /* llena la ventana visible, estable, sin glitches */
  z-index: 0;
  background: #fff;     /* placeholder blanco: la foto se funde desde blanco */
  overflow: hidden;
}
.landing__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--landing-focus);
  /* Fundido sobre el placeholder OSCURO, no sobre blanco: sin destello beige. */
  animation: heroFade var(--dur-slow) var(--ease) both;
}
@keyframes heroFade { from { opacity: 0; } to { opacity: 1; } }

/* --- FUNDIDO FINAL DE LA PORTADA ----------------------------
   --fundido (0 → 1) lo publica main.js en el último 1/8 de scroll
   de la foto. Dos tiempos encadenados:
     1) la imagen se invierte  → el negro se vuelve claro
     2) al 55% entra el blanco → remate hasta el fondo de página
   Si el JS no corre, --fundido no existe, vale 0 y no pasa nada. */
.landing__media img {
  filter: invert(var(--fundido, 0));
}
.landing__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #fff;
  pointer-events: none;
  /* de 0 a 1 mientras --fundido va de .55 a 1 */
  opacity: clamp(0, calc((var(--fundido, 0) - .55) / .45), 1);
}
/* MÓVIL VERTICAL: la foto queda FIJA a la ventana, igual que en
   escritorio (position:fixed + inset:0 vienen ya de la regla base).
   Así permanece de fondo, invertible por el fundido, mientras el índice
   scrollea por encima. Un elemento fijo con inset:0 cubre siempre la
   ventana visible, sin el hueco inferior del home indicator: por eso ya
   no hacen falta --hero-h ni las compensaciones de safe-area. */
@media (max-aspect-ratio: 3/4) {
  /* La foto SCROLLEA: entra en el flujo y se va hacia arriba dejando
     paso al índice. Altura anclada a screen.height (--hero-h) para que
     no reencuadre con la barra de Safari (el antiguo micro-zoom).
     El borde INFERIOR se difumina: la foto se desvanece hacia el blanco
     del índice en vez de cortarse en línea recta. */
  .landing__media {
    position: relative;
    inset: auto;
    width: 100%;
    height: var(--hero-h, 100lvh);
    /* Difuminado inferior ATADO AL SCROLL. --hero-mask-top (que publica
       main.js) marca dónde acaba la parte opaca: 100% en reposo (foto
       entera), y baja conforme scrolleas, subiendo el difuminado y
       comiéndose la foto de abajo arriba hasta desaparecer. La banda de
       difuminado son los 24% siguientes al borde opaco. */
    -webkit-mask-image: linear-gradient(to bottom,
        #000 var(--hero-mask-top, 100%),
        rgba(0,0,0,.6) calc(var(--hero-mask-top, 100%) + 4%),
        rgba(0,0,0,.22) calc(var(--hero-mask-top, 100%) + 8%),
        transparent calc(var(--hero-mask-top, 100%) + 12%));
            mask-image: linear-gradient(to bottom,
        #000 var(--hero-mask-top, 100%),
        rgba(0,0,0,.6) calc(var(--hero-mask-top, 100%) + 4%),
        rgba(0,0,0,.22) calc(var(--hero-mask-top, 100%) + 8%),
        transparent calc(var(--hero-mask-top, 100%) + 12%));
  }
  .landing__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
  }
}

/* --- CABECERA ----------------------------------------------- */
/* Fija arriba, por encima de todo, sin bloquear el scroll.
   Lleva una banda blanca translúcida con desenfoque, en móvil y en
   escritorio. En móvil arranca en el borde superior de la pantalla
   —por debajo del reloj y la batería— y baja hasta cerrar bajo
   JORG ADOT. Referencia: hermes.com.
   La banda no es sólo estética: aclara el fondo antes de la mezcla.
   Sin ella, el difference de .brand se resta de la foto casi negra y
   255-20 = 235, o sea blanco: el texto se quedaría blanco fijo y el
   efecto negativo no se vería. Se regula con --brand-band-bg. */
.cabecera {
  position: fixed;
  top: 0; left: 0; right: 0;
  /* SIN z-index a propósito. Un z-index numérico crearía contexto de
     apilamiento y el mix-blend-mode de .brand quedaría encerrado dentro
     de la cabecera, sin ver la foto. Con z-index:auto, position:fixed no
     crea contexto y el texto se mezcla con todo lo pintado debajo.
     Va por delante de la foto porque la sigue en el orden del documento.
     La foliación (z-index:40) se pinta por encima de la banda, pero
     arranca por debajo de ella (padding-top) y nunca se solapan. */
  /* El grosor de la banda sale de --band-pad, no del gutter: ceñida al
     texto, no un bloque. Cambiarlo ajusta también --band-h y por tanto
     dónde arranca la foliación, sin tocar nada más. */
  padding-top: calc(env(safe-area-inset-top, 0px) + var(--band-pad));
  padding-bottom: var(--band-pad);
  padding-inline: calc(var(--gutter) * 2 + var(--col-fol));
  pointer-events: none;
}

/* La banda va en un pseudo-elemento, NO en .cabecera.
   backdrop-filter crea un contexto de apilamiento aislado: si el fondo
   viviera en .cabecera, el mix-blend-mode de .brand se mezclaría con la
   propia banda y no vería nunca la foto. Separándolos, la banda se
   pinta al fondo y el texto queda libre para mezclarse con todo. */
.cabecera::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--brand-band-bg);
  -webkit-backdrop-filter: blur(var(--brand-band-blur)) saturate(115%);
          backdrop-filter: blur(var(--brand-band-blur)) saturate(115%);
}

/* Sin soporte de backdrop-filter (Firefox con la opción desactivada,
   navegadores viejos) el blur no existe y la banda se queda casi
   invisible. Ahí compensamos con más opacidad para que siga
   leyéndose como banda. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .cabecera::before { background: rgba(255,255,255,.42); }
}

@media (max-width: 720px) {
  .cabecera { padding-inline: var(--gutter); }
}

/* --- MARCA EN PORTADA --------------------------------------- */
.brand {
  display: flex;
  justify-content: center;
  gap: var(--brand-gap);
  font-family: var(--f-mono);
  font-size: var(--fs-brand);
  font-weight: 700;
  letter-spacing: var(--brand-track);
  text-indent: var(--brand-track);   /* compensa el tracking de la última letra */

  /* MODO NEGATIVO, igual que la foliación: JORG ADOT se resta de lo que
     tenga debajo (banda + foto) y se invierte solo. Sobre la portada
     oscura sale claro; cuando la foto se funde en blanco, se vuelve
     negro sin que haya que cambiar nada.
     Depende de que .cabecera NO cree contexto de apilamiento — ver el
     comentario de arriba. */
  position: relative;                /* por delante de la banda del ::before */
  mix-blend-mode: var(--fol-blend, difference);
  color: #fff;                       /* blanco puro = inversión total */
}

/* --- BANCO DE PRUEBAS --------------------------------------- */
/* TEMPORAL. Sólo existe para poder hacer scroll y ver correr la
   foliación mientras no hay contenido. Se borra este bloque y el
   <div class="probador"> del HTML cuando empiece a haberlo. */
.probador {
  height: var(--test-scroll);
  margin-inline: calc(-1 * (var(--gutter) * 2 + var(--col-fol)));
}
@media (max-width: 720px) {
  .probador { margin-inline: calc(-1 * var(--gutter)); }
}

/* --- ÍNDICE DE TRABAJOS ------------------------------------- */
/* Primer bloque tras el hero. Todo con tokens ya existentes. Sin
   animación: el peso lo llevan la tipografía, el aire y la retícula.
   La única transición es el color al pasar el cursor, y sólo en
   dispositivos con puntero fino (nada de estados pegados en táctil). */
.indice {
  min-height: 100svh;
  /* Abajo, hueco generoso para que la última fila NO quede tapada por
     la barra flotante de Safari. env(safe-area-inset-bottom) en este
     iOS devuelve 0, así que el colchón fijo (sp-7) es lo que de verdad
     la aparta; el env suma por si acaso en otros dispositivos. */
  /* Arriba, colchón moderado (antes sp-7 centrado dejaba el menú a media
     pantalla del hero, "alejado"). Alineado ARRIBA para que "Trabajos"
     aparezca justo tras la foto. Abajo, hueco generoso para que la última
     fila no quede tapada por la barra flotante de Safari. */
  padding-block: var(--sp-6) calc(var(--sp-7) + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  justify-content: center;   /* el menú queda centrado en la pantalla */
}
/* En móvil la sección .landing (vacía) NO debe reservar otra pantalla:
   la foto ya la da. Colapsándola, el índice sube y aparece tras UNA
   pantalla —hacia el cambio de foliación a 02— en vez de dos. */
@media (max-aspect-ratio: 3/4) {
  .landing { min-height: 0; }
}

.indice__rotulo {
  font-family: var(--f-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--c-ink);
  opacity: .55;
  margin-bottom: var(--sp-5);
}

.indice__lista { display: block; }

.obra { border-top: 1px solid var(--c-rule); }
.obra:last-child { border-bottom: 1px solid var(--c-rule); }

/* Fila: número mono a la izquierda, nombre grande, meta a la derecha.
   subgrid no: una retícula propia de 3 pistas basta y es más robusta. */
.obra__enlace {
  display: grid;
  grid-template-columns: 3.5ch 1fr auto;
  align-items: baseline;
  gap: var(--gutter);
  padding-block: clamp(var(--sp-4), 4vw, var(--sp-6));
  color: var(--c-ink);
  /* Fuera el relleno gris de iOS al tocar. */
  -webkit-tap-highlight-color: transparent;
}

/* Al tocar (móvil) o pulsar, el texto se pone ROJO, igual que el hover de
   escritorio. Nada de relleno gris. El elegido se QUEDA rojo mientras el
   menú se repliega. */
a.obra__enlace:active { color: var(--c-accent); }
a.obra__enlace.is-elegido { color: var(--c-accent); }

/* REPLIEGUE: al elegir, el menú se pliega hacia arriba y se funde, sobre
   el velo blanco, antes de navegar. */
.indice.is-replegando {
  transform-origin: top center;
  animation: indiceRepliega .42s var(--ease) forwards;
}
@keyframes indiceRepliega {
  to { opacity: 0; transform: translateY(-12px) scaleY(.96); }
}

.obra__num {
  font-family: var(--f-mono);
  font-size: var(--fs-small);
  font-variant-numeric: tabular-nums;
  opacity: .55;
}

.obra__nombre {
  font-size: var(--fs-title);
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
}

.obra__meta {
  font-family: var(--f-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  opacity: .55;
  text-align: right;
}

/* Interacción sobria: sólo con puntero fino, sólo color. */
@media (hover: hover) and (pointer: fine) {
  a.obra__enlace { transition: color var(--dur-fast) var(--ease); }
  a.obra__enlace:hover { color: var(--c-accent); }
}

/* Ranuras aún sin destino: se leen como huecos, no como enlaces rotos. */
.obra--pendiente .obra__enlace { opacity: .35; cursor: default; }

/* Móvil: la meta baja bajo el nombre, no compite por el ancho. */
@media (max-width: 720px) {
  .obra__enlace {
    grid-template-columns: 3ch 1fr;
    row-gap: var(--sp-1);
  }
  .obra__meta { grid-column: 2; text-align: left; }
}

/* --- DIAL · menú radial vivo (Carne y metal) ---------------- */
/* Tecnología invisible: sin cromo, la luz es la interfaz. Colores
   por token para que herede el tema oscuro. */
/* Fondo en degradado, del negro arriba al gris de abajo (ref. del orbe).
   Va en la sección de acceso, no en todo el body, para no pisar el tema. */
/* SCROLL BLOQUEADO en la página del dial, POR AHORA: el menú es lo
   único que hay y no debe desplazarse. Cuando las secciones tengan
   contenido, quitar este bloque. position:fixed + inset:0 corta el
   rebote de iOS que overflow:hidden no siempre detiene.
   El GRADIENTE vive aquí, en el body a pantalla completa: llena de
   borde a borde sin depender de márgenes de ninguna caja interior. */
html:has(body[data-page="carne-y-metal"]),
body[data-page="carne-y-metal"] {
  position: fixed;
  inset: 0;
  overflow: hidden;
  overscroll-behavior: none;
  /* Base BLANCA: enlaza con el fundido a blanco del home. El gradiente
     oscuro florece encima, radialmente (ver .acceso). */
  background: #ffffff;
}

/* BANDAS DE SEGURIDAD EN BLANCO: los insets superior (notch) e inferior
   (home indicator) se pintan de blanco, enmarcando el gradiente oscuro.
   Si el dispositivo no reporta safe-area (env = 0), no se ven; en ese
   caso hay que darles una altura fija. */
html:has(body[data-page="carne-y-metal"])::before {
  content: "";
  position: fixed;
  top: 0; left: 0; right: 0;
  height: env(safe-area-inset-top, 0px);
  background: #fff;
  z-index: 60;
  pointer-events: none;
}
html:has(body[data-page="carne-y-metal"])::after {
  content: "";
  position: fixed;
  bottom: 0; left: 0; right: 0;
  height: env(safe-area-inset-bottom, 0px);
  background: #fff;
  z-index: 60;
  pointer-events: none;
}

/* La sección de acceso ocupa la ventana entera y centra el dial. Lleva
   el GRADIENTE y es TODA la superficie de control (touch-action:none
   para que ningún gesto se lo lleve el navegador). */
.acceso {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  min-height: 0;
  padding: 0;
  touch-action: none;
  background: radial-gradient(125% 95% at 50% 6%,
              #101010 0%, #232323 45%, #3a3a3a 100%);
}

/* ENTRADA: fundido plano de blanco al color del proyecto. Un velo blanco
   a pantalla completa que se desvanece (~2s), dejando aparecer el
   gradiente por debajo. Sin bordes, sin iris: sólo el color subiendo. */
body[data-page="carne-y-metal"]::before {
  content: "";
  position: fixed;
  inset: 0;
  background: #ffffff;
  z-index: 50;
  pointer-events: none;
  animation: dialFunde 2s var(--ease) forwards;
}
@keyframes dialFunde { to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  body[data-page="carne-y-metal"]::before { display: none; }
}

/* El dial es un cuadrado centrado con el lienzo del anillo y, encima,
   el texto de la sección. Toda la ventana es la superficie de control
   (ver dial.js), así que aquí sólo se centra y se dimensiona. */
.dial {
  position: relative;
  z-index: 2;                 /* por encima del velo de entrada */
  width: min(88vmin, 600px);
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  outline: none;
  touch-action: none;
}
.dial:focus-visible { outline: 1px solid rgba(255,255,255,.3); outline-offset: 12px; }

.dial__lienzo {
  grid-area: 1 / 1;
  display: block;
}

/* Texto central: sólo el nombre de la sección (sin números). Blanco
   fijo —el dial no sigue el tema—. Oculto hasta que el anillo está
   dibujado (.is-listo). */
.dial__label {
  grid-area: 1 / 1;
  align-self: center;
  justify-self: center;
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 1.5px;
  color: #ffffff;
  text-shadow: 0 0 8px rgba(255,255,255,.55), 0 0 2px rgba(255,255,255,.8);
  pointer-events: none;
  opacity: 0;
  transition: opacity .6s var(--ease);
}
.dial.is-listo .dial__label { opacity: 1; }
/* Al cambiar de sección, el nombre parpadea: se apaga y vuelve. */
.dial.is-listo .dial__label.is-cambio { animation: dialParpadeo var(--dur-base) var(--ease); }
@keyframes dialParpadeo { from { opacity: .25; } to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .dial__label { opacity: 1; }
}

/* La lista fuente del dial se oculta a la vista DESDE EL PRIMER PINTADO
   (por eso el selector es .dial-fuente, no la clase que añade el JS): así
   no parpadea al cargar la página antes de que corra dial.js. Sigue en el
   DOM, accesible a lectores de pantalla. */
.dial-fuente,
.dial__lista--oculta {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* --- VELO DE SALIDA (home → proyecto) ----------------------- */
/* Al elegir un trabajo, cubre todo de blanco antes de navegar, de modo
   que las letras desaparecen y la página siguiente arranca ya en blanco
   (que enlaza con la cortina de entrada del proyecto). */
.salida {
  position: fixed;
  inset: 0;
  background: #ffffff;
  opacity: 0;
  pointer-events: none;
  z-index: 100;
  transition: opacity .5s var(--ease);
}
.salida.is-activa { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .salida { transition: none; }
}

/* --- PIE ---------------------------------------------------- */
.footer {
  padding: var(--sp-5) calc(var(--gutter) * 2 + var(--col-fol));
  font-family: var(--f-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-wide);
  opacity: .5;
}
