/* ============================================================================
   styles.css — Link in bio · Dra. Jessica Castañeda
   ---------------------------------------------------------------------------
   Consume exclusivamente los tokens de tokens.css. Este archivo no contiene
   ni un hex ni un valor de espaciado literal, salvo las excepciones marcadas
   con la palabra EXCEPCION y su motivo.

   Página clara: fondo crema, tipografía Manrope con acentos en itálica Libre
   Baskerville, y el bordo (terracota) como respiro oscuro en bloques
   puntuales — el filo del retrato del hero, el punto de cada chip, la
   palabra clave en degradado del reclamo, los asteriscos de las cifras, la
   baldosa de cada icono y el fondo del footer. La fuente de verdad de la
   paleta y la tipografía es `docs/brand/jessica-brand-extract.md`; si un
   valor de marca cambia, cambia AHÍ primero y luego en `tokens.css`.

   Índice
     1 · Reset y base
     2 · Utilidades
     3 · Fondo
     4 · Shell y ritmo
     5 · Hero
     6 · Fila de enlace — el componente que paga la página
     7 · Banda de tratamientos
     8 · Bio y redes
     9 · Footer
    10 · Capa de movimiento
    11 · Preferencias del usuario
   ========================================================================= */

/* ════════════════════════════════════════════════════════════════════════════
   1 · RESET Y BASE
   ══════════════════════════════════════════════════════════════════════════ */

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

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-block-start: var(--sp-7);
  font-family: var(--font-sans);
  font-optical-sizing: auto;

  /* Tiene que estar en el elemento RAÍZ, no solo en body: el overflow del root
     es el único que propaga al viewport. Con `clip` únicamente en body, la
     geometría del fondo generaba 50px de scroll horizontal real en móvil
     — medido en el navegador, no supuesto. */
  overflow-x: clip;
}

body {
  /* Marco de referencia del fondo: `.backdrop` es absoluto y se dimensiona
     contra el documento entero, no contra el viewport. */
  position: relative;
  min-height: 100svh;
  background: var(--cream);
  color: var(--text-body);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);   /* Manrope Medium. El 400 se ve anémico
                         sobre crema: hay menos contraste que sobre negro y
                         el trazo fino se deshilacha. */
  line-height: var(--lh-relaxed);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

img,
svg,
picture {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

ul {
  list-style: none;
  padding: 0;
}

:where(a, button, [tabindex]) {
  -webkit-tap-highlight-color: transparent;
}

p {
  text-wrap: pretty;
}

/* ── Foco visible: un solo tratamiento para toda la página ────────────────
   Anillo de TRES capas —halo claro, aro de tinta, aura translúcida—, no de
   dos tonos: es lo que permite que el mismo anillo se lea tanto sobre la
   crema como sobre el bordo sin una regla por componente. La página no tiene
   ningún fondo negro; el halo claro es lo que le da 3:1 contra el bordo, la
   superficie oscura real de la página. */
:where(a, button, [tabindex]:not([tabindex="-1"])):focus {
  outline: none;
}

:where(a, button, [tabindex]:not([tabindex="-1"])):focus-visible {
  outline: none;
  box-shadow: var(--focus-shadow);
  border-radius: inherit;
  transition: box-shadow var(--d-base) var(--e-glide);
}

/* ════════════════════════════════════════════════════════════════════════════
   2 · UTILIDADES
   ══════════════════════════════════════════════════════════════════════════ */

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--sp-4);
  inset-block-start: var(--sp-2);
  z-index: var(--z-skip);
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-full);
  /* Invertido a propósito: la página es clara, así que el aviso que solo
     aparece con teclado tiene que leerse como una superficie propia y no
     como un bloque más de crema — igual que el anillo de foco, que también
     se invirtió. */
  background: var(--ink);
  color: var(--cream);
  font-size: var(--fs-small);
  font-weight: var(--fw-bold);
  /* Sin esto medía 39px: el padding vertical solo no llega a los 44px de
     `--tap-min`. `inline-flex` + `align-items` reparte la altura mínima
     alrededor del texto sin tocar su forma de píldora. */
  min-height: var(--tap-min);
  display: inline-flex;
  align-items: center;
  translate: 0 -250%;
  transition: translate var(--d-fast) var(--e-glide);
}

.skip-link:focus-visible {
  translate: 0 0;
}

/* El itálico de acento. Los cuatro renglones tienen que ir juntos: sin
   `color: transparent` el degradado queda tapado por el color del texto, y sin
   `-webkit-background-clip` no funciona en Safari, que es de donde llega la
   mayoría del tráfico de un link in bio.

   `padding-bottom` + `margin-bottom` negativo no es un ajuste fino: los
   descendentes de la itálica (la j, la g, la p) se recortan sin él, porque la
   caja del degradado termina en la línea base. */
.ital {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: var(--fw-regular);
  background: var(--grad-italic);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}

/* El brillo que recorre el itálico. La tentación era pasar una banda blanca
   por encima, y es justo lo que NO se hace: sobre el texto recortado eso
   aclara las letras y hunde el contraste en el titular, que es texto grande y
   ya va justo con 3.00:1 en su parada clara.

   En vez de eso se mueve el propio degradado entre sus dos paradas
   aprobadas. Cada fotograma usa colores que ya pasaron la medición, así que
   el brillo no puede sacar el titular de rango por definición.

   El barrido dura poco y la pausa es larga: un brillo continuo se convierte
   en un cartel de neón y deja de dirigir la mirada para pasar a competir con
   ella. */
html.motion .ital {
  background-image: linear-gradient(
    100deg,
    var(--italic-to) 0%,
    var(--italic-from) 30%,
    var(--italic-to) 58%,
    var(--italic-to) 100%);
  background-size: 280% 100%;
  background-position: 100% 0;
  animation: ital-brillo var(--d-ital-brillo) var(--e-sweep) 1.4s infinite;
}
@keyframes ital-brillo {
  0%   { background-position: 100% 0; }
  26%  { background-position: -20% 0; }
  100% { background-position: -20% 0; }
}

/* El eyebrow: bullet + itálica, tal cual su web. El bullet va en ::before y
   no en el HTML para que un lector de pantalla no lea "punto" antes de cada
   rótulo. */
.eyebrow {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--fs-small);
  color: var(--coral-ink);
  margin-bottom: var(--sp-3);
}
.eyebrow::before {
  content: "•";
  margin-right: var(--sp-2);
}

/* El wordmark por máscara. Un solo PNG, dos colores, y el día que cambie el
   logo hay un solo archivo que regenerar. */
.hero__wordmark,
.foot__wordmark {
  background-color: currentColor;
  mask: url("../assets/img/wordmark.png") center / contain no-repeat;
  -webkit-mask: url("../assets/img/wordmark.png") center / contain no-repeat;
}

/* ════════════════════════════════════════════════════════════════════════════
   3 · FONDO
   ---------------------------------------------------------------------------
   El fondo son cuatro curvas y un círculo, tomados de las líneas orgánicas
   que recorren su web. Van en `currentColor` a muy baja opacidad: se tienen
   que INTUIR, no ver. En cuanto se leen como un dibujo compiten con el
   titular, que es lo único que debe leerse en esa pantalla.
   ══════════════════════════════════════════════════════════════════════════ */

.backdrop {
  position: absolute;
  inset: 0;
  z-index: var(--z-backdrop);
  pointer-events: none;
  color: var(--coral-soft);
  overflow: hidden;
}

/* Las curvas se TRAZAN al cargar en vez de aparecer de golpe: es la firma
   gráfica de su web y estaba ahí, quieta, sin que nadie la aprovechara.
   El dasharray es un número grande a propósito —mayor que cualquiera de los
   cuatro trazos— para no tener que medir la longitud de cada uno con JS. */
html.motion .backdrop__lines > * {
  stroke-dasharray: 3000;
  stroke-dashoffset: 3000;
  animation: trazar var(--d-trazo) var(--e-light) forwards;
}
html.motion .backdrop__lines > :nth-child(2) { animation-delay: 180ms; }
html.motion .backdrop__lines > :nth-child(3) { animation-delay: 360ms; }
html.motion .backdrop__lines > :nth-child(4) { animation-delay: 540ms; }
@keyframes trazar { to { stroke-dashoffset: 0; } }

/* El resplandor que respira detrás de ella. En su web hay manchas rosadas
   desenfocadas tras la figura; ésta hace lo mismo y late muy despacio.
   Ocho segundos y un 6% de recorrido: tiene que notarse solo si te quedas
   mirando, nunca al pasar. */
.backdrop__glow {
  position: absolute;
  top: -12%;
  right: 6%;
  width: min(48vw, 34rem);
  aspect-ratio: 1;
  border-radius: var(--r-full);
  background: radial-gradient(circle,
    rgb(var(--rgb-coral) / 14%) 0%,
    rgb(var(--rgb-coral) / 5%) 45%,
    transparent 68%);
}
html.motion .backdrop__glow {
  animation: respirar var(--d-respirar) var(--e-breathe) infinite;
}
@keyframes respirar {
  0%, 100% { transform: scale(1);    opacity: 0.85; }
  50%      { transform: scale(1.06); opacity: 1; }
}

.backdrop__lines {
  position: absolute;
  /* Alto de VENTANA, no de documento. `.backdrop` abarca ahora la página
     entera, y si el SVG se estirase con él, `slice` lo escalaría 3.36x: el
     dibujo salía gigante y vacío, dos curvas en toda la pantalla. Atado a
     `svh` conserva exactamente la escala de siempre. */
  top: -10svh;
  left: -10%;
  width: 120%;
  height: 120svh;
  opacity: 0.28;
}
/* El resto de la página se cubre repitiendo el mismo dibujo. Empieza justo
   donde acaba el SVG de arriba —`top: 100svh`— por dos razones: el mosaico
   encaja sin costura porque es el mismo trazo a la misma escala, y sobre todo
   deja la primera pantalla para el SVG de verdad, que es el que se anima al
   cargar. Si el mosaico también cubriera arriba, el trazado se dibujaría por
   debajo de una copia ya pintada y no se vería. */
.backdrop::after {
  content: "";
  position: absolute;
  /* 110svh y no 100: el SVG de arriba va de -10svh a 110svh, así que su caja
     termina ahí. Empezando en 100 se solapaban 74px y el dibujo daba un salto
     justo donde se relevan. */
  top: 110svh;
  right: 0;
  bottom: 0;
  left: 0;
  opacity: 0.28;
  background-image: var(--curvas);
  background-repeat: repeat-y;
  background-position: center top;
  background-size: 120% 120svh;
}

/* ════════════════════════════════════════════════════════════════════════════
   4 · SHELL Y RITMO
   ══════════════════════════════════════════════════════════════════════════ */

.page,
.foot {
  position: relative;
  z-index: var(--z-content);
}

/* Utilidad de contenido: el mismo max-width y el mismo padding-inline en
   hero, enlaces, bio, redes y footer. NO es un eje único de alineación
   vertical, y no hay que presuponerlo por el nombre de la clase: medido a
   1440px el hero arranca a 102px del borde, la bio a 200px, y enlaces, redes
   y footer a 450px. Cada `.shell` centra dentro del contenedor que la
   envuelve, y esos contenedores no comparten ancho —la bio, por ejemplo,
   ensancha el suyo a `--w-wide` en escritorio (sección 8)—, así que el mismo
   max-width interno produce bordes exteriores distintos. La cinta de
   colaboraciones, su chip y su botón de pausa que mencionaba este comentario
   se retiraron del diseño; no existen en el marcado actual. */
.shell {
  width: 100%;
  max-width: var(--w-col);
  margin-inline: auto;
  padding-inline: var(--sp-gutter);
}

.section {
  padding-block-start: var(--sp-section);
}

/* La foto va DEBAJO de los botones en móvil y al lado del texto en escritorio.
   Para poder intercalarla con los enlaces tiene que ser hermana suya, no hija
   del hero, y de ahí que el orden se declare aquí y no dentro de `.hero`.

   El motivo no es estético: en un iPhone con Safari quedan 664px útiles, y con
   la foto arriba el primer botón caía 82px por debajo del borde. En un link in
   bio los botones son la página y la foto es decoración; cuando no caben los
   dos, decide eso. */
.page { display: flex; flex-direction: column; }
/* La bolita abre la página: es el patrón que la gente reconoce como perfil
   antes de leer una palabra. Va en `order` y no en el marcado porque en
   escritorio no está arriba sino a la derecha, y el HTML solo puede tener
   un orden. */
.hero__media { order: 0; }
.hero        { order: 1; }
.links       { order: 2; }
.treatments  { order: 3; }
.bio         { order: 4; }
/* Único descendiente directo de `.page` que aún no tiene orden: la sección de
   redes. Se referencia por posición y no por una clase propia —no tiene
   ninguna que la distinga de `.bio`, las dos son `.section`— para no tener
   que añadir marcado solo para esto. */
.page > section:last-of-type { order: 5; }

@media (min-width: 60rem) {
  /* En escritorio la foto vuelve a estar junto al texto: dos columnas, foto a
     la derecha, y todo lo demás ocupa el ancho completo por debajo. */
  .page {
    display: grid;
    grid-template-columns: 1fr 0.85fr;
    align-items: center;
    /* Aire sobre el logotipo. Medido: el bloque de texto ocupa 407px dentro de
       una lámina de 431, así que al centrarse le quedaban 11px por arriba y el
       logotipo salía pegado al borde de la ventana.

       Se paga aquí y no agrandando la lámina porque cuesta la mitad: si crece
       la caja, el texto va centrado y solo baja la MITAD de lo que crece,
       mientras el primer botón se lleva el golpe entero. Con relleno bajan los
       dos lo mismo.

       Es invisible como banda: el fondo de la lámina es exactamente `--cream`,
       la misma de la página, así que no aparece ningún borde — la lámina
       simplemente empieza más abajo. */
    padding-top: var(--sp-8);
  }
  .hero        { grid-column: 1; grid-row: 1; }
  .hero__media { grid-column: 2; grid-row: 1; }
  .links, .treatments, .bio, .page > section:last-of-type { grid-column: 1 / -1; }
}

/* ════════════════════════════════════════════════════════════════════════════
   5 · HERO
   ---------------------------------------------------------------------------
   En su web el hero es texto a la izquierda y foto a la derecha, y la foto
   sangra por arriba y por la derecha. En móvil se apila: el texto primero,
   porque el titular es lo que tiene que leerse antes de decidir.
   ══════════════════════════════════════════════════════════════════════════ */

.hero {
  position: relative;
  /* Sin relleno superior en móvil: la bolita va justo encima y ya trae su
     propio aire. Los dos juntos sumaban 96px de hueco entre el borde de la
     ventana y el nombre, el doble de lo que hace falta. En escritorio no
     aplica porque allí el texto va centrado sobre la lámina. */
}
.hero__text {
  display: grid;
  gap: var(--sp-5);
  justify-items: start;
}
.hero__wordmark {
  width: clamp(9rem, 34vw, 12rem);
  aspect-ratio: 292 / 81;
  color: var(--ink);
}
.hero__title {
  font-size: var(--fs-h1);
  font-weight: var(--fw-semi);         /* Manrope SemiBold */
  line-height: 1.06;
  letter-spacing: var(--ls-h1);  /* -0.032em. Su web usa -1.668px sobre 56px,
                                    que son -0.030em: el escalón del sistema
                                    cae a dos milésimas y no se distingue.
                                    Sin tracking negativo Manrope a cuerpo de
                                    titular se ve suelta y pierde el aire de
                                    marca. */
  color: var(--text-primary);
  text-wrap: balance;
}
.hero__claim {
  max-width: var(--w-read);
  color: var(--text-body);
}
/* Las cifras, separadas por el asterisco floral. El separador va en el
   `::after` de todas menos la ÚLTIMA, no en el `::before` de todas menos la
   primera: con `flex-wrap: wrap` un `::before` que cae al inicio de una fila
   nueva se pinta ahí, en el arranque de la línea, y a 390px la segunda línea
   abría con un asterisco suelto. Puesto en el `::after` del elemento ANTERIOR,
   el separador pertenece a la línea de la que se va. Medido a 390px. */
.stats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  list-style: none;
  padding: 0;
  /* `.hero__text` es un grid con `justify-items: start`, así que sus hijos se
     encogen a su contenido. Sin este ancho las tres cifras se apilan en una
     columna estrecha en vez de ponerse en fila. */
  width: 100%;
}
.stat {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}
.stat:not(:last-child)::after {
  content: "";
  width: 0.9rem;
  height: 0.9rem;
  margin-left: var(--sp-4);
  background: var(--coral);
  mask: var(--aster-mask) center / contain no-repeat;
  -webkit-mask: var(--aster-mask) center / contain no-repeat;
}
.stat__num {
  font-size: var(--fs-h3);
  font-weight: var(--fw-extra);
  color: var(--text-primary);
  letter-spacing: var(--ls-h2);
}
.stat__label {
  font-size: var(--fs-small);
  color: var(--text-muted);
  max-width: 7ch;
  line-height: 1.2;
}

.hero__media {
  /* La bolita abre la página, así que necesita su propio aire arriba: es
     hermana del hero y no hija suya, de modo que el `padding-top` del hero no
     la alcanza y quedaba pegada al borde superior de la ventana. */
  margin-top: var(--sp-9);
  /* LA BOLITA. En móvil la foto es un círculo y no un rectángulo, y eso cambia
     lo que hay que recortar: dentro de un círculo pequeño lo único que se lee
     es la cara, así que el archivo que carga aquí ya viene cuadrado y centrado
     en ella. Estirar el retrato de cuerpo entero habría dejado la cara del
     tamaño de una moneda dentro del círculo.

     El ancho va en `rem` y no en `svh` como el resto del hero: un avatar tiene
     un tamaño reconocible propio y no debe encoger con la ventana. A 7rem son
     112px, lo bastante para leer una expresión sin comerse el sitio de los
     botones. */
  display: block;
  width: 7rem;
  aspect-ratio: 1;
  margin-inline: auto;
  border-radius: var(--r-full);
  overflow: hidden;
  /* El filete la separa de la crema: sin él, el fondo claro de la foto y el de
     la página se funden y el círculo deja de leerse como tal. */
  border: var(--stroke-w) solid var(--stroke-soft);
  box-shadow: var(--sh-2);
}
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 60rem) {
  /* En escritorio deja de ser una bolita y deja también de ser un recuadro:
     se usa la cabecera panorámica de su web ENTERA, a todo lo ancho, con el
     texto encima. Es como está compuesta la imagen —ella a la derecha, más de
     media lámina de crema vacía a la izquierda— y recortarla en una columna
     desperdiciaba justo ese hueco, que es donde va el titular.

     Imagen y texto comparten celda de rejilla a propósito. `.hero__media` va
     después en el marcado, así que sin `z-index` se pintaría ENCIMA del
     titular: de ahí el `position: relative` y el `z-index` de `.hero`. */
  .hero, .hero__media { grid-column: 1 / -1; grid-row: 1; }

  .hero__media {
    /* Se deshace todo lo que era del círculo, incluido su aire superior: aquí
       la lámina sangra desde el borde de la ventana. */
    margin-top: 0;
    width: 100%;
    height: auto;
    aspect-ratio: auto;
    margin-inline: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    align-self: center;
  }
  .hero__media img {
    width: 100%;
    height: 100%;
    /* Con `cover` y no `fill`. La lámina se recortó por la derecha —el 31% del
       archivo era crema vacía— y al estrecharse pasó de relación 2.69 a 1.92:
       a ancho completo marcaría un hero de 842px, más alto que muchas
       ventanas. `cover` deja que la caja mande el alto y recorta arriba y
       abajo, que es donde no hay nada que perder.

       El encuadre va al 22% de altura y no al centro: ahí está su cara, y es
       lo primero que debe sobrevivir a cualquier recorte. */
    object-fit: cover;
    object-position: center 22%;
  }

  .hero {
    position: relative;
    z-index: var(--z-content);
    align-self: center;
    padding-top: 0;
    /* El hero se ciñe al ancho de lectura de la página y se centra, igual que
       el resto de bloques, para que el titular arranque en el mismo eje que
       los enlaces de abajo. */
    width: min(100% - 2 * var(--sp-gutter), var(--w-wide));
    margin-inline: auto;
  }
  .stats {
    /* Tres columnas fijas en escritorio en vez de dejarlas envolver. Al
       envolver, la última cifra de la línea de arriba se quedaba con su
       asterisco colgando al final —el mismo separador huérfano de siempre,
       ahora por el otro extremo— y la de abajo empezaba sola.
       Con tres columnas no hay línea que partir. */
    display: grid;
    grid-template-columns: repeat(3, auto);
    justify-content: start;
    align-items: baseline;
  }

  .hero__text {
    /* `.shell` centra su contenido, y aquí no puede: la lámina tiene a la Dra.
       a partir del 52% de su ancho y el texto centrado le caía encima. Se
       ancla a la izquierda y se le pone tope en la mitad, que es exactamente
       el hueco de crema que la composición de la foto deja libre.
       Ambos —hueco y tope— escalan con la ventana, así que la relación se
       mantiene de 960px en adelante. */
    margin-inline: 0;
    max-width: 50%;
  }
  /* Las dos columnas ya no las declara `.hero` —desde que la foto salió de
     dentro, solo contiene texto—, las declara `.page` (sección 4). Aquí solo
     queda ajustar el tamaño y la forma de la foto para su columna. */
  .hero__media {
    /* Bajada de 66svh a 56: con las cifras en su fila propia bajo el hero, la
       lámina a 568px empujaba el primer botón fuera de la ventana en un
       portátil de 860px de alto. No se recorta la imagen —sigue completa— sino
       la altura de su caja. Medido: el botón pasa de -5 a verse entero. */
    /* 58svh. Se probó a 64 —más presencia para ella— y el primer botón caía a
       25 de sus 108px en un portátil de 860 de alto. Seis puntos de svh
       devuelven 50px al botón y a ella apenas se le nota. */
    height: min(58svh, 33rem);
    border-radius: var(--r-xl) 0 0 var(--r-xl);
    /* El corte de abajo era una línea recta y dura: la lámina simplemente se
       acababa. Se funde con una máscara en degradado.

       Funciona tan limpio porque el fondo de la foto es EXACTAMENTE la crema
       de la página —#fbf7f4, medido de su web—: al desvanecerse no aparece
       ningún borde de color, la imagen se disuelve en el papel.

       El fundido ocupa el último 18% y no más: por debajo empezaría a comerse
       los hombros de la Dra. */
    mask-image: linear-gradient(to bottom, #000 82%, transparent 100%);
    -webkit-mask-image: linear-gradient(to bottom, #000 82%, transparent 100%);
    /* SIN sangrado. `.page` no lleva `shell`, así que su columna derecha ya
       termina en el borde de la ventana. Un `margin-right: calc(50% - 50vw)`
       aquí sangraría sobre algo que ya está a sangre: el `50%` se resuelve
       contra el área del grid y no contra la ventana, y a 1440 se
       desbordaría 404px que solo `overflow-x: clip` mantendría invisibles. */
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   6 · FILA DE ENLACE — el componente que paga la página
   ─────────────────────────────────────────────────────────────────────────
   Es la píldora de su web: relleno claro, filete, y un badge circular a la
   derecha con la flecha diagonal. La flecha diagonal es firma de marca —la
   llevan los seis botones de su sitio sin una sola excepción— y cambiarla por
   una horizontal rompe el parecido más de lo que rompería cambiar un color.
   ══════════════════════════════════════════════════════════════════════════ */
.row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: var(--tap-min);
  /* Relleno holgado y simétrico. Estaba en 20/24 y con el icono delante la
     fila quedaba apretada: entre el filete y el icono cabía menos aire que
     entre el icono y el texto, y el ojo lee eso como un descuadre aunque no
     sepa nombrarlo. */
  padding: var(--sp-6);
  border-radius: var(--r-full);
  background: var(--card);
  border: var(--stroke-w) solid var(--stroke-soft);
  box-shadow: var(--sh-1);
  text-decoration: none;
  color: var(--text-primary);
  transition: transform var(--d-base) var(--e-glide),
              box-shadow var(--d-base) var(--e-glide);
}
/* El icono de cabecera de cada fila. Es la pieza que faltaba: sin él las
   cuatro filas se distinguen solo por su texto, y una lista de botones se
   recorre mirando, no leyendo. Con un icono delante, el ojo encuentra el que
   busca antes de descifrar el rótulo.

   Va en círculo relleno como los pasos del proceso de su web, y del mismo
   bordo. Hereda `currentColor`, así que en la fila destacada —donde el
   contraste manda otra cosa— basta con cambiarle el color al contenedor. */
.row__icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--r-full);
  background: var(--cream-deep);
  color: var(--bordo);
}
.row__icon svg { width: 1.4rem; height: 1.4rem; }

.row__body {
  display: grid;
  gap: var(--sp-1);
  min-width: 0;   /* Sin esto el subtítulo largo empuja el badge fuera de la
                     píldora en vez de partirse. Es el fallo clásico de un
                     hijo de flex con texto. */
}
.row__title {
  font-weight: var(--fw-semi);
  font-size: var(--fs-row);   /* token hecho a medida para esta fila: 17→22 */
  letter-spacing: var(--ls-h3);
}
.row__note {
  font-size: var(--fs-small);
  color: var(--text-muted);
  line-height: 1.35;
}
.row__badge {
  flex: none;
  margin-left: auto;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--r-full);
  background: var(--cream-deep);
  color: var(--ink);
  transition: transform var(--d-base) var(--e-glide);
}
.row__badge svg { width: 1.35rem; height: 1.35rem; }

/* LA DESTACADA. Lleva relleno, sombra de color y badge invertido: tres
   señales APILADAS sobre una sola fila, no repartidas entre varias. Cuatro
   filas con una señal cada una son cuatro filas iguales. */
.row--featured {
  background: var(--grad-cta);
  border-color: transparent;
  box-shadow: var(--sh-cta);
}
.row--featured .row__note { color: var(--ink-deep); opacity: 0.72; }
.row--featured .row__badge {
  background: var(--bordo-deep);
  color: var(--cream);
}
.row--featured .row__icon {
  /* Sobre el degradado rosa, la crema del icono se perdería: se invierte
     igual que el badge para que los dos extremos de la fila pesen lo mismo. */
  background: rgb(var(--rgb-cream) / 55%);
  color: var(--bordo-deep);
}

@media (hover: hover) {
  .row:hover {
    transform: translateY(-2px);
    box-shadow: var(--sh-2);
  }
  .row--featured:hover { box-shadow: var(--sh-cta-hi); }
  .row:hover .row__badge { transform: translate(2px, -2px); }
}
.row:active { transform: translateY(0); }

/* El anillo global de foco (sección 1) y `.row` compiten por la misma
   propiedad con la MISMA especificidad —una clase—, y `.row` llega después
   en el archivo: sin esta regla se come el anillo en silencio y el
   tabulador cruza las cuatro filas sin ninguna marca visible. Con la pareja
   estado+clase la especificidad sube a dos y gana pase lo que pase después.
   La destacada necesita su propio par porque si no pierde también su
   sombra de color al enfocarse. */
.row:focus-visible { box-shadow: var(--focus-shadow), var(--sh-1); }
.row--featured:focus-visible { box-shadow: var(--focus-shadow), var(--sh-cta); }

.links {
  display: grid;
  gap: var(--sp-5);
  /* La sección no llevaba separación superior y quedaba pegada a la lámina
     del hero: el titular arrancaba a 0px de su borde inferior.

     El escalón NO es el de sección completa (112px), y esa diferencia está
     medida: con 112 el primer botón caía por debajo del pliegue en escritorio
     —quedaban 2 de sus 108px a la vista— y todo el trabajo de que se viera sin
     scroll se perdía. El hero y los enlaces son la misma unidad visual, así
     que no necesitan el hueco que sí separa a los bloques de verdad. */
  padding-block-start: var(--sp-9);
}
.links__title {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semi);
  letter-spacing: var(--ls-h2);
  color: var(--text-primary);
  margin-bottom: var(--sp-3);
  text-wrap: balance;
  /* Centrado en TODOS los tamaños, no solo en móvil. El rótulo corona una
     columna de botones que ya está centrada en la página y lleva el asterisco
     separador —también centrado— justo encima: alineado a la izquierda era el
     único elemento del bloque fuera del eje.

     Va aquí y no en el contenedor: `text-align: center` en `.links` se lo
     llevaría también a las filas, que son hermanas de este titular, y el
     rótulo de los botones se queda a la izquierda a propósito (ver el bloque
     de móvil más abajo). Centrar `.links` es el error fácil; esta regla existe
     para no tener que recordarlo. */
  text-align: center;
}

/* Pantallas MUY bajas: un iPhone SE deja 553px útiles con la barra de Safari,
   y ahí el primer botón asomaba dos píxeles de sus 112. Dos píxeles cumplen
   «el borde superior entra en la ventana» y no sirven de nada: lo que decide
   no es si el botón EMPIEZA por encima del pliegue, es si se ve lo bastante
   como para leer su rótulo. El umbral es el propio objetivo táctil mínimo
   del proyecto (44px, `--tap-min`): menos que eso no se percibe como «hay un
   botón ahí», se percibe como un borde suelto.

   Con esto se ven 55 de los 112px de la fila destacada a 375×553 — de sobra
   para leer «Agenda tu cita de valoración». Se recorta relleno y cuerpo del
   titular de la sección de enlaces, no contenido: el claim y las cifras del
   hero se quedan enteros, y por encima de 640px de alto esta regla no se
   aplica y nada cambia. */

/* ════════════════════════════════════════════════════════════════════════════
   7 · BANDA DE TRATAMIENTOS
   ─────────────────────────────────────────────────────────────────────────
   El único bloque oscuro de la página, y está aquí por ritmo: seis pantallas
   seguidas de crema se leen planas por muy bien compuestas que estén. Sangra
   al viewport completo, que es la única excepción al eje único de la página.
   ══════════════════════════════════════════════════════════════════════════ */
.treatments {
  /* Margen solo ARRIBA. Abajo lo pone la bio con su `.section`, y con los dos
     el hueco salía de 224px, el doble que el resto de la página.

     SIN sangrado lateral. Llevaba `calc(50% - 50vw)` y desbordaba la página 9px
     exactos: `100vw` incluye la barra de scroll y el ancho visible no. Además
     sobraba, porque `.page` no tiene tope de ancho y esta banda ya llega al
     borde por sí sola. Es el mismo fallo que tenía la lámina del hero. */
  margin: var(--sp-section) 0 0;
  padding: var(--sp-8) 0;
  background: var(--bordo);
  color: var(--text-on-bordo);
  border-radius: var(--r-xl);
}
.treatments__set {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--sp-3) var(--sp-5);
  list-style: none;
  padding: 0 var(--sp-gutter);
  margin: 0;
}
.treatments__set li {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--fs-body);
  font-weight: var(--fw-semi);
}
/* El asterisco separa, y va en el `::after` de todos menos el ÚLTIMO, no en el
   `::before` de todos menos el primero: esta lista envuelve en varias líneas
   a 390px, y un `::before` que cae al inicio de una línea nueva se pinta ahí
   — a 390px, 3 de las 4 líneas de la banda abrían con un asterisco suelto. En
   el `::after` del elemento ANTERIOR el separador se queda pegado a la línea
   de la que se va, así que la línea que empieza siempre arranca limpia.
   Medido a 390px. */
.treatments__set li:not(:last-child)::after {
  content: "";
  width: 0.75rem;
  height: 0.75rem;
  flex: none;
  background: var(--peach);
  mask: var(--aster-mask) center / contain no-repeat;
  -webkit-mask: var(--aster-mask) center / contain no-repeat;
}

/* ════════════════════════════════════════════════════════════════════════════
   8 · BIO Y REDES
   ══════════════════════════════════════════════════════════════════════════ */
.bio { display: grid; gap: var(--sp-7); }
.bio__photo {
  position: relative;
  border-radius: var(--r-xl);
  overflow: hidden;
}
/* Aquí hubo un intento de recrear en CSS el pétalo rosa del bloque "¿Quién
   soy?" de su web: un `::before` con `inset` negativo pensado para asomar por
   detrás de la foto. Era completamente invisible: `.bio__photo` tiene
   `overflow: hidden`, así que el inset negativo se recortaba fuera de la caja
   antes de pintarse, y aunque no lo tuviera, el `<img>` opaco de encima lo
   habría tapado igual. Se borra en vez de arreglarse porque no hace falta:
   las formas rosas que sí se ven hoy vienen incrustadas en las fotos
   provisionales (recortes de captura de su web), no las pinta este CSS. */
.bio__photo img { width: 100%; height: auto; display: block; }
.bio__title {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semi);
  letter-spacing: var(--ls-h2);
  color: var(--text-primary);
  margin-bottom: var(--sp-5);
  text-wrap: balance;
}
.bio__text { display: grid; gap: var(--sp-4); color: var(--text-body); }
.bio__text p { max-width: var(--w-read); }

.socials { display: flex; gap: var(--sp-4); justify-content: center; }
.social {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--r-full);
  background: var(--card);
  border: var(--stroke-w) solid var(--stroke-soft);
  color: var(--bordo);
  transition: transform var(--d-base) var(--e-glide),
              background var(--d-base) var(--e-glide);
}
.social svg { width: 1.35rem; height: 1.35rem; }
@media (hover: hover) {
  .social:hover { transform: translateY(-2px); background: var(--cream-deep); }
}

/* ════════════════════════════════════════════════════════════════════════════
   FILAS COMPACTAS EN PANTALLAS PEQUEÑAS
   ─────────────────────────────────────────────────────────────────────────
   El icono cuesta 64px de ancho entre él y su separación, y en una pantalla
   de 375px eso empuja el subtítulo a tres líneas: la fila pasó de 111px a
   166px y el primer botón desapareció bajo el pliegue.

   Así que en móvil el icono y el badge encogen a 36px. Siguen por encima del
   objetivo táctil porque quien recibe el toque es la fila entera, no ellos.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 59.999rem) {
  .links { padding-block-start: var(--sp-9); }
  /* «Todo lo que necesitas, en un solo lugar» ocupa dos líneas en móvil y a
     cuerpo de titular son 89px. Es un rótulo de sección, no el titular de la
     página: baja un escalón y devuelve casi 30px al primer botón. */
  .links__title {
    font-size: var(--fs-lead);
    margin-bottom: var(--sp-2);
  }
  .row { padding: var(--sp-5); gap: var(--sp-4); }
  .row__icon,
  .row__badge { width: 2.25rem; height: 2.25rem; }
  .row__icon svg,
  .row__badge svg { width: 1.15rem; height: 1.15rem; }
}

@media (min-width: 60rem) {
  /* Bajado de 64px a 40: con el separador en medio, 64 dejaban un hueco que
     se leía como un salto y no como una pausa. De paso devuelve 24px al
     primer botón. */
  .links { padding-block-start: var(--sp-8); }
}

/* El separador: el asterisco floral de su marca entre el hero y los botones.
   Va en `::before` y no en el marcado porque es decoración pura y un lector de
   pantalla no tiene nada que anunciar aquí.

   La condición es DOBLE, de ancho y de alto, y la de alto se añadió después de
   medir: en un portátil de 1280x720 —resolución de lo más común— estos 56px
   hundían el primer botón de 107px visibles a 9. Un adorno no se paga con el
   botón que sostiene la página, así que solo aparece donde sobra sitio. */
@media (min-width: 60rem) and (min-height: 800px) {
  /* El rótulo de la sección baja un escalón: compite con el titular del hero,
     que ahora va a 56px, y no debería. Devuelve 38px al primer botón. */
  .links__title { font-size: var(--fs-h3); }

  .links::before {
    content: "";
    width: 1.375rem;
    height: 1.375rem;
    margin: 0 auto var(--sp-4);
    background: var(--coral);
    mask: var(--aster-mask) center / contain no-repeat;
    -webkit-mask: var(--aster-mask) center / contain no-repeat;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   CENTRADO EN MÓVIL — con una excepción deliberada
   ─────────────────────────────────────────────────────────────────────────
   En una columna estrecha el texto centrado se lee mejor: no deja el borde
   derecho deshilachado ni obliga al ojo a volver siempre al mismo margen.
   Por eso en móvil se centra todo… menos los BOTONES.

   El texto de las píldoras se queda a la izquierda a propósito. Un botón no se
   lee, se reconoce: el ojo lo escanea buscando dónde empieza, y si el rótulo
   flota en el centro cada fila arranca en un sitio distinto según lo largo que
   sea su texto. Alineados a la izquierda, las cuatro filas comparten un mismo
   punto de entrada y la lista se recorre de un vistazo.

   Por eso el centrado del rótulo de la sección vive en `.links__title` (arriba,
   sección 6) y NUNCA en `.links`: `text-align: center` en el contenedor se lo
   llevaría también a las filas, que es justo lo que no queremos.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 59.999rem) {
  .hero__text {
    justify-items: center;
    text-align: center;
  }
  /* En móvil las cifras se APILAN: el número arriba y la etiqueta debajo.
     En fila no caben —medido: 210, 200 y 134px de ancho en un contenedor de
     332— así que cada una se iba a su propia línea y el bloque ocupaba 124px
     justo donde la pantalla se acaba.

     Apiladas en tres columnas ocupan la mitad. Y se van los asteriscos: con
     tres columnas ya hay separación visual, y un separador entre cosas que ya
     están separadas es ruido. */
  .stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--sp-3);
    text-align: center;
  }
  .stat {
    flex-direction: column;
    align-items: center;
    gap: 0;
  }
  .stat:not(:last-child)::after { display: none; }
  .stat__label {
    max-width: none;
    font-size: var(--fs-micro);
  }

  .bio__main { text-align: center; }
  /* El párrafo lleva `max-width`, así que centrar el texto no basta: hay que
     centrar también la caja o se queda pegada al margen izquierdo. */
  .bio__text p { margin-inline: auto; }

  /* El bullet del eyebrow va en `::before`, así que se centra con su línea. */
  .eyebrow { text-align: center; }
}

/* Este bloque va DESPUÉS de los de ancho a propósito: comparte especificidad
   con ellos y en CSS gana el último. Colocado antes, sus valores para
   pantallas bajas los pisaba el bloque de móvil y no servían de nada. */
@media (max-height: 640px) {
  /* El aire sobre el logotipo se retira: en una ventana de menos de 640px de
     alto, 40px son la diferencia entre ver el primer botón y no verlo, y un
     margen no se paga con el botón que sostiene la página. */
  .page { padding-top: 0; }
  .hero { padding-top: var(--sp-5); }
  /* En un iPhone SE quedan 553px útiles. Con el escalón de móvil el botón
     seguía asomando solo 14 de sus 139px; con esto sube por encima del mínimo
     táctil de 44 que el proyecto usa como umbral de «se ve». */
  .links { padding-block-start: var(--sp-6); }

  .hero__media { height: min(18svh, 11rem); }
  .links__title {
    font-size: var(--fs-lead);
    margin-bottom: var(--sp-2);
  }
}

/* El claim se retira SOLO en pantallas pequeñas Y bajas: la condición es doble
   a propósito. El bloque de arriba solo mira el alto, y con eso una ventana de
   escritorio estrecha de altura habría perdido la frase sin motivo —ahí el
   espacio no es el problema—. Esto es cosa de teléfonos como el iPhone SE, que
   deja 553px útiles.

   Son 79px y tres líneas, justo lo que le falta para que el primer botón se
   vea. Y se oculta solo A LA VISTA, no del todo: un lector de pantalla recorre
   la página en línea, el alto no le cuesta nada, así que `display: none` le
   quitaría la frase que sitúa la consulta —«El Poblado, Medellín»— sin
   devolverle un solo píxel. Con esta técnica sigue ahí para quien escucha y
   desaparece para quien mira. */
@media (max-width: 59.999rem) and (max-height: 640px) {
  .hero__claim {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

@media (min-width: 60rem) {
  .bio {
    /* `.bio` lleva la clase `shell`, que topa el ancho en 620px. Ese ancho da
       de sobra para una columna de texto, pero partido en dos deja columnas de
       211 y 264px: la foto sale a 212px de ancho —de un original de 1100— y el
       titular de 44px se rompe en cinco renglones. En dos columnas hace falta
       el ancho ancho. Medido en navegador: con esto la foto pasa a 434px y el
       titular cabe en dos renglones. */
    max-width: var(--w-wide);
    grid-template-columns: 0.8fr 1fr;
    align-items: center;
    gap: var(--sp-11);
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   9 · FOOTER
   ══════════════════════════════════════════════════════════════════════════ */

.foot {
  margin-top: var(--sp-section);
  padding: var(--sp-10) 0 var(--sp-8);
  /* --bordo-card y no --bordo: el legal va atenuado al 82% y sobre --bordo se
     cae a 3.62:1. Sobre éste mide 4.85:1. */
  background: var(--bordo-card);
  color: var(--text-on-bordo);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  text-align: center;
}
.foot__wordmark {
  width: clamp(8rem, 30vw, 10rem);
  aspect-ratio: 292 / 81;
  margin: 0 auto var(--sp-6);
  color: var(--peach);
}
.foot__line { display: grid; gap: var(--sp-2); }
.foot__claim { font-weight: var(--fw-semi); }
.foot__legal { font-size: var(--fs-small); color: var(--text-on-bordo-muted); }

/* ════════════════════════════════════════════════════════════════════════════
  10 · CAPA DE MOVIMIENTO
   ---------------------------------------------------------------------------
   Todo depende de `html.motion`, que añade un script inline en <head>. Sin JS
   la clase no existe, nada se oculta y la página es estática y completa.
   ══════════════════════════════════════════════════════════════════════════ */

html.motion [data-enter] {
  opacity: 0;
}

html.motion.is-ready [data-enter] {
  animation: enter-light var(--dur, var(--d-enter)) var(--e-light) var(--delay, 0ms) both;
}

@keyframes enter-light {
  from {
    opacity: 0;
    transform: translate3d(0, var(--from-y, 14px), 0) scale(var(--from-s, 1));
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

/* Escalonado del hero en su orden de lectura: wordmark, titular, claim y por
   último las cifras. El wordmark SÍ lleva `data-enter` en el marcado —es un
   `<p role="img">` con su nombre accesible en el `aria-label`, no un adorno
   sin semántica— así que estas variables son el primer escalón real de la
   cascada, no un resto inerte. */
.hero__wordmark {
  --delay: 140ms;
  --dur: 400ms;
}

.hero__title {
  --delay: 210ms;
  --dur: 380ms;
  --from-y: 12px;
}

.hero__claim {
  --delay: 280ms;
  --dur: 380ms;
  --from-y: 10px;
}

/* Stagger de 70ms, no de 120: a 120 se percibe como una lista cargando, a 70
   como una corriente recorriéndola.

   `nth-of-type`, NO `nth-child`: dentro de `.links` cada fila va precedida de
   su `<span>` de número, así que en el orden de hijos las filas eran la 2, la
   4 y la 6. Con `nth-child(1..3)` solo acertaba una —la primera fila, por el
   `(2)`— y las demás entraban sin retardo, todas a la vez.

   Los números ya no están y hoy `nth-child` volvería a funcionar, pero se
   deja `nth-of-type`: cuenta solo los `<a>`, así que el escalonado sobrevive
   a que alguien vuelva a meter cualquier otro elemento entre las filas. */
/* Entra justo antes de la primera fila: el titular tiene que estar puesto
   cuando llega lo que nombra, no después. */
.stats {
  --delay: 350ms;
  --dur: 380ms;
  --from-y: 10px;
}

.links__title {
  --delay: 290ms;
  --dur: 380ms;
  --from-y: 12px;
}

/* Las cuatro reglas siguientes son la escala posicional del escalonado de
   entrada de las CUATRO filas de enlace de la página —las cuatro activas,
   ninguna inerte—, y van de arriba a abajo porque ese es el orden en que se
   leen. Si el día de mañana se añade o se quita una fila, hay que añadir o
   quitar su escalón aquí también. */
.row:nth-of-type(1) {
  --delay: 340ms;
  --from-y: 18px;
  --from-s: 0.988;
}

.row:nth-of-type(2) {
  --delay: 410ms;
  --from-y: 18px;
  --from-s: 0.988;
}

.row:nth-of-type(3) {
  --delay: 480ms;
  --from-y: 18px;
  --from-s: 0.988;
}

.row:nth-of-type(4) {
  --delay: 550ms;
  --from-y: 18px;
  --from-s: 0.988;
}

/* Al terminar la entrada, main.js marca <html> con `is-entered` y las
   animaciones se apagan. Esto no es cosmético: `animation-fill-mode: both`
   deja fijado el `transform` del último keyframe y, en la cascada, una
   animación siempre gana a una declaración — sin este apagado los :hover y
   :active de las filas no volverían a mover nada nunca. Se apaga la animación
   y se repone la opacidad, pero NO se declara `transform: none`: eso ganaría
   por especificidad a `.row:hover` y reintroduciría el bug. */
html.motion.is-entered [data-enter] {
  animation: none;
  opacity: 1;
}

/* ── Revelado al scroll ──────────────────────────────────────────────────── */
html.motion [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity var(--d-enter) var(--e-light),
    transform var(--d-enter) var(--e-light);
}

html.motion [data-reveal].is-revealed {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

html.motion [data-reveal-stagger] > * {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity var(--d-enter) var(--e-light) calc(var(--i, 0) * 70ms),
    transform var(--d-enter) var(--e-light) calc(var(--i, 0) * 70ms);
}

html.motion [data-reveal-stagger].is-revealed > * {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* ════════════════════════════════════════════════════════════════════════════
  11 · PREFERENCIAS DEL USUARIO
   ══════════════════════════════════════════════════════════════════════════ */

/* No es una versión degradada: es la misma página sin movimiento. Las filas y
   las redes sociales siguen reaccionando —fondo y sombra—, y el badge que se
   desplaza en :hover se queda fijo; el anillo de foco no se toca nunca. */
@media (prefers-reduced-motion: reduce) {
  /* Las tres animaciones decorativas del hero se apagan enteras, no se
     ralentizan. El brillo del itálico vuelve a su degradado fijo, las curvas
     aparecen ya trazadas y el resplandor deja de latir. Ninguna llevaba
     información, así que apagarlas no quita nada. */
  html.motion .ital {
    background-image: var(--grad-italic);
    background-size: auto;
    background-position: 0 0;
    animation: none;
  }
  html.motion .backdrop__lines > * {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    animation: none;
  }
  html.motion .backdrop__glow {
    animation: none;
    transform: none;
  }

  html {
    scroll-behavior: auto;
  }

  html.motion [data-enter],
  html.motion [data-reveal],
  html.motion [data-reveal-stagger] > * {
    opacity: 1;
    transform: none;
    animation: none;
    transition: none;
  }

  /* Se apagan traslaciones, escalas y rotaciones; se conservan las
     transiciones de color y opacidad, que son feedback y no movimiento. */
  .row,
  .social {
    transition-property: opacity, color, background-color, background-image,
      border-color, box-shadow, filter;
  }

  .row:hover,
  .row:active,
  .social:hover,
  .social:active {
    transform: none;
  }

  /* El badge se desplaza 2px en :hover (sección 6); se queda fijo aquí. La
     fila sigue destacando por su fondo y su sombra, que no se mueven. */
  .row:hover .row__badge {
    transform: none;
  }

  .skip-link {
    transition: none;
  }
}

@media (prefers-contrast: more) {
  :root {
    /* En la página oscura este bloque empujaba hacia `--paper`: ganar
       contraste allí era aclarar. Aquí es al revés — el fondo es la
       superficie clara, así que ganar contraste es oscurecer hacia
       `--ink`, no aclarar hacia `--cream`. */
    --text-body: var(--ink);
    --text-muted: var(--ink);
    /* Los tres bordes de la página oscura (--border-soft/-hair/-edge) se
       consolidaron en un único filete, --stroke-soft: es el único que
       consume algún selector hoy. */
    --stroke-soft: var(--ink);
  }
}

/* Windows HCM: los fondos decorativos desaparecen solos, hay que devolver
   bordes reales. */
@media (forced-colors: active) {
  .backdrop {
    display: none;
  }

  /* `.row__badge` vuelve a la lista: vuelve a ser una caja. En colores
     forzados no se pinta el fondo que la distingue, así que sin un borde real
     la baldosa desaparecería y el glifo quedaría flotando. */
  .row,
  .row__badge,
  .social {
    border: 1px solid ButtonText;
  }

  .row__badge {
    color: ButtonText;
  }

  /* El itálico de acento pierde su degradado de texto: sin esto quedaría
     `color: transparent` sobre un `background-image` que aquí no se pinta, y
     el texto desaparecería entero. */
  .ital {
    color: LinkText;
    background-image: none;
    -webkit-text-fill-color: currentColor;
  }

  /* La fila destacada pierde su relleno en degradado aquí y no pasa nada: en
     colores forzados el sistema manda, y el borde `1px solid ButtonText` que
     reciben todas las filas más arriba ya la distingue del fondo. Lo único
     que hay que impedir es que el tinte de superficie sobreviva a medias. */
  .row--featured {
    background-image: none;
  }

  :where(a, button, [tabindex]):focus-visible {
    outline: 3px solid Highlight;
    outline-offset: 2px;
    box-shadow: none;
  }
}

@media print {
  .backdrop {
    display: none;
  }

  body {
    background: #fff;
    color: #000;
  }
}
