/* =========================================================
   DOTS.MEMORIES — case study (v3)
   Se apoya en base.css + components.css + case.css.
   NO usa dots.css: aquel montaba la rejilla de 12 columnas
   de la v2 y esta versión tiene rejilla propia.

   Maquetación tomada del Figma "Dots. Memories — v2
   (revisado)" (542:21253): lienzo de 1512px con el contenido
   en una caja de 943px centrada, o sea 285px de margen a cada
   lado. Dentro, OCHO columnas con canal de 21px:

     columna    = (943 - 7·21) / 8 = 99,5px
     cols 1-8   = 943px   <- ancho completo del contenido
     cols 1-7   = 822px   <- columna de lectura y medios
     cols 1-6   = 700px   <- titulares de sección
     cols 1-3   = 341px   <- etiqueta y fuente del insight
     cols 4-7   = 461px   <- cuerpo del insight
     2 columnas = 220px   <- cada dato de la ficha del hero

   Que son los 942/938, 818/821, 700, 341, 460/461 y 217/220
   que miden las cajas del Figma.
   ========================================================= */

.dots {
  /* Hasta que la ventana no da para 943 + aire, manda el
     padding; a partir de ahí el contenido se queda quieto en
     el centro, como en el lienzo. */
  --content: 943px;
  --gutter: 21px;
  --edge: max(24px, (100vw - var(--content)) / 2);

  /* --- Color ---
     Muestreado del propio Figma. Los dos grises coinciden con
     los que ya usaba la v2, así que se conservan los nombres. */
  --td-mute:  #6b6b73;   /* etiquetas, notas y apoyos */
  --td-cap:   #969696;   /* pies de imagen */
  --td-sheet: #f8f7f5;   /* tarjeta de citas */
  --td-mark:  #e9e9a1;   /* subrayado de la cita */
  --td-dot:   #cccccc;   /* punto del índice, apagado */
  --td-pill:  #f0f0f0;   /* fondo del índice al apuntar */
  --td-node:  #1a1a1a;   /* pasos del diagrama */
  --td-start: #0a84ff;   /* entrada del diagrama */

  /* --- Escala tipográfica ---
     Medida sobre el propio render del Figma (altura de tinta y
     ancho de línea), no a ojo. Ojo con la raíz: base.css pone el
     html al 125%, así que 1rem son 20px y no 16. Los máximos de
     cada clamp son el valor del Figma dividido por esos 20.

       48px h1 · 38 h2 · 30 insight · 22 entradilla y ancla
       17 cuerpo · 15 etiqueta de insight · 13,5 versalita
       52 cifra · 13 pie de cifra · 12 nota y pie de imagen

     Las interlíneas van sin unidad y salen del paso real entre
     líneas del original: el cuerpo son 28px sobre 17. */
  --t-h1:    clamp(1.5rem,  3.17vw, 2.4rem);    /* 48px */
  --t-h2:    clamp(1.3rem,  2.51vw, 1.9rem);    /* 38px */
  --t-ins:   clamp(1.05rem, 1.98vw, 1.5rem);    /* 30px */
  --t-lede:  clamp(0.85rem, 1.46vw, 1.1rem);    /* 22px */
  --t-pull:  clamp(1rem,    1.85vw, 1.4rem);    /* 28px */
  --t-body:  clamp(0.75rem, 1.12vw, 0.85rem);   /* 17px */
  --t-quote: clamp(0.75rem, 1.12vw, 0.85rem);   /* 17px */
  --t-stat:  clamp(1.7rem,  3.44vw, 2.6rem);    /* 52px */
  --t-lab:   clamp(0.65rem, 0.99vw, 0.75rem);   /* 15px */
  --t-kick:  clamp(0.6rem,  0.89vw, 0.675rem);  /* 13,5px */
  --t-scap:  clamp(0.6rem,  0.86vw, 0.65rem);   /* 13px */
  --t-card:  clamp(0.65rem, 0.93vw, 0.7rem);    /* 14px */
  --t-note:  clamp(0.55rem, 0.79vw, 0.6rem);    /* 12px */
  --t-cap:   clamp(0.55rem, 0.79vw, 0.6rem);    /* 12px */
  --t-attr:  clamp(0.52rem, 0.73vw, 0.55rem);   /* 11px */

  background: #fff;
  padding-bottom: var(--space-6);
}
/* El lienzo del Figma es blanco, no el #f9f9f9 del resto del
   sitio. Se respeta, pero sólo en esta página: por eso va aquí
   y no en base.css. */
body:has(.dots) { background: #fff; }

.dots .wrap {
  width: 100%;
  padding-inline: var(--edge);
}

/* ---------- La rejilla de 8 ---------- */
.dots .g {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  column-gap: var(--gutter);
  align-items: start;
}
/* Por defecto todo ocupa el ancho del contenido */
.dots .g > *         { grid-column: 1 / -1; }
.dots .g > .c-read   { grid-column: 1 / span 6; }   /* lectura: una columna menos que los medios, que la línea no se alargue */
.dots .g > .media.c-read,
.dots .g > .cap.c-read { grid-column: 1 / span 7; }   /* medios y su pie */
.dots .g > .c-title  { grid-column: 1 / span 6; }   /* titular de sección */
.dots .g > .c-note   { grid-column: 1 / span 3; }   /* etiqueta del insight */
.dots .g > .c-ins    { grid-column: 4 / span 4; }   /* cuerpo del insight */
.dots .g > .c-half   { grid-column: span 4; }       /* las dos piezas del research */

/* Ficha del hero: cuatro datos de 2 columnas */
.dots .g > .c-m1 { grid-column: 1 / span 2; }
.dots .g > .c-m2 { grid-column: 3 / span 2; }
.dots .g > .c-m3 { grid-column: 5 / span 2; }
.dots .g > .c-m4 { grid-column: 7 / span 2; }

@media (max-width: 900px) {
  .dots .g > *,
  .dots .g > .c-read, .dots .g > .media.c-read, .dots .g > .cap.c-read,
  .dots .g > .c-title, .dots .g > .c-note,
  .dots .g > .c-ins,  .dots .g > .c-half,
  .dots .g > .c-m1, .dots .g > .c-m2,
  .dots .g > .c-m3, .dots .g > .c-m4 { grid-column: 1 / -1; }
  .dots .g { row-gap: var(--space-4); }
}

/* =========================================================
   ÍNDICE LATERAL
   Medido sobre el frame 653:78443 del Figma a tamaño real. La
   geometría es FIJA: nada crece ni se mueve al apuntar.

     punto            11px, paso de 38px entre puntos
     punto activo     15px
     nombre           16px a la derecha de la caja del punto
     al apuntar       entra el listado entero de nombres, y el que
                      está bajo el cursor recibe su píldora: un
                      fondo gris de 30px de alto ceñido A LA PALABRA.
                      La fila no se pinta: el punto se queda fuera
                      del rectángulo.

   Los puntos se quedan exactamente donde están: entre reposo y
   hover no se mueve ni cambia de tamaño nada.

   Se apoya en el mismo JS que el resto del sitio (case.js marca
   .is-here), así que aquí sólo cambia la piel.
   ========================================================= */
.dots-dex {
  /* El índice cuelga de <body>, no de .dots, así que no hereda sus
     variables: las suyas se declaran aquí. */
  --dot-off:  #cccccc;
  --dot-on:   #191919;
  --dot-size: 11px;
  --dot-on-size: 15px;  /* el de la sección en la que estás */
  --step:     38px;
  --pill:     #f3f3f3;
  --lab-off:  #b3b3b3;

  position: fixed;
  left: clamp(16px, 2.9vw, 44px);
  top: 50%;
  transform: translateY(-50%);
  z-index: 120;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 0;
  background: none;
  border: 0;
}
/* Cada fila mide lo que su propio nombre, no lo que la más larga:
   así el área que responde al cursor es exactamente la que se va a
   encender, sin zonas muertas a la derecha.

   La fila NO se pinta. case.css le mete un `background: var(--sheet)`
   al apuntar, que aquí dibujaba un rectángulo gris alrededor del
   punto y el nombre; se anula, porque lo único que se enciende es
   la píldora del nombre. */
.dots-dex .dex__link {
  display: flex;
  align-items: center;
  gap: 16px;                 /* del borde de la caja del punto al nombre */
  height: var(--step);
  padding: 0;
  border-radius: 0;
  color: inherit;
}
.dots-dex .dex__link,
.dots-dex .dex__link:hover,
.dots-dex .dex__link:focus-visible { background: none; }

/* El punto: lo único que se ve en reposo.
   La caja mide siempre lo que el punto activo, y dentro se dibuja
   el círculo con su tamaño. Así los seis puntos comparten eje y el
   nombre arranca siempre a la misma distancia, mida lo que mida el
   punto de al lado. El tamaño no se anima: sólo cambia al pasar de
   una sección a otra al hacer scroll. */
.dots-dex .dex__dot {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--dot-on-size);
  height: var(--dot-on-size);
}
.dots-dex .dex__dot::before {
  content: "";
  display: block;
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  background: var(--dot-off);
  transition: background var(--dur-med) var(--ease-out);
}
.dots-dex .dex__link.is-here .dex__dot::before {
  width: var(--dot-on-size);
  height: var(--dot-on-size);
  background: var(--dot-on);
}

/* El nombre: sólo existe dentro de la píldora, y sólo la del
   elemento apuntado.
   case.css despliega su índice animando max-width desde 0; aquí el
   nombre entra con opacidad, así que hay que soltar ese candado o
   sólo se ve la primera letra. */
.dots-dex .dex__t {
  max-width: none;
  overflow: visible;
  font-size: 0.84rem;          /* 16,8px */
  line-height: 30px;           /* alto de la píldora */
  color: var(--lab-off);
  white-space: nowrap;
  /* La píldora sobresale 10px de la palabra a cada lado y el
     margen negativo lo compensa: el texto no se mueve al aparecer
     el fondo, y el área resaltada es la palabra, no la fila. */
  padding-inline: 10px;
  margin-left: -10px;
  border-radius: 8px;
  /* No entra deslizándose ni cambia de tamaño: aparece donde ya
     ocupaba su sitio. */
  opacity: 0;
  background: transparent;
  pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
/* Al apuntar la columna salen todos los nombres */
.dots-dex:hover .dex__t,
.dots-dex:focus-within .dex__t { opacity: 1; }
.dots-dex .dex__link.is-here .dex__t { color: var(--fg-strong); }

/* Y sólo el que está bajo el cursor recibe la píldora. Va sobre el
   nombre, no sobre la fila: el punto queda fuera. */
@media (hover: hover) and (pointer: fine) {
  .dots-dex .dex__link:hover .dex__t { background: var(--pill); }
}
.dots-dex .dex__link:focus-visible .dex__t {
  background: var(--pill);
  outline: 2px solid var(--fg-strong);
  outline-offset: 2px;
}

/* Por debajo de 1100px el índice se comería el texto */
@media (max-width: 1100px) { .dots-dex { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .dots-dex .dex__t,
  .dots-dex .dex__dot::before { transition: none; }
}

/* =========================================================
   SECCIONES
   El Figma separa las secciones con 130-240px de aire. No hay
   filetes ni fondos: el aire es la separación.
   ========================================================= */
.dots .sec {
  margin-block: clamp(var(--space-6), 11vw, 160px);
  scroll-margin-top: 100px;
}
.dots .sec .g + .g,
.dots .sec .g + .strip,
.dots .strip + .g { margin-top: clamp(var(--space-3), 3.7vw, 56px); }
/* Insights: el vídeo va pegado a su texto (40px) y el siguiente
   insight abre un poco más (72px), para que cada pareja texto +
   vídeo se lea como un bloque. */
.dots .sec .g + .g--l { margin-top: clamp(var(--space-4), 4.8vw, 72px); }
.dots .sec .g + .g--m { margin-top: clamp(var(--space-3), 2.6vw, 40px); }

/* La tira de tarjetas va fuera de su sección (es a sangre), así que
   la separación con el texto la pone el margen de la sección: se
   acorta cuando lo que sigue es una tira. */
.dots .sec:has(+ [data-car]) { margin-bottom: clamp(var(--space-3), 3.7vw, 56px); }

.dots .head { margin-bottom: 36px; }

.dots .kick {
  font-size: var(--t-kick);
  font-weight: var(--w-bold);
  line-height: 1.4;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--td-mute);
  margin-bottom: 16px;
}

.dots .h2 {
  font-size: var(--t-h2);
  font-weight: var(--w-bold);
  line-height: 1.21;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

/* ---------- Cuerpo ----------
   16px sobre 26 de interlínea, con 22px extra entre párrafos:
   los 48px que separan los bloques en el original. */
.dots .copy {
  font-size: var(--t-body);
  line-height: 1.647;          /* 28px sobre 17 */
}
.dots .copy + .copy { margin-top: 28px; }
.dots .copy b, .dots .copy strong { font-weight: var(--w-bold); }

/* "QUÉ DECIDIMOS:" va dentro del párrafo, en versalita, como
   en el Figma. No es un bloque aparte. */
.dots .did {
  display: inline;
  font-size: var(--t-kick);
  font-weight: var(--w-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--td-mute);
}

/* =========================================================
   HERO
   Al revés que la v2: primero la imagen, y el nombre debajo.
   ========================================================= */
.dots .dhero { padding-top: clamp(56px, 7vw, 69px); }

/* La apertura es una foto vertical en un hueco de 3:2: se recorta con
   object-fit y el encuadre deja a la vista el QR, el título y la cara. */
.dots .media--cover {
  aspect-ratio: 929 / 619;
  overflow: hidden;
  border-radius: var(--radius);
}
.dots .media--cover img {
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
}


.dots .dhero__name {
  margin-top: clamp(48px, 6.5vw, 85px);
  font-size: var(--t-h1);
  font-weight: var(--w-bold);
  line-height: 1.1;
  letter-spacing: -0.02em;
}
.dots .dhero__lede {
  margin-top: 15px;
  grid-column: 1 / span 6;
  font-size: var(--t-lede);
  line-height: 1.636;
}
@media (max-width: 900px) {
  .dots .dhero__lede { grid-column: 1 / -1; line-height: 1.55; }
}

/* Ficha: Ámbito · Rol y periodo · Equipo · Herramientas */
.dots .spec {
  margin-top: 46px;
  row-gap: var(--space-4);
}
.dots .spec dt {
  font-size: var(--t-kick);
  font-weight: var(--w-bold);
  line-height: 1.5;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--td-mute);
  margin-bottom: 5px;
}
.dots .spec dd {
  font-size: var(--t-body);
  line-height: 1.647;
}
/* ---------- Las caras del equipo ----------
   Cuatro círculos de 40px superpuestos 12px, en el mismo orden y
   con el mismo solape que en el Figma (de 526 a 650 = 124px para
   cuatro de 40).

   Al apuntar una sale su bocadillo. La medida está tomada del
   frame 674:80114: caja de 185px mínimo, 16 de padding, radio 24,
   fondo translúcido con desenfoque detrás y una cola de
   20,84 × 12,87 colgando del centro. */
.dots .faces {
  display: flex;
  margin-top: 10px;
  /* La tira es la referencia de posición de los bocadillos y no
     debe recortarlos */
  padding-top: 0;
}
.dots .face {
  position: relative;
  flex: none;
  width: 40px;
  height: 40px;
}
.dots .face + .face { margin-left: -12px; }
.dots .face img {
  display: block;
  width: 40px;
  height: 40px;
  border-radius: 50%;
}
/* Al apuntar una cara no cambia el orden de la tira: sólo sale su
   bocadillo, que va por encima de todas (z-index propio, más abajo). */

.dots .face__tip {
  position: absolute;
  /* 20 de separación menos los 12,87 que baja la cola: la punta
     queda a ~7px del avatar, como en el Figma */
  bottom: calc(100% + 20px);
  left: 50%;
  translate: -50%;
  z-index: 3;
  min-width: 185px;
  max-width: 318px;
  padding: 16px;
  border-radius: 24px;
  /* Opaco, sin el cristal del Figma: el backdrop-filter impedía que la
     sombra envolviera la cola (Chrome la deja fuera al componer la
     capa), y con el 85 % de opacidad se leía la etiqueta de debajo a
     través del pico. Sobre fondo blanco la diferencia no se aprecia. */
  background: #f8f7f5;
  border: 1px solid rgba(255, 255, 255, 0.3);
  /* La sombra va como filtro y no como box-shadow: así sigue el
     contorno real, cola incluida (la cola es un clip-path, y el
     box-shadow la ignoraba). */
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.18));
  font-size: 0.65rem;          /* 13px */
  line-height: 17px;
  letter-spacing: -0.006em;
  text-align: center;
  white-space: nowrap;
  color: var(--fg-strong);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out);
}
/* La cola. Va con el contorno exacto del Figma —incluida la punta
   redondeada, que es lo que le da el carácter— en vez del cuadrado
   girado de siempre, que sale con la punta en pico y más ancho de
   lo que toca. El path apunta hacia arriba y se voltea. */
.dots .face__tip::after {
  content: "";
  position: absolute;
  top: calc(100% - 1px);
  left: 50%;
  width: 20.84px;
  height: 12.87px;
  translate: -50%;
  transform: scaleY(-1);
  background: #f8f7f5;
  clip-path: path("M9.30664 0.241775C10.06 -0.0804833 10.9126 -0.080383 11.666 0.241775C12.5288 0.610996 13.2041 1.68674 14.5527 3.83748L17.9238 9.21443C18.4384 10.0351 18.6969 10.4459 19.0449 10.7437C19.353 11.0071 19.7119 11.2056 20.0986 11.3267C20.3159 11.3947 20.5454 11.4285 20.8398 11.4459V12.8697H0V11.4498C0.360139 11.4353 0.625134 11.4046 0.874023 11.3267C1.26091 11.2056 1.61961 11.0072 1.92773 10.7437C2.27588 10.4459 2.53409 10.0353 3.04883 9.21443L6.41992 3.83748C7.76862 1.68664 8.44391 0.611033 9.30664 0.241775Z");
}

@media (hover: hover) and (pointer: fine) {
  .dots .face:hover .face__tip { opacity: 1; }
}
.dots .face:focus-within .face__tip { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .dots .face__tip { transition: none; }
}

/* =========================================================
   INSIGHTS
   Etiqueta y procedencia a la izquierda (cols 1-3); a su
   altura, la frase grande con su explicación (cols 4-7).
   ========================================================= */
.dots .ins__n {
  font-size: var(--t-lab);
  font-weight: var(--w-bold);
  line-height: 1.6;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-strong);
}
.dots .ins__src {
  margin-top: 16px;
  font-size: var(--t-note);
  line-height: 1.55;
  color: var(--td-mute);
}
.dots .ins__q {
  font-size: var(--t-ins);
  font-weight: var(--w-bold);
  line-height: 1.167;
  letter-spacing: -0.01em;
  text-wrap: balance;
  margin-bottom: 18px;
}

/* Frase ancla del bloque de prototipo.
   case.css ya tiene un .pull con 96px de margen arriba y abajo para
   los otros case studies: aquí se anula, que el ritmo lo pone el
   Figma y no aquel. */
.dots .pull {
  margin: 0 0 37px;
  font-size: var(--t-pull);
  font-weight: var(--w-bold);
  line-height: 1.25;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* =========================================================
   TARJETA DE CITAS
   Las tres citas del research. Van en HTML y no como imagen:
   son texto real, se pueden copiar y un lector de pantalla las
   recorre. El subrayado es un <mark>.
   ========================================================= */
.dots .quotes {
  background: var(--td-sheet);
  border-radius: var(--radius);
  padding: 47px 48px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 40px;
  aspect-ratio: 461 / 486;
}
.dots .quotes blockquote { margin: 0; }
.dots .quotes p {
  font-size: var(--t-quote);
  font-weight: var(--w-bold);
  line-height: 1.59;
}
.dots .quotes mark {
  background: var(--td-mark);
  color: inherit;
  padding: 2px 1px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.dots .quotes cite {
  display: block;
  margin-top: 12px;
  font-style: normal;
  font-size: var(--t-attr);
  line-height: 1.27;
  color: var(--td-cap);
}
@media (max-width: 900px) {
  .dots .quotes { aspect-ratio: auto; padding: 32px; }
}

/* =========================================================
   DIAGRAMA DE FLUJO
   Va en HTML y no como imagen por lo mismo que las citas.
   Se llama .dflow y no .flow porque case.css ya usa .flow.

   A sangre y con arrastre lateral si no cabe: el mismo gesto
   que ya hacen las tiras de esta página.
   ========================================================= */
.dots .dflow {
  --wire: #b9b9b9;
  --bracket: 26px;
  margin: 0;
  width: 100vw;
  margin-inline: calc(50% - 50vw);
}
.dots .dflow__scroll {
  display: flex;
  align-items: center;
  padding-inline: var(--edge);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.dots .dflow__scroll::-webkit-scrollbar { display: none; }

/* Cada paso: píldora oscura con el texto en blanco */
.dots .dflow__node {
  flex: none;
  display: grid;
  place-items: center;
  text-align: center;
  min-width: 73px;
  min-height: 55px;
  padding: 8px 10px;
  border-radius: 12px;
  background: var(--td-node);
  color: #fff;
  font-size: var(--t-note);
  line-height: 1.3;
}
/* La entrada: círculo azul */
.dots .dflow__in {
  flex: none;
  display: grid;
  place-items: center;
  width: 73px;
  height: 73px;
  border-radius: 50%;
  background: var(--td-start);
  color: #fff;
  font-size: var(--t-note);
  line-height: 1.3;
}
/* La decisión: rombo de contorno */
.dots .dflow__q {
  flex: none;
  position: relative;
  display: grid;
  place-items: center;
  width: 110px;
  height: 110px;
  font-size: var(--t-note);
  line-height: 1.3;
  text-align: center;
}
.dots .dflow__q::before {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: 78px;
  height: 78px;
  translate: -50% -50%;
  rotate: 45deg;
  border: 1px solid var(--wire);
  border-radius: 4px;
}
.dots .dflow__q span { position: relative; max-width: 84px; }

/* Cables */
.dots .dflow__wire {
  flex: none;
  width: 22px;
  height: 1px;
  background: var(--wire);
}
.dots .dflow__wire--grow { flex: 1 1 22px; width: auto; }

/* La rama: dos carriles con su etiqueta */
.dots .dflow__fork { flex: none; display: flex; flex-direction: column; }
.dots .dflow__lanes {
  position: relative;
  display: grid;
  grid-template-rows: 1fr 1fr;
  margin-inline: var(--bracket);
}
.dots .dflow__lane {
  display: flex;
  align-items: center;
  padding-block: 16px;
  min-width: 0;
}
.dots .dflow__lab {
  flex: none;
  font-size: var(--t-note);
  line-height: 1.3;
  padding-inline: 8px;
}
/* Apertura y cierre de la llave: una caja sin un lado */
.dots .dflow__split,
.dots .dflow__close {
  position: absolute;
  top: 25%;
  bottom: 25%;
  width: var(--bracket);
  border: 1px solid var(--wire);
}
.dots .dflow__split {
  left: calc(var(--bracket) * -1);
  border-right: 0;
  border-radius: 12px 0 0 12px;
}
.dots .dflow__close {
  right: calc(var(--bracket) * -1);
  border-left: 0;
  border-radius: 0 12px 12px 0;
}
/* Tronco común */
.dots .dflow__trunk { display: flex; align-items: center; }
.dots .dflow__trunk li { display: flex; align-items: center; }
.dots .dflow__trunk li:not(:last-child)::after {
  content: "";
  width: 10px;
  height: 1px;
  background: var(--wire);
  flex: none;
}
@media (max-width: 640px) {
  .dots .dflow { --bracket: 20px; }
  .dots .dflow__in { width: 64px; height: 64px; }
  .dots .dflow__q  { width: 96px; height: 96px; }
  .dots .dflow__q::before { width: 68px; height: 68px; }
}

/* =========================================================
   MEDIA
   Todo bloque de imagen es un .media. Para cambiar cualquiera
   por vídeo basta sustituir <img> por
   <video autoplay muted loop playsinline>: las reglas valen
   para los dos.
   ========================================================= */
.dots .media { margin: 0; }
.dots .media img,
.dots .media video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}
.dots .media figcaption {
  margin-top: 14px;
  font-size: var(--t-cap);
  line-height: 1.65;
  color: var(--td-cap);
  text-align: center;
}

/* Pie que no cuelga de una .media: el del insight 03, que va debajo
   de las dos piezas y no de una sola. */
.dots .cap {
  font-size: var(--t-cap);
  line-height: 1.65;
  color: var(--td-cap);
  text-align: center;
}
/* Va en su propia fila de la rejilla, justo después de las piezas:
   se pega a ellas con el mismo margen que un figcaption (14px), no
   con el aire entre bloques. */
.dots .sec .g + .g:has(> .cap:only-child) { margin-top: 14px; }

/* A sangre: el ancho de la ventana menos el margen del sitio */
.dots .media--full {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding-inline: var(--inset);
}

/* Las dos piezas del insight 03, en proporción 500:320 */
.dots .duo2 {
  grid-column: 1 / span 7;
  display: grid;
  grid-template-columns: 500fr 320fr;
  gap: 18px;
}
.dots .duo2 img { height: 100%; object-fit: cover; }
@media (max-width: 900px) {
  .dots .duo2 { grid-column: 1 / -1; grid-template-columns: 1fr; }
  .dots .duo2 img { height: auto; }
}

/* =========================================================
   TIRA HORIZONTAL
   En el Figma las cuatro tarjetas de restricciones suman
   2.399px sobre un lienzo de 943: se salen por la derecha.
   Eso es un carrusel, así que aquí se maqueta como tal.
   ========================================================= */
.dots .strip {
  display: flex;
  gap: 24px;
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding-inline: var(--edge);
  scroll-padding-inline: var(--edge);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
  align-items: start;
}
.dots .strip::-webkit-scrollbar { display: none; }
.dots .strip.is-dragging { cursor: grabbing; scroll-snap-type: none; }
.dots .strip.is-dragging * { pointer-events: none; }
/* Dentro de una tira las tarjetas entran una detrás de otra, de
   izquierda a derecha: fundido y un pequeño desplazamiento en el
   sentido del carrusel (no vertical, que aquí no pega). Los escalones
   son más largos que los de case.css para que la secuencia se note. */
.dots .strip .rise { transform: translateX(32px); }
.dots .strip .rise.is-in { transform: none; }
.dots .strip .rise[data-delay="1"] { transition-delay: 160ms; }
.dots .strip .rise[data-delay="2"] { transition-delay: 320ms; }
.dots .strip .rise[data-delay="3"] { transition-delay: 480ms; }

.dots .strip .card {
  flex: 0 0 auto;
  width: min(582px, 82vw);
  scroll-snap-align: start;
}
.dots .strip .card img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}
.dots .card__t {
  margin-top: 16px;
  font-size: var(--t-body);
  font-weight: var(--w-bold);
  line-height: 1.5;
}
.dots .card__d {
  margin-top: 6px;
  font-size: var(--t-body);
  line-height: 1.6;
}



/* =========================================================
   MÉTRICAS
   Tres columnas dentro del ancho de lectura, con dos ocupadas.
   La tercera se queda vacía a propósito: en el Figma sólo hay
   dos cifras y no se estiran para rellenar.
   ========================================================= */
.dots .stats {
  grid-column: 1 / span 7;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gutter);
}
.dots .stat b {
  display: block;
  font-size: var(--t-stat);
  font-weight: var(--w-bold);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.dots .stat span {
  display: block;
  margin-top: 14px;
  font-size: var(--t-scap);
  line-height: 1.85;
  color: var(--td-mute);
}
@media (max-width: 900px) { .dots .stats { grid-column: 1 / -1; } }
@media (max-width: 640px) {
  .dots .stats { grid-template-columns: 1fr; gap: var(--space-4); }
}

/* Hueco pendiente: se ve que falta, no se disimula.
   Mismo criterio que los .hole del resto del sitio. */
.dots .gap {
  display: grid;
  place-items: center;
  aspect-ratio: 461 / 486;
  border: 1px dashed rgba(0, 0, 0, 0.22);
  border-radius: var(--radius);
  padding: 24px;
  text-align: center;
  font-size: var(--t-note);
  line-height: 1.6;
  color: var(--td-mute);
}

@media (prefers-reduced-motion: reduce) {
  .dots .strip { scroll-behavior: auto; }
}

/* =========================================================
   MÓVIL CON VÍDEO
   Las grabaciones de los insights son pantalla limpia, en
   proporción 9:19,5. El marco, la isla, la sombra y el fondo
   van aquí y no en el vídeo: pesa menos, sale nítido en retina
   y las tres piezas quedan idénticas.

   Medido sobre las imágenes que había (839×488): fondo #e8eef2,
   móvil de 204px (24,3 % del escenario) con bisel de 6px y
   radio exterior de ~30px. Todo se escala con el ancho del
   escenario (cqw), así que en móvil se encoge en proporción.
   ========================================================= */
.dots .stage {
  --stage-bg: #e8eef2;
  container-type: inline-size;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8%;
  aspect-ratio: 839 / 488;
  border-radius: var(--radius);
  background: var(--stage-bg);
  overflow: hidden;
}
/* El del insight 03 va en la caja cuadrada del dúo */
.dots .stage--sq { aspect-ratio: 500 / 487; }

/* El marco, tal cual el Figma (569:25589): 202,7×427 con bisel de
   5px, radio exterior 38 y de pantalla 33, sin isla dinámica. El
   bisel es un gris translúcido sobre el fondo, no negro. Todo se
   expresa en función del ancho del móvil (--k = 1px del Figma), así
   que en el escenario cuadrado basta con cambiar --pw.

   Las esquinas van con radio normal: medido sobre el render del
   Figma, el suavizado del 60 % que lleva allí apenas se separa del
   arco circular de 38px, y forzar un squircle en CSS lo exageraba. */
.dots .phone {
  /* Ancho y bisel en % del escenario (el padding en % se mide sobre
     el ancho del contenedor): 202,7 y 5 sobre 839. Así no dependen
     de unidades de contenedor, que no todos los navegadores resuelven
     igual dentro de una variable. */
  --pw: 24.16%;
  --bezel: 0.596%;
  --k: 0.2416cqw;                       /* 1px del Figma; sólo para la sombra */
  position: relative;
  flex: none;
  width: var(--pw);
  padding: var(--bezel);
  /* Radio en %: 38px sobre 202,7 de ancho y sobre 427 de alto. Como la
     caja tiene proporción fija, la esquina sale circular. */
  border-radius: 18.75% / 8.9%;
  background: rgba(27, 27, 27, 0.6);
  box-shadow:
    calc(-1.153 * var(--k))  calc(8.074 * var(--k))   calc(17.302 * var(--k)) rgba(0, 0, 0, 0.05),
    calc(-5.767 * var(--k))  calc(32.298 * var(--k))  calc(32.298 * var(--k)) rgba(0, 0, 0, 0.04),
    calc(-12.688 * var(--k)) calc(71.516 * var(--k))  calc(43.833 * var(--k)) rgba(0, 0, 0, 0.03),
    calc(-21.916 * var(--k)) calc(128.037 * var(--k)) calc(51.907 * var(--k)) rgba(0, 0, 0, 0.01);
}
.dots .stage--sq .phone { --pw: 40.5%; --bezel: 1%; --k: 0.405cqw; }

.dots .phone video,
.dots .phone img {
  display: block;
  width: 100%;
  aspect-ratio: 192.69 / 417;
  object-fit: cover;
  border-radius: 17.13% / 7.91%;        /* 33px sobre 192,7 × 417 */
  background: #000;
}

/* =========================================================
   GRID DE LA INTRO
   Cinco piezas en dos columnas, con el reparto que tenía la
   imagen compuesta (942×706): a la izquierda dos cuadrados y
   la home grande; a la derecha el perfil de grupo y la
   ilustración, que rellena lo que queda.
   ========================================================= */
.dots .intro {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin: 0;
}
.dots .intro__col {
  display: grid;
  gap: 20px;
  align-content: start;
}
.dots .intro__col--r { grid-template-rows: auto minmax(0, 1fr); }
.dots .intro__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}
/* Cada pieza entra por su cuenta, en cascada: el .rise va en la
   imagen y no en el bloque. case.css sólo trae dos escalones de
   retardo; aquí hacen falta cuatro. */
.dots .intro .rise[data-delay="3"] { transition-delay: 270ms; }
.dots .intro .rise[data-delay="4"] { transition-delay: 360ms; }
.dots .intro img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius);
}
@media (max-width: 640px) {
  .dots .intro { grid-template-columns: 1fr; gap: 12px; }
  .dots .intro__col, .dots .intro__pair { gap: 12px; }
  .dots .intro__col--r { grid-template-rows: auto auto; }
  .dots .intro__col--r img { aspect-ratio: auto; height: auto; }
}

/* =========================================================
   ABANICO DE PANTALLAS
   Las cinco pantallas del flujo, con la entrada de la sección
   «Cosas de familia» de apple.com/iphone-17: los móviles llegan
   apilados en el centro, algo más grandes y más abajo, y al
   entrar en pantalla se abren en abanico durante 1,5s. El del
   centro queda delante y a tamaño; los de los lados, más
   pequeños cuanto más lejos.

   Los PNG traen el móvil con su sombra sobre transparente: el
   móvil ocupa el 62,6 % del ancho del archivo y el 68 % del
   alto, el resto es la sombra. --iw es el ancho del archivo.
   El disparo lo da case.js con .is-in (mismo IntersectionObserver
   que el resto de imágenes).
   ========================================================= */
.dots .fan {
  --iw: 38cqw;                  /* archivo → móvil de ~24cqw */
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  container-type: inline-size;
  position: relative;
  margin: 0;
  width: 100%;
  /* El alto va en proporción y no en cqw: en el propio contenedor
     los cqw se miden contra el ancestro, no contra él mismo.
     1,42 × 38 % ≈ 54 % del ancho: el móvil del centro (70 % del
     alto del archivo) y un poco de aire para el pie. */
  aspect-ratio: 100 / 54;
  margin-bottom: 40px;   /* sitio para el pie */
}
.dots .fan__p {
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--iw);
  height: auto;
  margin-left: calc(var(--iw) / -2);
  /* Apilados y ligeramente por debajo, como en el original */
  transform: translateY(6%) scale(1.3);
  transition: transform 1.5s var(--ease);
  will-change: transform;
}
.dots .fan__p[data-pos="0"]  { z-index: 5; }
.dots .fan__p[data-pos="-1"],
.dots .fan__p[data-pos="1"]  { z-index: 4; }
.dots .fan__p[data-pos="-2"],
.dots .fan__p[data-pos="2"]  { z-index: 3; }

/* .rise anima al propio contenedor (fundido y subida); aquí sólo
   se abre el abanico cuando llega .is-in. Los porcentajes de
   translateX van sobre el ancho del archivo (--iw), no del móvil. */
.dots .fan.is-in .fan__p[data-pos="0"]  { transform: none; }
.dots .fan.is-in .fan__p[data-pos="-1"] { transform: translateX(-55%)  translateY(2%) scale(0.92); }
.dots .fan.is-in .fan__p[data-pos="1"]  { transform: translateX(55%)   translateY(2%) scale(0.92); }
.dots .fan.is-in .fan__p[data-pos="-2"] { transform: translateX(-104%) translateY(4%) scale(0.84); }
.dots .fan.is-in .fan__p[data-pos="2"]  { transform: translateX(104%)  translateY(4%) scale(0.84); }

/* El pie cuelga por debajo de la caja, que el móvil del centro
   llega hasta el borde inferior */
.dots .fan figcaption {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 14px);
  font-size: var(--t-cap);
  line-height: 1.65;
  color: var(--td-cap);
  text-align: center;
}

/* En estrecho no caben cinco: pasa a tira con arrastre, sin
   abanico, y las fotos se quedan a ancho de móvil */
@media (max-width: 640px) {
  .dots .fan {
    --iw: 70vw;
    display: flex;
    align-items: flex-start;
    gap: 0;
    aspect-ratio: auto;
    height: auto;
    margin-bottom: 0;
    width: 100vw;
    margin-inline: calc(50% - 50vw);
    padding-inline: var(--edge);
    overflow-x: auto;
    /* Imán al inicio de cada una, no al centro: centrada, la primera
       no puede quedar bien (haría falta scroll negativo) y el
       navegador salta a la segunda nada más cargar */
    scroll-snap-type: x proximity;
    scroll-padding-inline: var(--edge);
    scrollbar-width: none;
  }
  .dots .fan::-webkit-scrollbar { display: none; }
  .dots .fan__p,
  .dots .fan.is-in .fan__p[data-pos] {   /* gana a las reglas del abanico */
    position: static;
    flex: none;
    width: var(--iw);
    height: auto;
    margin: 0 -10% 0 0;     /* que la sombra de una no separe de la siguiente */
    transform: none;
    transition: none;
    scroll-snap-align: start;
  }
  /* La sombra baja hasta el 95 % del archivo: se recorta el aire
     sobrante por abajo. El pie iría al final de la tira, fuera de
     la vista, así que no se muestra: cada pantalla lleva su alt. */
  .dots .fan { margin-bottom: calc(var(--iw) * -0.45); }
  .dots .fan figcaption { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .dots .fan__p { transition: none; }
}

/* =========================================================
   ANTES / DESPUÉS
   Dos imágenes del mismo tamaño superpuestas; la de «antes» se
   recorta por la derecha hasta el corte (--x) y la de «después»
   por la izquierda, cada una con sus cuatro esquinas redondeadas.
   Entre las dos queda un hueco de 19px en el que se ve el fondo
   de la página, y en medio el tirador: un trazo de 4×60 gris.
   Medido sobre el Figma 755:46422.

   El corte lo mueve case.js escribiendo --x; al pulsar salta con
   una transición corta y al arrastrar la sigue en seco (.is-drag).
   ========================================================= */
.dots .cmp {
  --gap: 19px;
  --handle: #969696;
  position: relative;
  aspect-ratio: 943 / 563;
  border-radius: var(--radius);
  overflow: hidden;
  cursor: col-resize;
  user-select: none;
  -webkit-user-select: none;
  touch-action: pan-y;         /* el gesto horizontal es nuestro; el vertical, del scroll */
}
.dots .cmp:focus-visible { outline: 2px solid var(--fg-strong); outline-offset: 4px; }

.dots .cmp__layer {
  position: absolute;
  inset: 0;
  transition: clip-path 200ms ease-out;
}
.dots .cmp__layer img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  -webkit-user-drag: none;
}
/* Cada lado es una tarjeta con sus cuatro esquinas: el recorte lleva
   el mismo radio que el bloque, así el corte no parte la imagen sino
   que la cierra. */
.dots .cmp__before { clip-path: inset(0 calc(100% - var(--x) + var(--gap) / 2) 0 0 round var(--radius)); }
.dots .cmp__after  { clip-path: inset(0 0 0 calc(var(--x) + var(--gap) / 2) round var(--radius)); }

.dots .cmp__handle {
  position: absolute;
  top: 50%;
  left: var(--x);
  width: 4px;
  height: 60px;
  translate: -50% -50%;
  border-radius: 999px;
  background: var(--handle);
  pointer-events: none;
  transition: left 200ms ease-out;
}
/* Mientras se arrastra, sin transición: que siga al dedo */
.dots .cmp.is-drag .cmp__layer,
.dots .cmp.is-drag .cmp__handle { transition: none; }

@media (max-width: 640px) {
  .dots .cmp { --gap: 12px; }
  .dots .cmp__handle { height: 44px; }
}
@media (prefers-reduced-motion: reduce) {
  .dots .cmp__layer, .dots .cmp__handle { transition: none; }
}

/* =========================================================
   SIN SQUIRCLE
   base.css pone corner-shape: squircle a todo lo redondeado con un
   selector de tres clases (*:not():not():not()), así que un simple
   .dots .x no le gana. Aquí van, con tres clases, los redondeos que
   tienen que ser arcos circulares: el marco del móvil y su pantalla
   (radios del Figma) y el tirador del comparador, que con 4px de
   ancho se quedaba con los extremos cuadrados.
   ========================================================= */
.dots .cmp .cmp__handle,
.dots .mini .mini__btn,
.dots .mini .mini__dots,
.dots .mini .mini__dot { corner-shape: round; }   /* controles del mini carrusel: círculos y pastilla */
/* El móvil, con un suavizado ligero: round es superellipse(1) y
   squircle es superellipse(2); 1.3 se queda cerca del arco circular
   pero sin el cambio brusco de curvatura al entrar en la esquina. */
.dots .stage .phone,
.dots .stage .phone video,
.dots .stage .phone img { corner-shape: superellipse(1.3); }

/* =========================================================
   PIE: ANTERIOR / SIGUIENTE
   El pie de esta página no lleva la firma ni el reloj: sólo el
   paso al proyecto anterior y al siguiente, uno a cada lado, con
   el nombre grande. Se apoya en .footer (fijo, se destapa al
   llegar abajo) y sólo cambia el contenido.
   ========================================================= */
.footer--nav {
  justify-content: center;
  padding-block: var(--space-4);
}
.pnav {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-4);
}
.pnav__link {
  display: flex;
  flex-direction: column;
  gap: 10px;
  color: var(--ink-fg);
  text-decoration: none;
}
.pnav__link--next { text-align: right; align-items: flex-end; }
.pnav__lab {
  font-size: var(--t-kick, 0.675rem);
  font-weight: var(--w-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.55;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.pnav__name {
  font-size: clamp(1.4rem, 3.2vw, 2.6rem);
  font-weight: var(--w-bold);
  line-height: 1.05;
  letter-spacing: -0.02em;
}
/* La flecha se desplaza hacia su lado al apuntar */
.pnav__lab [aria-hidden] {
  display: inline-block;
  transition: transform var(--dur-med) var(--ease-out);
}
.pnav__link:hover .pnav__lab { opacity: 1; }
.pnav__link--prev:hover .pnav__lab [aria-hidden] { transform: translateX(-4px); }
.pnav__link--next:hover .pnav__lab [aria-hidden] { transform: translateX(4px); }
.pnav__link:focus-visible { outline: 2px solid var(--ink-fg); outline-offset: 6px; }

@media (max-width: 640px) {
  .pnav { flex-direction: column; align-items: stretch; gap: var(--space-4); }
  .pnav__link--next { align-items: flex-end; }
}

/* =========================================================
   MINI CARRUSEL
   Como el de amylalai.com/untitledlyrics-fromhome: el escenario
   gris es el propio carrusel; dentro, cada pantalla lleva su móvil
   y su pie, y sobre el escenario van las flechas a los lados y la
   paginación abajo, en una pastilla. Pasa solo cada pocos segundos
   deslizando en horizontal, y se para al apuntarlo o al salir de
   la vista.
   ========================================================= */
.dots .mini {
  --slide-gap: 48px;             /* aire entre pantallas al deslizar */
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background: #e8eef2;
}
.dots .mini__track {
  display: flex;
  gap: var(--slide-gap);
  transition: transform 700ms cubic-bezier(0.2, 0.7, 0.2, 1);
  will-change: transform;
}
.dots .mini__slide { flex: 0 0 100%; min-width: 0; }
/* El escenario "libre": sin fondo ni proporción propios (los pone
   .mini), el móvil arriba y el pie debajo; abajo queda sitio para
   la pastilla de puntos. */
.dots .stage--free {
  flex-direction: column;
  justify-content: flex-start;
  gap: 0;
  aspect-ratio: auto;
  background: none;
  border-radius: 0;
  padding: 36px 24px 52px;
}
/* Los pies, apilados en la misma celda y fundidos: sólo se ve el
   de la pantalla activa. El figcaption ya trae el margen y el
   estilo de .media. */
.dots .mini__caps { display: grid; }
.dots .mini__cap {
  grid-area: 1 / 1;
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out);
}
.dots .mini__cap.is-on { opacity: 1; }

/* Flechas y paginación, medidas sobre la referencia a tamaño real:
   botón de 40px en #c2c2c2 a 20px del borde con chevron blanco de
   2px; pastilla de 48×21 en el mismo gris, a 14px del borde
   inferior, con puntos de 8px separados 12px: #e0e0e0 el apagado,
   blanco el activo. Sin hover: en la referencia tampoco lo hay. */
.dots .mini__btn {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 2;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: #c2c2c2;
  color: #fff;
  cursor: pointer;
}
.dots .mini__btn svg { width: 24px; height: 24px; }
.dots .mini__btn--prev { left: 20px; }
.dots .mini__btn--next { right: 20px; }
.dots .mini__btn:focus-visible { outline: 2px solid var(--fg-strong); outline-offset: 3px; }

.dots .mini__dots {
  position: absolute;
  left: 50%;
  bottom: 14px;
  translate: -50%;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 12px;
  height: 21px;
  padding: 0 10px;
  border-radius: 999px;
  background: #c2c2c2;
}
.dots .mini__dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #e0e0e0;
  cursor: pointer;
  transition: background var(--dur-med) var(--ease-out);
}
.dots .mini__dot.is-on { background: #fff; }
.dots .mini__dot:focus-visible { outline: 2px solid var(--fg-strong); outline-offset: 3px; }

@media (max-width: 640px) {
  .dots .mini { --slide-gap: 24px; }
  .dots .stage--free { padding: 24px 16px 48px; }
  .dots .mini__btn { width: 34px; height: 34px; }
  .dots .mini__btn--prev { left: 10px; }
  .dots .mini__btn--next { right: 10px; }
}
@media (prefers-reduced-motion: reduce) {
  .dots .mini__track { transition: none; }
}
