/* ==========================================================================
   HackDynasty — hoja de estilos
   ==========================================================================

   LA PÁGINA SON DOS PIEZAS Y SE LEEN EN ESTE ORDEN:

   1. EL TRONO. La primera pantalla es un retrato de Estado: la foto grande, el
      nombre a ancho completo cruzando por delante y nada más. No hay titular de
      agencia, ni subtítulo, ni botones. Una portada.

   2. LA SESIÓN. A partir de ahí la página es una consola. Cada sección se abre
      con el comando que la produce —`whoami`, `ls -la work/`, `cat
      services.txt`, `sudo make contact`— y lo que sigue es su salida. El
      portafolio es literalmente un listado de directorio, con permisos a la
      izquierda y el ordinal de la casa al lado.

      No es decoración: un `ls -la work/` dice lo que viene después con más
      precisión que un «Selected work», y de paso enseña de qué va la casa. El
      cliente que no sepa leer un prompt tiene el título grande justo debajo.

   ---------------------------------------------------------------------------

   DOS ACENTOS, UN TRABAJO CADA UNO, Y NUNCA SE TOCAN:

     VERDE #00FF41 — la máquina. Prompts, cursor, enlaces, la segunda mitad del
     nombre. Es el verde de Anonyme sin retocar: dos marcas, un solo verde.

     ORO #C8A951 — la dinastía. Corona, sello, ordinales, permisos.

   La regla no admite excepción: el oro no toca texto corrido ni botones, y el
   verde no toca la corona. Dos acentos peleando por las mismas superficies
   convierten una página en un casino; cada uno en su sitio, se leen como lo que
   son —una casa que vive en una terminal— sin decirlo con palabras.

   El oro va apagado a propósito: un dorado saturado sobre negro es bisutería, y
   a #C8A951 da 9:1 de contraste, que pasa AA sin ayuda de nadie.

   Tipografía: Archivo, variable en ANCHURA. Los rótulos van expandidos (wdth
   112-118) y el texto normal, normal: es lo que da el aire de cartel sin meter
   una segunda familia de display. JetBrains Mono lleva la voz de terminal.
   ========================================================================== */

:root {
  --ink: #000;
  --surface: #0a0a0a;
  /* Los filetes llevan una gota de verde en vez de gris neutro. No se ve como
     color, pero hace que la página entera se sienta de una pieza con el acento. */
  --line: #16211a;
  --line-strong: #1f3a29;
  --fg: #fff;
  --muted: #8b8b88;
  --faint: #5c5c5a;
  --accent: #00ff41;
  --accent-dim: #0a3d18;
  --gold: #c8a951;
  --gold-dim: #3a2f14;

  --sans: 'Archivo', system-ui, -apple-system, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', monospace;

  --page: 1180px;
  --gap: clamp(4.5rem, 9vw, 9rem);
}

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

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* La rejilla del sótano. Va fija detrás de todo, al 3 % de verde, y con una
   máscara que la disuelve por arriba y por abajo. No se mira: se nota. Es lo
   que hace que la página se sienta dentro de una máquina en vez de sobre un
   folio, y cuesta dos gradientes y ningún archivo.

   `position: fixed` y no `absolute`: al no moverse con el scroll, el contenido
   pasa por encima de una retícula quieta, que es como se comporta el fondo de
   una consola de verdad. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(0, 255, 65, 0.03) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(0, 255, 65, 0.03) 1px, transparent 1px);
  background-size: 52px 52px;
  -webkit-mask-image: linear-gradient(to bottom, #000, transparent 55%);
  mask-image: linear-gradient(to bottom, #000, transparent 55%);
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

::selection {
  background: var(--accent);
  color: var(--ink);
}

.mono {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: 1rem;
  top: 1rem;
  z-index: 100;
  background: var(--fg);
  color: var(--ink);
  padding: 0.6rem 1rem;
  border-radius: 999px;
  text-decoration: none;
}

/* --- La franja de credencial, arriba del todo -----------------------------
   Lo primero que se ve de la página, por encima del logotipo. Es una línea y
   nada más: cuanto menos explica, más se la cree quien la lee. */

.topstrip {
  display: flex;
  justify-content: center;
  padding: 0.85rem clamp(1.25rem, 4vw, 2.5rem);
  border-bottom: 1px solid var(--gold-dim);
  /* Un lavado de oro muy tenue en vez de un relleno: la franja tiene que
     separarse del negro sin convertirse en una barra de aviso de cookies. */
  background: rgba(200, 169, 81, 0.06);
}

/* La franja ocupa el ancho de la página, no un grupito centrado.

   Con el texto apretado en medio de una pantalla de 1470 px, la credencial se
   leía como una etiqueta pequeña perdida en mucho negro. Estirada —cuerpo
   mayor, mucho más espaciado entre letras y huecos amplios entre las piezas—
   pasa a leerse como el encabezado de un documento oficial, que es de lo que
   va. El espaciado hace aquí más trabajo que el tamaño. */
.topstrip-in {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  width: 100%;
  max-width: var(--page);
  gap: clamp(0.7rem, 3vw, 2.75rem);
  font-family: var(--mono);
  font-size: clamp(0.72rem, 1.35vw, 1.02rem);
  letter-spacing: clamp(0.16em, 0.55vw, 0.38em);
  text-transform: uppercase;
  text-align: center;
}

/* La bandera va aquí mucho más pequeña que en la chapa de la sección personal:
   en una franja de una línea, los 48 px de allí la convertirían en el elemento
   dominante de la página antes de que se haya leído el nombre de la casa. */
.topstrip .flag-us {
  width: clamp(1.6rem, 3vw, 2.1rem);
}

.topstrip-a {
  color: var(--gold);
  font-weight: 700;
}

.topstrip-sep {
  color: var(--gold);
  opacity: 0.5;
}

.topstrip-b {
  color: var(--muted);
}

@media (max-width: 560px) {
  /* En un móvil las dos mitades no caben en una línea. Se dejan partir y se
     centran, en lugar de encoger la letra hasta que no se lea: una credencial
     ilegible no acredita nada. Dos líneas legibles ganan a una diminuta. */
  .topstrip-in {
    gap: 0.35rem 0.6rem;
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    line-height: 1.5;
  }

  .topstrip-sep {
    display: none;
  }
}

/* --- Barra superior ------------------------------------------------------ */

.bar {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 2rem;
  max-width: var(--page);
  margin: 0 auto;
  padding: 1.1rem clamp(1.25rem, 4vw, 2.5rem);
  background: rgba(0, 0, 0, 0.82);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s ease;
}

.bar.is-scrolled {
  border-bottom-color: var(--line);
}

/* El logotipo son tres piezas: corona, nombre partido en dos colores, y el
   bloque de cursor. Juntas dicen lo mismo que el nombre —una dinastía que vive
   en una terminal— sin necesidad de un archivo de imagen. */
/* El logotipo va grande —es la marca, no una nota al pie— pero tiene que
   convivir en la misma línea con el botón de contacto. En un móvil de 390 px
   los dos a tamaño completo se salen de la pantalla, así que el nombre escala
   con el ancho y el botón se queda fijo: quien manda en esa barra es la marca,
   pero el que tiene que seguir siendo pulsable es el botón. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: clamp(0.4rem, 1.5vw, 0.6rem);
  font-family: var(--mono);
  font-size: clamp(1rem, 3.4vw, 1.45rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  text-decoration: none;
  white-space: nowrap;
}

/* La corona va en oro y el nombre en verde: los dos acentos de la casa, uno al
   lado del otro, en la primera línea de la página. El logotipo es el único
   sitio donde conviven, y por eso funciona — ahí no compiten, se presentan. */
.brand-crown {
  width: clamp(1.25rem, 4.2vw, 1.8rem);
  height: clamp(1.15rem, 3.9vw, 1.65rem);
  flex: none;
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.9;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.brand-word {
  letter-spacing: 0.02em;
}

.brand-accent {
  color: var(--accent);
}

/* El cursor de Anonyme, tal cual: un rectángulo sólido que parpadea detrás del
   nombre. Es la pieza que hace que el logotipo se lea como una línea de consola
   y no como una palabra suelta. */
.brand-caret {
  width: clamp(0.45rem, 1.7vw, 0.7rem);
  height: clamp(1rem, 3.6vw, 1.5rem);
  background: var(--accent);
  animation: caret 1.1s steps(1) infinite;
}

@keyframes caret {
  0%,
  50% {
    opacity: 1;
  }
  50.01%,
  100% {
    opacity: 0;
  }
}

.bar-nav {
  display: flex;
  gap: 1.75rem;
  margin-left: auto;
  font-size: 0.9rem;
}

.bar-nav a {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.2s ease;
}

.bar-nav a:hover {
  color: var(--fg);
}

/* --- Botones ------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.7rem 1.35rem;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color 0.2s ease,
    color 0.2s ease,
    border-color 0.2s ease;
}

.btn-solid {
  background: var(--fg);
  color: var(--ink);
}
.btn-solid:hover {
  background: #fff;
}

.btn-line {
  border: 1px solid var(--line-strong);
  color: var(--fg);
}
.btn-line:hover {
  border-color: var(--accent);
}

.btn-lg {
  padding: 0.95rem 2rem;
  font-size: 1rem;
}

/* ==========================================================================
   1. EL TRONO
   ==========================================================================

   La primera pantalla no vende nada ni explica nada: enseña quién está detrás
   de la casa y se calla. Toda la persuasión de la página está más abajo; esto
   es la portada.

   La foto ya trae su trono, su corona y su rejilla, así que aquí no se dibuja
   nada encima. El CSS solo hace dos cosas, y las dos son de integración: fundir
   los bordes de la imagen contra el negro de la página, y bajarle la saturación
   para que su verde no compita con el verde de la marca. */

.throne {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: clamp(1rem, 3vw, 2.5rem) 0 clamp(2.5rem, 6vw, 5rem);
  overflow: hidden;
}

.throne-stage {
  position: relative;
  width: min(760px, 88vw);
}

/* El rosetón. Mismo sello del pie, ampliado hasta hacer de arquitectura y
   colocado a la altura de la cabeza: hace de nimbo sin dibujar un nimbo.
   Gira más despacio que el del pie —35s frente a 12s— porque a este tamaño la
   velocidad del pequeño se vería como un ventilador. */
.throne-seal {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 0;
  /* Más ancho que la foto a propósito: si cupiera dentro quedaría escondido
     detrás del retrato y solo se vería el arco de arriba. Desbordando por los
     lados, el anillo rodea a la figura y se lee como un rosetón. */
  width: min(1180px, 128vw);
  height: auto;
  transform: translate(-50%, -50%);
  color: var(--gold);
  opacity: 0.5;
  pointer-events: none;
}

.throne-seal .crest-ring {
  animation-duration: 35s;
}

.throne-seal .crest-text {
  font-size: 4.4px;
  letter-spacing: 0.42em;
}

.throne-seal .crest-circle {
  stroke-width: 0.35;
  opacity: 0.5;
}

/* La máscara es lo que hace que la foto no se vea como una foto pegada. Cuatro
   degradados desde los bordes hacia dentro: la imagen no termina, se disuelve
   en la página. Sin esto se ve el rectángulo y todo el efecto se cae. */
.throne-photo {
  position: relative;
  z-index: 1;
  width: 100%;
  filter: saturate(0.55) contrast(1.06) brightness(0.97);
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 14%, #000 78%, transparent 99%),
    linear-gradient(to right, transparent 0%, #000 10%, #000 90%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 14%, #000 78%, transparent 99%),
    linear-gradient(to right, transparent 0%, #000 10%, #000 90%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* El nombre a ancho de pantalla completo, cruzando por delante de la base del
   trono. El margen negativo es el gesto entero: sin él son dos elementos
   apilados; con él, el rótulo pisa la imagen y las dos piezas se convierten en
   una sola composición.

   Sale del ancho de la página a propósito —se pega a los dos bordes del
   navegador— porque una palabra que llega a los márgenes se lee como piedra
   tallada y no como un titular. */
.throne-word {
  position: relative;
  z-index: 2;
  display: block;
  width: 100%;
  margin-top: clamp(-7rem, -9vw, -3rem);
}

.throne-word text {
  font-family: var(--sans);
  font-variation-settings: 'wdth' 118;
  font-weight: 800;
  font-size: 160px;
  fill: var(--fg);
}

/* El selector lleva `text` a propósito: escrito solo como clase pierde contra
   `.throne-word text` —misma clase, pero el otro suma un selector de tipo— y la
   segunda mitad del nombre saldría en blanco. */
.throne-word text.throne-word-accent {
  fill: var(--accent);
}

.throne-sub {
  position: relative;
  z-index: 2;
  margin: clamp(0.75rem, 2vw, 1.5rem) 0 0;
  padding: 0 1.25rem;
  text-align: center;
  color: var(--muted);
}

.ts-sep {
  color: var(--gold);
  padding: 0 0.35em;
}

/* --- La cinta -------------------------------------------------------------
   Una banda que cruza la pantalla de lado a lado y no para. Separa la portada
   de la sesión y le da pulso a una página que por lo demás está muy quieta.

   El truco del bucle sin costura: dentro hay DOS copias idénticas del mismo
   contenido y la animación desplaza el carril exactamente un -50 %. Cuando
   termina, la segunda copia está justo donde empezó la primera, así que el
   salto al reiniciar es invisible. Con una sola copia se vería el hueco. */

.ticker {
  position: relative;
  overflow: hidden;
  padding: 1.1rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  /* Los extremos se disuelven en el fondo en vez de cortarse en seco contra el
     borde de la pantalla: el texto entra y sale, no aparece y desaparece. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}

.ticker-track {
  display: flex;
  width: max-content;
  animation: ticker 42s linear infinite;
}

.ticker-set {
  display: flex;
  align-items: center;
  flex: none;
}

.ticker-set > span {
  padding: 0 1.5rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}

/* La corona hace de separador. En oro, porque separar es exactamente el trabajo
   que un emblema dinástico ha hecho toda la vida en una cenefa. */
.ticker-set > span.tk-crown {
  padding: 0 0.5rem;
  color: var(--gold);
  font-size: 0.85rem;
  letter-spacing: 0;
}

@keyframes ticker {
  to {
    transform: translateX(-50%);
  }
}

.ticker:hover .ticker-track {
  animation-play-state: paused;
}

/* ==========================================================================
   2. LA SESIÓN
   ========================================================================== */

.session {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 2.5rem);
}

.block {
  padding-bottom: var(--gap);
}

.block-end {
  padding-bottom: clamp(3rem, 6vw, 5rem);
}

/* La línea de comando que abre cada sección. Es el elemento que sustituye a los
   antetítulos de siempre («Selected work», «What we build»): dice lo mismo, en
   menos espacio, y en el idioma de la casa. */
.term {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.55rem;
  margin: 0 0 clamp(1.75rem, 4vw, 2.75rem);
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  line-height: 1.9;
}

.term-prompt {
  color: var(--accent);
}

.term-cmd {
  color: var(--fg);
}

/* El cursor sigue al texto mientras se escribe y desaparece en cuanto la
   respuesta está en pantalla: un cursor parado bajo una salida ya impresa es
   ruido. Solo la primera línea de la página se anima; las demás son estáticas,
   porque seis animaciones de tecleo serían un circo. */
#term .term-cmd::after {
  content: '';
  display: inline-block;
  width: 0.5em;
  height: 1em;
  margin-left: 0.15em;
  vertical-align: -0.12em;
  background: var(--accent);
  animation: caret 1.1s steps(1) infinite;
}

#term.is-done .term-cmd::after {
  display: none;
}

.term-out {
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.16em;
}

.term-out[hidden] {
  display: none;
}

/* La flecha de la salida va en oro y el texto en gris: el comando lo lanza la
   máquina, pero quien contesta es la casa. */
.term-arrow {
  color: var(--gold);
}

/* --- Titulares de bloque -------------------------------------------------- */

.lede-title {
  margin: 0;
  font-size: clamp(2.4rem, 7vw, 5rem);
  font-variation-settings: 'wdth' 112;
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.035em;
}

.lede-body {
  max-width: 42rem;
  margin: 2rem 0 0;
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  color: var(--muted);
}

.lede-body strong {
  color: var(--fg);
  font-weight: 600;
}

.lede-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin-top: 2.5rem;
}

/* --- La sección personal y el idioma --------------------------------------

   El conmutador va arriba de la sección y no en la barra de navegación porque
   es lo único de la página que cambia de idioma: ponerlo en la barra prometería
   traducir la web entera. Cuando el resto esté traducido, este mismo botón sube
   a la barra sin tocar el JavaScript. */

.lang {
  display: inline-flex;
  gap: 1px;
  margin-bottom: 2rem;
  padding: 1px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
}

.lang-btn {
  padding: 0.4rem 0.95rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--faint);
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  cursor: pointer;
  transition:
    background-color 0.2s ease,
    color 0.2s ease;
}

.lang-btn:hover {
  color: var(--fg);
}

/* El activo va en verde sobre negro y no al revés: el acento es un verde de
   fósforo, y en un botón relleno con texto encima obligaría a poner el texto en
   negro, que a este cuerpo tan pequeño se lee peor que el verde sobre el
   fondo. */
.lang-btn.is-on {
  background: var(--accent-dim);
  color: var(--accent);
}

.about-body {
  max-width: 40rem;
  margin-top: 2rem;
}

.about-body p {
  margin: 0 0 1.15rem;
  color: var(--muted);
  font-size: clamp(1rem, 1.5vw, 1.1rem);
}

.about-body p:last-child {
  margin-bottom: 0;
  color: var(--fg);
}

/* --- La chapa de empresa estadounidense -----------------------------------
   Es puro golpe de autoridad, y por eso es una línea y no una ficha de
   registro: cuanto menos explica, más se la cree quien la lee. La bandera hace
   el trabajo y el texto solo la sostiene. */

.usbadge {
  display: inline-flex;
  align-items: center;
  gap: clamp(0.85rem, 2vw, 1.15rem);
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding: 0.9rem 1.35rem 0.9rem 1.1rem;
  border: 1px solid var(--gold-dim);
  border-radius: 12px;
  background: var(--surface);
}

/* La bandera, en el oro de la casa. El cantón va macizo y las franjas a media
   opacidad: es lo que separa las dos mitades del dibujo y hace que se lea
   «bandera» y no «gráfico de barras». */
.flag-us {
  width: clamp(2.4rem, 6vw, 3rem);
  height: auto;
  flex: none;
  /* Un filete finísimo alrededor para que las franjas no se deshagan contra el
     fondo de la tarjeta. */
  outline: 1px solid var(--gold-dim);
}

/* `fill-opacity` y no `opacity`: el SVG ya lleva `fill-opacity` como atributo,
   y si aquí se usara `opacity` las dos se multiplicarían (0,5 × 0,5 = 0,25) y
   las franjas saldrían casi negras. */
.flag-stripes rect {
  fill: var(--gold);
  fill-opacity: 0.5;
}

.flag-canton {
  fill: var(--gold);
}

.usbadge-text {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.usbadge-main {
  font-family: var(--mono);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
}

.usbadge-sub {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  color: var(--faint);
}

@media (max-width: 560px) {
  .usbadge {
    display: flex;
    padding: 0.85rem 1rem;
  }

  .usbadge-sub {
    font-size: 0.6rem;
    line-height: 1.45;
  }
}

/* --- El portafolio, en tarjetas -------------------------------------------

   Antes esto era un listado de terminal de tres columnas con párrafos largos.
   Ahora cada pieza es una tarjeta que se lee de un vistazo: captura, título,
   una línea, tecnologías y botón. La captura va arriba y manda, porque un
   cliente decide mirando el producto, no leyendo sobre él.

   `auto-fit` resuelve la rejilla entera sin una sola media query: dos columnas
   en escritorio y una en móvil, sola.

   El mínimo son 460 px y no 330 por una razón muy concreta: la página mide
   1180, y con 330 caben TRES columnas, así que las cuatro piezas salían 3 + 1 y
   la última se quedaba huérfana en su fila. Con 460 solo entran dos (2 × 460 =
   920 ✓, 3 × 460 = 1380 ✗) y las cuatro caen 2 + 2.

   El `min(100%, 460px)` no es adorno: sin él, en una pantalla más estrecha que
   460 px la columna seguiría midiendo 460 y desbordaría a lo ancho. */

.listing {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 460px), 1fr));
  gap: clamp(1.25rem, 2.5vw, 1.75rem);
}

.card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  overflow: hidden;
  transition: border-color 0.3s ease;
}

.card:hover {
  border-color: var(--line-strong);
}

.card-shot {
  display: block;
  overflow: hidden;
}

/* 16:9 recortando desde arriba. Las capturas son 1200×750, o sea 16:10, así que
   se pierde una franja por abajo — que en una captura de web es justo la zona
   muerta. Recortar es preferible a deformar, y a que cada tarjeta tenga una
   altura de imagen distinta y la rejilla quede desalineada. */
.card-shot img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: top;
  border-bottom: 1px solid var(--line);
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.card:hover .card-shot img {
  transform: scale(1.03);
}

/* `flex: 1` para que todas las tarjetas de una fila tengan la misma altura y el
   botón de todas quede alineado abajo, tengan el texto que tengan. Sin esto,
   las tarjetas sin bloque de tecnologías dejan el botón flotando a media
   altura. */
.card-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: clamp(1.25rem, 2.5vw, 1.6rem);
}

/* El número de linaje. No informa de nada que el visitante necesite —nadie
   viene a saber cuál fue el tercero— y ese es justo el motivo por el que
   funciona: es la marca contando una sucesión. Ordena el portafolio con un
   carácter y da sensación de casa con historia sin gastar una palabra. */
.ord {
  display: block;
  margin-bottom: 0.5rem;
  color: var(--gold);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
}

.card-title {
  margin: 0;
  font-size: clamp(1.35rem, 2.2vw, 1.65rem);
  font-variation-settings: 'wdth' 106;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.1;
}

.card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.75rem 0 0;
}

.card-line {
  margin: 0.9rem 0 0;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.55;
}

/* `margin-top: auto` empuja el bloque de tecnologías y el botón al fondo de la
   tarjeta: es lo que mantiene los botones de una fila en la misma línea. */
.card-stack {
  margin-top: auto;
  padding-top: 1.25rem;
}

.card-stack-label {
  margin: 0 0 0.5rem;
  color: var(--faint);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
}

/* Chips de texto y no insignias con icono. La web que sirvió de referencia usa
   iconos de marca, pero eso aquí serían veinte SVG nuevos y, sobre todo, veinte
   colores ajenos —el azul de React, el negro de Next— metidos en una paleta que
   solo admite verde y oro. En monoespaciada suenan a la misma voz que el resto
   de la página. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.chips li {
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* El único elemento de acción de la tarjeta, y por eso va a todo el ancho: en
   una tarjeta cuya mitad superior es una imagen que también enlaza, el botón
   tiene que ser inconfundible o nadie sabe dónde tiene que pulsar.

   Lleva `margin-top: auto` además del `.card-stack` porque en las tarjetas sin
   bloque de tecnologías es él quien tiene que bajar hasta el fondo. */
.card-visit {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: auto;
  padding: 0.75rem 1rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--fg);
  transition:
    border-color 0.2s ease,
    color 0.2s ease,
    background-color 0.2s ease;
}

/* Cuando hay bloque de tecnologías, el `auto` ya lo gastó él: aquí solo hace
   falta separación. */
.card-stack + .card-visit {
  margin-top: 1.1rem;
}

.card-visit:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-dim);
}

/* --- Servicios -----------------------------------------------------------
   Filas con el nombre a la izquierda y una frase a la derecha, en la misma
   retícula que el listado del portafolio: dos secciones seguidas con la misma
   estructura se leen como una sola voz. */

.cap {
  display: grid;
  grid-template-columns: minmax(180px, 22%) 1fr;
  gap: clamp(1rem, 3vw, 3rem);
  padding: 1.5rem 0;
  border-top: 1px solid var(--line-strong);
}

.cap:last-child {
  border-bottom: 1px solid var(--line-strong);
}

.cap h3 {
  margin: 0;
  font-size: clamp(1.1rem, 2vw, 1.45rem);
  font-variation-settings: 'wdth' 106;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.cap p {
  margin: 0;
  max-width: 46rem;
  color: var(--muted);
}

/* --- Alcance -------------------------------------------------------------- */

.strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.strip li {
  padding: 0.7rem 1.15rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: clamp(0.95rem, 1.7vw, 1.15rem);
  font-weight: 500;
  letter-spacing: -0.01em;
}

/* --- Etiquetas, meta ------------------------------------------------------ */

.tag {
  padding: 0.28rem 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.25rem;
  margin-top: 1.25rem !important;
}

.meta .mono {
  color: var(--faint);
}

.link {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 2px;
  transition: border-color 0.2s ease;
}

.link:hover {
  border-bottom-color: var(--accent);
}

/* --- Pie ------------------------------------------------------------------ */

/* SIN `overflow: hidden`, y es importante.

   El pie está limitado a 1180 px, pero el rótulo de dentro mide 100vw para
   tocar los dos bordes de la pantalla. Con `overflow: hidden` el navegador lo
   recortaba a la caja del pie y se comía la «h» de la izquierda y la «y» de la
   derecha: el nombre salía descabezado por los dos lados.

   Cuesta verlo midiendo, porque `getBoundingClientRect` devuelve la caja de
   maquetación —1470 px, aparentemente correcta— y no lo que se llega a pintar.
   Se ve mirando, no midiendo.

   Quitarlo no trae barra horizontal: de eso ya se encarga el `overflow-x`
   del `body`. */
.foot {
  max-width: var(--page);
  margin: 0 auto;
  padding: 2rem clamp(1.25rem, 4vw, 2.5rem) 0;
  border-top: 1px solid var(--line);
}

.foot-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
}

.foot-copy {
  color: var(--faint);
}

/* El sello de la casa. Un círculo de trazo, la corona dentro y el nombre dando
   la vuelta: la forma más vieja que existe de firmar algo, y la que cierra la
   mitad de realeza mejor que cualquier titular.

   Gira despacio y en continuo. Un sello quieto es un logotipo; uno que se mueve
   como una moneda sobre la mesa es el vacile de la casa. */

.crest {
  display: block;
  width: clamp(96px, 10vw, 124px);
  height: auto;
  margin: clamp(2rem, 4vw, 3rem) auto clamp(0.5rem, 1.5vw, 1rem);
  color: var(--gold);
}

/* `transform-box: view-box` no es opcional: sin él, el origen del giro es la
   caja del texto y no el centro del sello, y el anillo sale despedido en
   diagonal en vez de rodar sobre su eje. */
.crest-ring {
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: crest-spin 12s linear infinite;
}

@keyframes crest-spin {
  to {
    transform: rotate(360deg);
  }
}

.crest-text {
  font-family: var(--mono);
  font-size: 7.1px;
  letter-spacing: 0.24em;
  fill: currentColor;
  text-transform: uppercase;
}

.crest-circle {
  fill: none;
  stroke: currentColor;
  stroke-width: 0.6;
  opacity: 0.45;
}

.crest-crown {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* El nombre ocupando el ancho entero, apoyado en el borde inferior: lo último
   que se ve y lo que se recuerda. El viewBox llega a 168 y no a 134 para que
   quepa el descendente de la «y» — cortado se leía como un error de recorte y
   no como una decisión. */
/* Se escapa del contenedor para tocar los dos bordes de la pantalla.

   El pie está limitado a 1180 px como el resto de la página, y ahí dentro el
   nombre se quedaba flotando en medio con aire a los lados: parecía un logotipo
   grande, no un rótulo. `width: 100vw` con el margen negativo de medio
   contenedor lo saca de la caja y lo lleva de borde a borde, que es lo que hace
   que se lea como piedra tallada — exactamente igual que el titular del hero.

   Los 100vw no provocan scroll horizontal porque `body` lleva
   `overflow-x: hidden` y el `.foot` recorta lo que sobra. */
.foot-mark {
  display: block;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  margin-top: clamp(1.25rem, 3vw, 2rem);
  margin-bottom: 0;
}

.foot-mark text {
  font-family: var(--sans);
  font-variation-settings: 'wdth' 118;
  font-weight: 800;
  font-size: 160px;
  fill: var(--fg);
}

.foot-mark text.foot-mark-accent {
  fill: var(--accent);
}

/* --- Asistente ------------------------------------------------------------ */

.chat-toggle {
  position: fixed;
  right: clamp(1rem, 3vw, 2rem);
  bottom: clamp(1rem, 3vw, 2rem);
  z-index: 60;
  padding: 0.85rem 1.35rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--fg);
  cursor: pointer;
  transition:
    border-color 0.2s ease,
    background-color 0.2s ease;
}

.chat-toggle:hover {
  border-color: var(--accent);
  background: #101010;
}

.chat {
  position: fixed;
  right: clamp(1rem, 3vw, 2rem);
  bottom: calc(clamp(1rem, 3vw, 2rem) + 3.75rem);
  z-index: 60;
  display: flex;
  flex-direction: column;
  width: min(360px, calc(100vw - 2rem));
  height: min(460px, calc(100vh - 8rem));
  border: 1px solid var(--line-strong);
  border-radius: 16px;
  background: var(--surface);
  overflow: hidden;
}

.chat[hidden] {
  display: none;
}

.chat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
}

.chat-close {
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.chat-close:hover {
  color: var(--fg);
}

.chat-log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.msg {
  max-width: 85%;
  padding: 0.6rem 0.85rem;
  border-radius: 12px;
  font-size: 0.9rem;
  line-height: 1.5;
  white-space: pre-wrap;
}

.msg-bot {
  align-self: flex-start;
  background: #131313;
  border: 1px solid var(--line);
}

.msg-me {
  align-self: flex-end;
  background: var(--fg);
  color: var(--ink);
}

.msg-wait {
  color: var(--faint);
}

.chat-form {
  display: flex;
  gap: 0.5rem;
  padding: 0.75rem;
  border-top: 1px solid var(--line);
}

.chat-input {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--ink);
  color: var(--fg);
  /* 16px es el mínimo que evita que iOS haga zoom al enfocar el campo. */
  font-size: 16px;
  font-family: inherit;
}

.chat-input::placeholder {
  color: var(--faint);
}

.chat-send {
  width: 2.5rem;
  border: 0;
  border-radius: 999px;
  background: var(--fg);
  color: var(--ink);
  font-size: 1.1rem;
  cursor: pointer;
}

/* --- Adaptación ----------------------------------------------------------- */

/* ==========================================================================
   MÓVIL
   ==========================================================================

   No es «lo mismo pero estrecho». Un teléfono cambia dos cosas de raíz:

   1. La pantalla es ALTA, no ancha. El retrato del trono es vertical, así que
      en un móvil puede ocupar la pantalla entera — que es cuando de verdad
      funciona como portada. En escritorio hay que contenerlo; aquí, soltarlo.

   2. Un nombre largo a lo ancho se queda enano. `hackdynasty` estirado en
      390 px deja letras de 60 px. Partido en dos líneas, cada mitad ocupa el
      ancho completo y triplica su tamaño. Por eso el titular y el pie tienen
      una versión apilada, y no es un apaño: es la composición correcta para
      una pantalla vertical.

   El corte está en 700 px y no en los 900 del listado a propósito: son dos
   decisiones distintas —cuándo se rompe una rejilla y cuándo cambia una
   composición tipográfica— y meterlas en el mismo punto de corte solo porque
   está escrito más arriba es lo que acaba dando páginas a medio adaptar. */

/* Por defecto manda la versión de una línea; la apilada está apagada. */
.throne-word-stack,
.foot-mark-stack {
  display: none;
}

@media (max-width: 700px) {
  /* El hero llena la pantalla. `100svh` y no `100vh`: en iOS y Android la
     barra del navegador se recoge al hacer scroll, y con `vh` el hero mide más
     que la pantalla visible al cargar, así que la primera impresión llega
     cortada. `svh` usa la altura pequeña —la del navegador desplegado— que es
     justo la que ve alguien que acaba de abrir el enlace. */
  .throne {
    min-height: 100svh;
    justify-content: center;
    padding-top: 0;
  }

  /* La foto va de borde a borde: en vertical, el retrato ES la pantalla. */
  .throne-stage {
    width: 100vw;
  }

  /* Con la foto a sangre, el desvanecido lateral tiene que ser mucho más corto
     o se come los brazos del trono. */
  .throne-photo {
    -webkit-mask-image:
      linear-gradient(to bottom, transparent 0%, #000 10%, #000 82%, transparent 99%),
      linear-gradient(to right, transparent 0%, #000 4%, #000 96%, transparent 100%);
    mask-image:
      linear-gradient(to bottom, transparent 0%, #000 10%, #000 82%, transparent 99%),
      linear-gradient(to right, transparent 0%, #000 4%, #000 96%, transparent 100%);
  }

  .throne-seal {
    width: 150vw;
    opacity: 0.42;
  }

  /* Se cambia el rótulo de una línea por el de dos. */
  .throne-word-wide,
  .foot-mark-wide {
    display: none;
  }

  .throne-word-stack,
  .foot-mark-stack {
    display: block;
  }

  /* El apilado pisa menos la foto que el de una línea: son dos líneas de texto
     y con el margen de escritorio taparía la mitad del retrato. */
  .throne-word {
    margin-top: clamp(-3rem, -5vw, -1.5rem);
  }

  .throne-sub {
    font-size: 0.66rem;
    letter-spacing: 0.12em;
  }

  /* En una pantalla estrecha el cartel del pie ya llega solo a los dos bordes,
     así que no necesita salirse del contenedor — y saliéndose se llevaba por
     delante el margen lateral de la página. */
  .foot-mark-stack {
    margin-top: clamp(1rem, 4vw, 2rem);
  }

  /* La cinta corre más rápido: el mismo recorrido en una pantalla estrecha se
     percibe mucho más lento, y a 42s parecía parada. */
  .ticker-track {
    animation-duration: 24s;
  }

  .ticker-set > span {
    padding: 0 1rem;
    font-size: 0.68rem;
    letter-spacing: 0.22em;
  }
}

@media (max-width: 860px) {
  .bar-nav {
    display: none;
  }
}

@media (max-width: 640px) {
  .cap {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
}

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

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
