/* ============================================================
   Rodolfo Bravo — Fullstack AI Engineer · Editorial Noir
   1. Tokens  2. Reset  3. Base  4. Layout  5. Componentes base  6. Nav
   7. Motion  8. Hero  9. Proof  10. Work  11. Stack  12. Production
   13. (retirado)  14. About  15. Contact  16. Footer
   17. Utilidades  18. Modal
   ============================================================ */

/* ---------- 1. Tokens ---------- */
:root {
  --bg: #0B0B0C;
  --surface: #121214;
  --raised: #17171A;
  --hairline: rgba(244, 241, 234, .12);
  --text: #F4F1EA;
  --muted: rgba(244, 241, 234, .62);
  --faint: rgba(244, 241, 234, .55);
  --accent: #4A8CFF;
  --accent-ink: #0B0B0C;
  /* Los glows usan rgba() literal porque necesitan alfa; mantener sincronizado
     con --accent. Alfas más altos que el acento lima anterior: a igual alfa un
     azul (luminancia .26) casi desaparece donde el lima (.84) se veía. */
  --accent-rgb: 74, 140, 255;
  --live: #4ADE80;

  --font-display: "Newsreader", Georgia, "Times New Roman", serif;
  --font-ui: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, Consolas, "SF Mono", monospace;

  --fs-display: clamp(2.35rem, 1.1rem + 5vw, 5.25rem);
  --fs-h2: clamp(1.8rem, 1.15rem + 2.2vw, 3rem);
  --fs-h3: clamp(1.15rem, 1rem + .6vw, 1.5rem);
  --fs-body: clamp(.95rem, .9rem + .25vw, 1.0625rem);
  --fs-small: .875rem;
  --fs-mono: .6875rem;

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
  --sp-9: 96px; --sp-10: 128px;

  --gutter: clamp(20px, 5vw, 64px);
  --measure: 68ch;
  --radius: 6px;
  --section-y: clamp(88px, 12vh, 168px);
  --nav-h: 68px;

  --ease: cubic-bezier(.22, .61, .36, 1);
}

/* ---------- 2. Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body { min-height: 100vh; -webkit-font-smoothing: antialiased; }
img, picture, svg { display: block; max-width: 100%; }
button, input, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; padding: 0; }

/* ---------- 3. Base ---------- */
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: 1.65;
  font-feature-settings: "cv11", "ss01";
}

/* Newsreader es variable (opsz 6..72): con `font-optical-sizing: auto` el
   titular grande adelgaza sus remates y el texto chico los engorda solo. Es
   justo lo que le faltaba a Instrument Serif, que a 14px se veía anémica. */
h1, h2, h3 {
  font-family: var(--font-display); font-weight: 400; letter-spacing: -.018em;
  font-optical-sizing: auto;
}
h1 { font-size: var(--fs-display); line-height: 1.02; }
h2 { font-size: var(--fs-h2); line-height: 1.08; }
h3 { font-size: var(--fs-h3); line-height: 1.2; }
p { color: var(--muted); max-width: var(--measure); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}
::selection { background: var(--accent); color: var(--accent-ink); }

/* ---------- 4. Layout ---------- */
.container { width: min(1180px, 100% - 2 * var(--gutter)); margin-inline: auto; }
.section { padding-block: var(--section-y); border-top: 1px solid var(--hairline); }
.section__head { margin-bottom: var(--sp-8); }
.section__head h2 { max-width: 22ch; }
.section__head p { margin-top: var(--sp-4); }

/* ---------- 5. Componentes base ---------- */
.kicker {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent);
  display: block;
}
.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--faint);
}
.accent { color: var(--accent); }

.btn {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 13px 22px; border-radius: var(--radius);
  border: 1px solid var(--hairline);
  font-size: var(--fs-small); font-weight: 500;
}
.btn:hover { border-color: var(--text); }
.btn--primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }
.btn--primary:hover { background: var(--text); border-color: var(--text); color: var(--bg); }

@media (prefers-reduced-motion: no-preference) {
  .btn { transition: border-color .2s var(--ease), background-color .2s var(--ease), color .2s var(--ease); }
}

/* ---------- 6. Nav ---------- */
.nav {
  position: fixed; inset: 0 0 auto; z-index: 50;
  height: var(--nav-h);
  background: transparent;
  border-bottom: 1px solid transparent;
}
.nav[data-scrolled="true"] {
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--hairline);
}
/* Con el menú móvil abierto el panel de enlaces sí lleva fondo propio, pero la
   barra necesita el suyo para que marca y botones no floten sobre el hero. */
.nav:has(.nav__links[data-menu-open="true"]) {
  background: var(--bg);
  border-bottom-color: var(--hairline);
}
@supports not (backdrop-filter: blur(1px)) { .nav[data-scrolled="true"] { background: var(--bg); } }

.nav__progress {
  position: absolute; left: 0; bottom: -1px; height: 2px; width: 0%;
  background: var(--accent);
}
.nav__inner { display: flex; align-items: center; gap: var(--sp-6); height: 100%; }

.nav__brand { font-family: var(--font-display); font-size: 1.0625rem; letter-spacing: -.01em; line-height: 1; }
.nav__brandRole { display: block; margin-top: 3px; color: var(--faint); font-size: .5625rem; }

.nav__links { display: flex; gap: var(--sp-6); margin-left: auto; }
.nav__links a {
  position: relative; font-size: var(--fs-small); color: var(--muted);
}
.nav__links a:hover { color: var(--text); }
.nav__links a[aria-current="true"] { color: var(--text); }
.nav__links a[aria-current="true"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -6px;
  height: 1px; background: var(--accent);
}

.nav__actions { display: flex; align-items: center; gap: var(--sp-3); }
.nav__lang {
  display: inline-flex; gap: 3px; padding: 7px 11px;
  border: 1px solid var(--hairline); border-radius: 999px; color: var(--faint);
}
.nav__lang:hover { border-color: var(--text); }
.nav__langOn { color: var(--accent); }
.nav__langSep { opacity: .35; }
.nav__lang[aria-pressed="true"] .nav__langOn { color: var(--faint); }
.nav__lang[aria-pressed="true"] .nav__langOff { color: var(--accent); }

.nav__burger { display: none; flex-direction: column; gap: 5px; padding: 10px 6px; }
.nav__burger span { display: block; width: 20px; height: 1.5px; background: var(--text); }
.nav__burger[aria-expanded="true"] span:first-child { transform: translateY(3.25px) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:last-child { transform: translateY(-3.25px) rotate(-45deg); }

@media (prefers-reduced-motion: no-preference) {
  .nav { transition: background-color .25s var(--ease), border-color .25s var(--ease); }
  .nav__links a { transition: color .2s var(--ease); }
  .nav__lang { transition: border-color .2s var(--ease); }
  .nav__burger span { transition: transform .2s var(--ease); }
}

@media (max-width: 880px) {
  .nav__burger { display: flex; }
  .nav__links {
    position: fixed; inset: var(--nav-h) 0 auto; flex-direction: column; gap: 0;
    background: var(--bg); border-bottom: 1px solid var(--hairline);
    padding: var(--sp-3) var(--gutter) var(--sp-5);
    transform: translateY(-8px); opacity: 0; pointer-events: none; visibility: hidden;
  }
  .nav__links[data-menu-open="true"] { transform: none; opacity: 1; pointer-events: auto; visibility: visible; }
  .nav__links a { padding: var(--sp-4) 0; border-bottom: 1px solid var(--hairline); font-size: 1rem; }
}

@media (max-width: 880px) and (prefers-reduced-motion: no-preference) {
  .nav__links { transition: opacity .2s var(--ease), transform .2s var(--ease); }
}

#main { padding-top: var(--nav-h); }

/* ---------- 7. Motion: sistema de reveal ---------- */
/* `[data-reveal]` usa la propiedad `translate`, no `transform`, para la entrada:
   `transform` queda libre para que `.card` (tilt 3D) y `.word > span` compongan
   su propio movimiento sin que la regla de reveal los pise por especificidad. */
@media (prefers-reduced-motion: no-preference) {
  [data-js="true"] [data-reveal] {
    opacity: 0;
    translate: 0 16px;
    transition: opacity .6s var(--ease), translate .6s var(--ease);
  }
  [data-reveal][data-revealed="true"] { opacity: 1; translate: none; }

  .word { display: inline-block; overflow: hidden; vertical-align: bottom; }
  .word > span {
    display: inline-block;
    transform: translateY(105%);
    transition: transform .7s var(--ease);
    transition-delay: var(--d, 0ms);
  }
  [data-revealed="true"] .word > span { transform: none; }
}
/* Con reduce, nada se oculta: [data-reveal] no recibe estilos y todo es visible.
   Sin `data-js="true"` (JS no llegó a ejecutar), tampoco: ver Fix 9. */

/* ---------- 8. Hero ---------- */
/* `min-height` reserva el alto del bloque para que el font swap de Google Fonts
   (Georgia -> Instrument Serif con `display=swap`) no reduzca/aumente la caja y
   arrastre `.hero__aurora` con ella (CLS). Es un piso relativo al viewport, no
   un alto fijo: nunca recorta contenido, solo evita que la caja encoja. */
.hero {
  position: relative; overflow: hidden;
  /* El nav es transparente arriba: el margen negativo anula el padding-top de
     #main para que el fondo del hero (aurora + lightfall) corra bajo el nav,
     y el padding-top devuelve el contenido a su posición original. */
  margin-top: calc(-1 * var(--nav-h));
  padding-block: calc(clamp(64px, 10vh, 120px) + var(--nav-h)) var(--sp-8);
  min-height: clamp(600px, 90vh, 860px);
}

.hero__aurora {
  /* Alto Y desplazamiento anclados al viewport, nunca al alto de `.hero`.
     `height: 100vh` ya desacoplaba el alto, pero un `top: -20%` seguía siendo
     porcentaje del alto del contenedor: al cambiar de fuente el hero encoge 27px
     y esto se movía ~6px. Como la aurora cubre casi todo el viewport, ese
     movimiento mínimo arrastraba un `impact fraction` enorme y era el 90% del
     CLS que medía Lighthouse. Con `vh` el reflow del texto no puede moverla. */
  position: absolute; top: -20vh; right: -10%; left: -10%; bottom: auto; height: 100vh;
  pointer-events: none;
  background:
    radial-gradient(46% 32% at 50% 0%,
      rgba(var(--accent-rgb), .18), transparent 70%),
    radial-gradient(38% 46% at calc(18% + var(--mx, 0px) * .04) calc(12% + var(--my, 0px) * .04),
      rgba(var(--accent-rgb), .22), transparent 62%),
    radial-gradient(34% 40% at calc(84% - var(--mx, 0px) * .03) calc(72% - var(--my, 0px) * .03),
      rgba(var(--accent-rgb), .10), transparent 66%);
}

/* Lightfall: canvas de "lluvia de luz" que pinta initLightfall() en main.js.
   Vive entre la aurora y el contenido (.hero__inner ya lleva z-index: 1) y la
   máscara disuelve los rayos antes de que crucen la cinta de tecnologías. El
   bloom superior de la aurora (primer radial, fijo en 50% 0%) es la "fuente"
   de la que parecen caer. Con reduce-motion el JS ni arranca; ocultar el
   canvas además evita pagar su capa de composición vacía. */
.hero__lightfall {
  position: absolute; inset: 0;
  /* canvas es replaced: con inset solo NO se estira (usa su 300×150
     intrínseco), necesita width/height explícitos. */
  width: 100%; height: 100%;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent 97%);
  mask-image: linear-gradient(to bottom, #000 60%, transparent 97%);
}
@media (prefers-reduced-motion: reduce) {
  .hero__lightfall { display: none; }
}

.hero__inner {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(var(--sp-6), 5vw, var(--sp-9)); align-items: end;
}

.hero__available { display: inline-flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-4); }
.hero__available .mono { color: var(--muted); }
.hero__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--live); box-shadow: 0 0 10px var(--live);
}
@media (prefers-reduced-motion: no-preference) {
  .hero__dot { animation: pulse 2.4s var(--ease) infinite; }
  @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
}

.hero__title { margin-top: var(--sp-5); max-width: 19ch; }
.hero__title em { font-style: italic; }
.hero__lede { margin-top: var(--sp-5); max-width: 46ch; }
.hero__endToEnd {
  margin-top: var(--sp-4); padding-left: var(--sp-4);
  border-left: 2px solid var(--accent);
  color: var(--muted); letter-spacing: .08em; max-width: 52ch; line-height: 1.8;
}
.hero__ctas { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-7); }

.hero__aside { display: grid; gap: var(--sp-6); text-align: right; padding-bottom: var(--sp-2); }
.hero__role { display: grid; gap: var(--sp-1); }
.hero__roleLabel { color: var(--faint); }
.hero__roleText { font-family: var(--font-mono); font-size: .8125rem; color: var(--accent); }
.hero__years { display: grid; gap: var(--sp-2); }
.hero__yearsNum {
  font-family: var(--font-display); font-size: clamp(4rem, 9vw, 7rem);
  line-height: .78; letter-spacing: -.05em; color: var(--text); opacity: .18;
}
.hero__yearsLabel { color: var(--faint); max-width: 14ch; margin-left: auto; line-height: 1.6; }

.hero__rule {
  position: relative; z-index: 1;
  margin-top: clamp(var(--sp-8), 9vh, var(--sp-10));
  padding-top: var(--sp-4); border-top: 1px solid var(--hairline);
}

/* Cinta de tecnologías: iconos en color de marca (default del CDN de Simple
   Icons). Las marcas negras (Next.js, Angular, Express, Django, OpenTelemetry)
   se sirven en F4F1EA porque desaparecerían sobre --bg. */
.techstrip {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.techstrip__track {
  display: flex; align-items: center; gap: var(--sp-6);
  width: max-content; padding-block: var(--sp-2);
}
.techstrip__item { display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
.techstrip__item img { width: 15px; height: 15px; opacity: .9; }
.techstrip__item .mono { color: var(--faint); }
@media (prefers-reduced-motion: no-preference) {
  .techstrip__track { animation: marquee 75s linear infinite; }
  .techstrip:hover .techstrip__track { animation-play-state: paused; }
  @keyframes marquee { to { transform: translateX(-50%); } }
}
@media (prefers-reduced-motion: reduce) {
  /* Sin animación (main.js tampoco duplica el track): la cinta se muestra
     completa como grilla envuelta en lugar de quedar recortada. */
  .techstrip { -webkit-mask-image: none; mask-image: none; }
  .techstrip__track { flex-wrap: wrap; width: auto; row-gap: var(--sp-3); }
}

@media (max-width: 780px) {
  .hero__inner { grid-template-columns: 1fr; }
  .hero__aside { text-align: left; grid-auto-flow: column; justify-content: start; gap: var(--sp-8); align-items: end; }
  .hero__yearsLabel { margin-left: 0; }
}

/* ---------- 9. Proof ---------- */
.proof { padding-block: clamp(var(--sp-8), 8vh, var(--sp-9)); }

.proof__kicker { display: flex; align-items: center; gap: var(--sp-4); margin-bottom: var(--sp-6); }
.proof__kicker::after { content: ""; height: 1px; flex: 1; background: var(--hairline); }

.proof__grid {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
}
.proof__item {
  position: relative; overflow: hidden;
  display: grid; gap: var(--sp-4); align-content: start;
  padding: var(--sp-6) var(--sp-5) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--hairline); border-radius: var(--radius);
  transition: border-color .35s var(--ease), background .35s var(--ease), transform .35s var(--ease);
}
.proof__item::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(240px circle at 15% -10%, rgba(var(--accent-rgb), .11), transparent 70%);
  opacity: .55; transition: opacity .35s var(--ease);
}
.proof__item:hover {
  border-color: rgba(var(--accent-rgb), .5);
  background: var(--raised);
  transform: translateY(-4px);
}
.proof__item:hover::before { opacity: 1; }
.proof__num {
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 5.5vw, 4.25rem);
  line-height: .9; letter-spacing: -.04em; color: var(--accent);
  font-variant-numeric: tabular-nums;
}
/* Regla de ticks: guiño a instrumentación electrónica — el segmento acento
   se extiende al hover como la aguja de un medidor. */
.proof__ruler {
  position: relative; height: 9px;
  background-image: repeating-linear-gradient(90deg, var(--hairline) 0 1px, transparent 1px 8px);
}
.proof__ruler::after {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 33px;
  background-image: repeating-linear-gradient(90deg, rgba(var(--accent-rgb), .95) 0 1px, transparent 1px 8px);
  transition: width .45s var(--ease);
}
.proof__item:hover .proof__ruler::after { width: 65px; }
.proof__label { color: var(--muted); max-width: 22ch; line-height: 1.7; }

.proof__results { margin-top: var(--sp-6); display: grid; gap: var(--sp-4); }
.proof__resultsLabel { display: flex; align-items: center; gap: var(--sp-4); color: var(--faint); }
.proof__resultsLabel::after { content: ""; height: 1px; flex: 1; background: var(--hairline); }
.proof__deltas {
  display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.proof__delta {
  display: grid; gap: var(--sp-2); align-content: start;
  padding: var(--sp-4);
  border: 1px solid var(--hairline); border-radius: var(--radius);
  transition: border-color .3s var(--ease), background .3s var(--ease);
}
.proof__delta:hover { border-color: rgba(var(--accent-rgb), .4); background: var(--surface); }
.proof__deltaVal {
  font-family: var(--font-display);
  font-size: 1.75rem; line-height: 1; letter-spacing: -.02em;
  color: var(--accent); font-variant-numeric: tabular-nums;
}
.proof__deltaLabel { color: var(--muted); letter-spacing: .1em; line-height: 1.6; }

@media (prefers-reduced-motion: reduce) {
  .proof__item, .proof__item::before, .proof__ruler::after, .proof__delta { transition: none; }
  .proof__item:hover { transform: none; }
}

/* ---------- 10. Work ---------- */
.work__filters { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-7); }
.work__filter {
  font-family: var(--font-mono); font-size: var(--fs-mono);
  letter-spacing: .14em; text-transform: uppercase;
  padding: 9px 14px; border: 1px solid var(--hairline); border-radius: 999px;
  color: var(--faint);
}
.work__filter:hover { color: var(--text); border-color: var(--text); }
.work__filter[aria-pressed="true"] { color: var(--accent-ink); background: var(--accent); border-color: var(--accent); }

/* Carrusel de casos: la grilla pasa a ser una pista horizontal con scroll-snap.
   Se ven `--cols` cartas por página y el resto queda a un scroll de distancia,
   en vez de apilar fila tras fila. Degrada solo: sin JS sigue siendo un
   contenedor con scroll nativo (táctil, rueda, teclado); main.js únicamente
   añade flechas y puntos, que van en `hidden` hasta que él los habilite. */
.work__carousel { position: relative; }

.work__grid {
  --cols: 4;
  display: flex; gap: var(--sp-5);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* El tilt 3D asoma unos px arriba y abajo: sin este padding, `overflow-x`
     (que vuelve `overflow-y` computado en auto) lo recortaría en el hover. */
  padding-block: 6px;
  scrollbar-width: none; -ms-overflow-style: none;
}
.work__grid::-webkit-scrollbar { width: 0; height: 0; }

.work__grid > .card {
  flex: 0 0 calc((100% - (var(--cols) - 1) * var(--sp-5)) / var(--cols));
  min-width: 0; scroll-snap-align: start;
}

@media (max-width: 1240px) { .work__grid { --cols: 3; } }
@media (max-width: 960px)  { .work__grid { --cols: 2; } }
@media (max-width: 680px)  { .work__grid { --cols: 1; } }

.work__controls {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); margin-top: var(--sp-5);
}
.work__controls[hidden] { display: none; }

.work__dots { display: flex; align-items: center; gap: var(--sp-2); }
.work__dot { position: relative; width: 32px; height: 18px; }
.work__dot::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 2px; translate: 0 -50%; background: rgba(244, 241, 234, .2);
}
.work__dot:hover::before { background: var(--text); }
.work__dot[aria-current="true"]::before { background: var(--accent); }

.work__arrows { display: flex; gap: var(--sp-2); }
.work__arrow {
  width: 42px; height: 42px; display: grid; place-items: center;
  border: 1px solid var(--hairline); border-radius: 999px; color: var(--faint);
}
.work__arrow:hover:not([disabled]) {
  color: var(--accent-ink); background: var(--accent); border-color: var(--accent);
}
.work__arrow[disabled] { opacity: .28; cursor: default; }

.card {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: var(--sp-4);
  padding: var(--sp-6); text-align: left; width: 100%;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius);
}
.card:hover { border-color: rgba(244, 241, 234, .28); background: var(--raised); }
.card[hidden] { display: none; }

.card::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(340px circle at var(--px, 50%) var(--py, 50%), rgba(var(--accent-rgb), .14), transparent 70%);
}

.card__top { display: flex; align-items: center; gap: var(--sp-3); }
.card__cat { color: var(--accent); }
.card__lock { margin-left: auto; color: var(--faint); }
.card__title { font-size: var(--fs-h3); }
.card__tagline { color: var(--muted); font-size: var(--fs-small); }
.card__stack { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: auto; padding-top: var(--sp-2); }
.card__chip {
  font-family: var(--font-mono); font-size: var(--fs-mono); letter-spacing: .1em;
  padding: 5px 9px; border: 1px solid var(--hairline); border-radius: 4px; color: var(--faint);
}
.card__more { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-small); color: var(--accent); }

/* Bajo `reduce`, la carta debe permanecer visible, sin tilt ni halo: por eso el
   spotlight (::before en hover) y el tilt (transform) sólo se activan aquí dentro,
   junto con todas las transiciones/animaciones de esta sección. */
@media (prefers-reduced-motion: no-preference) {
  .work__filter { transition: color .2s var(--ease), border-color .2s var(--ease); }

  .card:hover::before { opacity: 1; }
  .card::before { transition: opacity .25s var(--ease); }

  .card:hover .card__more span:last-child { transform: translateX(4px); }
  .card__more span:last-child { transition: transform .2s var(--ease); }

  .work__grid { perspective: 1000px; scroll-behavior: smooth; }
  .work__arrow, .work__dot::before {
    transition: color .2s var(--ease), background-color .2s var(--ease), border-color .2s var(--ease);
  }
  .card {
    transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
    transform-style: preserve-3d;
    transition: border-color .25s var(--ease), background-color .25s var(--ease), transform .18s var(--ease);
  }
}

/* ---------- 11. Stack ---------- */
.stack__highlights {
  display: grid; gap: var(--sp-5);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.stack__hl { padding-top: var(--sp-4); border-top: 2px solid var(--accent); }
.stack__hlLogos { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-3); min-height: 18px; }
.stack__hlLogos img { width: 18px; height: 18px; opacity: .85; }
.stack__hlTitle { font-family: var(--font-mono); font-size: .875rem; letter-spacing: .04em; margin-bottom: var(--sp-3); }
.stack__hl p { font-size: var(--fs-small); }

/* Banda de cierre del stack. Antes era un párrafo con borde izquierdo que
   enumeraba las etapas en prosa; ahora la frase queda en display y las etapas
   se ven como el pipeline que son (01 → 05), que es el argumento real. */
/* No es una tarjeta: es una franja que cruza la página de lado a lado, con
   filete arriba y abajo. El ancho lo da estar fuera del .container (ver el
   comentario en index.html) y el texto lo realinea el .container interior. */
.e2e {
  position: relative; overflow: hidden;
  margin-block: var(--sp-7);
  padding-block: clamp(var(--sp-5), 3vw, var(--sp-6));
  border-block: 1px solid var(--hairline);
  background:
    radial-gradient(900px circle at 12% 0%, rgba(var(--accent-rgb), .13), transparent 62%),
    linear-gradient(180deg, var(--raised), transparent 85%);
}
/* Cubre el filete superior con el degradado del acento, en vez de sumarle otra
   línea: por eso el -1px arriba. */
.e2e::before {
  content: ""; position: absolute; inset: -1px 0 auto; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent) 12%, rgba(var(--accent-rgb), .08) 62%, transparent);
}

/* Una sola línea mientras quepa: la frase manda a la izquierda y el pipeline
   la acompaña a la derecha. Al no caber, `flex-wrap` lo baja entero. */
.e2e__inner {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--sp-5) clamp(var(--sp-6), 4vw, var(--sp-8));
}
.e2e__head { flex: 1 1 26ch; display: grid; gap: var(--sp-3); }
.e2e__kicker { color: var(--accent); }
.e2e__claim {
  font-family: var(--font-display); font-optical-sizing: auto;
  font-size: clamp(1.35rem, 1.05rem + 1.1vw, 2rem);
  line-height: 1.18; letter-spacing: -.018em;
  color: var(--text); max-width: 34ch; text-wrap: balance;
}

.e2e__flow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-4); }
.e2e__step {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--sp-3);
  padding: 10px 16px;
  border: 1px solid var(--hairline); border-radius: 999px;
  background: rgba(11, 11, 12, .55);
  font-size: var(--fs-small); color: var(--text); white-space: nowrap;
}
.e2e__step:hover { border-color: rgba(var(--accent-rgb), .5); background: var(--raised); }
.e2e__num { color: var(--accent); font-variant-numeric: tabular-nums; }
/* El guion vive en el gap del flex, no dentro de la píldora. Se retira al
   envolver en varias líneas, donde conectaría pasos que no van seguidos. */
@media (min-width: 881px) {
  .e2e__step + .e2e__step::before {
    content: ""; position: absolute; top: 50%; left: calc(var(--sp-4) * -1);
    width: var(--sp-4); height: 1px; background: var(--hairline);
  }
}

/* Cintas por categoría: una fila por grupo, cada una desplazándose sola en
   direcciones alternas (misma mecánica que .techstrip del hero). El ancho de
   recorrido (--shift) y la duración (--dur) los calcula main.js según el
   contenido real, para que todas las filas avancen a los mismos px/s. */
.stackrows { display: grid; gap: var(--sp-2); }
.stackrow {
  display: grid; align-items: center;
  grid-template-columns: minmax(0, 180px) minmax(0, 1fr);
  gap: var(--sp-5);
  padding-block: var(--sp-3);
  border-top: 1px solid var(--hairline);
}
.stackrow:last-child { border-bottom: 1px solid var(--hairline); }
.stackrow__title { color: var(--accent); }

.stackrow__strip {
  overflow: hidden; min-width: 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
.stackrow__track { --gap: var(--sp-2); display: flex; gap: var(--gap); }
.stackrow__track[data-ready] { width: max-content; }
.stackrow__set { display: flex; flex-wrap: wrap; gap: var(--gap); }
.stackrow__track[data-ready] .stackrow__set { flex-wrap: nowrap; }

.chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 7px 12px; white-space: nowrap;
  font-size: .8125rem; color: var(--muted);
  background: var(--surface); border: 1px solid var(--hairline); border-radius: 4px;
}
.chip__logo { width: 16px; height: 16px; flex: none; opacity: .8; }
.chip__dot {
  width: 5px; height: 5px; flex: none; border-radius: 50%;
  background: rgba(var(--accent-rgb), .55);
}
.chip:hover { color: var(--text); border-color: rgba(var(--accent-rgb), .5); background: var(--raised); }
.chip:hover .chip__logo { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .chip { transition: color .2s var(--ease), border-color .2s var(--ease), background-color .2s var(--ease); }
  .chip__logo { transition: opacity .2s var(--ease); }
  .e2e__step { transition: border-color .25s var(--ease), background-color .25s var(--ease); }
  .stackrow__track[data-ready] { animation: stackmarquee var(--dur, 60s) linear infinite; }
  .stackrow__track[data-dir="rtl"] { animation-direction: reverse; }
  .stackrow__strip:hover .stackrow__track { animation-play-state: paused; }
  @keyframes stackmarquee { to { transform: translate3d(calc(-1 * var(--shift, 0px)), 0, 0); } }
}
@media (prefers-reduced-motion: reduce) {
  /* Sin animación (main.js tampoco clona las cintas): cada fila se muestra
     completa como grilla envuelta. */
  .stackrow__strip { -webkit-mask-image: none; mask-image: none; }
}

@media (max-width: 780px) {
  .stackrow { grid-template-columns: 1fr; gap: var(--sp-3); }
}

/* ---------- 12. Production ---------- */
.diagram {
  margin: 0 0 var(--sp-9);
  padding: clamp(var(--sp-5), 4vw, var(--sp-7));
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius);
}
.diagram__head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: var(--sp-3);
  margin-bottom: var(--sp-6);
}
.diagram__caption { color: var(--accent); }
.diagram__live { display: inline-flex; align-items: center; gap: var(--sp-2); }
.diagram__live-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--live); box-shadow: 0 0 8px var(--live);
}

/* `overflow: visible` para que el drop-shadow del pulso y del nodo central no
   se recorte contra el viewBox. */
.diagram__svg { width: 100%; height: auto; overflow: visible; }
.diagram__svg--tall { display: none; }

/* Trazas: 1px real a cualquier escala del viewBox. */
.dg-wire path, .dg-rail-stub, .dg-rail-line, .dg-taps path {
  fill: none; stroke-width: 1; vector-effect: non-scaling-stroke;
}
.dg-wire path { stroke: rgba(var(--accent-rgb), .5); }
.dg-head { fill: rgba(var(--accent-rgb), .75); }
.dg-junction circle { fill: var(--accent); }

/* Los nodos van DESPUÉS de las trazas y con relleno opaco: el pulso desaparece
   al entrar y reaparece al salir, como una señal atravesando una etapa. */
.dg-node {
  fill: var(--bg); stroke: rgba(244, 241, 234, .16);
  stroke-width: 1; vector-effect: non-scaling-stroke;
}
.dg-node--core {
  stroke: var(--accent);
  filter: drop-shadow(0 0 14px rgba(var(--accent-rgb), .3));
}

.dg-labels text, .dg-label-core, .dg-subs text, .dg-return,
.dg-rail-label, .dg-taps text {
  font-family: var(--font-mono); text-anchor: middle;
}
.dg-labels text { font-size: 13px; fill: var(--text); letter-spacing: .02em; }
.dg-label-core { font-size: 14px; fill: var(--accent); letter-spacing: .04em; }
.dg-subs text { font-size: 11px; fill: var(--muted); }
.dg-labels--start text, .dg-subs--start text { text-anchor: start; }
.dg-return { font-size: 11px; fill: rgba(var(--accent-rgb), .85); letter-spacing: .08em; }
.dg-knockout { fill: var(--surface); }

/* Riel de observabilidad: bus común con derivaciones punteadas hacia cada
   etapa; el trazo discontinuo lo separa de la ruta de la petición. */
.dg-rail-line { stroke: rgba(244, 241, 234, .28); }
.dg-rail-stub { stroke: rgba(244, 241, 234, .18); stroke-dasharray: 3 4; }
.dg-taps path { stroke: rgba(244, 241, 234, .28); }
.dg-rail-label {
  font-size: 10px; fill: var(--faint); text-anchor: start;
  letter-spacing: .14em; text-transform: uppercase;
}
.dg-taps text {
  font-size: 11px; fill: var(--muted); text-anchor: start; letter-spacing: .06em;
}

/* El viewBox alto (440) se dibuja en ~300px, o sea a escala ~0.7: sin subir el
   cuerpo, las etiquetas caerían a 7px reales. Estos tamaños están calculados
   para llegar a ~12px en pantalla, no para verse bien en el viewBox. */
.diagram__svg--tall .dg-labels text { font-size: 18px; }
.diagram__svg--tall .dg-label-core { font-size: 19px; }
.diagram__svg--tall .dg-subs text { font-size: 15px; }
.diagram__svg--tall .dg-return { font-size: 14px; }
.diagram__svg--tall .dg-rail-label { font-size: 14px; }
.diagram__svg--tall .dg-taps text { font-size: 15px; }

/* El pulso es un guion corto viajando por la traza. `pathLength="1"` normaliza
   la longitud, así el mismo dasharray sirve para las dos rutas (ida y vuelta)
   y para los dos SVG, que miden distinto. */
.dg-pulse path {
  fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: .045 2;
  stroke-dashoffset: .045;
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .diagram[data-revealed] .dg-pulse path {
    opacity: 1;
    animation: dgTrace 5.6s linear infinite;
    filter: drop-shadow(0 0 5px rgba(var(--accent-rgb), .85));
  }
  /* La respuesta arranca a mitad del ciclo: nunca coinciden en pantalla. */
  .diagram[data-revealed] .dg-pulse--res { animation-delay: 2.8s; }
}
/* Sin animación el badge mentiría: no hay ninguna petición viajando. */
@media (prefers-reduced-motion: reduce) {
  .diagram__live { display: none; }
}
/* Pausa larga al final para que el ojo lea el esquema entre pulsos. */
@keyframes dgTrace {
  0%   { stroke-dashoffset: .045; }
  45%  { stroke-dashoffset: -1; }
  100% { stroke-dashoffset: -1; }
}

@media (max-width: 860px) {
  .diagram__svg--wide { display: none; }
  .diagram__svg--tall { display: block; }
}

/* Fila única tipo libro mayor: regla superior compartida y divisores
   verticales entre principios. En pantallas angostas vuelve a apilarse
   con la regla superior por tarjeta. */
.principles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--hairline);
}
.principle {
  position: relative;
  padding: var(--sp-5) var(--sp-5) 0 0;
}
.principle + .principle {
  padding-left: var(--sp-5);
  border-left: 1px solid var(--hairline);
}
.principle::before {
  content: "";
  position: absolute; top: -1px; left: 0; right: 0; height: 2px;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left center;
}
.principle:hover::before { transform: scaleX(1); }
/* Glifos: mismo trazo de 1px que el esquema. Lo que dibuja cada uno —
   evals contra un umbral, un presupuesto con tope, un desvío alrededor de un
   fallo, una traza muestreada — es el principio, no un icono decorativo. */
.principle__glyph {
  display: block; width: 40px; height: 40px; margin-bottom: var(--sp-4);
}
.principle__glyph path, .principle__glyph rect {
  fill: none; stroke: var(--accent); stroke-width: 1.25;
  stroke-linecap: square; vector-effect: non-scaling-stroke;
}
.principle__glyph .pg-dim { stroke: rgba(244, 241, 234, .3); }
.principle__glyph .pg-dash { stroke-dasharray: 3 3; }
.principle__glyph .pg-fill { fill: rgba(var(--accent-rgb), .2); stroke: none; }
.principle__glyph .pg-dot { fill: var(--accent); stroke: none; }

.principle__title { font-size: var(--fs-h3); margin-bottom: var(--sp-3); text-wrap: balance; }
.principle p { font-size: var(--fs-small); }

@media (prefers-reduced-motion: no-preference) {
  .principle::before { transition: transform .5s var(--ease); }
}

@media (max-width: 880px) {
  .principles { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--sp-6); border-top: 0; }
  .principle { padding: var(--sp-4) 0 var(--sp-6); border-top: 1px solid var(--hairline); }
  .principle + .principle { padding-left: 0; border-left: 0; }
}
@media (max-width: 560px) {
  .principles { grid-template-columns: 1fr; }
  .principle { padding-bottom: var(--sp-5); }
}

/* ---------- 13. (Marquee retirado: la cinta de tecnologías vive en el hero,
   sección 8, como .techstrip) ---------- */

/* ---------- 14. About ---------- */
.about__grid {
  display: grid; gap: clamp(var(--sp-6), 5vw, var(--sp-9));
  grid-template-columns: minmax(220px, 320px) minmax(0, 1fr);
  align-items: start;
}
/* El retrato es la única imagen a color de la página: se muestra tal cual,
   con un halo del acento en lugar del antiguo filtro de grises. */
.about__frame {
  border: 1px solid rgba(var(--accent-rgb), .3);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 14px 48px rgba(var(--accent-rgb), .13);
}
.about__frame img { width: 100%; height: auto; }
.about__media:hover .about__frame {
  border-color: rgba(var(--accent-rgb), .55);
  box-shadow: 0 18px 64px rgba(var(--accent-rgb), .22);
}
.about__caption { margin-top: var(--sp-4); }
.about__body { margin: var(--sp-5) 0 var(--sp-6); max-width: 60ch; }

.about__facts {
  display: grid; gap: var(--sp-5);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-bottom: var(--sp-7);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--hairline);
}
.about__fact dt {
  font-family: var(--font-mono); font-size: var(--fs-mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent); margin-bottom: var(--sp-2);
}
.about__fact dd {
  margin: 0;
  font-family: var(--font-display); font-size: 1.1875rem;
  line-height: 1.3; letter-spacing: -.01em; color: var(--text);
}

@media (prefers-reduced-motion: no-preference) {
  .about__frame { transition: box-shadow .3s var(--ease), border-color .3s var(--ease); }
}

@media (max-width: 760px) {
  .about__grid { grid-template-columns: 1fr; }
  .about__media { max-width: 280px; }
  .about__facts { grid-template-columns: 1fr; gap: var(--sp-4); }
}

/* ---------- 15. Contact ---------- */
.contact__grid {
  display: grid; gap: clamp(var(--sp-7), 6vw, var(--sp-9));
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  align-items: start;
}
.contact__direct { margin-top: var(--sp-7); padding-top: var(--sp-5); border-top: 1px solid var(--hairline); }
.contact__directLabel { display: block; color: var(--faint); margin-bottom: var(--sp-4); }
.contact__links { display: grid; gap: var(--sp-3); }
.contact__links a {
  display: inline-block; font-size: var(--fs-small); color: var(--muted);
  border-bottom: 1px solid transparent;
}
.contact__links a:hover { color: var(--accent); border-color: var(--accent); }

.form { display: grid; gap: var(--sp-5); }
.form__row { display: grid; gap: var(--sp-2); }
.form__label { color: var(--faint); }
.form__input {
  background: var(--surface); color: var(--text);
  border: 1px solid rgba(244, 241, 234, .38); border-radius: var(--radius);
  padding: 13px 15px; font-size: var(--fs-small);
}
.form__input:hover { border-color: rgba(244, 241, 234, .26); }
.form__input:focus-visible { border-color: var(--accent); }
.form__textarea { resize: vertical; min-height: 120px; }
.form__submit { justify-self: start; }
.form__submit[disabled] { opacity: .55; cursor: progress; }
.form__status { font-size: var(--fs-small); min-height: 1.5em; }
.form__status[data-state="ok"] { color: var(--live); }
.form__status[data-state="error"] { color: #F87171; }

/* ---------- Toasts ---------- */
/* Debajo del nav (top: --nav-h) para no taparlo; z-index 90 queda por encima
   del resto del contenido pero bajo cualquier overlay futuro a 100+. */
.toast-stack {
  position: fixed; top: calc(var(--nav-h) + var(--sp-4)); right: var(--sp-4);
  z-index: 90; display: grid; gap: var(--sp-2);
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  font-size: var(--fs-small);
  background: var(--raised); color: var(--text);
  border: 1px solid var(--hairline); border-left: 2px solid var(--accent);
  border-radius: var(--radius);
  padding: var(--sp-3) var(--sp-4);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
  max-width: min(340px, calc(100vw - 2 * var(--sp-4)));
}
.toast[data-state="ok"] { border-left-color: var(--live); }
.toast[data-state="error"] { border-left-color: #F87171; }
.toast[data-leaving="true"] { opacity: 0; }

@media (prefers-reduced-motion: no-preference) {
  .toast {
    animation: toastIn .3s var(--ease);
    transition: opacity .25s var(--ease);
  }
}
@keyframes toastIn {
  from { opacity: 0; transform: translateX(16px); }
}

@media (prefers-reduced-motion: no-preference) {
  .contact__links a { transition: color .2s var(--ease), border-color .2s var(--ease); }
  .form__input { transition: border-color .2s var(--ease); }
}

/* ---------- 16. Footer ---------- */
.footer { border-top: 1px solid var(--hairline); padding-block: var(--sp-5); }
.footer__inner { display: flex; justify-content: center; text-align: center; }
.footer .mono { color: var(--faint); }

/* ---------- 17. Utilidades ---------- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: var(--sp-4); top: -100px; z-index: 100;
  background: var(--accent); color: var(--accent-ink);
  padding: var(--sp-3) var(--sp-4); border-radius: var(--radius); font-weight: 600;
}
.skip-link:focus { top: var(--sp-4); }

@media (prefers-reduced-motion: no-preference) {
  .skip-link { transition: top .2s var(--ease); }
}

/* ---------- 18. Modal ---------- */
.modal { position: fixed; inset: 0; z-index: 80; display: grid; place-items: center; padding: var(--sp-4); }
.modal[hidden] { display: none; }
.modal__backdrop { position: absolute; inset: 0; background: rgba(5, 5, 6, .82); backdrop-filter: blur(6px); }

.modal__panel {
  position: relative; z-index: 1;
  width: min(760px, 100%); max-height: 88vh; overflow-y: auto;
  background: var(--raised); border: 1px solid var(--hairline); border-radius: var(--radius);
  padding: clamp(var(--sp-6), 4vw, var(--sp-8));
}
.modal__close {
  position: sticky; top: 0; float: right; margin: calc(var(--sp-4) * -1) calc(var(--sp-4) * -1) 0 0;
  width: 38px; height: 38px; border: 1px solid var(--hairline); border-radius: 50%;
  color: var(--muted); display: grid; place-items: center;
}
.modal__close:hover { color: var(--text); border-color: var(--text); }

.modal__cat { color: var(--accent); }
.modal__title { margin: var(--sp-3) 0 var(--sp-2); font-size: var(--fs-h2); }
.modal__tagline { color: var(--muted); }

.modal__block { margin-top: var(--sp-6); padding-top: var(--sp-5); border-top: 1px solid var(--hairline); }
.modal__label { display: block; color: var(--faint); margin-bottom: var(--sp-3); }
.modal__block p { color: var(--muted); }

.modal__arch { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.modal__archStep {
  font-family: var(--font-mono); font-size: var(--fs-mono); letter-spacing: .1em;
  padding: 7px 11px; border: 1px solid var(--hairline); border-radius: 4px; color: var(--text);
}
.modal__archArrow { color: var(--accent); font-size: .75rem; }

.modal__chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.modal__impact { display: grid; gap: var(--sp-3); }
.modal__impact li { display: flex; gap: var(--sp-3); align-items: baseline; color: var(--text); }
.modal__impact li::before { content: "→"; color: var(--accent); flex: none; }

@media (prefers-reduced-motion: no-preference) {
  .modal__close { transition: color .2s var(--ease), border-color .2s var(--ease); }
  .modal__backdrop { animation: fadeIn .24s var(--ease); }
  .modal__panel { animation: panelIn .24s var(--ease); }
  @keyframes fadeIn { from { opacity: 0; } }
  @keyframes panelIn { from { opacity: 0; transform: scale(.98); } }
}

body[data-modal-open="true"] { overflow: hidden; }
