/* NIKOLA ZALE — layout, typography, layers, responsive.
   Layer order (back to front):
   body black -> .fallback -> .backdrop -> .vignette -> #stage (alpha canvas)
   -> .overlay -> .scroll-hint -> .preloader                                */

:root {
  --bone: #e8e2d6;
  --bone-dim: #9b948a;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --pad: clamp(1.25rem, 4vw, 4.5rem);
  --font-display: "Helvetica Neue", Helvetica, "Segoe UI", Arial, sans-serif;
}

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

html {
  background: #000;
  /* Lenis drives scrolling; the native smooth behaviour would fight it. */
  scroll-behavior: auto;
}

body {
  margin: 0;
  background: #000;
  color: var(--bone);
  font-family: var(--font-display);
  font-weight: 200;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body.is-loading { overflow: hidden; }

/* ---------------------------------------------------------------- layers */

#stage {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 3;
}

.backdrop,
.vignette,
.fallback {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

.fallback { z-index: 0; display: none; }

.backdrop {
  z-index: 1;
  opacity: 0;
  will-change: opacity, transform;
}

.backdrop img,
.fallback::after {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  display: block;
  /* The source is 1179x651 and gets blown up past 2x on most screens. A soft
     blur turns the upscaling artefacts into grain instead of mush, and the
     extra scale hides the transparent edge the blur would otherwise leave. */
  /* Held well down: the photograph is the room the skull ends up in, read as
     a silhouette behind it, never as a lit picture competing with the bone. */
  filter: grayscale(1) contrast(1.22) brightness(0.62) blur(3.5px);
  transform: scale(1.06);
}

/* Bottom gradient guarantees contrast for the contact line on any crop. */
.vignette {
  z-index: 2;
  opacity: 0;
  background:
    radial-gradient(120% 85% at 50% 45%, transparent 46%, rgba(0,0,0,.62) 100%),
    linear-gradient(to bottom, transparent 55%, rgba(0,0,0,.9) 100%);
}

/* ------------------------------------------------------------ text stages */

.overlay { position: fixed; inset: 0; z-index: 4; pointer-events: none; }

/* Below the canvas (z-index 3) and above the photograph. */
.underlay { position: fixed; inset: 0; z-index: 2; pointer-events: none; }

/* The back half of the tagline drops low: at the height the front word sits,
   the skull fills the right of the frame and swallows it whole. Down here the
   horns cross the lettering instead, which is the effect — type behind bone,
   not type hidden by bone. */
@media (orientation: landscape) {
  .underlay .stage--2 { align-items: flex-end; padding-bottom: 14vh; }
}

/* Keeps the box the same in both layers so the two halves of the tagline
   land in exactly the same place. */
.tw--ghost { visibility: hidden; }

/* The word behind the canvas is read through the earth wall, which is a
   semi-transparent plane covering the whole frame, so it arrives dimmed.
   Pushed brighter and heavier here to come back out at the same weight as
   the word in front — the horn still occludes it completely, because the
   bone is opaque. */
.underlay .tw,
.underlay .name,
.underlay .credit {
  color: #fff;
  text-shadow:
    0 1px 3px rgba(0, 0, 0, 0.9),
    0 0 16px rgba(0, 0, 0, 0.75);
}

.underlay .tw { font-weight: 300; }

.stage {
  position: absolute;
  inset: 0;
  display: flex;
  padding: var(--pad);
  padding-bottom: calc(var(--pad) + env(safe-area-inset-bottom));
}

/* Name and credit ride high: the skull needs the lower two thirds of the
   frame, and a title sitting on the crown is a title fighting the object. */
.stage--1 {
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding-top: clamp(2rem, 11vh, 9rem);
  gap: clamp(1.6rem, 4.6vw, 3.6rem);
}

.stage--bio   { align-items: center; justify-content: center; }
.stage--video { align-items: center; justify-content: center; }

.stage--2 { align-items: center;    justify-content: flex-start; }

/* El cierre reparte el alto: firma arriba (absoluta), Instagram justo debajo
   del nombre y el pie —volver arriba y crédito— pegado al borde inferior. */
.stage--3 {
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
}

.tagline .tw { display: block; }

/* GSAP owns this element's transform, opacity and filter. */
.reveal { will-change: transform, opacity, filter; }

.name,
.credit,
.tagline,
.contact,
.bio,
.bio__col {
  margin: 0;
  will-change: transform;   /* the parallax writes here, nothing else */
}

.name,
.credit,
.tagline,
.contact { text-transform: uppercase; }

/* The credit sits directly under the name, which is exactly where the lit
   crown of the skull is brightest. It carries its own soft scrim so it stays
   legible whatever the zenith light is doing behind it. */
.credit {
  position: relative;
  font-size: clamp(0.68rem, 1.7vw, 1rem);
  font-weight: 300;
  letter-spacing: 0.62em;
  text-indent: 0.62em;
  color: var(--bone);
  text-align: center;
  text-shadow: 0 0 22px rgba(0, 0, 0, 0.9), 0 0 6px rgba(0, 0, 0, 0.8);
}

.credit::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 560%;
  height: 1100%;
  transform: translate(-50%, -50%);
  /* Wide and very diffuse: a tighter gradient reads as a grey rectangle
     sitting on the bone instead of as shadow. */
  background: radial-gradient(closest-side,
    rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.26) 44%, transparent 76%);
  z-index: -1;
  pointer-events: none;
}

/* Two columns with the skull orbiting in the gap between them. The measure
   stays near 30 characters per column, which is what lets the type run this
   large without the eye losing the line. */
#reveal-bio { width: 100%; }

.bio {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1.5rem, 6vw, 6rem);
}

.bio__col {
  position: relative;
  max-width: 30ch;
  font-size: clamp(1.15rem, 1.6vw, 1.62rem);
  font-weight: 400;
  line-height: 1.7;
  letter-spacing: 0.004em;
  color: #f2ede3;
  text-wrap: pretty;
  hyphens: none;
  /* Three stacked shadows: a tight one to separate the stroke from whatever
     is directly behind it, a mid one for the bone highlights, and a wide
     diffuse one that darkens the whole area the text sits on. Body copy over
     a moving lit object needs all three. */
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.95),
    0 0 10px rgba(0, 0, 0, 0.9),
    0 0 30px rgba(0, 0, 0, 0.85);
}

.bio__col--right { text-align: right; }

.bio p { margin: 0; }
.bio p + p { margin-top: 1.25em; }

/* One span per word, injected by main.js so the copy can arrive at reading
   pace. inline-block is what makes them transformable; the text nodes between
   them keep the line breaking native. */
.bio .w { display: inline-block; will-change: transform, opacity; }

/* The skull orbits behind these columns, so each carries its own scrim. */
.bio__col::before {
  content: "";
  position: absolute;
  inset: -14% -16%;
  background: radial-gradient(closest-side,
    rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.46) 62%, transparent 100%);
  z-index: -1;
  pointer-events: none;
}

.tagline { text-wrap: balance; }

.name {
  font-size: clamp(2.4rem, 10.5vw, 9rem);
  font-weight: 200;
  letter-spacing: 0.16em;
  line-height: 1;
  text-indent: 0.16em; /* compensates the trailing letter-space, keeps it optically centred */
  text-align: center;
  white-space: nowrap;
}

/* One span per glyph, injected by main.js for the staggered reveal. */
.name .char { display: inline-block; }
.name .word { display: inline-block; }
.name .word + .word { margin-left: 0.42em; }

.tagline {
  font-size: clamp(1.5rem, 5.2vw, 3.4rem);
  font-weight: 300;
  letter-spacing: 0.26em;
  line-height: 1.18;
  color: var(--bone);
  max-width: 14ch;
}

/* Un solo destino de clic: el handle es el titular y el CTA su botón. Dos
   enlaces al mismo perfil serían dos decisiones para la misma acción. */
.contact {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  font-size: clamp(0.82rem, 2.2vw, 1.05rem);
  font-weight: 300;
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  color: var(--bone);
  text-decoration: none;
  pointer-events: auto;
  padding: 0.75rem 1rem;
  transition: color 0.4s var(--ease), opacity 0.4s var(--ease);
}

.contact__cta {
  font-size: clamp(0.6rem, 1.5vw, 0.72rem);
  letter-spacing: 0.28em;
  text-indent: 0.28em;
  color: var(--bone-dim);
  padding: 0.62rem 1.15rem;
  border: 1px solid rgba(232, 226, 214, 0.28);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.35);
  transition: border-color 0.5s var(--ease), background 0.5s var(--ease),
              color 0.4s var(--ease);
}

.contact:hover,
.contact:focus-visible { color: #fff; }

.contact:hover .contact__cta,
.contact:focus-visible .contact__cta {
  color: #fff;
  border-color: var(--bone);
  background: rgba(0, 0, 0, 0.6);
}
.contact:focus-visible { outline: 1px solid var(--bone-dim); outline-offset: 4px; }

/* ---------------------------------------------------------- selected work */

/* Dead centre, over the socket the camera is inside: at that point the frame
   is all bone texture, which is the one moment in the piece that can carry a
   block of type in the middle without a fight. */
.stage--works { align-items: center; justify-content: center; }

#reveal-works { max-width: min(52ch, 82vw); }

.works {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(1.6rem, 4vh, 3rem);
  will-change: transform;
}

.work { position: relative; }

/* Hairline between entries; the list has to read as three works, not as one
   paragraph with bold bits. */
.work + .work::before {
  content: "";
  position: absolute;
  top: calc(clamp(1.6rem, 4vh, 3rem) / -2);
  left: 50%;
  width: 2.2rem;
  height: 1px;
  transform: translateX(-50%);
  background: rgba(232, 226, 214, 0.22);
}

.work__title {
  margin: 0 0 0.4em;
  font-size: clamp(1.2rem, 2.1vw, 1.8rem);
  font-weight: 400;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  color: #fff;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.95),
    0 0 12px rgba(0, 0, 0, 0.9),
    0 0 32px rgba(0, 0, 0, 0.85);
}

.work__note {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(1rem, 1.3vw, 1.25rem);
  font-weight: 400;
  line-height: 1.6;
  /* Was --bone-dim: too little contrast over lit bone. */
  color: #ded8cc;
  text-wrap: pretty;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.95),
    0 0 10px rgba(0, 0, 0, 0.9),
    0 0 30px rgba(0, 0, 0, 0.85);
}

.work__note em { font-style: italic; color: var(--bone); }

@media (orientation: portrait) {
  .stage--works { justify-content: center; align-items: center; }
  #reveal-works { max-width: 100%; }
  .works { gap: 2rem; text-align: center; }
  .work__title { font-size: clamp(1rem, 4.2vw, 1.4rem); letter-spacing: 0.16em; text-indent: 0.16em; }
  .work__note { font-size: clamp(0.88rem, 3.6vw, 1.05rem); max-width: 30ch; margin: 0 auto; }
}

/* ------------------------------------------------------------- video card */

/* Only clickable inside its own stretch of the timeline — an invisible card
   swallowing pointer events for the rest of the scroll is a trap. */
.videocard.is-near { pointer-events: auto; }

.videocard {
  width: min(86vw, 60rem);
  /* The tilt is written by video.js on the inner node; the perspective has to
     live on the parent or every corner would get its own vanishing point. */
  perspective: 1400px;
}

.videocard__tilt {
  position: relative;
  transform-style: preserve-3d;
  will-change: transform;
}

.videocard__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: clamp(0.9rem, 2vw, 1.6rem);
  background: #050504;
  box-shadow:
    0 2rem 5rem rgba(0, 0, 0, 0.75),
    0 0 0 1px rgba(232, 226, 214, 0.09) inset;
}

.videocard__poster {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  margin: 0;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.videocard__poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Desaturated at rest so the card belongs to the piece; it returns to
     colour as the pointer approaches. */
  filter: grayscale(0.85) contrast(1.06) brightness(0.74);
  transition: filter 0.6s var(--ease), transform 0.9s var(--ease);
  transform: scale(1.02);
}

.videocard:hover .videocard__poster img,
.videocard__poster:focus-visible img {
  filter: grayscale(0.15) contrast(1.04) brightness(0.94);
  transform: scale(1.05);
}

.videocard__poster:focus-visible { outline: 1px solid var(--bone-dim); outline-offset: 4px; }

.videocard__play {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(3.2rem, 6vw, 4.6rem);
  height: clamp(3.2rem, 6vw, 4.6rem);
  transform: translate(-50%, -50%);
  border: 1px solid rgba(232, 226, 214, 0.55);
  border-radius: 50%;
  backdrop-filter: blur(2px);
  transition: border-color 0.5s var(--ease), transform 0.6s var(--ease);
}

.videocard__play::after {
  content: "";
  position: absolute;
  left: 54%;
  top: 50%;
  width: 0;
  height: 0;
  transform: translate(-50%, -50%);
  border-left: 0.85rem solid var(--bone);
  border-top: 0.55rem solid transparent;
  border-bottom: 0.55rem solid transparent;
}

.videocard:hover .videocard__play {
  border-color: var(--bone);
  transform: translate(-50%, -50%) scale(1.08);
}

/* Only there once the clip is running: it is the way back to the scroll. */
.videocard__close {
  position: absolute;
  top: clamp(-3.2rem, -5vw, -2.6rem);
  right: 0;
  width: 2.6rem;
  height: 2.6rem;
  display: none;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(232, 226, 214, 0.4);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  cursor: pointer;
  transition: border-color 0.4s var(--ease), background 0.4s var(--ease);
}

.videocard.is-playing .videocard__close { display: grid; }

.videocard__close:hover,
.videocard__close:focus-visible { border-color: var(--bone); background: rgba(0, 0, 0, 0.8); }
.videocard__close:focus-visible { outline: 1px solid var(--bone-dim); outline-offset: 3px; }

.videocard__close span,
.videocard__close span::before {
  content: "";
  position: absolute;
  width: 0.95rem;
  height: 1px;
  background: var(--bone);
}

.videocard__close span { transform: rotate(45deg); }
.videocard__close span::before { transform: rotate(-90deg); }

.videocard__frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Corner brackets. They sit slightly outside the frame and pull further out
   on hover, so the card reads as something being framed rather than a box. */
.videocard__corner {
  position: absolute;
  width: clamp(1.4rem, 2.6vw, 2.2rem);
  height: clamp(1.4rem, 2.6vw, 2.2rem);
  border: 1px solid rgba(232, 226, 214, 0.45);
  opacity: 0;
  transition: transform 0.7s var(--ease), opacity 0.5s var(--ease),
              border-color 0.5s var(--ease);
  pointer-events: none;
}

.videocard__corner--tl { top: -1px; left: -1px;   border-right: 0; border-bottom: 0; }
.videocard__corner--tr { top: -1px; right: -1px;  border-left: 0;  border-bottom: 0; }
.videocard__corner--bl { bottom: -1px; left: -1px;  border-right: 0; border-top: 0; }
.videocard__corner--br { bottom: -1px; right: -1px; border-left: 0;  border-top: 0; }

.videocard.is-near .videocard__corner { opacity: 0.5; }

.videocard:hover .videocard__corner,
.videocard:focus-within .videocard__corner {
  opacity: 1;
  border-color: var(--bone);
}

.videocard:hover .videocard__corner--tl { transform: translate3d(-0.75rem, -0.75rem, 40px); }
.videocard:hover .videocard__corner--tr { transform: translate3d( 0.75rem, -0.75rem, 40px); }
.videocard:hover .videocard__corner--bl { transform: translate3d(-0.75rem,  0.75rem, 40px); }
.videocard:hover .videocard__corner--br { transform: translate3d( 0.75rem,  0.75rem, 40px); }

/* --------------------------------------------------------------- sign-off */

/* The name returns over the closing photograph. Pinned to the top of the
   stage rather than flowing with it, so the plan view of the skull keeps the
   middle of the frame to itself. */
.signoff {
  position: absolute;
  top: calc(var(--pad) * 0.9 + env(safe-area-inset-top));
  /* Centred by the box, not by a transform: GSAP owns transform on this
     element and a translateX(-50%) here would be wiped by the first tween. */
  left: 0;
  right: 0;
  text-align: center;
  margin: 0;
  opacity: 0;
  white-space: nowrap;
  text-transform: uppercase;
  font-size: clamp(1.1rem, 3.4vw, 2.1rem);
  font-weight: 200;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  color: var(--bone);
  text-shadow: 0 0 24px rgba(0, 0, 0, 0.95), 0 0 8px rgba(0, 0, 0, 0.85);
  will-change: opacity, transform, filter;
  pointer-events: none;
}

@media (orientation: portrait) {
  .signoff { font-size: clamp(0.95rem, 4.4vw, 1.5rem); letter-spacing: 0.3em; text-indent: 0.3em; }
}

/* ------------------------------------------------------------- back to top */

/* Ocupa el alto del cierre para separar sus dos extremos: Instagram arriba,
   pie abajo. El padding superior es el hueco que reserva la firma. */
#reveal-contact {
  position: relative;
  flex: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-top: clamp(4rem, 13vh, 8rem);
}

.outro-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
}

/* El crédito de la casa: presente, nunca compitiendo con el nombre. */
.powered {
  margin: 0;
  font-size: clamp(0.56rem, 1.3vw, 0.68rem);
  font-weight: 300;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  color: rgba(155, 148, 138, 0.75);
  text-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
}

.powered a {
  color: var(--bone-dim);
  text-decoration: none;
  pointer-events: auto;
  transition: color 0.4s var(--ease);
}

.powered a:hover,
.powered a:focus-visible { color: var(--bone); }
.powered a:focus-visible { outline: 1px solid var(--bone-dim); outline-offset: 3px; }

.to-top {
  flex: none;
  width: 3rem;
  height: 3rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(232, 226, 214, 0.28);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  color: var(--bone);
  cursor: pointer;
  pointer-events: auto;
  opacity: 0;
  transition: border-color 0.5s var(--ease), background 0.5s var(--ease),
              opacity 0.6s var(--ease);
}

/* Only reachable once the piece has run its course. */
body.is-end .to-top { opacity: 1; }
body:not(.is-end) .to-top { pointer-events: none; }

.to-top:hover, .to-top:focus-visible {
  border-color: var(--bone);
  background: rgba(0, 0, 0, 0.6);
}

.to-top:focus-visible { outline: 1px solid var(--bone-dim); outline-offset: 4px; }

.to-top__arrow {
  width: 0.62rem;
  height: 0.62rem;
  border-left: 1px solid currentColor;
  border-top: 1px solid currentColor;
  transform: rotate(45deg) translate(0.1rem, 0.1rem);
  transition: transform 0.5s var(--ease);
}

.to-top:hover .to-top__arrow { transform: rotate(45deg) translate(0.28rem, 0.28rem); }

/* -------------------------------------------------------- scroll affordance */

.scroll-hint {
  position: fixed;
  z-index: 5;
  left: 50%;
  bottom: calc(1.6rem + env(safe-area-inset-bottom));
  width: 1px;
  height: 3.2rem;
  transform: translateX(-50%);
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  background: rgba(232, 226, 214, 0.14);
}

.scroll-hint span {
  position: absolute;
  inset: 0;
  background: var(--bone);
  animation: hint 2.4s var(--ease) infinite;
}

@keyframes hint {
  0%   { transform: translateY(-100%); }
  60%  { transform: translateY(100%); }
  100% { transform: translateY(100%); }
}

/* ---------------------------------------------------------------- preloader */

.preloader {
  position: fixed;
  inset: 0;
  z-index: 9;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  background: #000;
  transition: opacity 0.9s var(--ease);
}

.preloader__count {
  font-size: clamp(0.72rem, 1.8vw, 0.86rem);
  letter-spacing: 0.4em;
  text-indent: 0.4em;
  color: var(--bone-dim);
  font-variant-numeric: tabular-nums;
}

.preloader__count::after { content: "%"; }

.preloader__bar {
  width: min(38vw, 240px);
  height: 1px;
  background: rgba(232, 226, 214, 0.16);
  overflow: hidden;
}

.preloader__bar i {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--bone);
  transform: scaleX(0);
  transform-origin: left center;
}

body.is-ready .preloader { opacity: 0; pointer-events: none; }

/* --------------------------------------------------------------- scroll track */

/* Longer than it was: the clip added a beat that needs room to be read as a
   stop rather than as something passing by. */
.scroll-track { height: 1500vh; pointer-events: none; }

/* A phone swipe covers far less of the track than a wheel gesture, so the
   same length that reads as inertia on a desktop reads as a stuck page on a
   handset. Shorter track, same choreography. */
@media (orientation: portrait) {
  .scroll-track { height: 1020vh; }
}

/* ------------------------------------------------------------------ fallback */

body.no-webgl #stage,
body.no-webgl .overlay .stage--2,
body.no-webgl .overlay .stage--video,
body.no-webgl .scroll-hint,
body.no-webgl .scroll-track { display: none; }

body.no-webgl { overflow: hidden; }

body.no-webgl .fallback {
  display: block;
  background: #000 url("../img/fondo.webp") center 42% / cover no-repeat;
  filter: grayscale(1) contrast(1.16) brightness(0.6) blur(3px);
}

body.no-webgl .vignette { opacity: 1; }

body.no-webgl .stage--1 .reveal,
body.no-webgl .stage--3 .reveal {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
}

body.no-webgl .name .char { opacity: 1 !important; transform: none !important; filter: none !important; }

/* ------------------------------------------------------------- orientation */

/* Landscape splits the tagline around the skull: one word hard left, the
   other hard right, with the 3D object holding the gap between them. */
@media (orientation: landscape) {
  .stage--2 { justify-content: center; }
  .js-tagline { width: 100%; }
  .tagline {
    display: flex;
    width: 100%;
    max-width: none;
    justify-content: space-between;
    align-items: center;
  }
}

/* Portrait: the tagline centres instead of hugging the left edge, and the
   name shrinks so the horns stay clear of it. */
@media (orientation: portrait) {
  /* Same grammar as the name: the two words go to opposite edges and the
     skull holds the space between them. Stacked under the object they read
     as a caption, which is not what a tagline is. */
  .stage--2 {
    justify-content: center;
    align-items: stretch;
    padding-top: 10vh;
    padding-bottom: calc(10vh + env(safe-area-inset-bottom));
  }

  .js-tagline { flex: 1 1 auto; width: 100%; display: flex; }

  .tagline {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: center;
    text-align: center;
    max-width: 100%;
    /* Sized off the longest word: INTERVENTION is 12 characters, so at this
       tracking it measures about 10em — anything wider runs off a 390px
       screen. */
    font-size: clamp(1.5rem, 8vw, 3.2rem);
    letter-spacing: 0.12em;
    text-indent: 0.12em;
    line-height: 1.05;
  }

  .tagline .tw { display: block; }

  .stage--bio { justify-content: center; align-items: center; }
  .name       { font-size: clamp(2.4rem, 11vw, 4.4rem); letter-spacing: 0.12em; text-indent: 0.12em; }
  .credit     { font-size: clamp(0.78rem, 3.2vw, 1rem); }
  .contact    { font-size: clamp(0.95rem, 4vw, 1.15rem); }

  /* No width to put the columns either side of the skull: they stack, one
     above it and one below, and the skull keeps the gap between them. The
     explicit height is what lets space-between push them apart — without it
     the two blocks meet in the middle and bury the object. */
  #reveal-bio { height: 100%; }

  .bio {
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    height: 100%;
    padding: 3vh 0 5vh;
    gap: 0;
  }

  .bio__col {
    max-width: 28ch;
    font-size: clamp(1.05rem, 4.5vw, 1.35rem);
    line-height: 1.5;
    text-align: center;
  }

  .bio__col--right { text-align: center; }
  .videocard { width: 92vw; }

  /* Portrait stacks the block whole at the top — name, surname and credit
     as one unit — and deja el resto del alto para el cráneo. Un nombre
     partido entre los bordes leía como dos títulos distintos. */
  .stage--1 {
    justify-content: flex-start;
    padding-top: 7vh;
    padding-bottom: calc(7vh + env(safe-area-inset-bottom));
    gap: clamp(0.9rem, 3vw, 1.8rem);
  }

  #reveal-name { width: 100%; display: flex; }

  .name {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.04em;
    white-space: normal;
    font-size: clamp(2.2rem, 13vw, 5rem);
    letter-spacing: 0.14em;
    text-indent: 0.14em;
  }

  .name .word + .word { margin-left: 0; }
}

@media (orientation: landscape) and (max-height: 560px) {
  .name    { font-size: clamp(1.8rem, 6vw, 3.4rem); }
  .tagline { font-size: clamp(1.1rem, 3vw, 2rem); }
  .bio__col { font-size: 0.9rem; line-height: 1.55; max-width: 32ch; }
  .videocard { width: min(70vw, 34rem); }
}

/* -------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  .scroll-hint span { animation: none; }
  .preloader { transition-duration: 0.01ms; }
}
