/* ===================================================================
   SEBS — sebs.mx
   Portafolio de Sebastián Méndez Villegas.
   Sistema de diseño: Identidad SEBS v1.1 (ver "Identidad SEBS.md").
   Los tokens y las superficies se ajustan en :root, no en los componentes.
   =================================================================== */

:root {
  /* ===================================================================
     SEBS — tokens de marca. Identidad SEBS v1.1
     Regla no negociable: --sebs-signal es SEÑAL, nunca SUPERFICIE.
     ~1-2% del área visible, jamás fondo de nav, hero, card o sección.
     Ver .claude/skills/sebs-brand/references/color.md
     =================================================================== */

  /* ---- Paleta base ---- */
  --technical-white: #ffffff;
  --graphite: #111317;
  --graphite-raised: #1a1d22;
  --alloy: #6b6f76;
  --alloy-light: #8e939b;   /* texto 2º sobre grafito: 6.02:1 (alloy normal falla) */
  --mist: #e5e7eb;
  --divider-dark: #2a2d33;

  /* ---- Señal ---- */
  --sebs-signal: #e30613;
  --sebs-signal-hover: #b80510;
  --sebs-signal-light: #ff3b47;   /* señal como texto sobre grafito: 5.28:1 */
  --sebs-signal-tint: rgba(227, 6, 19, 0.12);
  --sebs-signal-tint-soft: rgba(227, 6, 19, 0.06);

  /* ---- Estado ---- */
  --success: #16a34a;

  /* ---- Superficie por defecto (light). Las clases .surface-* la redefinen. ---- */
  --surface: var(--technical-white);
  --surface-secondary: var(--mist);
  --text: var(--graphite);
  --text-secondary: var(--alloy);
  --divider: var(--mist);
  --signal-text: var(--sebs-signal);

  /* ---- Alias del sistema anterior (SMV) ----
     Mantienen vivo el CSS que aún no se migra. Se retiran cuando no queden usos. */
  --red: var(--sebs-signal);
  --red-dark: var(--sebs-signal-hover);
  --ink: var(--graphite);
  --ink-soft: var(--graphite-raised);
  --paper: var(--technical-white);
  --white: var(--technical-white);
  --muted: var(--alloy);
  --card: var(--graphite);

  /* ---- Tipografía ----
     Manrope para todo. IBM Plex Mono solo para datos técnicos:
     revisiones, IDs, timestamps, nombres de señal, archivos. Nunca para prosa. */
  --font-display: "Manrope", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-sans: "Manrope", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Consolas, monospace;

  /* ---- Espaciado (base 4px) ---- */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 12px;
  --space-lg: 16px;
  --space-xl: 24px;
  --space-2xl: 32px;
  --space-3xl: 48px;
  --space-4xl: 64px;
  --space-5xl: 96px;
  --space-6xl: 128px;

  /* ---- Radios ---- */
  --radius-xs: 4px;
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-round: 9999px;

  /* ---- Layout ---- */
  --max: 1280px;
  --radius: var(--radius-xl);
  --radius-btn: var(--radius-md);
  --radius-pill: var(--radius-round);

  /* ---- Motion ---- */
  --transition: 0.2s ease;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-micro: 150ms;
  --dur-component: 250ms;
  --dur-section: 400ms;
}

/* ===================================================================
   Superficies. Toda sección, panel o card declara la suya; los hijos
   heredan los tokens correctos vía var(--surface) / var(--text), así
   el mismo componente sirve en claro y oscuro sin duplicar CSS.
   El ritmo visual de SEBS se construye alternando estas superficies,
   NO añadiendo rojo.
   =================================================================== */

.surface-light {
  --surface: var(--technical-white);
  --surface-secondary: var(--mist);
  --text: var(--graphite);
  --text-secondary: var(--alloy);
  --divider: var(--mist);
  --signal-text: var(--sebs-signal);
}

.surface-mist {
  --surface: var(--mist);
  --surface-secondary: var(--technical-white);
  --text: var(--graphite);
  --text-secondary: var(--alloy);   /* 4.08:1 — solo texto grande */
  --divider: var(--technical-white);
  --signal-text: var(--sebs-signal);
}

.surface-dark {
  --surface: var(--graphite);
  --surface-secondary: var(--graphite-raised);
  --text: var(--technical-white);
  --text-secondary: var(--alloy-light);
  --divider: var(--divider-dark);
  --signal-text: var(--sebs-signal-light);
}

.surface-light,
.surface-mist,
.surface-dark {
  background: var(--surface);
  color: var(--text);
}

/* ===== Reset ===== */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

/* El body es la superficie light por defecto. Las secciones que necesiten
   otra la declaran con .surface-dark / .surface-mist. */
body {
  font-family: var(--font-sans);
  color: var(--text);
  background: var(--surface);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

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

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

/* ===== Utilidades ===== */
.container {
  width: 100%;
  /* Ancho máximo: el contenido queda centrado en monitores grandes. */
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 3vw, 3.5rem);
}

.serif {
  font-family: var(--font-display);
}

.section {
  padding-block: var(--space-5xl);
}

/* El feed arranca pegado a los tabs: el aire lo pone .pins */
.section--flush {
  padding-top: 0;
}

/* ===== Botones / pills ===== */
.btn {
  /* appearance:none + background:transparent: algunos .btn ahora son
     <button> (antes solo <a>) — sin esto, el navegador pinta su propio
     chrome nativo (relleno gris/con acento del SO) por debajo del color
     que definan los modificadores, y en variantes que no fijan un
     background propio (p. ej. .btn--ghost-light) ese chrome se ve por
     encima del estado "sin hover" (se veía como si ya estuviera en hover). */
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  /* font: inherit asegura que <button> y <a> midan igual (los <button>
     no heredan tipografía por defecto → se veían más delgados) */
  font: inherit;
  padding: 0.55rem 1.2rem;
  border-radius: var(--radius-btn);
  font-weight: 600;
  font-size: 0.9rem;
  line-height: 1.15;
  letter-spacing: 0.01em;
  border: 1.5px solid transparent;
  cursor: pointer;
  transition: transform var(--transition), background var(--transition),
    color var(--transition), box-shadow var(--transition);
}

.btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);
}

/* Botones de la top bar: más compactos para no llenar la barra, y el mismo
   radio "punto medio" que .search-bar__featured (ver más abajo). */
.nav__actions .btn {
  padding: 0.34rem 0.9rem;
  font-size: 0.82rem;
  border-radius: 14px;
}

/* Botón primario: uno por vista. Es el mayor consumo de rojo que autoriza
   el sistema (regla 3.4). Si una vista necesita dos acciones, la segunda
   es .btn--black o .btn--red-outline. */
.btn--red {
  background: var(--red); /* sebs-allow: boton primario */
  color: var(--white);
}

.btn--red-outline {
  /* Rojo oscuro en reposo: sobre fondo claro da 5.92:1 (AA); el rojo vivo
     se quedaba en 4.49. Al hover se rellena con el rojo de marca. */
  border-color: var(--red-dark);
  color: var(--red-dark);
  background: transparent;
}

.btn--red-outline:hover {
  background: var(--red); /* sebs-allow: hover, estado transitorio */
  color: var(--white);
}

.btn--white {
  background: var(--white);
  color: var(--ink);
}

.btn--black {
  background: var(--ink);
  color: var(--white);
}

/* CTA "en vivo": botón blanco sólido + punto que late = máxima prioridad.
   Blanco/rojo-oscuro da ~7:1 (mejor contraste que blanco/rojo). */
.btn--live {
  background: var(--white);
  color: var(--red-dark);
  font-weight: 700;
}

.btn__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--success);
  box-shadow: 0 0 0 0 rgba(28, 158, 90, 0.55);
  animation: live-pulse 2.2s ease-out infinite;
}

.btn__logo {
  height: 1.05em;
  width: auto;
}

@keyframes live-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(28, 158, 90, 0.55);
  }
  70% {
    box-shadow: 0 0 0 7px rgba(28, 158, 90, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(28, 158, 90, 0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .btn__dot {
    animation: none;
  }
}

.btn--black.is-link {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.btn--ghost-light {
  border-color: rgba(255, 255, 255, 0.6);
  color: var(--white);
}

.btn--ghost-light:hover {
  background: var(--white);
  color: var(--red);
}

/* ===== Header ===== */
/* Superficie dark declarada en el HTML (class="site-header surface-dark"):
   el header toma color de la superficie, no de un valor propio. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--surface);
  color: var(--text);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 56px;
}

/* El wordmark SEBS es un asset geométrico, nunca texto con fuente (§2.2).
   Versión negativa porque el header es superficie dark. */
.nav__logo {
  display: inline-flex;
  align-items: center;
}

.nav__logo img {
  /* 32px: mínimo verificado del símbolo. Por debajo, los canales internos
     del monograma se cierran y la marca se vuelve una mancha (§2.7). */
  height: 32px;
  width: auto;
}

.nav__actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* Disparador de los paneles About/Contact: texto plano, no pill (los
   distingue del botón sólido de Contact). Ahora es un <button> (antes un
   <a> de ancla) — border/background reseteados para que no herede chrome
   nativo del navegador. */
.nav__link {
  background: none;
  border: none;
  font: inherit;
  color: rgba(255, 255, 255, 0.85);
  font-weight: 600;
  font-size: 0.85rem;
  padding: 0.34rem 0.35rem;
  cursor: pointer;
  transition: color var(--transition);
}

.nav__link:hover {
  color: #ffffff;
}

.nav__link[aria-expanded="true"] {
  color: #ffffff;
}

/* ===== Paneles About / Contact (acordeón bajo el header) =====
   Siempre están en el DOM; se expanden con el truco grid-template-rows
   0fr → 1fr para animar una altura que
   no conocemos de antemano, sin medir con JS. */
/* Superficie dark, no roja: un panel a pantalla completa en rojo lee como
   identidad institucional (regla 3.4). En grafito gana presencia y queda
   alineado con el header. La superficie se declara en el HTML. */
.reveal-panel {
  display: grid;
  grid-template-rows: 0fr;
  background: var(--surface);
  transition: grid-template-rows 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.reveal-panel.is-open {
  grid-template-rows: 1fr;
}

.reveal-panel__inner {
  overflow: hidden;
  min-height: 0;
}

/* Botón "Collapse": centrado en el ancho del panel, no atado a la columna
   de texto (antes era un link "Show less" bajo el párrafo). */
.reveal-panel__footer {
  display: flex;
  justify-content: center;
  padding: 0 0 1.5rem;
}

.reveal-panel__collapse {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.06);
  font: inherit;
  color: rgba(255, 255, 255, 0.85);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  padding: 0.4rem 0.95rem;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), transform var(--transition);
}

.reveal-panel__collapse svg {
  width: 13px;
  height: 13px;
}

.reveal-panel__collapse:hover {
  background: rgba(255, 255, 255, 0.16);
  color: #ffffff;
}

@media (prefers-reduced-motion: reduce) {
  .reveal-panel {
    transition: none;
  }
}

/* Contact: el hero se divide en tres columnas (texto / iconos / foto).
   Selector con ambas clases (mayor especificidad): así gana sin importar
   el orden de aparición frente a la regla base .hero__inner de más abajo. */
.hero__inner.hero__inner--contact {
  grid-template-columns: 1fr auto auto;
}

.hero__social {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.hero__social-icon {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--white);
  color: var(--ink);
  display: grid;
  place-items: center;
  transition: transform var(--transition), box-shadow var(--transition);
}

.hero__social-icon svg {
  width: 22px;
  height: 22px;
}

.hero__social-icon:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}

/* About: reusa el círculo de .hero__photo para el carrusel (mismo marco
   que la foto de Contact), con crossfade entre imágenes. Más grande que la
   de Contact — el bloque de texto de About es más largo (2 párrafos), y a
   240px la foto quedaba con mucho más margen arriba/abajo que el texto. */
.hero__photo[data-about] {
  background: var(--graphite-raised);
  width: 300px;
  height: 300px;
}

/* Texto de About: renglones más largos (menos wraps → bloque más corto)
   para acercar su altura a la de la foto ya agrandada arriba. */
.hero__text--wide .hero__lead {
  max-width: 58ch;
}

.hero__photo .slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.8s ease;
}

.hero__photo .slide.is-active {
  opacity: 1;
}

/* Utilidad: oculta visualmente pero deja el texto disponible para lectores
   de pantalla / SEO (la home ya no lleva un <h1> visible: prioriza el
   contenido sobre un hero). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ===== Hero (superficie dark) =====
   Era una banda roja a sangre: el mayor consumo de rojo de la página y la
   razón principal de que se leyera como marca institucional. El contraste
   lo da ahora la superficie, no el color (regla 3.3). */
.hero {
  background: var(--surface);
  color: var(--text);
}

.hero__inner {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2rem;
  padding-block: 2rem;
}

.hero__text {
  /* Sin tope de ancho: el texto usa toda la columna para caber en 2 líneas
     en vista de monitor (en tablet/móvil se reajusta con el responsive). */
  max-width: none;
}

/* El h1 manda en la primera pantalla, por encima de cualquier título de
   sección. Manrope ExtraBold + tracking negativo: sin el -0.02em la grotesca
   se ve suelta y pierde el carácter técnico (escala H1 del sistema). */
.hero__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.3rem, 5vw, 3.6rem);
  line-height: 1.03;
  letter-spacing: -0.02em;
  text-wrap: balance;
  max-width: 16ch;
  margin-bottom: 0.35rem;
}

/* Variante del título para index: el rol reemplaza al nombre como h1
   (mismo peso tipográfico que "Projects", ajustado a un texto más largo). */
.hero__title--role {
  font-size: clamp(1.8rem, 3.6vw, 2.75rem);
  max-width: 22ch;
  margin-bottom: 0.5rem;
}

.hero__lead {
  /* Texto de apoyo bajo el nombre + rol. Blanco sólido = 4.98:1 sobre el rojo
     (la translucidez lo dejaba al borde de fallar AA). */
  max-width: 40ch;
  font-size: 0.92rem;
  line-height: 1.5;
  margin-bottom: 1rem;
  color: #ffffff;
}

.hero__cta {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* Entrada orquestada del hero: un solo momento al cargar (no el reflejo de
   animar cada sección). El contenido es visible por defecto; el reveal solo
   corre si el usuario no pidió menos movimiento. */
@media (prefers-reduced-motion: no-preference) {
  .hero__text > *,
  .hero__photo {
    animation: hero-rise 0.7s cubic-bezier(0.22, 1, 0.36, 1) backwards;
  }

  .hero__title {
    animation-delay: 0.05s;
  }
  .hero__lead {
    animation-delay: 0.23s;
  }
  .hero__cta {
    animation-delay: 0.32s;
  }
  .hero__photo {
    animation-duration: 0.8s;
    animation-delay: 0.12s;
  }
}

@keyframes hero-rise {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.hero__photo {
  /* Contenida dentro de la banda roja, con espaciado del padding del hero. */
  position: relative;
  z-index: 2;
  width: 240px;
  height: 240px;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  background: transparent;
  border: none;
}

/* Nombre sobre la foto, aparece al hacer hover */
.hero__photo-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  padding: 0 15%;
  text-align: center;
  background: rgba(0, 0, 0, 0.55);
  color: #ffffff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 1.25;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.hero__photo:hover .hero__photo-overlay {
  opacity: 1;
}

.hero__photo img {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero__photo-fallback {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center;
  color: rgba(255, 255, 255, 0.7);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.5rem;
}

/* ===== Section head ===== */
.section-head {
  margin-bottom: 2.5rem;
}

.section-head--center {
  text-align: center;
}

.section-head__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.section-head__sub {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  margin-left: 0.15rem;
}

.section-head__lead {
  margin-top: 0.85rem;
  font-size: 1.05rem;
  line-height: 1.55;
  color: var(--text-secondary);
  max-width: 52ch;
  text-wrap: pretty;
}

.section-head--center .section-head__lead {
  margin-inline: auto;
}

/* ===== Timeline horizontal (index) ===== */
.timeline {
  position: relative;
  overflow-x: auto;
  padding-block: 2rem 3rem;
}

.timeline__track {
  position: relative;
  display: flex;
  gap: 0;
  min-width: 640px;
  border-top: 3px solid var(--ink);
  margin-top: 90px;
}

.tl-node {
  position: relative;
  flex: 1 1 0;
  min-width: 140px;
}

/* Pin de año (grafito, apunta hacia abajo, sobre la línea).
   El año es un dato, no un acento: va en grafito y en mono. */
.tl-year {
  position: absolute;
  bottom: 100%;
  left: 0;
  transform: translateY(-6px);
  width: 56px;
  height: 56px;
  background: var(--graphite);
  color: var(--technical-white);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.95rem;
  display: grid;
  place-items: center;
  clip-path: polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%);
  padding-bottom: 8px;
}

/* Pin de mes (contorno, debajo de la línea) */
.tl-month {
  position: absolute;
  top: 14px;
  left: 0;
  width: 54px;
  height: 46px;
  color: var(--text);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.8rem;
  display: grid;
  place-items: center;
  background: var(--surface);
  border: 2px solid var(--divider);
  clip-path: polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%);
  padding-bottom: 6px;
}

/* ===== Proyectos destacados (composición lead + support) ===== */
.apps__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.5rem;
}

.app-card {
  position: relative;
  overflow: hidden;
  background: var(--graphite);
  color: var(--white);
  border-radius: 20px;
  display: flex;
}

/* Lead (Torke): ancho completo, video 3:1 arriba y contenido debajo
   (nada de texto superpuesto sobre el video). */
.app-card--lead {
  grid-column: 1 / -1;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: 0;
  text-align: left;
  background: var(--card);
}

.feature__video {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 1;
  overflow: hidden;
  background: var(--graphite);
  flex-shrink: 0;
}

.feature__video video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.app-card--lead .app-card__body {
  position: relative;
  padding: clamp(1.1rem, 2.5vw, 1.55rem) clamp(1.5rem, 4vw, 3rem) clamp(1.25rem, 2.5vw, 1.55rem);
}

/* Support (Teleop Mobile Manipulator, Capa8): vertical y centrado, más compacto */
.app-card--support {
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 2.75rem 2rem 2.25rem;
}

/* Variante con video de portada (Teleop Mobile Manipulator): mismo tratamiento que el
   lead — video a sangre, tarjeta negra, franja de acciones delgada.
   La tarjeta entera es cuadrada; el video ocupa el espacio restante tras
   la franja de acciones y se recorta por abajo (object-position: top). */
.app-card--support.app-card--media {
  padding: 0;
  background: var(--card);
  align-items: stretch;
  aspect-ratio: 1 / 1;
}

.app-card--media .feature__video {
  flex: 1;
  min-height: 0;
}

.app-card--media .feature__video video {
  object-position: top;
}

.app-card--media .app-card__body {
  width: 100%;
  flex: none;
  padding: clamp(1.1rem, 3vw, 1.55rem) 1.5rem clamp(1.25rem, 3vw, 1.55rem);
}

/* Cuerpo interior: marca + descripción + acciones */
.app-card__body {
  display: flex;
  flex-direction: column;
}

.app-card--support .app-card__body {
  align-items: center;
  width: 100%;
}

.app-card--lead .app-card__body {
  flex: 1;
  align-items: stretch;
}

.feature__art {
  position: relative;
  border-radius: 50%;
  background: var(--surface-secondary);
  overflow: hidden;
  flex-shrink: 0;
}

/* El arte del lead es mayor que el de los support → jerarquía por tamaño */
.app-card--lead .feature__art {
  width: clamp(200px, 24vw, 280px);
  height: clamp(200px, 24vw, 280px);
}

.app-card--support .feature__art {
  width: 200px;
  height: 200px;
  margin-bottom: 1.5rem;
}

/* Torke: comercial 3D en el círculo (fondo oscuro estudio) */
.feature__art--reel {
  background: var(--graphite);
}
.feature__reel {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Carrusel: avatar + capturas de proyecto que rotan con crossfade */
.feature__art .slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.6s ease;
}

.feature__art .slide.is-avatar {
  object-fit: contain;
  padding: 11%;
}

.feature__art .slide.is-active {
  opacity: 1;
}

/* Botones outline sobre fondo rojo: siempre visibles (contorno claro) */
.app-card .btn--red-outline {
  border-color: var(--white);
  color: var(--white);
  background: transparent;
}

.app-card .btn--red-outline:hover {
  background: var(--white);
  color: var(--red);
}

.app-card .btn[disabled] {
  cursor: default;
}

.app-card .btn[disabled]:hover {
  transform: none;
  box-shadow: none;
}

.brandmark {
  /* Altura fija: las 3 placas (MECATRONICA / REMOTE HANDS / TOPOLOGÍAS)
     miden exactamente lo mismo; el contenido se centra vertical. */
  height: 3.4rem;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: var(--ink);
  color: var(--white);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.9rem;
  letter-spacing: 0.04em;
  padding: 0 1.2rem;
  border-radius: 10px;
  margin-bottom: 1.25rem;
}

/* Variante con imagen de logo (Capa8 = "Topologías", fondo negro) */
.brandmark--logo {
  padding: 0 0.9rem;
  background: var(--graphite); /* igual al fondo del PNG, sin borde visible */
}

.brandmark__img {
  display: block;
  height: 2.5rem;
  width: auto;
}

/* Variante solo texto (Teleop Mobile Manipulator, sin logo propio) */
.brandmark--text {
  font-size: 1.5rem;
  letter-spacing: 0.02em;
}

/* "O" de MECATRONICA = engrane 100% CSS.
   Dos capas sobre un span vacío:
   ::before = disco dentado (12 dientes vía clip-path) que gira.
   ::after  = disco central del color del fondo (--ink) = agujero falso. */
.gear-o {
  width: 1.15em;
  height: 1.15em;
  position: relative;
  display: inline-block;
  vertical-align: middle;
  margin: 0 1px;
}

/* Blanco, no rojo: el engrane va sobre el ribbon grafito, así que necesita
   ser claro para leerse. Nota aparte: el engrane como recurso de marca es
   un antipatrón explícito del sistema (§26) — decisión pendiente. */
.gear-o::before {
  content: "";
  position: absolute;
  inset: 4.5%;
  border-radius: 50%;
  background: var(--technical-white);
  clip-path: polygon(
    50% 0%, 58% 12%, 72% 7%, 78% 21%, 93% 22%, 88% 38%,
    100% 50%, 88% 62%, 93% 78%, 78% 79%, 72% 93%, 58% 88%,
    50% 100%, 42% 88%, 28% 93%, 22% 79%, 7% 78%, 12% 62%,
    0% 50%, 12% 38%, 7% 22%, 22% 21%, 28% 7%, 42% 12%
  );
  transform-origin: center;
  animation: spin-slow 18s linear infinite;
}

/* Agujero central falso: DEBE coincidir con el fondo del brandmark (--ink) */
.gear-o::after {
  content: "";
  position: absolute;
  inset: 32%;
  border-radius: 50%;
  background: var(--ink);
}

/* Capa8: engrane blanco */
.gear-o--light::before {
  background: var(--white);
}

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

@media (prefers-reduced-motion: reduce) {
  .gear-o::before {
    animation: none;
  }
}

.feature__desc {
  max-width: 60ch;
  margin-bottom: 1.75rem;
  line-height: 1.7;
}

.app-card--support .feature__desc {
  margin-inline: auto;
}

.feature__actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.app-card--support .feature__actions {
  justify-content: center;
}

.app-card--lead .feature__actions {
  align-items: center;
  justify-content: center;
  gap: 0.47rem;
}

/* Botón = solo el logo de Torke, sin texto dentro. */
.btn--logo-only {
  padding: 0.35rem 1rem;
}

.app-card--lead .btn__logo {
  height: 1.7rem;
}

/* Indicación "WEB APP →" fuera del botón, a su izquierda: invita a hacer
   clic con un ligero vaivén horizontal hacia el botón. */
.feature__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.56rem;
}

.feature__cta-hint {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: #ffffff;
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  animation: cta-nudge 1.6s ease-in-out infinite;
}

.feature__cta-arrow {
  font-size: 1rem;
}

@keyframes cta-nudge {
  0%,
  100% {
    transform: translateX(0);
  }
  50% {
    transform: translateX(6px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .feature__cta-hint {
    animation: none;
  }
}

/* Cinta "IA powered" */
.ribbon {
  position: absolute;
  top: 0;
  right: 1.5rem;
  background: var(--ink);
  color: var(--white);
  text-align: center;
  padding: 0.5rem 0.7rem 1rem;
  line-height: 1.1;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 82%, 0 100%);
}

.ribbon__ia {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  display: block;
}

.ribbon__txt {
  font-size: 0.62rem;
  letter-spacing: 0.05em;
}

/* Barra de búsqueda + filtro (reemplaza los tabs de categoría) */
.search-bar {
  display: flex;
  justify-content: center;
  padding-block: 2rem 0;
}

/* Grid de 3 pistas: la barra de búsqueda (columna central) queda siempre
   centrada en la página; el botón Featured Projects vive en la primera
   columna, pegado a su izquierda (justify-self: end) — la tercera columna
   es un espaciador invisible que balancea el ancho.
   La columna central usa minmax(0, 640px) en vez de "auto": un track "auto"
   se mide por el max-content de sus hijos, y con un placeholder corto
   ("Search") ese contenido es angosto — la barra terminaba encogida en vez
   de ocupar el ancho que le da .search-bar__group. minmax() le da un ancho
   explícito, así el 100% del grupo sí se respeta. */
.search-bar__row {
  display: grid;
  grid-template-columns: 1fr minmax(0, 640px) 1fr;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
}

.search-bar__featured {
  grid-column: 1;
  justify-self: end;
  flex-shrink: 0;
  white-space: nowrap;
  /* Punto medio entre el radio cuadrado de .btn y el pill total de la
     barra de búsqueda (ver .search-bar__group más abajo). */
  border-radius: 14px;
}

.search-bar__group {
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  max-width: 640px;
  background: var(--surface);
  border: 1px solid var(--divider);
  /* Antes era un pill completo (999px); se suaviza un poco hacia el radio
     de los botones de al lado, sin llegar a ser tan cuadrada. */
  border-radius: 20px;
  padding: 0.55rem 0.6rem 0.55rem 1.1rem;
  box-shadow: 0 8px 20px -14px rgba(13, 13, 13, 0.35);
}

/* Escritorio: la barra crece — se ve angosta contra todo el ancho del
   contenido en monitores grandes. Se cambia en el track del grid (no solo
   el max-width del grupo): ver la nota en .search-bar__row de arriba. */
@media (min-width: 1024px) {
  .search-bar__row {
    grid-template-columns: 1fr minmax(0, 860px) 1fr;
  }
  .search-bar__group {
    max-width: 860px;
  }
}

.search-bar__icon {
  width: 19px;
  height: 19px;
  color: var(--muted);
  flex-shrink: 0;
}

.search-bar__input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  font: inherit;
  color: var(--ink);
  outline: none;
}

.search-bar__input::placeholder {
  color: var(--muted);
}

/* ===== Tabs de categoría =====
   Sustituyen al panel de filtros con checkboxes: selección única, con la
   barra de subrayado como único indicador de estado. El subrayado va en
   Graphite, no en rojo — es navegación, no una acción (regla 3.4). */
.tabs {
  display: flex;
  align-items: center;
  gap: var(--space-xl);
  overflow-x: auto;
  scrollbar-width: none;
  padding-block: var(--space-lg) 0;
  border-bottom: 1px solid var(--divider);
}

.tabs::-webkit-scrollbar {
  display: none;
}

.tab {
  appearance: none;
  background: none;
  border: none;
  font: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
  cursor: pointer;
  padding: 0 0 var(--space-md);
  /* -1px: el subrayado del tab activo monta sobre el borde del contenedor
     en vez de dibujar una segunda línea debajo. */
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
}

.tab:hover {
  color: var(--text);
}

.tab[aria-selected="true"] {
  color: var(--text);
  font-weight: 700;
  border-bottom-color: var(--text);
}

.tab:focus-visible {
  outline: 2px solid var(--sebs-signal);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}

/* ===== Feed masonry (pins) =====
   Flujo continuo: una sola columna de masonry para todo el catálogo. El año
   dejó de ser un encabezado de bloque y viaja como dato en cada tarjeta. */
.pins {
  column-count: 3;
  column-gap: var(--space-lg);
  padding-top: var(--space-xl);
}

@media (max-width: 1099px) {
  .pins { column-count: 3; }
}

@media (max-width: 699px) {
  .pins {
    column-count: 2;
    column-gap: var(--space-md);
  }
}

.pin {
  break-inside: avoid;
  margin-bottom: var(--space-xl);
  /* Ancla del toast de "Copy link" */
  position: relative;
}

/* La imagen ES la tarjeta. Sin panel, sin marco, sin recorte a una
   proporción fija: cada portada entra con su relación natural, que es lo
   que produce el escalonado del masonry. */
.pin__media {
  position: relative;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--surface-secondary);
  box-shadow: 0 1px 2px rgba(17, 19, 23, 0.06), 0 6px 16px -10px rgba(17, 19, 23, 0.18);
  cursor: pointer;
}

.pin__media img,
.pin__media video {
  display: block;
  width: 100%;
  height: auto;
}

/* Sin portada: bloque grafito con proporción de reserva */
.pin__media--empty {
  aspect-ratio: 4 / 3;
  background: linear-gradient(135deg, var(--graphite-raised), var(--graphite));
}

/* Foco de teclado: lo dispara el <button> del título */
.pin:focus-within .pin__media {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* Tarjeta bloqueada ("Coming soon"): portada atenuada + velo oscuro fijo
   (no solo al hover, para que se lea "bloqueada" de entrada) con el
   candado y la leyenda centrados. Sin cursor pointer: no hay nada que abrir. */
.pin--locked .pin__media {
  cursor: default;
}

.pin--locked .pin__media img {
  filter: grayscale(60%) brightness(0.55);
}

.pin__lock {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  background: rgba(10, 10, 12, 0.38);
  color: var(--technical-white);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.pin__lock svg {
  width: 22px;
  height: 22px;
}

.pin--locked .pin__title {
  color: var(--text-secondary);
}

/* ---- Acciones sobre la imagen ----
   Sin transición a propósito: aparecen y desaparecen en seco, sin animación
   de movimiento ni de opacidad. La tarjeta tampoco se desplaza en hover. */
.pin__act {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  /* Contorno, no solo sombra: sobre una portada blanca (la primera página de
     un paper) la sombra suave desaparecía y el botón se perdía dentro de la
     imagen. El hairline lo despega de cualquier fondo claro. */
  border: 1px solid rgba(17, 19, 23, 0.14);
  border-radius: var(--radius-round);
  background: var(--technical-white);
  color: var(--graphite);
  box-shadow: 0 1px 3px rgba(17, 19, 23, 0.22);
  cursor: pointer;
  opacity: 0;
  text-decoration: none;
}

.pin__act svg {
  flex: none;
  width: 17px;
  height: 17px;
}

/* Barra superior: "More info" a la izquierda, el destino externo a la derecha.
   Los dos son relevantes y ninguno debe ganar por omisión, así que van al
   mismo peso visual y en extremos opuestos. */
.pin__acts {
  position: absolute;
  z-index: 2;
  top: var(--space-sm);
  left: var(--space-sm);
  right: var(--space-sm);
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-xs);
  /* La barra ocupa todo el ancho: sin esto taparía el clic al modal en la
     franja superior de la imagen. Solo los botones reciben el puntero. */
  pointer-events: none;
}

.pin__acts > * {
  pointer-events: auto;
}

/* Variante con etiqueta: los botones de destino llevan texto porque un tooltip
   solo aparece si ya apuntaste, y para eso hay que sospechar que el botón
   hace algo distinto de lo que ya hace la tarjeta. */
.pin__act--pill {
  position: static;
  width: auto;
  max-width: 100%;
  height: var(--space-2xl);
  padding: 0 var(--space-sm);
  gap: var(--space-xs);
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1;
  color: var(--graphite);
}

.pin__act--pill svg {
  width: 14px;
  height: 14px;
}

.pin__act-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Orden de lectura dentro de la píldora: el icono antecede al texto en "More
   info", pero en el enlace externo la flecha cierra la frase. */
.pin__act--link {
  flex-direction: row-reverse;
}

.pin__act--copy {
  left: var(--space-sm);
  bottom: var(--space-sm);
}

.pin__media:hover .pin__act,
.pin:focus-within .pin__act,
.pin__act:focus-visible {
  opacity: 1;
}

.pin__act:hover {
  background: var(--mist);
}

.pin__act:focus-visible {
  outline: 2px solid var(--sebs-signal);
  outline-offset: 2px;
}

/* En táctil no existe hover: las acciones se quedan visibles o serían
   inalcanzables. */
@media (hover: none) {
  .pin__act { opacity: 1; }
}

/* Debajo de ~800px la tarjeta baja de 224px y las dos píldoras con texto ya
   no caben lado a lado (la más larga, "View KiCad files", pide 214px con
   "More info"). Se retiran las etiquetas y quedan los iconos; el aria-label
   sigue completo, así que el lector de pantalla no pierde nada. El corte no
   coincide con el de columnas a propósito: manda el ancho de tarjeta. */
@media (max-width: 799px) {
  .pin__act--pill {
    width: 32px;
    height: 32px;
    padding: 0;
  }
  .pin__act-label { display: none; }
}

/* ---- Pie: título ---- */
.pin__foot {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-xs) 0;
}

.pin__text {
  flex: 1;
  min-width: 0;
}

.pin__title {
  margin: 0;
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--text);
}

/* Botón real (accesible por teclado) sin estilos de botón: es la vía de
   entrada al modal desde el teclado. */
.pin__open {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pin__open:focus-visible {
  outline: none;
}

.pin__meta {
  margin: 2px 0 0;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Confirmación de "Copy link": texto, no solo color (regla de no depender del
   color como única señal). */
.pin__toast {
  position: absolute;
  z-index: 21;
  left: var(--space-sm);
  bottom: 52px;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-sm);
  background: var(--graphite);
  color: var(--technical-white);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  pointer-events: none;
}

/* Estado vacío (Papers, Competitions, PCB's) */
.empty-state {
  text-align: center;
  padding: 4rem 1rem;
  /* Gris más oscuro que --muted: 4.6:1 sobre --paper (AA); el --muted fallaba */
  color: var(--alloy);
}

.empty-state h3 {
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--ink);
  margin-bottom: 0.5rem;
}

.empty-state p {
  max-width: 52ch;
  margin-inline: auto;
  line-height: 1.6;
}

.empty-state__cta {
  margin-top: 1.5rem;
}

/* ===== Modal de proyecto (galería) ===== */
.modal[hidden] {
  display: none;
}

.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

.modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.72);
  animation: fade-in 0.2s ease;
}

.modal__window {
  position: relative;
  z-index: 1;
  /* Se dimensiona por el ancho Y por el alto de la pantalla: el stage es
     16:9, así que el ancho máximo que cabe a lo alto es
     (alto disponible − cabecera y pie) × 16/9 + columna de miniaturas.
     En una laptop baja la ventana se estrecha en vez de desbordarse con
     scroll; en un monitor grande crece hasta 1400 px. */
  width: min(1400px, 100%, calc((92dvh - 17rem) * 16 / 9 + 9rem));
  max-height: 92dvh;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
  background: var(--surface);
  color: var(--text);
  border-radius: var(--radius-xl);
  padding: 1.4rem;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.55);
  animation: pop-in 0.22s ease;
}

.modal__close {
  position: absolute;
  top: 0.75rem;
  right: 1rem;
  background: none;
  border: none;
  color: var(--white);
  font-size: 2rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--transition);
}

.modal__close:hover {
  opacity: 1;
}

.modal__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.3rem;
  margin-bottom: 0.5rem;
  padding-right: 2rem;
}

/* Detalle de mes ("Mon YYYY"), discreto — la tarjeta ya muestra el año solo */
.modal__date {
  color: var(--text-secondary);
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  margin: -0.3rem 0 0.9rem;
}

/* Cabecera del modal: descripción a la izquierda y CTA visible a la derecha */
.modal__head {
  display: flex;
  align-items: flex-start;
  gap: 1.25rem;
  margin-bottom: 1.1rem;
}

.modal__headtext {
  flex: 1;
  min-width: 0;
}

.modal__headtext .modal__subtitle {
  margin-bottom: 0.35rem;
}

.modal__desc {
  max-width: 75ch;
  color: var(--text-secondary);
  font-size: 0.9rem;
  line-height: 1.5;
  margin-bottom: 0;
}

/* En pantallas angostas el CTA baja debajo de la descripción, a lo ancho */
@media (max-width: 640px) {
  .modal__head {
    flex-direction: column;
    gap: 0.85rem;
  }
  .modal__head .modal__cta {
    width: 100%;
    justify-content: center;
  }
}

.modal__stage {
  position: relative;
  /* 16:9: el mismo formato que las imágenes de galería, que lo llenan sin
     bandas vacías. El tamaño lo decide el ancho de la ventana. */
  aspect-ratio: 16 / 9;
  width: 100%;
  background: var(--graphite);
  border-radius: 12px;
  overflow: hidden;
  display: grid;
  place-items: center;
}

/* Con modelo 3D el fondo iguala la ventana → el modelo "flota" integrado,
   sin sensación de canvas recuadrado. */
.modal__stage.has-model {
  background: var(--graphite-raised);
}

/* Absoluta para que su caja sea exactamente la del stage: con height:100%
   normal el grid no resuelve el porcentaje (altura circular) y la imagen
   caía a su altura natural, recortándose. Con contain nunca se corta. */
.modal__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.modal__empty {
  /* Gris claro para el fondo oscuro del modal (alloy-light sobre graphite-raised: 5.47:1) */
  color: var(--alloy-light);
  font-size: 0.95rem;
}

/* Visor 3D interactivo (PCB) — llena el stage por encima de la imagen.
   Fondo igual a la ventana para que el modelo se vea integrado, no en un canvas. */
.modal__model {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  background: var(--graphite-raised);
  --poster-color: transparent;
  --progress-bar-color: transparent; /* usamos nuestro spinner propio */
  cursor: grab;
}
.modal__model:active {
  cursor: grabbing;
}

/* Badge 360° (arriba-izquierda del visor) — indica que el modelo es rotable */
.modal__badge360 {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  padding: 0.26rem 0.6rem 0.26rem 0.48rem;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.92);
  color: var(--graphite);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}
.modal__badge360 svg {
  width: 15px;
  height: 15px;
}

/* Spinner de carga del modelo (mínimo 2 s; permanece si tarda más) */
.modal__spinner {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  background: var(--graphite-raised);
  transition: opacity 0.4s ease;
}
.modal__spinner.is-done {
  opacity: 0;
  pointer-events: none;
}
.modal__spinner-ring {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--red);
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .modal__spinner-ring {
    animation: pulse 1.2s ease-in-out infinite;
    border-top-color: rgba(255, 255, 255, 0.15);
  }
}
@keyframes pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}

/* Botón Ampliar: esquina inferior derecha del stage, sobre la imagen */
.modal__expand {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.7rem;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.92);
  color: var(--graphite);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition);
}

.modal__expand:hover {
  background: var(--white);
}

.modal__expand:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 2px;
}

.modal__expand[hidden] {
  display: none;
}

/* La imagen del stage también abre el visor */
.modal__img:not([hidden]) {
  cursor: zoom-in;
}

.modal__caption {
  text-align: center;
  color: var(--text-secondary);
  font-size: 0.9rem;
  min-height: 1.2em;
  margin: 0.85rem 0 1rem;
}

/* Contenedor de galería: por defecto stage arriba y miniaturas debajo */
.modal__gallery {
  display: flex;
  flex-direction: column;
}

.modal__gallery[hidden] {
  display: none;
}

/* Variante "selector" (PCBs con modelo 3D): miniaturas en columna a la
   izquierda del stage. */
.modal__gallery--side {
  flex-direction: row;
  gap: 0.75rem;
  align-items: stretch;
}

.modal__gallery--side .modal__stage {
  flex: 1;
  min-width: 0;
}

.modal__gallery--side .modal__thumbs {
  order: -1;
  flex-direction: column;
  flex-wrap: nowrap;
  justify-content: flex-start;
  margin-bottom: 0;
  overflow-y: auto;
}

/* En la columna lateral cada miniatura conserva la proporción natural de su
   imagen: la caja abraza a la imagen (nada de recortes) y ambas dimensiones
   están limitadas para que cuadradas y horizontales se vean de tamaño parejo. */
.modal__gallery--side .modal__thumb {
  width: auto;
  height: auto;
  aspect-ratio: auto;
  flex: 0 0 auto;
  align-self: center;
  background: none;
}

.modal__gallery--side .modal__thumb img {
  width: auto;
  height: auto;
  max-width: 96px;
  max-height: 62px;
}

/* La columna mantiene ancho estable aunque las miniaturas varíen, y su
   alto es el del stage: si no caben, se desplaza con una barra discreta. */
.modal__gallery--side .modal__thumbs {
  flex: 0 0 102px;
  align-items: center;
  align-self: stretch;
  /* Sin tamaño propio: la columna toma el alto del stage (16:9) en vez de
     estirar la galería, y lo que no cabe se desplaza. */
  contain: size;
  padding-right: 4px;
}

/* Imagen principal con un pequeño margen interior: escala las imágenes
   (sobre todo las cuadradas) para que no toquen los bordes del stage. */
.modal__gallery--side .modal__img {
  padding: 0.9rem;
}

/* El visor 3D permanece en el DOM (precargado) mientras se muestra otra
   imagen: solo se oculta visualmente junto con su badge y spinner. */
.modal__stage.model-hidden .modal__model,
.modal__stage.model-hidden .modal__badge360,
.modal__stage.model-hidden .modal__spinner {
  visibility: hidden;
  pointer-events: none;
}

.modal__thumbs {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
  margin-bottom: 1.5rem;
}

.modal__thumb {
  position: relative;
  width: 84px;
  aspect-ratio: 16 / 9;
  background: var(--graphite);
  border-radius: 7px;
  overflow: hidden;
  cursor: pointer;
  opacity: 0.5;
  border: 2px solid transparent;
  transition: opacity var(--transition), border-color var(--transition);
}

.modal__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.modal__thumb:hover {
  opacity: 0.85;
}

.modal__thumb.is-active {
  opacity: 1;
  border-color: var(--red);
}

/* Badge "360°" para la miniatura del modelo 3D */
.modal__thumb--model::after {
  content: "360°";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}

/* En pantallas angostas la columna lateral no cabe: miniaturas debajo,
   conservando el mismo escalado natural (sin recortes). */
@media (max-width: 640px) {
  .modal__gallery--side {
    flex-direction: column;
  }
  .modal__gallery--side .modal__thumbs {
    order: 0;
    flex: 0 0 auto;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-start;
    margin-top: 0.75rem;
  }
  .modal__gallery--side .modal__thumb img {
    max-width: 96px;
    max-height: 68px;
  }
}

/* Badge de "play" para la miniatura de video */
.modal__thumb--video::after {
  content: "▶";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 0.8rem;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}

.modal__video {
  position: absolute;
  inset: 0;
}

.modal__video[hidden] {
  display: none;
}

.modal__video iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

.modal__renders {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.modal__renders[hidden] {
  display: none;
}

.modal__subtitle {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  margin-bottom: 0.25rem;
}

.modal__render {
  width: 100%;
  border-radius: 10px;
}

.modal__cta {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 0.95rem;
  padding: 0.65rem 1.2rem;
}

.modal__cta.is-disabled {
  opacity: 0.5;
  pointer-events: none;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes pop-in {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.98);
  }
}

/* ===== Footer ===== */
/* Superficie dark declarada en el HTML. --text-secondary sobre grafito es
   --alloy-light (6.02:1); el rgba(255,255,255,.65) anterior era un valor suelto. */
.site-footer {
  background: var(--surface);
  color: var(--text-secondary);
  padding-block: var(--space-2xl);
  text-align: center;
  font-size: 0.9rem;
}

.site-footer a {
  color: var(--text);
}

/* ===== Responsive ===== */
/* Tarjetas destacadas: una sola columna y el lead se vuelve vertical */
@media (max-width: 720px) {
  .apps__grid {
    grid-template-columns: 1fr;
  }

  .app-card--lead .app-card__body {
    align-items: center;
    width: 100%;
    text-align: center;
    padding: 2rem 1.5rem 1.75rem;
  }



  .app-card--lead .feature__actions {
    justify-content: center;
  }
}

@media (max-width: 640px) {
  .hero__inner,
  .hero__inner.hero__inner--contact {
    grid-template-columns: 1fr;
    text-align: center;
  }

  .hero__photo {
    justify-self: center;
    margin-block: 0;
    width: 210px;
    height: 210px;
  }

  .hero__title,
  .hero__lead {
    margin-inline: auto;
  }

  .hero__cta {
    justify-content: center;
  }

  .nav__logo {
    font-size: 1.4rem;
  }

  .search-bar__row {
    grid-template-columns: 1fr;
    margin-inline: 1rem;
  }

  .search-bar__group {
    grid-column: 1;
    max-width: none;
  }

  .search-bar__featured {
    grid-column: 1;
    justify-self: stretch;
    justify-content: center;
  }

  /* Contact: los 3 círculos pasan de columna a fila, y se reordenan para
     quedar debajo de la foto (no entre el texto y la foto). */
  .hero__social {
    flex-direction: row;
    justify-content: center;
    order: 2;
    margin-block: 0.5rem 0;
  }

  .hero__inner--contact .hero__photo {
    order: 1;
  }
}

/* -------------------------------------------------------------------
   Modal a pantalla completa en móvil
   Sin márgenes ni esquinas: la imagen va a todo lo ancho arriba y las
   miniaturas en una tira que se desliza, en vez de envolverse en filas.
   ------------------------------------------------------------------- */
@media (max-width: 640px) {
  .modal {
    padding: 0;
  }

  .modal__window {
    width: 100%;
    height: 100%;
    height: 100dvh;
    max-height: 100dvh;
    max-height: none;
    border-radius: 0;
    padding: 1rem;
  }

  /* `flex: none`: en la galería lateral apilada, `flex: 1` con base 0
     dejaba el stage en 0 px de alto. */
  .modal__stage,
  .modal__gallery--side .modal__stage {
    flex: none;
    border-radius: 10px;
  }

  .modal__gallery--side .modal__thumbs {
    contain: none;
    padding-right: 0;
  }

  .modal__thumbs {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    padding-bottom: 0.25rem;
    margin-top: 0.75rem;
    margin-bottom: 0.5rem;
  }

  .modal__thumb {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
}

/* -------------------------------------------------------------------
   Visor a pantalla completa
   Dos estados: «encajada» (la imagen entera cabe en la pantalla) y
   «tamaño real» (a su resolución, recorrible arrastrando o con scroll).
   Superficie grafito: la imagen es el único elemento claro.
   ------------------------------------------------------------------- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(8, 9, 11, 0.96);
  animation: fade-in 0.18s ease;
}

.lightbox[hidden] {
  display: none;
}

.lightbox__viewport {
  position: absolute;
  inset: 0 0 4.5rem;
  display: grid;
  place-items: center;
  overflow: hidden;
  touch-action: pinch-zoom;
  cursor: zoom-in;
}

.lightbox__img {
  /* Medidas de pantalla, no %: dentro de la rejilla el % de alto no se
     resolvía y la imagen se metía bajo la barra inferior (4.5rem). */
  max-width: calc(100vw - 7rem);
  max-height: calc(100dvh - 4.5rem - 2rem);
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  background: var(--white);
  border-radius: 6px;
}

/* Tamaño real: la imagen conserva su resolución y el viewport se recorre */
.lightbox.is-zoomed .lightbox__viewport {
  display: block;
  overflow: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
  cursor: grab;
  touch-action: auto;
}

.lightbox.is-zoomed .lightbox__viewport.is-dragging {
  cursor: grabbing;
}

.lightbox.is-zoomed .lightbox__img {
  max-width: none;
  max-height: none;
  margin: 1rem;
  border-radius: 0;
}

.lightbox__btn {
  position: absolute;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 50%;
  background: rgba(20, 22, 26, 0.85);
  color: var(--white);
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.lightbox__btn:hover {
  border-color: rgba(255, 255, 255, 0.5);
}

.lightbox__btn:focus-visible,
.lightbox__zoom:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 2px;
}

.lightbox__btn[hidden] {
  display: none;
}

.lightbox__close {
  top: 1rem;
  right: 1rem;
}

.lightbox__prev,
.lightbox__next {
  top: calc(50% - 2.25rem);
}

.lightbox__prev {
  left: 1rem;
}

.lightbox__next {
  right: 1rem;
}

.lightbox__bar {
  position: absolute;
  inset: auto 0 0;
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 4.5rem;
  padding: 0 1.25rem;
  color: var(--alloy-light);
}

.lightbox__caption {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lightbox__count {
  font-family: var(--font-mono, monospace);
  font-size: 0.78rem;
}

.lightbox__zoom {
  flex: none;
  padding: 0.45rem 0.9rem;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--white);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
}

.lightbox__zoom[aria-pressed="true"] {
  background: var(--white);
  color: var(--graphite);
}

@media (max-width: 640px) {
  .lightbox__img {
    max-width: 100vw;
  }

  /* En táctil se cambia de imagen deslizando: las flechas estorban */
  .lightbox__prev,
  .lightbox__next {
    display: none;
  }

  .lightbox__bar {
    gap: 0.6rem;
    padding: 0 0.85rem;
  }
}

/* Barras de desplazamiento discretas en el modal y el visor (WebKit/Blink;
   Firefox usa scrollbar-width/scrollbar-color de cada contenedor). */
.modal__window::-webkit-scrollbar,
.modal__thumbs::-webkit-scrollbar,
.lightbox__viewport::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

.modal__window::-webkit-scrollbar-track,
.modal__thumbs::-webkit-scrollbar-track,
.lightbox__viewport::-webkit-scrollbar-track {
  background: transparent;
}

.modal__window::-webkit-scrollbar-thumb,
.modal__thumbs::-webkit-scrollbar-thumb,
.lightbox__viewport::-webkit-scrollbar-thumb {
  border: 2px solid transparent;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.22) padding-box;
}

.modal__window::-webkit-scrollbar-thumb:hover,
.modal__thumbs::-webkit-scrollbar-thumb:hover,
.lightbox__viewport::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.4) padding-box;
}

.modal__window::-webkit-scrollbar-corner,
.lightbox__viewport::-webkit-scrollbar-corner {
  background: transparent;
}
