/* ==========================================================================
   ALCAR STUDIO — hoja principal
   --------------------------------------------------------------------------
   1. Reset y base
   2. Polaridad y utilidades
   3. Fondo del hero: tunel WebGL + capa CRT (confinados al hero)
   4. Nav y menu movil
   5. Hero  ← INTOCABLE: traduccion literal de los style="" originales
   6. Revelados y marquee
   7. Movimiento reducido
   ========================================================================== */

/* ==========================================================================
   1. RESET Y BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  /* El color del documento lo pinta <html>, no <body>: el canvas del tunel va
     a z-index -1 y un fondo propio en <body> lo dejaria invisible. Literal del
     original — cambiarlo apagaria el tunel del hero. */
  background: var(--color-night);
  -webkit-text-size-adjust: 100%;
}

html, body { margin: 0; padding: 0; }

body {
  background: transparent;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: var(--lh-body);
  /* overflow-x SOLO en body: en html corta el scroll del viewport.
     Y `clip` en lugar de `hidden` por precaucion: `hidden` convierte al
     <body> en contenedor de scroll, lo que rompe position:sticky en sus
     descendientes. Comprobado que aqui NO era la causa del carril que se
     despegaba (lo era la caja de contenido del contenedor, ver motion.js),
     pero `clip` recorta igual sin crear contenedor y evita la trampa. */
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
}

img, video, canvas, svg { display: block; max-width: 100%; }
img, video { height: auto; }

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

/* Foco visible en todo control (brief §9). El anillo usa --accent, que ya
   esta calibrado por polaridad: 6.14 sobre noche, 6.21 sobre papel. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: absolute; top: 0; left: 0; z-index: 999;
  padding: 0.75rem 1.25rem;
  background: var(--color-brand); color: var(--color-night);
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: var(--ls-wide);
  transform: translateY(-120%);
  transition: transform var(--dur-micro) var(--ease);
}
.skip-link:focus { transform: translateY(0); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ==========================================================================
   2. POLARIDAD Y UTILIDADES
   ========================================================================== */

/* Toda seccion por debajo del hero pinta su propio fondo opaco: es lo que
   tapa el canvas fijo del tunel al salir de la portada. */
.section {
  position: relative;
  z-index: 1;
  background: var(--bg);
  color: var(--ink);
  padding: var(--space-xl) var(--pad);
}

.wrap { width: min(100%, var(--wrap)); margin-inline: auto; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  color: var(--muted);
}

.section-title {
  margin-top: clamp(2rem, 4vw, 3.25rem);
  transition: opacity var(--dur-reveal) var(--ease),
              transform var(--dur-reveal) var(--ease);
  font-size: var(--step-3);
  font-weight: 400;            /* grande y ligero, no grande y bold (Collins) */
  line-height: var(--lh-snug);
  letter-spacing: -0.02em;
  max-width: 22ch;
}

.section-marker { display: flex; align-items: flex-start; gap: 1.5rem; overflow: hidden; }
.section-marker__num {
  font-family: var(--font-sans); font-weight: 300;
  font-size: clamp(3.5rem, 9vw, 7rem); line-height: 0.78;
  letter-spacing: -0.055em;
  color: var(--accent);
  /* Sube desde debajo de su propia caja, recortado por el overflow del
     padre: el numero de seccion es el elemento de marca mas repetido de la
     pagina y entraba plano. */
  display: inline-block;
  transform: translateY(100%);
  transition: transform 900ms var(--ease);
}
.section-marker.is-visible .section-marker__num { transform: none; }

.section-marker__label {
  opacity: 0;
  transition: opacity 700ms var(--ease) 160ms;
}
.section-marker.is-visible .section-marker__label { opacity: 1; }
.section-marker__label {
  font-family: var(--font-mono); text-transform: uppercase;
  letter-spacing: 0.3em; font-size: 0.66rem; color: var(--muted);
  writing-mode: vertical-rl; transform: rotate(180deg);
  white-space: nowrap; margin-top: 0.35rem;
}

/* ==========================================================================
   3. FONDO DEL HERO — tunel WebGL + capa CRT
   --------------------------------------------------------------------------
   DECISION (brief §4, documentada en DISENO.md §4): los dos se confinan al
   viewport del hero y se desvanecen al salir de el, en vez de acompanar a
   todas las secciones. Con el sistema de polaridad no es una concesion: la
   textura PERTENECE a la polaridad noche, asi que muere donde muere el negro.

   La capa CRT tiene que seguir estando sobre el hero o este dejaria de ser
   indistinguible del original — por eso se desvanece en lugar de borrarse.
   ========================================================================== */

#neural-tunnel {
  position: fixed; inset: 0; z-index: var(--z-tunnel);
  display: block; width: 100%; height: 100%;
  pointer-events: none;
}

#crt-layer {
  position: fixed; inset: 0; z-index: var(--z-crt);
  pointer-events: none;
}

/* --hero-veil lo mueve js/motion.js segun cuanto queda de hero en pantalla:
   1 dentro del hero, 0 fuera. Sin JS se queda en 1 y todo sigue como estaba.

   El monograma 3D entra en el mismo velo, y no solo por coherencia: es una
   pieza CROMADA, pensada para leerse sobre negro. Al pasar el cuerpo a papel
   se quedaba como un borron gris de bajo contraste flotando sobre la crema.
   El original tampoco lo anclaba bien dentro de la barra —queda por debajo,
   verificado— asi que no se esta perdiendo un anclaje que funcionase.
   Dentro del hero el velo vale 1: §4 no se toca. */
/* [data-hero3d] SALE del velo: el cliente quiere el monograma cromado
   visible tambien fuera del hero (24-08-2026). El tunel y la capa CRT si
   siguen muriendo con la portada. */
#neural-tunnel, #crt-layer {
  transition: opacity var(--dur-ui) linear;
}

#crt-layer .scanlines {
  position: absolute; inset: 0;
  background: repeating-linear-gradient(to bottom, rgba(0,0,0,.28) 0px, rgba(0,0,0,.28) 1px, transparent 1px, transparent 3px);
  mix-blend-mode: multiply; opacity: .5;
}
#crt-layer .sweep {
  position: absolute; inset: 0;
  background: repeating-linear-gradient(to bottom, rgba(255,255,255,.05) 0px, transparent 2px, transparent 5px);
  mix-blend-mode: screen; opacity: .4;
}
#crt-layer .vignette {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse 78% 78% at 50% 50%, transparent 58%, rgba(0,0,0,.55) 100%);
}
#crt-layer .glow {
  position: absolute; inset: 0;
  box-shadow: inset 0 0 140px rgba(0,0,0,.6);
}
#crt-layer .flicker {
  position: absolute; inset: 0; background: #fff; opacity: 0;
  animation: crtFlicker 6.5s infinite steps(1);
}
@keyframes crtFlicker {
  0%,96%,100% { opacity: 0; }
  97% { opacity: .025; }
  98% { opacity: 0; }
  99% { opacity: .015; }
}

/* ==========================================================================
   3b. PRELOADER
   --------------------------------------------------------------------------
   RESTAURADO a peticion del cliente (24-08-2026). Yo lo habia quitado por
   rendimiento — retrasa el LCP en la web que vende LCP — y la decision del
   cliente lo devuelve. Queda anotado en PROYECTO.md como enmienda.
   CSS literal del original, sin tocar valores.
   ========================================================================== */

/* Preloader — copiado literal de la demo alcar-preloadyhero.html.
     El logo sube a clamp(5.5rem, 20vw, 15rem), el pase baja a 2.4s y el
     relleno de la barra se queda solo en 3.6s para cerrar mas pausado.
     La "A" y la "S" dejan de ser color plano: cromado real por
     background-clip:text con un unico pico de luz que pasa por el trazo
     una vez hacia el final (al-a-glint / al-s-glint), y lo mismo para el
     relleno magenta (al-bar-glint). Colores del asset de marca, no los
     tokens del sitio. */
  .preload-ui {
    position:fixed; inset:0; z-index:200;
    display:flex; align-items:center; justify-content:center;
    background: radial-gradient(circle at 50% 48%, rgba(255,51,204,.055), transparent 55%), #0b0b0c;
    will-change:opacity, filter;
    transition: opacity 460ms cubic-bezier(0.55,0,0.2,1), filter 460ms cubic-bezier(0.55,0,0.2,1);
  }
  .preload-ui.is-out { opacity:0; filter:blur(16px); pointer-events:none; }

  .al-logo {
    position:relative;
    font-family:'Arial Black', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight:900; line-height:1;
    font-size:clamp(5.5rem, 20vw, 15rem);
    width:5.625em; height:2.875em;
  }
  .al-lcar {
    position:absolute; left:50%; top:50%;
    transform: translate(0,-50%) translate(-1.5222em,-0.6444em);
    font-size:.5625em; letter-spacing:.3em; white-space:nowrap; color:#F2F0ED;
    animation: al-lcar-trim 2.4s cubic-bezier(0.55,0,0.2,1) both;
  }
  .al-studio {
    position:absolute; left:50%; top:50%;
    transform: translate(-50%,-50%) translate(4em,1.5122em);
    font-size:.25625em; letter-spacing:.42em; padding-left:.42em; white-space:nowrap;
    color:#FF3ACF;
    animation: al-studio-trim 2.4s linear both;
  }
  .al-a, .al-s {
    position:absolute; left:50%; top:50%; z-index:2;
    background-image: linear-gradient(100deg, #9a9a9a 0%, #c4c4c4 22%, #ffffff 50%, #c4c4c4 78%, #9a9a9a 100%);
    background-size: 420% 100%;
    background-position: 145% 50%;
    -webkit-background-clip: text; background-clip: text; color: transparent;
    -webkit-text-fill-color: transparent;
  }
  .al-a { animation: al-a-trim 2.4s cubic-bezier(0.5,0,0.2,1) both, al-a-glint 2.4s linear both; }
  .al-s {
    transform: translate(-50%,-50%) translate(.413em,0);
    animation: al-write-trim 2.4s cubic-bezier(0.5,0,0.25,1) both, al-s-glint 2.4s linear both;
  }
  .al-bar-wrap {
    position:absolute; left:50%; top:50%; z-index:3;
    margin:-.425em 0 0 -.025em; width:.05em; height:.85em;
    animation: al-bar-trim 2.4s cubic-bezier(0.3,0.9,0.2,1) both;
  }
  .al-track { position:absolute; inset:0; background:#2a2a2e; }
  .al-fill-bar {
    position:absolute; left:0; right:0; bottom:0;
    background-image: linear-gradient(100deg, #ff33cc 0%, #ff33cc 40%, #ffb8ec 50%, #ff33cc 60%, #ff33cc 100%);
    background-size: 220% 100%;
    background-position: 100% 50%;
    animation: al-fill-trim 3.6s linear both, al-bar-glint 3.6s linear both;
  }
  @keyframes al-a-trim {
    0%          { opacity:0; transform: translate(-50%,-50%) translate(-1.225em,-0.363em) scale(0.561); }
    4%, 18%     { opacity:1; transform: translate(-50%,-50%) translate(-1.225em,-0.363em) scale(0.561); }
    30%, 100%   { opacity:1; transform: translate(-50%,-50%) translate(-0.438em,0) scale(1); }
  }
  @keyframes al-lcar-trim { 0%{opacity:0;} 6%,18%{opacity:1;} 27%,100%{opacity:0;} }
  @keyframes al-studio-trim { 0%,8%{opacity:0;} 13%,18%{opacity:1;} 27%,100%{opacity:0;} }
  @keyframes al-write-trim { 0%,30%{clip-path:inset(0 100% 0 0);opacity:1;} 40%,100%{clip-path:inset(0 0 0 0);opacity:1;} }
  @keyframes al-bar-trim { 0%,40%{transform:rotate(15deg) scale(1,0);opacity:1;} 46%,100%{transform:rotate(15deg) scale(1,1);opacity:1;} }
  @keyframes al-fill-trim { 0%,46%{height:0;} 100%{height:100%;} }
  @keyframes al-a-glint { 0%,55%{background-position:145% 50%;} 82%{background-position:-45% 50%;} 100%{background-position:-45% 50%;} }
  @keyframes al-s-glint { 0%,60%{background-position:145% 50%;} 87%{background-position:-45% 50%;} 100%{background-position:-45% 50%;} }
  @keyframes al-bar-glint { 0%,65%{background-position:100% 50%;} 92%{background-position:-60% 50%;} 100%{background-position:-60% 50%;} }

  /* ---------- Ancla del logo en el nav ----------
     El monograma 3D se ancla aqui al hacer scroll. Necesita ocupar sitio
     de verdad: con un ancla de 1x1 px el logo quedaba centrado justo
     donde empieza "ALCAR STUDIO" y se le montaba encima. 44x28 reserva
     su hueco y el gap del wordmark hace el resto. */
  /* 19-09-2026: de 44x28 a 54x36 a peticion del cliente. La caja crece con
     la pieza: el ancho reserva su hueco —si no, se monta sobre la A— y el
     alto es lo que la centra, porque con align-self:center la fila de la
     marca se dimensiona por el elemento mas alto y el wordmark se centra
     respecto a el. El 36 va tambien en js/hero-3d.js, que es quien escala
     el monograma al anclarlo. */
  #logo-dock { width: 54px; height: 36px; align-self: center; }
  /* Por debajo de 900px no hay logo 3D (js/hero-3d.js se va a su
     respaldo estatico), asi que el hueco reservado sobra. */
  /* 04-09-2026: ya no se oculta — el 3D corre tambien en movil. */

  /* ---------- CTA de presupuesto del hero ----------
     Los atributos style-hover del export son papel mojado: ni support.js
     ni enhance.js los leen. El hover va aqui, en CSS de verdad. */
  /* CTA primario: magenta macizo, el mismo tratamiento que el sitio ya da
     al boton de presupuesto en el menu movil y en la seccion de contacto.
     En version outline se perdia contra el tunel — parecia deshabilitado.
     El "VER EL TRABAJO" se queda en outline, y asi hay jerarquia: uno
     primario y otro secundario en lugar de dos pildoras iguales. */
  .hero-quote {
    cursor: inherit;
    box-shadow: 0 4px 18px rgba(255, 51, 204, 0.28);
    transition: background 240ms cubic-bezier(0.16, 1, 0.3, 1),
                box-shadow 240ms cubic-bezier(0.16, 1, 0.3, 1),
                transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .hero-quote-arrow { transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1); }
  .hero-quote:hover, .hero-quote:focus-visible {
    background: #ff5cd6;
    box-shadow: 0 8px 30px rgba(255, 51, 204, 0.45);
    transform: translateY(-2px);
  }
  .hero-quote:hover .hero-quote-arrow { transform: translateX(4px); }
  .hero-quote:active { transform: translateY(0); }
  /* Por debajo de 620px chocaria con el indicador SCROLL, y el menu movil
     ya lleva su propio boton de presupuesto. */
  @media (max-width: 620px) { .hero-quote { display: none !important; } }
  @media (prefers-reduced-motion: reduce) {
    .hero-quote, .hero-quote-arrow { transition: none !important; }
    .hero-quote span[aria-hidden="true"] { animation: none !important; }
  }

  /* ---------- CRT ----------
     Copiado literal de la demo alcar-preloadyhero.html. A pantalla
     completa sobre todo el documento, como alli: z-index 50 lo deja por
     encima del contenido y del logo cromado (20), y por debajo del nav
     (106), el menu movil, el modal de briefing, el caso de estudio (500)
     y el cursor propio, que siguen leyendose limpios. pointer-events:none
     para que no se coma ningun clic. */

/* ==========================================================================
   3c. CURSOR PROPIO
   --------------------------------------------------------------------------
   Restaurado a peticion del cliente. Punto por defecto, anillo sobre lo
   interactivo. Colores por token, no fijos.

   `cursor: none` va SOLO donde hay puntero fino. En el original estaba en el
   <body> a secas y habia que deshacerlo despues con !important para tactil;
   asi no se pone nunca donde no toca.
   ========================================================================== */

@media (hover: hover) and (pointer: fine) {
  body { cursor: none; }
  a, button, [role="button"], summary, label, input, textarea, select { cursor: none; }
}

.cursor {
  position: fixed; top: 0; left: 0; z-index: var(--z-cursor);
  pointer-events: none;
  width: 56px; height: 56px;
  transform: translate(-50%, -50%);
  will-change: transform;
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
.cursor.is-visible { opacity: 1; }
.cursor::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: var(--color-paper);
  border: 1px solid transparent;
  transform: scale(0.143);
  /* La curva de apertura es elastica a proposito: es el unico sitio del
     sitio donde el movimiento se pasa de la raya, y ahi se nota bien. */
  transition: transform 180ms cubic-bezier(0.34, 1.56, 0.64, 1),
              background 180ms var(--ease),
              border-color 180ms var(--ease);
}
.cursor.is-active::before {
  transform: scale(1);
  background: transparent;
  border-color: var(--color-paper);
}

@media (hover: none), (pointer: coarse) {
  .cursor { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .cursor { display: none !important; }
  body, a, button, [role="button"], summary, label, input, textarea, select { cursor: auto !important; }
}

/* ==========================================================================
   4. NAV Y MENU MOVIL
   ========================================================================== */

.scroll-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 2px;
  z-index: var(--z-progress); pointer-events: none;
  background: var(--color-brand);
  transform: scaleX(0); transform-origin: 0 50%;
  will-change: transform;
}

.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-nav);
  display: flex; justify-content: space-between; align-items: center;
  padding: 1.6rem var(--pad);
  /* difference SOLO arriba, sobre el hero, que es donde funciona y donde
     §4 exige que nada cambie. En cuanto se scrollea, .is-condensed lo apaga
     y la barra pasa a color solido por polaridad — ver mas abajo. */
  mix-blend-mode: difference;
  /* Mismo motivo que en .hero: reproducir el contexto heredado original. */
  font-size: 1rem;
  line-height: normal;
  color: var(--color-paper);
}

.nav__brand {
  display: flex; align-items: baseline; gap: 0.9rem;
  font-family: var(--font-mono); text-transform: uppercase;
  font-size: 0.85rem; letter-spacing: 0.02em;
  color: var(--color-paper);
}
.nav-section {
  font-size: 0.66rem; letter-spacing: 0.16em;
  color: var(--color-muted-on-night); white-space: nowrap;
  opacity: 0; transform: translateY(3px);
  transition: opacity var(--dur-ui) var(--ease), transform var(--dur-ui) var(--ease);
}
.nav-section.is-visible { opacity: 1; transform: none; }

.nav-links {
  display: flex; gap: 2rem;
  font-family: var(--font-mono); text-transform: uppercase;
  letter-spacing: var(--ls-wide); font-size: 0.72rem;
  color: var(--color-paper);
}
.nav-links a {
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-micro) var(--ease);
}
.nav-links a:hover,
.nav-links a[aria-current="true"] { border-bottom-color: var(--color-brand); }

.lang-toggle { display: flex; align-items: center; gap: 0.45rem; }
.lang-toggle a, .lang-toggle span {
  font: inherit; letter-spacing: inherit; text-transform: uppercase;
  color: var(--color-muted-on-night);
}
.lang-toggle a[aria-current="true"] { color: var(--color-paper); }
.lang-toggle .sep { color: var(--color-muted-on-night); }

.nav__status {
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-mono); text-transform: uppercase;
  font-size: 0.7rem; letter-spacing: 0.1em; color: var(--color-paper);
}
.nav__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--color-brand);
  animation: alcarPulse 2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
@keyframes alcarPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

.nav-burger {
  display: none; align-items: center; justify-content: center;
  flex-direction: column; gap: 5px; width: 44px; height: 44px;
  background: transparent; border: none; cursor: pointer; padding: 0;
}
.nav-burger span {
  display: block; width: 22px; height: 1.5px;
  background: var(--color-paper);
  transition: transform var(--dur-ui) var(--ease), opacity var(--dur-ui) var(--ease);
}
.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); }

.mobile-menu {
  position: fixed; inset: 0; z-index: var(--z-menu);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 6rem var(--pad) 3rem;
  background: var(--color-night);
  /* `visibility` NO se interpola: al ponerla en la lista de transiciones con
     una duracion (como hacia el original) tarda 340ms en pasar a `visible`, y
     durante ese rato .focus() sobre los enlaces del panel no hace nada — en
     silencio. Resultado: al abrir el menu con teclado el foco se quedaba
     atrapado en la hamburguesa y el menu era inalcanzable.
     El patron correcto es un retardo en lugar de una duracion: al abrir salta
     a visible en 0s, al cerrar espera a que termine el fundido. */
  visibility: hidden; opacity: 0;
  transition: opacity 340ms var(--ease), visibility 0s linear 340ms;
}
.mobile-menu.is-open {
  visibility: visible; opacity: 1;
  transition: opacity 340ms var(--ease), visibility 0s linear 0s;
}
.mobile-menu-item {
  display: flex; align-items: baseline; gap: 1rem;
  padding: 0.9rem 0;
  border-bottom: 1px solid rgba(246, 243, 238, 0.12);
  font-size: 2rem; color: var(--color-paper);
  opacity: 0; transform: translateY(14px);
  transition: opacity 420ms var(--ease), transform 420ms var(--ease);
}
.mobile-menu.is-open .mobile-menu-item { opacity: 1; transform: none; }
.mobile-menu-item__num {
  font-family: var(--font-mono); font-weight: 400; font-size: 0.7rem;
  letter-spacing: 0.14em; color: var(--color-brand);
}

.mobile-menu-foot {
  display: flex; flex-direction: column; gap: 1.5rem;
  color: var(--color-paper);
}
.mobile-menu-foot > a[href^="mailto"] {
  font-family: var(--font-mono); font-size: 1rem;
  border-bottom: 1px solid rgba(246, 243, 238, 0.12);
  padding-bottom: 0.6rem; width: fit-content;
}
.mobile-menu-legal {
  font-family: var(--font-mono); text-transform: uppercase;
  letter-spacing: 0.14em; font-size: 0.62rem;
  color: var(--color-muted-on-night);
}

/* CTA primario reutilizable: mismo tratamiento que .hero-quote, pero en
   flujo normal. El unico boton macizo del sistema. */
.btn-brand {
  width: 100%; min-height: 56px;
  border: none; border-radius: var(--radius-pill);
  background: var(--color-brand); color: var(--color-night);
  font-family: var(--font-sans); font-weight: 700; font-size: 1.05rem;
  cursor: pointer;
  transition: background var(--dur-micro) var(--ease);
}
.btn-brand:hover { background: var(--color-brand-lift); }

/* La nav se condensa al salir del hero y deja de mezclar.
   -----------------------------------------------------------------------
   `difference` solo vale sobre el hero. El propio codigo original ya lo
   sabia — "se vuelve ilegible sobre el video del hero y sobre el panel
   magenta" — y al scrollear pasaba a barra solida con blur. Aqui se hace lo
   mismo.

   COLOR FIJO. Antes la barra adoptaba la polaridad de la seccion de debajo
   (js/motion.js le copiaba el data-polarity), asi que en Contacto se volvia
   magenta. Fuera por peticion del cliente: "no quiero que el header cambie
   de color". Ahora la nav lleva data-polarity="night" escrito en el HTML y
   no se lo toca nadie — el color no depende del scroll ni de que corra el JS.

   Ojo si alguien quita ese atributo: los tokens por defecto de :root son los
   de PAPEL, asi que la barra saldria clara y el punto magenta perderia
   contraste. La polaridad de la nav tiene que ser explicita. */
.nav {
  transition: padding 320ms var(--ease), background 320ms var(--ease),
              border-color 320ms var(--ease);
  border-bottom: 1px solid transparent;
}

.nav.is-condensed {
  mix-blend-mode: normal;
  padding-top: 1.05rem; padding-bottom: 1.05rem;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  border-bottom-color: var(--rule);
  color: var(--ink);
}
.nav.is-condensed .nav__brand,
.nav.is-condensed .nav-links,
.nav.is-condensed .nav__status { color: var(--ink); }
.nav.is-condensed .nav-section,
.nav.is-condensed .lang-toggle a,
.nav.is-condensed .lang-toggle .sep { color: var(--muted); }
.nav.is-condensed .lang-toggle a[aria-current="true"] { color: var(--ink); }
.nav.is-condensed .nav-burger span { background: var(--ink); }

/* Hueco que el monograma 3D ocupa al anclarse. Sin reservarlo, la pieza se
   monta encima de "ALCAR STUDIO". Valor literal del original. */
/* 19-09-2026: de 44x28 a 54x36 a peticion del cliente. La caja tiene que
   crecer con la pieza: el ancho le reserva el sitio —si no, se monta sobre
   la A— y el alto es lo que la centra, porque con align-self:center la fila
   de la marca se dimensiona por el elemento mas alto y el wordmark se
   centra respecto a el. El 36 va tambien en js/hero-3d.js, que es quien
   escala el monograma al anclarlo: los dos tienen que decir lo mismo. */
#logo-dock { width: 54px; height: 36px; align-self: center; }

/* Cuatro cortes distintos, todos literales del original. Sin ellos la nav
   se rompe en movil: la etiqueta de estado empujaba "ALCAR STUDIO" a dos
   lineas. */
@media (max-width: 999px) {
  /* El monograma no viaja a la nav en movil (se desvanece con el hero), asi
     que el hueco reservado para el ancla vuelve a sobrar. */
  #logo-dock { display: none !important; }
}
/* 999 y no 900. Medido de 20 en 20 px: a 901 la nav de escritorio ya no cabe
   —"ALCAR STUDIO" se parte en dos lineas, la nav crece de 80 a 92px de alto
   y los enlaces arrancan pegados a la marca, 0px de separacion— y no vuelve
   a respirar hasta ~980. El menu de hamburguesa cubre ahora toda esa franja:
   a 1000px la separacion ya es de 21px, y de 31 a 1024. */
@media (max-width: 999px) {
  .nav-links { display: none; }
  .nav-burger { display: flex; }
}
/* Un solo corte para lo que la nav suelta en pantallas estrechas, en vez
   de los tres heredados (780 / 620 / 560) que no respondian a un sistema. */
@media (max-width: 620px) {
  .nav-section { display: none !important; }
  .nav-status-label { display: none !important; }
}

/* ── Pie ───────────────────────────────────────────────────────────────────
   Sin LinkedIn: el estudio no tiene perfil, asi que el enlace se elimina en
   lugar de reapuntarse a la home generica de la red (brief §10.8). */
.site-footer {
  position: relative; z-index: 1;
  background: var(--bg); color: var(--ink);
  padding: var(--space-l) var(--pad);
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: var(--ls-wide);
}
.site-footer .wrap {
  display: flex; flex-wrap: wrap; gap: var(--space-s) var(--space-l);
  justify-content: space-between; align-items: center;
}
.site-footer a { border-bottom: 1px solid transparent; transition: border-color var(--dur-micro) var(--ease); }
.site-footer a:hover { border-bottom-color: var(--accent); }

/* ==========================================================================
   5. HERO — INTOCABLE (brief §4)
   --------------------------------------------------------------------------
   Traduccion literal de los style="" originales a clases y tokens. Ni un
   valor cambia: los numeros de aqui son los mismos que habia en linea.
   Verificado por comparacion de pixeles contra la captura previa a 1440x900
   y 375x812 — ver _hero-check/.

   El hero NO usa la escala tipografica del sistema (--step-*) a proposito:
   conserva su propio clamp(3.2rem, 11vw, 9.5rem).
   ========================================================================== */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex; flex-direction: column;
  /* 'safe center' y no 'center' a secas: cuando el contenido no cabe —un
     portatil de 640 o 700px de alto—, un centrado normal lo desborda por
     ARRIBA tambien, y la cejilla acababa debajo de la cabecera fija
     (medido: top 0 con la nav llegando a 80). Con 'safe' el desbordamiento
     solo va hacia abajo, que es donde hay scroll. */
  justify-content: safe center;
  /* El aire vertical es simetrico a proposito: asi en pantallas altas el
     bloque sigue centrado exactamente donde estaba y no se mueve nada de
     lo ya aprobado. En pantallas bajas es lo que separa la cejilla de la
     cabecera y el boton del indicador SCROLL. */
  padding: clamp(7rem, 12vh, 8.5rem) var(--pad);
  overflow: hidden;
  color: var(--color-paper);
  /* El <body> original no declaraba font-size ni line-height: heredaba los
     16px y el `normal` del navegador. El sistema nuevo si los fija, y eso
     se colaba dentro del hero inflando alturas (la barra de metadatos
     pasaba de 40 a 47 px) y subiendo el bloque 3 px. Aqui se restaura el
     contexto heredado exacto del original. Verificado por geometria. */
  font-size: 1rem;
  line-height: normal;
}

.hero-video-scrim {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(12,12,13,0.58) 0%,
    rgba(12,12,13,0.66) 45%,
    rgba(12,12,13,0.95) 100%);
}
.hero-noise {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.hero-bloom {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(circle at 78% 18%, color-mix(in srgb, var(--color-brand) 22%, transparent), transparent 45%);
}

/* Solo los hijos EN FLUJO se elevan sobre las tres capas decorativas.
   .scroll-cue y .hero-quote van posicionados en absoluto y llevan su z-index
   en su propia regla: meterlos aqui les pisaba el position y el boton salia
   estirado a todo el ancho como item de flex. */
.hero-eyebrow,
.hero-title,
.hero-sub,
.hero-meta,
.hero-cta { position: relative; z-index: 1; }

.hero-eyebrow {
  font-family: var(--font-mono); text-transform: uppercase;
  letter-spacing: var(--ls-wide); font-size: 0.72rem;
  color: var(--color-muted-on-night);
  margin: 0 0 1.75rem; overflow: hidden;
}
.hero-eyebrow > span { display: inline-block; }

.hero-title {
  margin: 0;
  font-size: clamp(3.2rem, 11vw, 9.5rem);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
}
.hero-title em {
  color: var(--color-brand);
  font-style: italic;
  font-weight: 300;
}

.hero-sub {
  margin: 2.25rem 0 0; max-width: 40ch;
  font-family: var(--font-mono); font-size: 0.95rem; line-height: var(--lh-body);
  color: var(--color-muted-on-night); overflow: hidden;
}
.hero-sub > span { display: block; }

.hero-meta {
  display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem; margin-top: 3.5rem;
  font-family: var(--font-mono); text-transform: uppercase;
  letter-spacing: var(--ls-wide); font-size: 0.7rem;
  color: var(--color-muted-on-night);
}
.hero-meta strong {
  display: block; color: var(--color-paper);
  font-size: 0.95rem; margin-bottom: 0.3rem;
  font-family: var(--font-sans); font-weight: 600;
}

.hero-cta {
  display: inline-flex; width: fit-content; will-change: transform;
  margin-top: 2.75rem; padding: 0.95rem 1.8rem;
  border: 1px solid rgba(246, 243, 238, 0.12);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono); font-size: 0.72rem;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-paper);
  transition: border-color var(--dur-micro) var(--ease),
              background var(--dur-micro) var(--ease);
}
.hero-cta:hover {
  border-color: var(--color-brand);
  background: color-mix(in srgb, var(--color-brand) 8%, transparent);
}

.scroll-cue {
  position: absolute; bottom: 2.5rem; left: var(--pad); z-index: 1;
  display: flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.1em;
  color: var(--color-muted-on-night);
}
.scroll-cue__bar {
  width: 1px; height: 28px; background: var(--color-muted-on-night);
  position: relative; overflow: hidden;
}
.scroll-cue__bar::after {
  content: ""; position: absolute; top: -100%; left: 0;
  width: 100%; height: 100%; background: var(--color-paper);
  animation: alcarScrollBar 1.8s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
@keyframes alcarScrollBar { to { top: 100%; } }

/* CTA primario: magenta macizo. En version outline se perdia contra el
   tunel y parecia deshabilitado. El "VER EL TRABAJO" se queda en outline,
   y asi hay jerarquia — uno primario y otro secundario — en lugar de dos
   pildoras iguales. La sombra magenta NO es decorativa: es lo que despega
   el boton del tunel, y quitarla cambia la captura. */
.hero-quote {
  position: absolute; bottom: 2.5rem; right: var(--pad); z-index: 1;
  display: inline-flex; align-items: center; gap: 0.7rem;
  padding: 1rem 1.75rem; border: none; border-radius: var(--radius-pill);
  background: var(--color-brand);
  font-family: var(--font-mono); font-weight: 700; text-transform: uppercase;
  font-size: 0.75rem; letter-spacing: 0.1em;
  color: var(--color-night);
  -webkit-appearance: none; appearance: none; cursor: pointer;
  box-shadow: 0 4px 18px color-mix(in srgb, var(--color-brand) 28%, transparent);
  transition: background 240ms var(--ease),
              box-shadow 240ms var(--ease),
              transform 240ms var(--ease);
}
.hero-quote-arrow { transition: transform 240ms var(--ease); }
.hero-quote:hover, .hero-quote:focus-visible {
  background: var(--color-brand-lift);
  box-shadow: 0 8px 30px color-mix(in srgb, var(--color-brand) 45%, transparent);
  transform: translateY(-2px);
}
.hero-quote:hover .hero-quote-arrow { transform: translateX(4px); }
.hero-quote:active { transform: translateY(0); }

/* Por debajo de 620px el boton chocaria con el indicador SCROLL, y el menu
   movil ya lleva su propio boton de presupuesto. Ademas el hero abre hueco
   al pie para ese indicador. Los dos valores son literales del original:
   sin el padding-bottom, el bloque de copy quedaba 52 px mas abajo. */
@media (max-width: 620px) {
  .hero-quote { display: none !important; }
  .hero { padding-bottom: 6.5rem; }
}

/* ==========================================================================
   5b. SERVICIOS
   --------------------------------------------------------------------------
   Tres filas. Numero, titulo y UNA linea. Nada mas: el detalle vive en los
   proyectos, que es donde se puede comprobar.
   ========================================================================== */

.section-lead {
  margin-top: var(--space-m);
  max-width: 52ch;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  line-height: 1.75;
  color: var(--muted);
}

.services { margin-top: clamp(3rem, 7vw, 5rem); border-top: 1px solid var(--rule); }

.service {
  display: grid;
  gap: 0.4rem 2rem;
  padding: clamp(1.75rem, 4vw, 3rem) 0;
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 800px) {
  .service {
    grid-template-columns: 4rem minmax(0, 1fr) minmax(0, 1fr);
    align-items: baseline;
    gap: 0 clamp(2rem, 5vw, 4rem);
  }
}

.service__num {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  color: var(--accent);
}

.service__title {
  font-size: var(--step-2);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.service__lead {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  line-height: 1.7;
  color: var(--muted);
  max-width: 42ch;
}

/* ── El gesto de Servicios (19.2) ─────────────────────────────────────────
   Queja del cliente: "mucho texto de pronto sin motion". Entraba con el
   .reveal generico — las tres filas subiendo 18px a la vez — y no decia nada.

   Ahora la fila se DESPLIEGA por partes: una linea magenta barre el ancho y
   se apaga sin dejar residuo, el numero entra desde la izquierda, el titulo
   asoma palabra a palabra y la entradilla llega la ultima. Cuatro tiempos en
   vez de uno.

   La fila anula el .reveal generico: se queda solo con la clase .is-visible
   que le pone el observador, y el movimiento lo ponen sus partes. */
.service.reveal { opacity: 1; transform: none; transition: none; }

.service { position: relative; isolation: isolate; }

/* El barrido. Se apaga al final: tres reglas magenta permanentes serian mas
   magenta del que esta pagina se puede gastar (REFERENCIAS.md §1.1). */
.service::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--accent);
  transform: scaleX(0); transform-origin: 0 50%;
  pointer-events: none;
}
.service.is-visible::after {
  animation: svcBarrido 1500ms var(--ease) both;
  animation-delay: var(--reveal-delay, 0ms);
}
@keyframes svcBarrido {
  0%   { transform: scaleX(0); opacity: 1; }
  62%  { transform: scaleX(1); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}

.service__num {
  display: inline-block;
  opacity: 0; transform: translateX(-14px);
  transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  transition-delay: calc(var(--reveal-delay, 0ms) + 120ms);
}
.service.is-visible .service__num { opacity: 1; transform: none; }

.service__lead {
  opacity: 0; transform: translateY(10px);
  transition: opacity 700ms var(--ease), transform 700ms var(--ease);
  transition-delay: calc(var(--reveal-delay, 0ms) + 420ms);
}
.service.is-visible .service__lead { opacity: 1; transform: none; }

/* Con puntero fino, un lavado magenta cruza la fila al pasar por encima. Va
   detras del texto (z-index -1 dentro del contexto que crea `isolation`). */
@media (hover: hover) and (pointer: fine) {
  .service::before {
    content: '';
    position: absolute; inset: 0; z-index: -1;
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, var(--accent) 8%, transparent) 50%,
        transparent 100%);
    opacity: 0; transform: translateX(-25%);
    transition: opacity 260ms var(--ease), transform 780ms var(--ease);
    pointer-events: none;
  }
  .service:hover::before { opacity: 1; transform: translateX(25%); }
}

@media (prefers-reduced-motion: reduce) {
  .service::after { animation: none !important; opacity: 0; }
  .service__num, .service__lead {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .service::before { display: none; }
}

/* ==========================================================================
   5c. TRABAJO — patron Obys
   --------------------------------------------------------------------------
   Indice tipografico a la izquierda, pieza activa al centro entre corchetes.
   El foco se marca con un GLIFO, no con un borde: tipografia haciendo de
   interfaz, que es lo que se robaba de la referencia (REFERENCIAS §1.9).

   Los items del indice son <button> de verdad, asi que el teclado y el lector
   de pantalla funcionan sin anadir nada. Sin JavaScript no hay estado activo:
   se ven las cinco fichas apiladas, que sigue siendo una lista legible.
   ========================================================================== */

/* --------------------------------------------------------------------------
   REDISENO. Lo que habia era una escena de tres columnas del mismo peso
   —indice, imagen, texto— con la imagen a 293x370 px: el 8,4% de una ventana
   de 1440x900, medido. En la unica seccion cuyo argumento es «entra y
   rompelo», el trabajo era lo mas pequeno de la pantalla, flotando en 470 px
   de aire vertical y abrazado por dos corchetes decorativos.

   La placa pasa a ser lo que manda: apaisada 16:10 —la proporcion en la que
   se ven las webs de verdad, no el 4:5 vertical de antes—, sangrando por el
   borde izquierdo, con el nombre encima y el detalle debajo en una columna
   estrecha. Del 8,4% al 34% del area de la ventana.
   -------------------------------------------------------------------------- */

.work { padding-bottom: 0; }

.work-pin {
  margin-top: clamp(2rem, 4.5vw, 3.5rem);
  /* SIN padding horizontal: .section ya mete var(--pad) a los lados y aqui se
     estaba metiendo otra vez. El carril quedaba a 128 px del borde en una
     ventana de 1440 mientras el titulo de la seccion estaba a 64, y la
     sangria de la placa se comia el sobrante en vez de pasar del borde.
     Medido: la ficha empezaba en x=128 con 1184 de ancho. */
  padding: 0 0 var(--space-xl);
}

.work-stage {
  display: grid;
  gap: clamp(1.4rem, 3vw, 2.4rem);
  align-content: center;
}

/* ── El carril de proyectos ─────────────────────────────────────────────── */
.work-index {
  display: flex; flex-wrap: wrap;
  gap: clamp(0.9rem, 2.2vw, 2.2rem);
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--rule);
}
.work-index li { position: relative; }
.work-index__item {
  display: flex; align-items: baseline; gap: 0.5rem;
  background: none; border: 0; padding: 0.2rem 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--muted);
  cursor: pointer;
  text-align: left;
  transition: color var(--dur-micro) var(--ease);
}
.work-index__n { color: var(--rule); transition: color var(--dur-micro) var(--ease); }
.work-index__item:hover { color: var(--ink); }
.work-index__item:hover .work-index__n { color: var(--muted); }
.work-index__item.is-active { color: var(--ink); }
.work-index__item.is-active .work-index__n { color: var(--accent); }
/* La marca del activo cuelga del filete, no del texto: el carril se lee como
   una regla graduada y el proyecto en curso como la muesca. */
.work-index__item.is-active::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -0.9rem;
  height: 1px; background: var(--accent);
}

/* ── La placa ───────────────────────────────────────────────────────────── */
.work-media { display: block; }
.work-frames { position: relative; }

.work-slide {
  display: grid;
  gap: clamp(1.1rem, 2.4vw, 2rem);
  opacity: 0;
  transition: opacity var(--dur-ui) var(--ease);
}
/* Con el carril anclado el relevo lo escribe js/motion.js fotograma a
   fotograma. Cualquier transicion aqui pelea con el scroll: cada valor nuevo
   arranca una interpolacion que el fotograma siguiente reemplaza, y la ficha
   va siempre por detras del dedo. */
.work-pin.is-pinned .work-slide { transition: none; }
.work-slide + .work-slide { position: absolute; inset: 0; }
.work-slide.is-active { opacity: 1; }
.work-slide[inert] { pointer-events: none; }

@media (min-width: 1000px) {
  .work-slide {
    grid-template-columns: minmax(0, 1.9fr) minmax(260px, 0.85fr);
    /* El detalle a la altura de la placa, no colgando del fondo de la escena. */
    align-items: center;
    gap: clamp(1.5rem, 3vw, 3rem);
  }
  /* La placa SANGRA por el borde izquierdo. El titulo de seccion sigue
     alineado a la retícula; la placa se sale. Esa tension es la que hace que
     el carril se sienta continuar mas alla de la pantalla en vez de estar
     posado en una caja. */
  .work-pin.is-pinned .work-slide__col {
    margin-left: calc(var(--pad) * -1);
    /* La anchura EXPLICITA es lo que hace que la sangria sea predecible. Sin
       ella la caja se dimensiona desde `aspect-ratio` y el techo de altura
       —la altura mandaba sobre la anchura—, la placa encogia y el margen
       negativo solo la devolvia al borde del padding en vez de pasarlo.
       Medido: x=64 con el margen puesto, que es exactamente no sangrar. */
    inline-size: calc(100% + var(--pad));
  }
  /* El pie se separa del borde de la pantalla lo justo para no pegarse. */
  .work-pin.is-pinned .work-slide__stamp { padding-left: clamp(0.6rem, 1.4vw, 1.2rem); }
}

.work-slide__plate {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-s);
  background: var(--color-night);
  aspect-ratio: 16 / 10;
  /* La anchura manda y la altura tiene techo. Sin `inline-size: 100%` la caja
     se dimensiona desde la proporcion y el techo —la altura mandaba— y la
     placa encogia sola: medido, 720x450 en vez de 941x450.

     Con el techo puesto la proporcion se rompe y la captura se recorta por
     los lados, que es lo que se quiere: en una ventana baja se pierde imagen
     antes que empujar el detalle fuera de la pantalla. */
  inline-size: 100%;
  max-block-size: 62svh;
  /* El contenedor contra el que el recorrido mide su desborde (100cqh). */
  container-type: size;
}
.work-slide__media { display: block; width: 100%; height: 100%; position: relative; }

/* LAS DOS CAPAS DE LA PLACA.

   Debajo el POSTER —la portada, 16:10— y encima el RECORRIDO, que es la web
   entera a lo largo. El poster va en el HTML y es lo que se ve sin
   JavaScript; el recorrido lo pide js/motion.js al llegar al proyecto y se
   funde encima cuando ha cargado. Si no carga, o si no hay JavaScript, queda
   el poster: exactamente lo que habia antes. */
.work-slide__poster,
.work-slide__fly {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.work-slide__poster { object-position: 50% 0%; }

/* EL RECORRIDO. js/motion.js escribe --fly, de 0 (arriba del todo de la web)
   a 1 (abajo del todo), siguiendo el scroll.

   Va en `object-position` y no en un `translate` sobre la imagen: la captura
   mide 1400x2625 y con `cover` en un marco apaisado se escala al ancho y
   desborda a lo alto. El porcentaje de object-position reparte ese desborde,
   asi que 0% y 100% son exactamente el principio y el final de la web sin
   tener que saber cuanto mide el marco. Un translate en porcentaje se
   resuelve contra la imagen, no contra el marco, y habria que medir. */
.work-slide__fly {
  /* EL RECORRIDO. --fly va de 0 (arriba del todo de la web) a 1 (abajo del
     todo). El porcentaje de object-position reparte el desborde que deja
     `cover` con una captura mas alta que el marco, asi que 0% y 100% son
     exactamente el principio y el final sin tener que medir el marco.

     Lo pase a `translate` con unidades de contenedor pensando que
     object-position repintaria de mas. Medido, no cuesta nada: recorriendo,
     25 fotogramas de 60 pasan de 16,7 ms CON la capa y 30 SIN ella, o sea
     ruido de vsync. Se queda la version simple. */
  /* CORREGIDO: iba en object-position y eso es lo que daba los tirones.

     El comentario de arriba medía la capa equivocada. object-position no lo
     puede resolver el compositor: cada valor nuevo obliga a rehacer el
     encuadre de la imagen y a repintar la placa entera en el hilo principal,
     sesenta veces por segundo, con una captura de 1400x2625. Mientras la
     galeria recorre, el hilo principal es tambien el que atiende el scroll
     de la pagina — de ahi que empiece a tirones justo al abrirse.

     translate SI lo lleva el compositor: se sube a su propia capa y el
     recorrido deja de tocar el hilo del scroll.

     Las unidades de contenedor evitan tener que medir nada en JavaScript.
     La imagen va al ancho del marco con la altura libre, asi que desborda
     por abajo; 100cqh - 100% es exactamente ese desborde —alto del marco
     menos alto de la imagen, en negativo—, y --fly de 0 a 1 lo recorre de
     principio a fin de la web. Vale para cualquier proporcion de captura y
     para el techo de 62svh/56svh que recorta la placa. */
  inset: 0 0 auto 0;
  block-size: auto;
  translate: 0 calc(var(--fly, 0) * (100cqh - 100%));
  opacity: 0;
  transition: opacity 500ms var(--ease);
}
.work-slide__fly.is-listo { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  /* Sin movimiento no hay recorrido que valga: se queda la portada. */
  .work-slide__fly { display: none; }
}

/* EL RELEVO: LA PLACA BARRE, EL TEXTO FUNDE.

   js/motion.js escribe cuatro variables por ficha: --tx (opacidad del texto),
   --dy (su desplazamiento) y --ct/--cb (el recorte de la placa por arriba y
   por abajo). La ficha entera se queda a opacidad 1 mientras esta en juego.

   Los dos recortes son complementarios —lo que la saliente pierde por abajo es
   exactamente lo que la entrante gana—, asi que la suma cubre el encuadre en
   todo momento. Medido: 54,1 + 45,9 = 100. Con las dos placas fundiendose, en
   pleno relevo estaban al 0,07 y al 0,21 y la pantalla se quedaba en negro.

   Sin `round`: el radio ya lo pone border-radius con overflow hidden. Con
   `round` se redondeaba tambien el filo que sube, y un filo de barrido con las
   esquinas redondeadas se lee como una pastilla creciendo. */
.work-pin.is-pinned .work-slide__plate {
  clip-path: inset(var(--ct, 0%) 0 var(--cb, 0%) 0);
}
.work-pin.is-pinned .work-slide__stamp,
.work-pin.is-pinned .work-slide__info {
  opacity: var(--tx, 1);
  translate: 0 var(--dy, 0px);
}

/* Sin `clip-path: inset()` no hay barrido posible: se vuelve al fundido de la
   placa, que es lo que habia. Peor, pero nunca dos placas a la vez. */
@supports not (clip-path: inset(0 0 0 0)) {
  .work-pin.is-pinned .work-slide__plate { opacity: var(--tx, 1); }
}

@media (prefers-reduced-motion: reduce) {
  .work-pin.is-pinned .work-slide__plate { clip-path: none; }
  .work-pin.is-pinned .work-slide__stamp,
  .work-pin.is-pinned .work-slide__info { translate: none; }
}

/* EL PIE DE PLACA. El nombre NO va encima de la imagen.

   Lo probe encima, con velo y todo, y solo montado se ve por que no funciona:
   las cinco capturas son portadas de web y todas traen ya su propia
   composicion tipografica grande. Sobre la de Deriva el nombre caia justo
   encima de su barra de cifras. Cualquier cosa que metas dentro del encuadre
   compite con lo que el encuadre ya dice.

   Debajo y al mismo borde sangrado, el nombre es un pie: no tapa nada, no
   necesita velo —y por tanto no hay riesgo de contraste sobre una foto
   ajena—, y la tipografia a sangre contra el borde izquierdo de la pantalla
   es el gesto editorial que la seccion pedia. */
.work-slide__stamp {
  display: grid;
  gap: 0.3rem;
  padding-top: clamp(0.7rem, 1.6vw, 1.1rem);
}

.work-slide__meta {
  display: flex; align-items: baseline; gap: 0.7rem;
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.1em;
  color: color-mix(in srgb, var(--color-paper) 72%, transparent);
}
.work-slide__num { color: var(--accent); }

/* EL NOMBRE VA EN LA GROTESCA, NO EN LA SERIF.

   Lo monte primero en Fraunces italic a escala de display, encima de la
   placa. Solo se ve el problema montado: las cinco capturas son portadas de
   web y TODAS traen ya su propia tipografia editorial grande —«Your marina is
   wherever you stand», «Cuidado de la piel sin prisa», «Disenamos marcas que
   no se olvidan»—. Dos serifs grandes en el mismo encuadre no jerarquizan,
   se estorban.

   En grotesca condensada y caja alta el nombre deja de competir: se lee como
   un sello sobre la imagen, no como otro titular. Y es la voz del resto del
   sitio, que era lo que el cliente echaba en falta.

   Consecuencia: Fraunces se queda sin uso y sale del proyecto (17 KB y una
   familia menos). Era la unica cosa que la pedia. */
.work-slide__title {
  margin-top: 0.5rem;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: clamp(2.1rem, 4.6vw, 4.1rem);
  line-height: 0.9;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  color: var(--color-paper);
}

.work-slide__desc {
  margin-top: 0.8rem;
  font-size: var(--step-0);
  line-height: 1.5;
  max-width: 34ch;
}

/* El detalle, plegado. <details> nativo: se abre con teclado, funciona sin
   JavaScript y mantiene el caso dentro del HTML (brief §5.02). */
.work-slide__case { margin-top: var(--space-s); }
.work-slide__case > summary {
  list-style: none; cursor: pointer;
  padding: 0.6rem 0;
  display: flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted);
  transition: color var(--dur-micro) var(--ease);
  max-width: 34ch;
}
.work-slide__case > summary::-webkit-details-marker { display: none; }
.work-slide__case > summary::after {
  content: "+"; margin-left: auto;
  font-size: 1.1em; line-height: 1; color: var(--accent);
  transition: transform var(--dur-ui) var(--ease);
}
.work-slide__case[open] > summary::after { transform: rotate(45deg); }

/* Los cinco <details> abrian de golpe: la ficha pasaba de 365 a 428px en un
   fotograma, y encima dentro de un carril anclado, que no acompana el salto.
   Medido antes de tocarlo.

   Se anima con `interpolate-size` y `::details-content`, que es lo que
   permite transicionar hacia `auto` sin medir alturas con JavaScript. Donde
   no este soportado abre de golpe, exactamente como abria antes: riesgo cero. */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }

  .work-slide__case::details-content {
    block-size: 0;
    overflow: clip;
    opacity: 0;
    transition: block-size var(--dur-ui) var(--ease),
                opacity var(--dur-ui) var(--ease),
                content-visibility var(--dur-ui) allow-discrete;
  }
  .work-slide__case[open]::details-content {
    block-size: auto;
    opacity: 1;
  }

  /* Con reduced-motion no se anima la ALTURA —eso es recorrido— pero se deja
     el fundido, que es el puente sin desplazamiento. */
  @media (prefers-reduced-motion: reduce) {
    .work-slide__case::details-content {
      block-size: auto;
      transition: opacity 180ms var(--ease),
                  content-visibility 180ms allow-discrete;
    }
  }
}
.work-slide__case > summary:hover { color: var(--ink); }
.work-slide__case ul { display: grid; gap: 0.5rem; max-width: 40ch; padding-bottom: 0.5rem; }
.work-slide__case li {
  position: relative; padding-left: 1.4rem;
  font-size: 0.92rem; line-height: 1.55; color: var(--muted);
}
.work-slide__case li::before {
  content: ""; position: absolute; left: 0; top: 0.6em;
  width: 8px; height: 1px; background: var(--muted);
}
.work-slide__case code {
  font-family: var(--font-mono); font-size: 0.88em;
  padding: 0.1em 0.35em; border-radius: 3px;
  background: var(--surface);
}

.work-slide__link {
  display: inline-flex; align-items: center; gap: 0.5rem;
  margin-top: var(--space-s);
  padding: 0.75rem 1.3rem;
  border: 1px solid var(--rule); border-radius: var(--radius-pill);
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.08em;
  transition: border-color var(--dur-micro) var(--ease),
              background var(--dur-micro) var(--ease),
              scale 140ms var(--ease);
}
.work-slide__link:hover,
.work-slide__link:focus-visible {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--color-brand) 10%, transparent);
}

.work-hint {
  display: flex; align-items: center; gap: 0.9rem;
  margin-top: clamp(1.5rem, 4vw, 3rem);
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted);
}
.work-hint__bar {
  display: block; width: clamp(80px, 18vw, 220px); height: 2px;
  background: var(--rule); overflow: hidden;
}
.work-hint__bar > span {
  display: block; height: 100%; width: 100%;
  background: var(--accent);
  transform: scaleX(0); transform-origin: 0 50%;
}

/* Anclado: el scroll vertical recorre el indice. Solo escritorio con puntero
   fino; en movil se apilan las fichas y se lee en vertical, sin secuestrar
   el scroll de la pagina. */
.work-pin.is-pinned {
  position: sticky; top: 0;
  height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
  padding-top: 4.5rem; padding-bottom: 1rem;
}

/* Sin JS (o en movil) no hay ficha activa: se muestran todas apiladas en
   flujo normal, que es la lista legible que exige el brief §5.02.

   El carril de proyectos AHORA SE QUEDA tambien apilado. Antes se ocultaba
   porque, siendo una columna de nombres al lado de la escena, sin anclaje no
   tenia a donde llevarte. Convertido en carril horizontal cabe arriba y
   sigue sirviendo: js/motion.js ya trata el caso sin anclar llevandote a la
   ficha con scrollIntoView. */
.work-pin:not(.is-pinned) .work-frames { display: grid; gap: clamp(3rem, 8vw, 5rem); }
.work-pin:not(.is-pinned) .work-slide { opacity: 1; position: static; }
.work-pin:not(.is-pinned) .work-hint { display: none; }
/* Apilado el carril NO se pega arriba. Lo probe pegajoso y en 390 px el
   resultado es el que se ve en cuanto lo montas: los cinco nombres envuelven
   a dos lineas, la barra de navegacion tambien esta pegada, y las dos se
   montan encima de la primera ficha. Quieto arriba de la seccion sigue
   sirviendo de indice y no tapa nada. */
.work-pin:not(.is-pinned) .work-index { margin-bottom: clamp(1.5rem, 5vw, 2.5rem); }

/* Cada ficha apilada entra desde abajo al aparecer. */
.work-pin:not(.is-pinned) .work-slide__col,
.work-pin:not(.is-pinned) .work-slide__info {
  opacity: 0; transform: translateY(22px);
  transition: opacity var(--dur-reveal) var(--ease),
              transform var(--dur-reveal) var(--ease);
}
.work-pin:not(.is-pinned) .work-slide.is-in .work-slide__col { opacity: 1; transform: none; }
.work-pin:not(.is-pinned) .work-slide.is-in .work-slide__info {
  opacity: 1; transform: none;
  transition-delay: 90ms;
}

@media (prefers-reduced-motion: reduce) {
  .work-pin:not(.is-pinned) .work-slide__col,
  .work-pin:not(.is-pinned) .work-slide__info { opacity: 1 !important; transform: none !important; }
}

/* ==========================================================================
   5e. EL ESTUDIO
   --------------------------------------------------------------------------
   El manifiesto viejo era un parrafo denso con tres argumentos apilados. Aqui
   van separados y numerados: se leen de uno en uno y cada uno pega solo.
   ========================================================================== */

.studio__title { max-width: 20ch; }

.studio__points {
  margin-top: clamp(3rem, 7vw, 5rem);
  display: grid;
  gap: 0;
  border-top: 1px solid var(--rule);
}
.studio__point {
  display: grid;
  gap: 0.5rem 2rem;
  padding: clamp(1.5rem, 3.5vw, 2.5rem) 0;
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 800px) {
  .studio__point { grid-template-columns: 4rem minmax(0, 1fr); align-items: baseline; }
}
.studio__mark {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  color: var(--accent);
}
.studio__point p {
  font-size: var(--step-1);
  line-height: 1.4;
  letter-spacing: -0.01em;
  max-width: 46ch;
  text-wrap: pretty;
}

/* El cierre: la invitacion a abrir el inspector es LA frase del sitio —
   es lo que este rediseno entero viene a poder sostener. */
.studio__proof {
  margin-top: clamp(2.5rem, 6vw, 4rem);
  font-family: var(--font-mono);
  font-size: var(--step-0);
  line-height: 1.7;
  color: var(--muted);
}
.studio__proof-em { color: var(--accent); }

/* ==========================================================================
   5e-bis. EL PANEL DE INSTRUMENTOS
   --------------------------------------------------------------------------
   El objeto de la pagina. Sustituye al orbe del manifiesto, que estaba
   apagado bajo 900px (brief §2). Aqui no hay corte por ancho: el panel es el
   mismo objeto en el movil y en el escritorio, solo cambia como se apila.

   Tres colores propios, derivados del acento, para que js/instrumento.js no
   tenga que conocer ningun valor: lee estas tres variables y dibuja. Cambiar
   --color-brand sigue propagando hasta la traza del osciloscopio.
   ========================================================================== */

.inst {
  --inst-trace: var(--accent);
  --inst-fill:  color-mix(in srgb, var(--accent) 11%, transparent);
  --inst-guide: color-mix(in srgb, var(--ink) 22%, transparent);

  margin-top: clamp(2.5rem, 6vw, 4rem);
  border: 1px solid var(--rule);
  border-radius: var(--radius-m);
  background: var(--surface);
  overflow: hidden;   /* la linea de barrido no se sale de la caja */
}

.inst__head {
  padding: clamp(1.5rem, 3.5vw, 2.25rem) clamp(1.25rem, 3.5vw, 2.25rem);
  border-bottom: 1px solid var(--rule);
}
.inst__title {
  font-size: var(--step-1);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.015em;
  max-width: 24ch;
}
.inst__note {
  margin-top: 0.7rem;
  max-width: 58ch;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.7;
  color: var(--muted);
  text-wrap: pretty;
}

/* ── El osciloscopio ─────────────────────────────────────────────────────
   Lo destapa js/instrumento.js quitando [hidden]. Es instrumental, no
   contenido: sin JS no aparece y no deja hueco muerto. */
.inst__scope {
  position: relative;
  height: clamp(190px, 24vw, 260px);
  border-bottom: 1px solid var(--rule);
  background:
    linear-gradient(to right, var(--rule) 1px, transparent 1px)
      0 3.4rem / 12.5% calc(100% - 3.4rem) repeat-x,
    transparent;
}
.inst__scope[hidden] { display: none; }

/* El lienzo arranca por debajo de la franja de lectura: superpuestos, la
   traza pasaba por encima del numero de fotogramas y no se leia ninguno. */
.inst__canvas {
  position: absolute; inset: 3.4rem 0 0 0;
  width: 100%; height: calc(100% - 3.4rem);
  display: block;
}

.inst__scan {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg,
      transparent 0%,
      var(--inst-fill) 46%,
      var(--inst-trace) 50%,
      var(--inst-fill) 54%,
      transparent 100%);
  opacity: 0.2;
  /* Solo transform: se compone en el hilo del compositor y no repinta. */
  animation: instScan 6s linear infinite;
}
@keyframes instScan {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

.inst__fps {
  position: absolute; top: 0; left: 0;
  display: flex; align-items: baseline; gap: 0.5rem;
  padding: clamp(0.85rem, 2vw, 1.15rem) clamp(1.25rem, 3.5vw, 2.25rem);
}
.inst__fps-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--step-2);
  line-height: 1;
  color: var(--ink);
}
.inst__fps-unit {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted);
}

/* El reto. Es la parte que convierte la grafica en objeto: se puede probar. */
.inst__dare {
  position: absolute; right: 0; top: 0;
  padding: clamp(0.85rem, 2vw, 1.15rem) clamp(1.25rem, 3.5vw, 2.25rem);
  font-family: var(--font-mono);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent);
}

/* ── Las cinco cifras ────────────────────────────────────────────────────
   Estas SI son contenido: van escritas en el HTML con medidas reales y el JS
   solo las sustituye por las de la sesion del visitante. */
.inst__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.inst__cell {
  padding: clamp(1.25rem, 3vw, 1.75rem) clamp(1.25rem, 3.5vw, 2.25rem);
  border-top: 1px solid var(--rule);
  border-right: 1px solid var(--rule);
}
/* Sin borde colgando al final de cada fila. */
.inst__cell:nth-child(2n) { border-right: 0; }
.inst__cell--lead { grid-column: 1 / -1; border-top: 0; border-right: 0; }

@media (min-width: 860px) {
  .inst__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .inst__cell:nth-child(2n) { border-right: 1px solid var(--rule); }
  .inst__cell:last-child { border-right: 0; }

  /* La celda de cabecera ocupa el ancho entero, asi que el numero y su
     explicacion van uno al lado del otro: apilados dejaban medio panel
     vacio. */
  .inst__cell--lead .inst__read {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    align-items: center;
    gap: clamp(1.5rem, 4vw, 3.5rem);
  }
  .inst__cell--lead .inst__foot { margin-top: 0; max-width: 52ch; }
}

.inst__label {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted);
}
.inst__read { margin: 0.6rem 0 0; line-height: 1; }
.inst__num,
.inst__unit {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.inst__num { font-size: var(--step-2); color: var(--ink); }
.inst__unit { font-size: var(--step-0); color: var(--muted); margin-left: 0.25rem; }

.inst__cell--lead .inst__num {
  font-size: var(--step-4);
  color: var(--accent);
  display: inline-block;
}
.inst__cell--lead .inst__label { color: var(--ink); }

.inst__foot {
  display: block;
  margin-top: 0.75rem;
  max-width: 34ch;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.65;
  color: var(--muted);
  text-wrap: pretty;
}
.inst__cell--lead .inst__foot { max-width: 46ch; }

/* El cero no se puede contar de 0 a 0, asi que se estampa: entra grande y
   se asienta. Lo dispara js/instrumento.js con .is-medido. */
.inst.is-medido .inst__cell--lead .inst__num {
  animation: instSello 620ms var(--ease) both;
}
@keyframes instSello {
  from { transform: scale(1.35); opacity: 0; filter: blur(6px); }
  to   { transform: none;        opacity: 1; filter: none; }
}

/* ── Movil ───────────────────────────────────────────────────────────────
   Nada se apaga ni se recorta: el objeto que sustituye al orbe TIENE que
   estar entero aqui (brief §2). Solo se reordena.

   A dos columnas los pies de cada cifra caian a una palabra por linea. A
   una columna se leen, aunque la seccion salga mas alta. */
@media (max-width: 620px) {
  .inst__grid { grid-template-columns: minmax(0, 1fr); }
  .inst__cell { border-right: 0; }

  /* El reto se baja a la esquina inferior: arriba, junto a los fotogramas,
     no cabe en 375px. El lienzo le deja el hueco. */
  .inst__dare {
    top: auto; right: auto; bottom: 0; left: 0;
    font-size: 0.6rem; letter-spacing: 0.1em;
  }
  .inst__canvas {
    inset: 3.4rem 0 2.3rem 0;
    height: calc(100% - 5.7rem);
  }
  .inst__scope {
    background-size: 25% calc(100% - 5.7rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .inst__scan { display: none; }
  .inst.is-medido .inst__cell--lead .inst__num { animation: none; }
}

/* ==========================================================================
   5f. CONTACTO
   --------------------------------------------------------------------------
   Polaridad magenta a sangre: el bloque que el brief §5.05 decia que
   funcionaba y podia sobrevivir. Con el sistema de tokens deja de ser una
   excepcion de color y pasa a ser la tercera polaridad — tinta #0c0c0d sobre
   #ff33cc, 6,14 de contraste.
   ========================================================================== */

.contact__title {
  margin-top: clamp(2rem, 4vw, 3.25rem);
  font-size: var(--step-3);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.03em;
  max-width: 18ch;
}

.contact__lead {
  margin-top: var(--space-m);
  max-width: 46ch;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  line-height: 1.75;
  color: var(--muted);
}

/* ── La banda magenta (19.1) ──────────────────────────────────────────────
   Queja del cliente: "demasiado todo rosa, es demasiado estatica".

   El magenta era el fondo de la seccion entera. Ahora es una banda a sangre
   que envuelve SOLO lo accionable — el boton y el correo — y la seccion
   vuelve a noche. La superficie magenta baja de una pantalla completa a una
   franja, y el color recupera el trabajo que tenia: senalar donde se pulsa.

   Sigue siendo la tercera polaridad, no una excepcion: la banda lleva su
   data-polarity="brand" y no hay una sola regla de color nueva.

   Lo estatico se arregla con el barrido: la banda entra de izquierda a
   derecha con clip-path, que se compone sin repintar. */
.contact__band {
  margin-top: clamp(2.5rem, 6vw, 4rem);
  margin-inline: calc(var(--pad) * -1);
  padding: clamp(2rem, 5vw, 3.25rem) var(--pad);
  background: var(--bg);
  color: var(--ink);
  clip-path: inset(0 100% 0 0);
  transition: clip-path 1100ms var(--ease);
}
.contact__band.is-visible { clip-path: inset(0 0 0 0); }

.contact__band-tag {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
}

.contact__actions {
  margin-top: clamp(1.25rem, 3vw, 1.75rem);
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 1rem 2rem;
}

@media (prefers-reduced-motion: reduce) {
  .contact__band { clip-path: none; transition: none; }
}

/* Dentro de la banda el CTA no puede ser magenta: se invierte a tinta
   solida. Los tokens ya lo resuelven, la regla es literal del original. */
.contact__cta {
  display: inline-flex; align-items: center; gap: 0.7rem;
  padding: 1.1rem 2rem;
  border: none; border-radius: var(--radius-pill);
  background: var(--color-night); color: var(--color-paper);
  font-family: var(--font-mono); font-weight: 700;
  text-transform: uppercase; font-size: 0.78rem; letter-spacing: 0.1em;
  cursor: pointer;
  transition: transform var(--dur-micro) var(--ease),
              box-shadow var(--dur-micro) var(--ease),
              scale 140ms var(--ease);
  box-shadow: 0 4px 18px rgba(12, 12, 13, 0.22);
}
.contact__cta:hover, .contact__cta:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 10px 30px rgba(12, 12, 13, 0.32);
}
.contact__cta span { transition: transform var(--dur-micro) var(--ease); }
.contact__cta:hover span { transform: translateX(4px); }

.contact__email {
  font-family: var(--font-mono);
  font-size: var(--step-0);
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 35%, transparent);
  padding-bottom: 2px;
  transition: border-color var(--dur-micro) var(--ease);
}
.contact__email:hover, .contact__email:focus-visible { border-bottom-color: var(--ink); }

.contact__facts {
  margin-top: clamp(2.5rem, 6vw, 4rem);
  display: grid; gap: clamp(1.5rem, 3vw, 2.5rem);
  border-top: 1px solid var(--rule);
  padding-top: clamp(1.75rem, 4vw, 2.5rem);
}
@media (min-width: 700px) {
  .contact__facts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.contact__facts li { display: grid; gap: 0.4rem; align-content: start; }
.contact__facts strong {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--step-2); line-height: 1;
  letter-spacing: -0.02em;
}
.contact__facts span {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.65;
  color: var(--muted);
  max-width: 30ch;
}

/* ==========================================================================
   5d. MODAL DE BRIEFING
   --------------------------------------------------------------------------
   Reescrito con clases y tokens. Traia 48 atributos style="" en linea del
   sitio viejo — la ultima deuda de la migracion §7 — y con ellos tres
   residuos muertos: data-i18n* (los leia i18n.js, eliminado), style-hover
   (atributo del exportador que no leia nadie) y data-lenis-prevent.

   `visibility` con RETARDO, no interpolada: con una duracion tarda 320ms en
   volverse visible y .focus() sobre los campos no hace nada durante ese rato,
   dejando el modal inalcanzable con teclado. Mismo caso que el menu movil.
   ========================================================================== */

.briefing {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center;
  padding: clamp(1rem, 4vw, 2.5rem);
  visibility: hidden; opacity: 0;
  transition: opacity 320ms var(--ease), visibility 0s linear 320ms;
}
.briefing.is-open,
/* :target es la via SIN JavaScript. Los disparadores son <a href="#briefing">,
   asi que sin JS el navegador salta al hash y esto abre el modal; con JS,
   briefing.js hace preventDefault() y manda la clase, y el hash nunca cambia.
   Sin esto el formulario era literalmente inalcanzable sin JavaScript, que es
   lo que exige el brief §10.2. */
.briefing:target {
  visibility: visible; opacity: 1;
  transition: opacity 320ms var(--ease), visibility 0s linear 0s;
}
.briefing:target .briefing-panel { transform: none; }
.briefing__scrim {
  position: absolute; inset: 0;
  background: rgba(6, 6, 7, 0.82);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.briefing-panel {
  position: relative;
  width: 100%; max-width: 720px; max-height: 92svh;
  overflow-y: auto; overscroll-behavior: contain;
  padding: clamp(1.75rem, 4vw, 3rem);
  border: 1px solid rgba(246, 243, 238, 0.12);
  border-radius: var(--radius-m);
  background: #101012;
  color: var(--color-paper);
  transform: translateY(18px) scale(0.985);
  transition: transform 420ms var(--ease);
}
.briefing.is-open .briefing-panel { transform: none; }

.briefing__close {
  text-decoration: none;
  position: absolute; top: 1rem; right: 1rem;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border: 1px solid rgba(246, 243, 238, 0.16);
  border-radius: 50%;
  background: transparent; color: var(--color-muted-on-night);
  cursor: pointer; font-size: 0.9rem;
  transition: border-color var(--dur-micro) var(--ease), color var(--dur-micro) var(--ease);
}
.briefing__close:hover, .briefing__close:focus-visible {
  border-color: var(--color-brand); color: var(--color-brand);
}

.brief-honeypot { position: absolute; left: -9999px; }

.brief-head { margin-bottom: clamp(1.75rem, 4vw, 2.5rem); }
.brief-head__tag {
  font-family: var(--font-mono); font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.2em;
  color: var(--color-brand);
}
.brief-head__title {
  margin-top: 0.75rem;
  font-size: var(--step-2); font-weight: 400;
  line-height: 1.1; letter-spacing: -0.02em;
}
.brief-head__intro {
  margin-top: 0.9rem; max-width: 46ch;
  font-family: var(--font-mono); font-size: 0.85rem; line-height: 1.7;
  color: var(--color-muted-on-night);
}

.brief-field { display: grid; gap: 0.5rem; margin-bottom: 1.25rem; }
.brief-field--group { border: 0; padding: 0; margin: 0 0 1.25rem; }
.brief-field__label {
  font-family: var(--font-mono); font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--color-muted-on-night);
  padding: 0;
}

.brief-input {
  width: 100%;
  padding: 0.85rem 1rem;
  border: 1px solid rgba(246, 243, 238, 0.14);
  border-radius: var(--radius-s);
  background: rgba(246, 243, 238, 0.04);
  color: var(--color-paper);
  font-family: var(--font-sans); font-size: 0.95rem;
  transition: border-color var(--dur-micro) var(--ease), background var(--dur-micro) var(--ease);
}
.brief-input::placeholder { color: rgba(148, 145, 140, 0.75); }
.brief-input:hover { border-color: rgba(246, 243, 238, 0.26); }
.brief-input:focus {
  outline: none;
  border-color: var(--color-brand);
  background: rgba(246, 243, 238, 0.06);
}
.brief-input:focus-visible { outline: 2px solid var(--color-brand); outline-offset: 2px; }
textarea.brief-input { resize: vertical; min-height: 7rem; }
/* LA LISTA DESPLEGABLE LA PINTA EL SISTEMA, NO ESTA HOJA.

   El desplegable de Plazo salia con fondo blanco y las opciones en gris
   claro —ilegibles— porque el menu nativo no lo alcanza el CSS del
   documento: lo dibuja el sistema operativo, y por defecto lo hace en claro.
   Lo unico que decide su polaridad es color-scheme, asi que el campo declara
   que es oscuro y el menu sale oscuro con el.

   Las opciones llevan ademas color de fondo y de texto explicitos: en Windows
   y en Linux el menu si respeta esos dos, y sin ellos el texto quedaba al
   gris heredado sobre el fondo del sistema. */
select.brief-input {
  appearance: none; cursor: pointer;
  color-scheme: dark;
}
select.brief-input option {
  background: var(--color-night);
  color: var(--color-paper);
}

.brief-row { display: grid; gap: 0 1rem; }
@media (min-width: 620px) { .brief-row { grid-template-columns: 1fr 1fr; } }

.brief-budget { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 0.6rem; }
.brief-radio { position: relative; display: inline-flex; align-items: center; cursor: pointer; }
/* El radio nativo se queda para el teclado y el lector: solo se esconde a la
   vista, y su estado se pinta sobre el span. */
.brief-radio input {
  position: absolute; opacity: 0; width: 100%; height: 100%;
  margin: 0; cursor: pointer;
}
.brief-radio span {
  padding: 0.55rem 1rem;
  border: 1px solid rgba(246, 243, 238, 0.14);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono); font-size: 0.78rem;
  color: var(--color-muted-on-night);
  transition: border-color var(--dur-micro) var(--ease),
              color var(--dur-micro) var(--ease),
              background var(--dur-micro) var(--ease);
}
.brief-radio:hover span { border-color: rgba(246, 243, 238, 0.3); color: var(--color-paper); }
.brief-radio input:checked + span {
  border-color: var(--color-brand);
  color: var(--color-night);
  background: var(--color-brand);
}
.brief-radio input:focus-visible + span { outline: 2px solid var(--color-brand); outline-offset: 3px; }

.brief-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem;
  margin-top: clamp(1.5rem, 3vw, 2rem);
}
.brief-submit {
  padding: 1rem 2rem;
  border: none; border-radius: var(--radius-pill);
  background: var(--color-brand); color: var(--color-night);
  font-family: var(--font-mono); font-weight: 700;
  text-transform: uppercase; font-size: 0.78rem; letter-spacing: 0.1em;
  cursor: pointer;
  box-shadow: 0 4px 18px color-mix(in srgb, var(--color-brand) 28%, transparent);
  transition: background var(--dur-micro) var(--ease),
              transform var(--dur-micro) var(--ease),
              box-shadow var(--dur-micro) var(--ease),
              scale 140ms var(--ease);
}
.brief-submit:hover, .brief-submit:focus-visible {
  background: var(--color-brand-lift);
  transform: translateY(-2px);
  box-shadow: 0 8px 28px color-mix(in srgb, var(--color-brand) 40%, transparent);
}
.brief-submit[disabled] { opacity: 0.6; cursor: progress; transform: none; }

.brief-status { font-family: var(--font-mono); font-size: 0.78rem; color: var(--color-muted-on-night); }

.briefing__close-btn {
  margin-top: clamp(1.5rem, 3vw, 2rem);
  padding: 0.85rem 1.6rem;
  border: 1px solid rgba(246, 243, 238, 0.16);
  border-radius: var(--radius-pill);
  background: transparent; color: var(--color-paper);
  font-family: var(--font-mono); font-size: 0.75rem;
  text-transform: uppercase; letter-spacing: 0.1em;
  cursor: pointer;
  transition: border-color var(--dur-micro) var(--ease), color var(--dur-micro) var(--ease),
              scale 140ms var(--ease);
}
.briefing__close-btn:hover, .briefing__close-btn:focus-visible {
  border-color: var(--color-brand); color: var(--color-brand);
}

#briefing-done[hidden], #briefing-form[hidden] { display: none !important; }

/* El envio del briefing era un corte seco: `hidden` al formulario y `hidden`
   fuera a la confirmacion, las dos cosas en el mismo fotograma. Es el momento
   con mas carga emocional de la pagina —alguien acaba de mandarte su
   negocio— y era lo unico sin un solo gesto.

   Dos tiempos: primero se va el formulario, despues entra la confirmacion.
   Los encadena js/briefing.js. */
.briefing-panel.is-done .brief-form {
  opacity: 0;
  translate: 0 -8px;
  pointer-events: none;
  transition: opacity 200ms var(--ease), translate 200ms var(--ease);
}
.brief-done {
  opacity: 0;
  translate: 0 12px;
  transition: opacity var(--dur-ui) var(--ease), translate var(--dur-ui) var(--ease);
}
.briefing-panel.is-done-in .brief-done { opacity: 1; translate: 0 0; }

@media (prefers-reduced-motion: reduce) {
  .briefing-panel.is-done .brief-form,
  .brief-done { transition-duration: 1ms; translate: 0 0; }
}

/* El mensaje de fallo aparecia de golpe, con su enlace de rescate. Solo la
   opacidad: el texto entra en el DOM al instante, asi que el role="status"
   lo sigue anunciando sin retraso. */
.brief-status { opacity: 0; transition: opacity var(--dur-micro) var(--ease); }
.brief-status:not(:empty) { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .briefing, .briefing-panel { transition: none !important; }
  .briefing-panel { transform: none !important; }
}

/* ==========================================================================
   5g. 404
   --------------------------------------------------------------------------
   Reutiliza el hero entero: la pagina lleva #hero, asi que el tunel WebGL, el
   velo y la entrada caracter a caracter de js/motion.js funcionan sin escribir
   una linea nueva de JavaScript. La unica pieza propia es la fila de salidas.

   Bilingue en la misma pagina: Netlify sirve un unico 404.html desde la raiz,
   asi que no hay uno por idioma. Detectarlo por JS seria un parche que ademas
   falla justo sin JavaScript.
   ========================================================================== */

.notfound .hero-title em { font-style: italic; font-weight: 300; }

.notfound__en {
  position: relative; z-index: 1;
  margin: 0.9rem 0 0; max-width: 46ch;
  font-family: var(--font-mono); font-size: 0.88rem; line-height: 1.6;
  color: var(--color-muted-on-night);
  opacity: 0.72;
}

.notfound__exits {
  position: relative; z-index: 1;
  display: flex; flex-wrap: wrap; gap: 0.75rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}
.notfound__exit {
  display: inline-flex; align-items: center; gap: 0.6rem;
  padding: 0.9rem 1.6rem;
  border: 1px solid rgba(246, 243, 238, 0.14);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono); font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--color-paper);
  transition: border-color var(--dur-micro) var(--ease),
              background var(--dur-micro) var(--ease),
              scale 140ms var(--ease);
}
.notfound__exit:hover, .notfound__exit:focus-visible {
  border-color: var(--color-brand);
  background: color-mix(in srgb, var(--color-brand) 10%, transparent);
}
/* Una sola salida en magenta macizo: la de volver al inicio. El resto en
   contorno, para que haya una jerarquia y no cuatro pildoras iguales. */
.notfound__exit--primary {
  border-color: transparent;
  background: var(--color-brand);
  color: var(--color-night);
  font-weight: 700;
  box-shadow: 0 4px 18px color-mix(in srgb, var(--color-brand) 28%, transparent);
}
.notfound__exit--primary:hover, .notfound__exit--primary:focus-visible {
  background: var(--color-brand-lift);
  border-color: transparent;
}

.notfound .nav__brand a { color: inherit; }
.notfound .scroll-cue span:last-child { letter-spacing: 0.12em; }

/* ==========================================================================
   6. REVELADOS Y MARQUEE
   ========================================================================== */

.reveal {
  opacity: 0; transform: translateY(18px);
  transition: opacity var(--dur-reveal) var(--ease),
              transform var(--dur-reveal) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
.reveal.is-visible { opacity: 1; transform: none; }

/* ── Revelado tras mascara, palabra a palabra ──────────────────────────────
   El gesto propio de los titulares. La palabra no se desvanece: ASOMA desde
   debajo de su propia caja. js/motion.js parte el texto y escribe el retardo
   de cada una; aqui solo esta el movimiento.

   El padding con margen negativo que lo compensa NO es adorno: con
   overflow:hidden y line-height 1.05, la caja corta los acentos por arriba y
   las colas de la g y la j por abajo. Se ensancha la caja y se recupera el
   espacio con el margen, asi que la maquetacion no se mueve ni un pixel.

   Y por eso el estado oculto es 130% y no 100%: la caja es mas alta que la
   palabra, asi que a 100% asomaria una franja por abajo. */
.reveal.is-split { opacity: 1; transform: none; transition: none; }

.mw {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding: 0.14em 0 0.18em;
  margin: -0.14em 0 -0.18em;
}
.mw__i {
  display: inline-block;
  transform: translateY(130%);
  transition: transform 760ms var(--ease);
}
.is-visible .mw__i { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .mw__i { transform: none !important; transition: none !important; }
}

.marquee {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  overflow: hidden;
  background: var(--bg);
  position: relative; z-index: 1;
}
.marquee__track {
  display: inline-flex; will-change: transform;
  padding: clamp(2.5rem, 7vw, 4.5rem) 0;
  white-space: nowrap;
  font-family: var(--font-mono); text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: clamp(1.4rem, 4.2vw, 2.6rem);
  /* SIN opacity. El original llevaba 0.55, que sobre el negro de entonces
     colaba; sobre el papel claro rebaja --muted a #a39f9a y da 2.38 — falla
     AA y Lighthouse lo cazo. El token ya viene calibrado a 6.09: si hace
     falta bajarlo mas, se baja el token, no se le pone alfa encima. */
  color: var(--muted);
}

/* ==========================================================================
   7. MOVIMIENTO REDUCIDO (brief §9)
   --------------------------------------------------------------------------
   De verdad: estado final aplicado directamente, sin listeners de scroll,
   sin tunel y sin parpadeo. No basta con esconder el cursor, que es lo
   unico que hacia el sitio anterior.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  #neural-tunnel { display: none !important; }
  #crt-layer .flicker { animation: none !important; opacity: 0 !important; }
  .scroll-cue__bar::after { animation: none !important; top: 0 !important; }
  .nav__dot { animation: none !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
  .marquee__track { transform: none !important; }
}

/* ==========================================================================
   RETROALIMENTACION DE PULSACION
   --------------------------------------------------------------------------
   Habia DOS reglas :active en toda la hoja, y las dos eran del .hero-quote.
   En una web llena de gestos, nada respondia al momento de pulsar.

   Va en `scale` y no en `transform` por dos motivos concretos de esta base:
   .contact__cta ya ocupa `transform` con el translateY de su hover, y
   .work-slide__link y .notfound__exit lo llevan escrito por el bucle
   magnetico de js/motion.js CADA FOTOGRAMA — un :active{transform} lo
   pisaria el frame siguiente. Siendo propiedades distintas, conviven.

   140ms: dentro de la banda de 100-160 que pide una pulsacion.
   ========================================================================== */
.contact__cta:active,
.brief-submit:active,
.briefing__close-btn:active,
.work-slide__link:active,
.notfound__exit:active { scale: 0.97; }

@media (prefers-reduced-motion: reduce) {
  .contact__cta:active, .brief-submit:active, .briefing__close-btn:active,
  .work-slide__link:active, .notfound__exit:active { scale: 1; }
}


/* ══ TRABAJO: EL NAVEGADOR ═══════════════════════════════════════════════
   Sustituye al carril anclado. El chrome no es decoracion: es el argumento
   de la seccion —"esto se abre"— dicho con la forma. Tres piezas, ni una
   mas: barra de pestanas, barra de direccion y filete de carga.

   Las reglas del carril anclado (.work-pin*) se quedan en la hoja sin
   aplicarse: ya no hay ningun elemento con esas clases. */

.work-browser {
  margin-top: clamp(2rem, 4.5vw, 3.5rem);
  border: 1px solid var(--rule);
  border-radius: var(--radius-m);
  background: color-mix(in srgb, var(--color-paper) 4%, var(--color-night));
  overflow: hidden;
}

.wb-chrome { border-bottom: 1px solid var(--rule); }

/* ── Las pestanas ──────────────────────────────────────────────────────
   En movil el carril se desliza en horizontal en vez de envolver en cuatro
   filas: cinco nombres apilados median mas que la propia captura. */
.wb-tabs {
  display: flex;
  gap: 1px;
  margin: 0; padding: 0; list-style: none;
  background: var(--rule);
  overflow-x: auto;
  scrollbar-width: none;
}
.wb-tabs::-webkit-scrollbar { display: none; }
/* El li NO puede encoger por debajo de su boton. Con `flex: 1 1 0` y
   `min-inline-size: 0` el li medía 134,9 y el boton 163,1: "Domus Prime"
   desbordaba 28 px y el li de al lado —fondo opaco, hermano posterior— le
   pintaba el nombre encima. Los seis a max-content suman 837 en un carril de
   815, asi que algo tiene que ceder; que ceda desplazandose, no tapando. */
.wb-tabs li { display: flex; flex: 1 0 auto; min-inline-size: max-content; }
.wb-tab {
  display: flex; align-items: center; gap: 0.55rem;
  inline-size: 100%; min-inline-size: max-content;
  padding: 0.85rem clamp(0.9rem, 1.6vw, 1.3rem);
  background: color-mix(in srgb, var(--color-paper) 2%, var(--color-night));
  border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: 0.02em; color: var(--muted); white-space: nowrap;
  transition: background var(--dur-micro) var(--ease),
              color var(--dur-micro) var(--ease);
}
.wb-tab__dot {
  inline-size: 6px; block-size: 6px; border-radius: 50%; flex: none;
  background: var(--rule);
  transition: background var(--dur-micro) var(--ease);
}
.wb-tab__n { color: var(--rule); transition: color var(--dur-micro) var(--ease); }
.wb-tab:hover { color: var(--ink); }
.wb-tab.is-active { background: var(--color-night); color: var(--ink); }
.wb-tab.is-active .wb-tab__dot { background: var(--accent); }
.wb-tab.is-active .wb-tab__n { color: var(--accent); }

/* ── La barra de direccion ─────────────────────────────────────────────── */
.wb-bar {
  display: flex; align-items: center; gap: clamp(0.7rem, 1.6vw, 1.1rem);
  padding: 0.7rem clamp(0.8rem, 1.6vw, 1.2rem);
  background: color-mix(in srgb, var(--color-paper) 3%, var(--color-night));
}
.wb-nav {
  flex: none;
  font-family: var(--font-mono); font-size: var(--step--1); color: var(--muted);
}
.wb-url {
  display: flex; align-items: center; gap: 1px;
  flex: 1 1 auto; min-inline-size: 0;
  padding: 0.45rem 0.8rem;
  border: 1px solid var(--rule); border-radius: var(--radius-s);
  background: var(--color-night);
  font-family: var(--font-mono); font-size: var(--step--1);
  color: var(--ink); white-space: nowrap; overflow: hidden;
}
.wb-url__scheme { color: var(--accent); }
.wb-caret {
  inline-size: 7px; block-size: 1em; flex: none;
  margin-left: 2px; background: var(--accent);
  animation: wb-caret 1s steps(1) infinite;
}
@keyframes wb-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
.wb-estado {
  flex: none;
  font-family: var(--font-mono); font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted);
}

/* El filete de carga. Sin JavaScript no se enciende nunca: no hay carga que
   anunciar y el <span> se queda en reposo fuera del marco. */
.wb-load { block-size: 2px; background: var(--rule); overflow: hidden; }
.wb-load > span {
  display: block; block-size: 100%; inline-size: 34%;
  background: var(--accent);
  transform: translateX(-100%);
  opacity: 0;
}
.wb-load.is-carga > span { opacity: 1; animation: wb-load 1s linear infinite; }
@keyframes wb-load {
  from { transform: translateX(-100%); }
  to   { transform: translateX(320%); }
}

/* ── El interior ───────────────────────────────────────────────────────── */
.wb-view { padding: clamp(1.2rem, 2.6vw, 2.2rem); }
.wb-view .work-stage { display: block; }

/* LAS CINCO WEBS SE APILAN EN REJILLA, NO EN ABSOLUTO.

   El carril anclado las apilaba con `position: absolute; inset: 0`, y alli
   tenia sentido: la escena medida una pantalla y las fichas no crecian.
   Dentro del navegador se rompe — `inset: 0` clava la altura de cada web a
   la del marco, asi que al abrir el <details> del caso la lista crecia por
   debajo y `overflow: hidden` la cortaba. Medido: el listado llegaba a 845
   con la caja terminando en 793.

   Apiladas en la MISMA CELDA de una rejilla siguen ocupando el mismo sitio,
   pero el marco se dimensiona por la mas alta y crece al abrir un caso. */
.wb-view .work-frames { display: grid; }
.wb-view .work-slide { grid-area: 1 / 1; }
.wb-view .work-slide + .work-slide { position: static; inset: auto; }

/* LAS WEBS CERRADAS NO SE PINTAN.

   Apiladas en la misma celda solo se veia una, pero las seis seguian
   maquetandose y decodificando sus dos capas: doce imagenes vivas para
   ensenar dos. Con content-visibility el navegador se salta el arbol de las
   cerradas por completo, y como ademas dejan de contar para el tamano de la
   celda, el marco lo fija la web abierta —que es justo lo que se quiere para
   que crezca al abrir su caso. */
.wb-view .work-slide[inert] { content-visibility: hidden; }
/* La placa ya no sangra por el borde de la pantalla. Aqui esta dentro de una
   ventana, y una captura que se sale del navegador que la contiene deshace
   justo lo que la seccion afirma. */
.wb-view .work-slide__plate { max-block-size: 56svh; }

/* EN MOVIL LA BARRA SE QUEDA EN EL DATO.

   A 360 px la direccion se cortaba: el dominio es justo lo que la seccion
   esta afirmando, y "derivacharter.netl…" no afirma nada. Las flechas y el
   estado son marco, no informacion, asi que se retiran y el dominio entra
   completo. */
@media (max-width: 34rem) {
  .wb-nav, .wb-estado { display: none; }
  .wb-bar { padding-inline: 0.7rem; }
  .wb-url { font-size: 0.72rem; }

  /* LAS PESTANAS DEJAN DE DESLIZARSE Y SE APILAN EN DOS COLUMNAS.

     El carril horizontal funciona con dos o tres pestanas. Con seis, a 360
     px entran una y media: los nombres quedaban cortados y los otros cuatro
     proyectos fuera de la pantalla, sin nada que indicara que estaban ahi.
     Un indice que hay que descubrir arrastrando no es un indice.

     En dos columnas los seis nombres se leen enteros de una vez —"Domus
     Prime" incluido, que es el que manda la medida— y sigue pareciendo la
     barra de pestanas de un navegador, que es lo que tiene que parecer. */
  .wb-tabs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    overflow-x: visible;
  }
  .wb-tabs li { min-inline-size: 0; }
  .wb-tab {
    min-inline-size: 0;
    gap: 0.45rem;
    padding: 0.8rem 0.9rem;
    font-size: 0.82rem;
  }
  /* El numero sale: en dos columnas el orden ya se lee de la rejilla, y esos
     dos digitos eran justo lo que estrechaba el nombre. */
  .wb-tab__n { display: none; }
  .wb-tab__name { overflow: hidden; text-overflow: ellipsis; }

  /* LA GALERIA RECUPERA ALTURA.

     El marco es 16:10 porque eso es lo que mide una portada de escritorio.
     A 360 px de ancho eso son 210 px de alto: una rendija. Y la captura que
     va dentro es la web ENTERA a lo largo, asi que la rendija era ademas lo
     que menos ensenaba de ella.

     En vertical la placa pasa a 4:5 —el formato de la propia captura— y el
     recorrido tiene sitio para leerse. La web se ve, no se intuye. */
  .wb-view { padding: 0.9rem; }
  .wb-view .work-slide__plate {
    aspect-ratio: 4 / 5;
    max-block-size: 68svh;
  }
  /* El nombre del proyecto, a la escala del resto de la pagina en movil: era
     lo unico de la seccion que se quedaba en tamano de subtitulo. */
  .work-slide__title { font-size: clamp(2.6rem, 13vw, 3.4rem); }
  .work-slide__desc { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  .wb-caret, .wb-load > span { animation: none; }
}
