/* =============================================================
   AGENCIA LUZ — hoja de estilos única
   Escrita mobile-first: la base es el teléfono y las media queries
   (min-width) van sumando espacio hacia arriba.

   ÍNDICE
   1.  Tokens (color, tipografía, espaciado, easings, capas)
   2.  Reset y base
   3.  Utilidades (wrap, kicker, mono, skip-link, honeypot)
   4.  Capas de fondo (luz que sigue al cursor, grano)
   5.  Botones
   6.  Navegación
   7.  Hero
   8.  Manifiesto
   9.  Servicios (grilla de tarjetas)
   10. Desarrollo (bloque destacado)
   11. Proceso (línea de tiempo)
   12. Público
   13. FAQ (acordeón)
   14. Contacto + formulario
   15. Footer + WhatsApp flotante
   16. Reveals (animación de entrada)
   17. Breakpoints
   18. Movimiento reducido (prefers-reduced-motion)
   ============================================================= */


/* =============================================================
   1. TOKENS
   Todo el color, tipo y espaciado sale de acá. Si hay que
   recolorear la marca, se toca este bloque y nada más.

   PALETA (papel claro + tres acentos):
     · Papel  → fondo cálido, casi blanco. Es el 90% de la página.
     · Azul   → color principal: CTAs, énfasis en titulares, íconos.
     · Ámbar  → "la luz": el resplandor que sigue al cursor y los
                detalles decorativos. Nunca se usa para texto chico
                (sobre papel no llega al contraste mínimo).
     · Terracota → tercer color: números, etiquetas y micro-beneficios.
   ============================================================= */
:root {
  /* Base clara */
  --paper:       #FAF8F4;   /* fondo general, blanco cálido */
  --paper-2:     #F1ECE3;   /* secciones alternas, arena */
  --surface:     #FFFFFF;   /* tarjetas y formulario */

  --ink:         #17150F;   /* texto principal */
  --ink-soft:    #4A463D;   /* párrafos */
  --ink-mute:    #6B6459;   /* metadatos (contraste AA sobre papel) */
  --line:        rgba(23, 21, 15, 0.11);
  --line-strong: rgba(23, 21, 15, 0.24);

  /* Acentos */
  --accent:      #4A6899;   /* azul apagado: CTA y énfasis (suave, no corporativo) */
  --accent-deep: #3A5279;   /* misma familia, un paso más oscuro: hover */
  --accent-ink:  #33496D;   /* versión legible para texto sobre papel */
  --accent-soft: rgba(74, 104, 153, 0.10);
  --sun:         #E8A317;   /* ámbar: la luz, sólo decorativo */
  --clay:        #C4562F;   /* terracota: números y micro-beneficios */
  --rose:        #E9A8BE;   /* rosa claro: manchas de fondo y detalles en oscuro */
  --rose-deep:   #B5567C;   /* rosa legible cuando hace falta contraste */

  /* Sección oscura (bloque de Desarrollo) — casi negro con tinte violeta,
     para que el rosa y el ámbar respiren encima sin ensuciarse */
  --dark:        #15121C;
  --dark-2:      #1E1A28;
  --dark-ink:    #F3EFEA;
  --dark-soft:   rgba(243, 239, 234, 0.72);
  --dark-mute:   rgba(243, 239, 234, 0.52);
  --dark-line:   rgba(243, 239, 234, 0.14);

  /* Sombras — en fondo claro la profundidad la dan las sombras, no los bordes */
  --shadow-sm: 0 1px 2px rgba(23, 21, 15, 0.04), 0 10px 24px -16px rgba(23, 21, 15, 0.18);
  --shadow-md: 0 2px 6px rgba(23, 21, 15, 0.05), 0 20px 40px -22px rgba(23, 21, 15, 0.28);

  /* Tipografía */
  --serif: "Instrument Serif", Georgia, "Times New Roman", serif;
  --sans:  "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --mono:  "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Escala tipográfica fluida: clamp(mínimo mobile, ideal, máximo desktop) */
  --fs-display: clamp(2.5rem, 7.4vw, 5.6rem);   /* h1 */
  --fs-h2:      clamp(1.9rem, 4.6vw, 3.4rem);
  --fs-h3:      clamp(1.28rem, 2vw, 1.55rem);
  --fs-lede:    clamp(1.02rem, 1.9vw, 1.28rem);
  --fs-body:    clamp(0.97rem, 1.3vw, 1.05rem);
  --fs-mono:    0.72rem;

  /* Espaciado */
  --gutter:      1.25rem;                          /* margen lateral en mobile */
  --maxw:        1240px;
  --maxw-narrow: 820px;
  --section-y:   clamp(4.5rem, 10vw, 9rem);        /* aire vertical por sección */
  --gap:         clamp(1rem, 2.2vw, 1.6rem);
  --radius:      16px;
  --radius-sm:   10px;

  /* Easings — un solo vocabulario de movimiento en todo el sitio */
  --ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.25, 0.46, 0.45, 0.94);

  /* Capas */
  --z-bg:   0;
  --z-body: 1;
  --z-nav:  60;
  --z-wa:   70;

  /* Fondo del nav sólido al scrollear / menú mobile: en un color propio
     para poder invertirlo en tema oscuro sin tocar el resto del sitio */
  --nav-bg:    rgba(250, 248, 244, 0.86);
  --nav-bg-solid: rgba(250, 248, 244, 0.98);

  color-scheme: light;
}

/* =============================================================
   TEMA OSCURO
   Redefine sólo los tokens de color de la sección clara: todo lo
   que ya vive en fondo oscuro (.dev, .footer, .wa) usa sus propias
   variables --dark-* y no necesita cambios. Activado por JS
   (initTheme en main.js) agregando data-theme="dark" al <html> y
   guardado en localStorage; sin JS el sitio se queda en claro.
   ============================================================= */
:root[data-theme="dark"] {
  --paper:       #15121C;
  --paper-2:     #1E1A28;
  --surface:     #211D2C;

  --ink:         #F3EFEA;
  --ink-soft:    rgba(243, 239, 234, 0.72);
  --ink-mute:    rgba(243, 239, 234, 0.52);
  --line:        rgba(243, 239, 234, 0.14);
  --line-strong: rgba(243, 239, 234, 0.26);

  --accent-soft: rgba(74, 104, 153, 0.20);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.18), 0 10px 24px -16px rgba(0, 0, 0, 0.5);
  --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.22), 0 20px 40px -22px rgba(0, 0, 0, 0.6);

  --nav-bg:       rgba(21, 18, 28, 0.86);
  --nav-bg-solid: rgba(21, 18, 28, 0.98);

  color-scheme: dark;
}


/* =============================================================
   2. RESET Y BASE
   ============================================================= */
*, *::before, *::after { box-sizing: border-box; margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;      /* scroll nativo: sin librerías, sin bugs de driver */
  scroll-padding-top: 88px;     /* que el nav fijo no tape el título al saltar */
  overflow-x: clip;             /* 'clip' y no 'hidden': no rompe position:sticky */
  background: var(--paper);
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.65;
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;   /* suaviza ClearType en Windows */
  text-rendering: optimizeLegibility;
}

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { border: 0; background: none; cursor: pointer; }
p { text-wrap: pretty; }

h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
h1 em, h2 em, h3 em {
  font-style: italic;
  color: var(--accent-ink);   /* un paso más oscuro que el botón: legible en texto */
}

/* En tema oscuro el titular del hero cambia a ámbar: el azul apagado
   pierde fuerza sobre fondo casi negro, el naranja de la marca (--sun)
   se lee mejor y conecta con la luz que sigue al cursor. */
:root[data-theme="dark"] .hero__title em { color: var(--sun); }

/* Mismo criterio en el manifiesto: el azul apagado no se lee bien sobre
   fondo casi negro, así que en oscuro pasa a ámbar como el hero. */
:root[data-theme="dark"] #manifiesto-title em { color: var(--sun); }

/* El titular de contacto usa el rosa de la marca en claro (en vez del
   azul genérico) y el mismo ámbar del resto de los títulos en oscuro. */
#contacto-title em { color: var(--rose-deep); }
:root[data-theme="dark"] #contacto-title em { color: var(--sun); }

::selection { background: var(--sun); color: #17130A; }

/* Foco visible y consistente para navegación por teclado */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
}


/* =============================================================
   3. UTILIDADES
   ============================================================= */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  position: relative;
  z-index: var(--z-body);
}
.wrap--narrow { max-width: var(--maxw-narrow); }

.section { padding-block: var(--section-y); position: relative; }

.section__head { margin-bottom: clamp(2rem, 5vw, 3.5rem); }
.section__title { font-size: var(--fs-h2); }

/* Etiqueta pequeña en monoespaciada: da el aire técnico de la marca */
.kicker, .mono {
  font-family: var(--mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.kicker { display: block; margin-bottom: 1rem; }
.kicker--accent { color: var(--clay); }

/* Enlace de salto: fuera de pantalla hasta recibir foco */
.skip-link {
  position: fixed;
  top: -100px; left: 1rem;
  z-index: 999;
  padding: 0.7rem 1.1rem;
  background: var(--accent);
  color: var(--paper);
  border-radius: 8px;
  font-weight: 500;
  transition: top 0.2s var(--ease-out);
}
.skip-link:focus { top: 1rem; }

/* Honeypot: presente en el DOM, invisible e inalcanzable para personas */
.honey {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}


/* =============================================================
   4. CAPAS DE FONDO
   .luz  → resplandor cálido que sigue al cursor (--mx / --my).
           Es la interacción firma: la marca se llama Luz. Sobre
           papel se usa muy diluido: tiñe, no mancha.
   .grain→ ruido sutil que le da textura de papel al fondo.
   Ambas son decorativas: fixed, sin eventos de puntero.
   ============================================================= */
.luz {
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  pointer-events: none;
  /* Posición inicial arriba y al centro: se ve bien aún sin mover el mouse */
  background:
    radial-gradient(620px circle at var(--mx, 50%) var(--my, 22%),
      rgba(232, 163, 23, 0.20), transparent 62%),
    radial-gradient(900px circle at calc(var(--mx, 50%) + 14%) calc(var(--my, 22%) + 12%),
      rgba(233, 168, 190, 0.22), transparent 60%);
  transition: background 0.45s var(--ease-soft);
}

/* -------------------------------------------------------------
   BLOBS — manchas de color difusas ancladas a una sección.
   Rompen la monotonía del fondo plano y se mueven muy despacio
   (28s) para que la página nunca esté del todo quieta.
   ------------------------------------------------------------- */
.blob {
  position: absolute;
  z-index: 0;
  width: min(560px, 85vw);
  aspect-ratio: 1;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.55;
  pointer-events: none;
  animation: blobDrift 28s var(--ease-soft) infinite alternate;
}
.blob--rose { top: -12%; right: -14%; background: var(--rose); }
.blob--sun  { bottom: -18%; left: -12%; background: var(--sun); opacity: 0.32; animation-duration: 34s; }

@keyframes blobDrift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-4%, 5%, 0) scale(1.12); }
}

.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  pointer-events: none;
  opacity: 0.035;               /* sobre claro, la mitad que sobre oscuro */
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Retícula de puntos, muy sutil: la textura "de plano técnico" que hace
   que el fondo se sienta diseñado y no un blanco liso. Sobre fondo oscuro
   casi no se nota (puntos oscuros sobre oscuro) y eso está bien: ahí ya
   hay halos de color haciendo ese trabajo. */
.dotgrid {
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  pointer-events: none;
  opacity: 0.05;
  background-image: radial-gradient(rgba(23, 21, 15, 0.5) 1px, transparent 1.5px);
  background-size: 28px 28px;
}


/* =============================================================
   5. BOTONES
   Altura mínima 48px en todos: área táctil cómoda en mobile.
   ============================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 48px;
  padding: 0.85rem 1.5rem;
  border-radius: 999px;
  font-size: 0.94rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-align: center;
  transition: transform 0.35s var(--ease-out),
              background-color 0.35s var(--ease-out),
              border-color 0.35s var(--ease-out),
              color 0.35s var(--ease-out),
              box-shadow 0.35s var(--ease-out);
}
.btn--primary {
  background: var(--accent);
  color: #FFFFFF;
  box-shadow: 0 12px 26px -14px rgba(74, 104, 153, 0.8);
}
.btn--ghost {
  border: 1px solid var(--line-strong);
  color: var(--ink);
  background: color-mix(in srgb, var(--surface) 60%, transparent);
}
.btn--sm { min-height: 42px; padding: 0.55rem 1.1rem; font-size: 0.86rem; }
.btn--block { width: 100%; }

/* El hover sólo se activa donde hay puntero real: en táctil se quedaría "pegado" */
@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover {
    background: var(--accent-deep);
    transform: translateY(-2px);
    box-shadow: 0 18px 32px -14px rgba(74, 104, 153, 0.95);
  }
  .btn--ghost:hover {
    border-color: var(--accent);
    color: var(--accent);
    transform: translateY(-2px);
  }
}
.btn:active { transform: translateY(0); }


/* =============================================================
   6. NAVEGACIÓN
   Fija arriba, transparente sobre el hero y con fondo sólido
   (.is-scrolled, la agrega main.js) al bajar.
   ============================================================= */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-nav);
  transition: background-color 0.4s var(--ease-out),
              border-color 0.4s var(--ease-out),
              backdrop-filter 0.4s var(--ease-out);
  border-bottom: 1px solid transparent;
}
.nav.is-scrolled {
  background: var(--nav-bg);
  border-bottom-color: var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}

.nav__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  padding: 0.85rem var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.nav__right {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--serif);
  font-size: 1.22rem;
  letter-spacing: -0.01em;
}
.brand__dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--sun);
  box-shadow: 0 0 14px 2px rgba(232, 163, 23, 0.55);
  flex: none;
}

/* Interruptor de tema: un botón redondo que alterna sol/luna con un
   crossfade simple. Vive siempre visible en el nav, mobile y desktop. */
.theme-toggle {
  position: relative;
  width: 42px; height: 42px;
  display: grid;
  place-content: center;
  border-radius: 50%;
  color: var(--ink-soft);
  flex: none;
  transition: background-color 0.3s var(--ease-out), color 0.3s var(--ease-out);
}
.theme-toggle:hover { background: var(--accent-soft); color: var(--accent); }
.theme-toggle__icon {
  grid-area: 1 / 1;
  width: 20px; height: 20px;
  transition: opacity 0.3s var(--ease-out), transform 0.4s var(--ease-out);
}
.theme-toggle__icon--moon { opacity: 0; transform: scale(0.6) rotate(-40deg); }
:root[data-theme="dark"] .theme-toggle__icon--sun  { opacity: 0; transform: scale(0.6) rotate(40deg); }
:root[data-theme="dark"] .theme-toggle__icon--moon { opacity: 1; transform: scale(1) rotate(0deg); }

/* Botón hamburguesa: dos barras que se cruzan al abrir */
.nav__toggle {
  width: 46px; height: 46px;
  display: grid;
  place-content: center;
  gap: 6px;
  border-radius: 10px;
}
.nav__bar {
  display: block;
  width: 22px; height: 1.5px;
  background: var(--ink);
  transition: transform 0.35s var(--ease-out), opacity 0.25s linear;
}
.nav__toggle[aria-expanded="true"] .nav__bar:first-child { transform: translateY(3.75px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] .nav__bar:last-child  { transform: translateY(-3.75px) rotate(-45deg); }

/* Menú mobile: panel desplegable a ancho completo bajo la barra */
.nav__menu {
  position: absolute;
  top: 100%; left: 0; right: 0;
  display: grid;
  gap: 0.35rem;
  padding: 0.75rem var(--gutter) 1.25rem;
  background: var(--nav-bg-solid);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);

  /* Cerrado por defecto: sin altura y sin foco alcanzable */
  visibility: hidden;
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 0.38s var(--ease-out), transform 0.38s var(--ease-out), visibility 0.38s;
}
.nav__menu.is-open { visibility: visible; opacity: 1; transform: translateY(0); }
.nav__menu a {
  padding: 0.7rem 0.2rem;
  min-height: 44px;
  display: flex;
  align-items: center;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line);
  opacity: 0;
  transform: translateY(-6px);
  transition: color 0.25s var(--ease-out),
              opacity 0.35s var(--ease-out),
              transform 0.35s var(--ease-out);
}
/* Cada link aparece un poco después que el anterior: da la sensación
   de un panel que se despliega, no que aparece de golpe. */
.nav__menu.is-open a { opacity: 1; transform: translateY(0); }
.nav__menu.is-open a:nth-child(1) { transition-delay: 0.05s; }
.nav__menu.is-open a:nth-child(2) { transition-delay: 0.09s; }
.nav__menu.is-open a:nth-child(3) { transition-delay: 0.13s; }
.nav__menu.is-open a:nth-child(4) { transition-delay: 0.17s; }
.nav__menu.is-open a:nth-child(5) { transition-delay: 0.21s; }
.nav__menu a:last-child { border-bottom: 0; }
.nav__menu .btn { justify-content: center; margin-top: 0.6rem; color: var(--paper); border-bottom: 0; }


/* =============================================================
   7. HERO
   Sin imagen: el elemento más grande del primer viewport es texto,
   así el LCP es prácticamente instantáneo.
   ============================================================= */
.hero {
  min-height: 100vh;
  min-height: 100svh;            /* evita el salto de la barra de Safari iOS */
  display: flex;
  align-items: center;
  padding-block: 7.5rem 5rem;
  position: relative;
}

.hero__kicker { color: var(--clay); }

/* Marcas de esquina: detalle "de blueprint" que suma sensación de producto
   diseñado a medida. Sólo desde tablet: en mobile el hero ya va justo. */
.hero__marks { display: none; }
.hero__marks .corner {
  position: absolute;
  width: 16px; height: 16px;
  opacity: 0.4;
}
.hero__marks .corner::before,
.hero__marks .corner::after { content: ""; position: absolute; background: var(--ink-mute); }
.hero__marks .corner::before { top: 50%; left: 0; right: 0; height: 1px; transform: translateY(-50%); }
.hero__marks .corner::after  { left: 50%; top: 0; bottom: 0; width: 1px; transform: translateX(-50%); }
.corner--tl { top: 6.5rem; left: var(--gutter); }
.corner--tr { top: 6.5rem; right: var(--gutter); }
.corner--bl { bottom: 2rem; left: var(--gutter); }
.corner--br { bottom: 2rem; right: var(--gutter); }

/* Círculos concéntricos: 3 anillos + 3 puntos, el mismo recurso que
   el hero de OrlandoStudio. Visibles también en mobile (antes sólo
   desde desktop). Se recortan contra la esquina superior derecha
   pero dejando entrar buena parte del diseño, no apenas un cuarto;
   gira carísimo de lento. Opacidad muy baja: es textura, no
   protagonista. */
.hero__rings {
  position: absolute;
  inset: 0;
  z-index: 0;
  color: var(--accent);
  opacity: 0.16;
  pointer-events: none;
}
/* En tema oscuro el azul apagado casi desaparece contra el fondo casi
   negro (sobre todo en mobile, donde el círculo entra más recortado y
   de canto). Se invierte a un trazo cálido y claro, más opaco, para
   que la textura siga siendo visible sin volverse protagonista. */
:root[data-theme="dark"] .hero__rings {
  color: var(--rose);
  opacity: 0.3;
}
.hero__rings svg {
  /* top/right en vw (no %) a propósito: en mobile el hero es muy alto
     (min-height:100svh) y un % ahí se dispara; en vw el recorte queda
     proporcional al ancho real en cualquier pantalla. Este <svg> NUNCA
     rota — ver .hero__rings-spin más abajo — así que esta posición es
     estable en todo momento de la animación, sin sorpresas. */
  position: absolute;
  top: -25vw;
  right: -8vw;
  width: min(76vw, 1080px);
  height: auto;
}
/* En mobile el recorte por defecto queda demasiado chico y casi no se
   nota; se agranda para que se parezca más a la proporción del hero
   de escritorio (el círculo ocupando buena parte de la esquina). */
@media (max-width: 719px) {
  .hero__rings svg {
    top: -46vw;
    right: -34vw;
    width: min(148vw, 1080px);
  }
}
/* La rotación va acá, en el <g> interno, nunca en el <svg>: si giraba
   el <svg> (un cuadrado), su caja de colisión se inflaba hasta ~41%
   en las diagonales y el círculo terminaba cruzando la línea de los
   pilares en ciertos momentos de la vuelta. Rotando sólo el contenido,
   el <svg> que lo contiene queda con un tamaño fijo siempre. */
.hero__rings-spin {
  transform-origin: 250px 250px;
  animation: heroRingsSpin 75s linear infinite;
}
@keyframes heroRingsSpin { to { transform: rotate(360deg); } }

.hero__title {
  font-size: var(--fs-display);
  max-width: 16ch;               /* nunca tan ancho como para cortar letras */
  margin-bottom: 1.5rem;
}

.hero__sub {
  font-size: var(--fs-lede);
  color: var(--ink-soft);
  max-width: 52ch;
  margin-bottom: 2.25rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 3rem;
}

/* Pilares: cinta en movimiento continuo, mismo recurso que .marquee
   en la sección de Desarrollo (ver 10. DESARROLLO). El track duplica
   la lista y se desplaza un 50% exacto: el loop no se nota. */
.hero__pillars {
  position: relative;
  overflow: hidden;
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--line);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.hero__pillars-track {
  display: flex;
  width: max-content;
  gap: 2rem;
  animation: marqueeSlide 32s linear infinite;
  font-family: var(--mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.hero__pillars-track span { white-space: nowrap; position: relative; }
.hero__pillars-track span::after { content: " ·"; color: var(--clay); margin-left: 2rem; }

/* Indicador de scroll: sólo desde tablet, en mobile ocupa lugar sin aportar */
.hero__scroll { display: none; }
.hero__scroll-line {
  display: block;
  width: 1px; height: 54px;
  margin-inline: auto 0;
  background: linear-gradient(to bottom, var(--clay), transparent);
}


/* =============================================================
   8. MANIFIESTO
   ============================================================= */
/* overflow:hidden contiene el blob para que no genere scroll horizontal */
.manifiesto { background: var(--paper-2); border-block: 1px solid var(--line); overflow: hidden; }
.manifiesto .section__title { margin-bottom: clamp(2.5rem, 6vw, 4rem); }

.pillars { display: grid; gap: clamp(1.75rem, 4vw, 2.5rem); }

.pillar__num { display: block; color: var(--clay); margin-bottom: 0.75rem; }
.pillar__title { font-size: var(--fs-h3); margin-bottom: 0.6rem; }
.pillar p { color: var(--ink-soft); }


/* -------------------------------------------------------------
   BANDA — declaración de marca SIN caja de color propia: vive sobre
   el mismo papel que las secciones vecinas, para que la transición
   sea invisible (nada de "aparece un bloque distinto de golpe").
   El efecto son dos resplandores + dos anillos concéntricos con
   movimiento continuo, en la paleta de siempre (ámbar/terracota),
   la misma idea que ya usan .blob en otras secciones pero más viva.
   ------------------------------------------------------------- */
.banda {
  /* Se guarda como variable porque los anillos necesitan calcular su
     diámetro a partir de ESTA altura: si un anillo mide más que la
     sección, overflow:hidden le corta arriba y abajo (el bug de la
     versión anterior). Atándolo a --banda-h, nunca puede pasarse. */
  --banda-h: clamp(300px, 46vh, 460px);
  position: relative;
  /* min-height, no height: con la frase en dos líneas largas y fuente
     grande, una altura fija recortaba el texto contra overflow:hidden.
     Con min-height la sección crece si hace falta y nunca corta nada;
     los anillos siguen usando --banda-h como tope de tamaño, así que
     no se disparan aunque la sección sea más alta de lo pensado. */
  min-height: var(--banda-h);
  padding-block: clamp(2.5rem, 6vw, 4rem);
  overflow: hidden;
  display: grid;
  place-items: center;
  isolation: isolate;
}
/* En mobile el 46vh dejaba mucho hueco vacío arriba/abajo del texto
   (la frase, con fuente chica, no llegaba a ocupar esa altura). Se
   baja el mínimo y se sube el tamaño de fuente relativo al viewport
   para que el texto llene mejor la sección. Los anillos escalan solos
   (dependen de --banda-h) así que nunca quedan recortados: sólo se
   achican en proporción, siempre completos. */
@media (max-width: 719px) {
  .banda { --banda-h: clamp(220px, 30vh, 320px); padding-block: clamp(2rem, 9vw, 2.75rem); }
}

.banda__fx { position: absolute; inset: 0; z-index: -1; }
.banda__glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(80px);
  will-change: transform;
}
.banda__glow--sun {
  width: min(520px, 60vw);
  aspect-ratio: 1;
  top: -20%; left: 6%;
  background: var(--sun);
  opacity: 0.22;
  animation: bandaDrift 16s var(--ease-soft) infinite alternate;
}
.banda__glow--rose {
  width: min(460px, 55vw);
  aspect-ratio: 1;
  bottom: -24%; right: 4%;
  background: var(--rose);
  opacity: 0.28;
  animation: bandaDrift 20s var(--ease-soft) infinite alternate-reverse;
}
@keyframes bandaDrift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(6%, 8%, 0) scale(1.18); }
}

/* Círculos concéntricos: el mismo SVG del hero (.hero__rings), acá
   centrado detrás de la frase en vez de recortado en una esquina.
   El calc(...) primero en el width: nunca deja que el diámetro supere
   la altura de la sección, así el círculo siempre entra completo. */
.banda__rings {
  position: absolute;
  top: 50%; left: 50%;
  width: min(640px, 84vw, calc(var(--banda-h) * 0.94));
  height: auto;
  color: var(--line-strong);
  opacity: 0.7;
  transform: translate(-50%, -50%);
}
/* Rotación en el <g> interno, no en el <svg> (mismo motivo que en
   .hero__rings-spin): así translate(-50%,-50%) queda quieto y sólo
   gira el contenido. */
.banda__rings-spin {
  transform-origin: 250px 250px;
  animation: bandaRingsSpin 150s linear infinite;
}
@keyframes bandaRingsSpin { to { transform: rotate(360deg); } }

.banda__quote {
  position: relative;
  z-index: 1;
  max-width: 24ch;
  padding-inline: var(--gutter);
  text-align: center;
  font-family: var(--sans);
  font-weight: 600;
  letter-spacing: -0.02em;
  /* 5vw se queda corto en pantallas angostas (queda en el piso del
     clamp, chica y con mucho aire alrededor); con más vw la fuente
     crece junto con el hueco que hay que llenar. */
  font-size: clamp(1.6rem, 8vw, 3rem);
  line-height: 1.16;
  color: var(--ink);
  text-wrap: balance;
}
.banda__quote em { font-style: normal; color: var(--clay); }
/* .word: sólo existe si initBandaQuote (main.js) corrió — envuelve cada
   palabra para poder animarlas de a una. display:inline-block es lo que
   permite que el transform/blur de cada palabra funcione por separado. */
.banda__quote .word { display: inline-block; will-change: transform, opacity, filter; }


/* =============================================================
   9. SERVICIOS
   Grilla de tarjetas: 1 columna en mobile, 2 en tablet, 3 en desktop.
   La séptima (destacada) ocupa todo el ancho disponible.
   ============================================================= */
.cards { display: grid; gap: var(--gap); }

.card {
  position: relative;
  padding: clamp(1.4rem, 3vw, 1.9rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: transform 0.4s var(--ease-out),
              border-color 0.4s var(--ease-out),
              box-shadow 0.4s var(--ease-out);
}
.card__num {
  position: absolute;
  top: 1.25rem; right: 1.25rem;
  color: var(--clay);
}
.card__icon {
  display: grid;
  place-content: center;
  width: 46px; height: 46px;
  margin-bottom: 1.1rem;
  border-radius: 12px;
  background: var(--accent-soft);
  color: var(--accent);
}
.card__icon svg { width: 22px; height: 22px; }
.card__title { font-size: var(--fs-h3); margin-bottom: 0.5rem; }
.card p { color: var(--ink-soft); font-size: 0.95rem; }

@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: translateY(-6px) scale(1.015);
    border-color: var(--line-strong);
    box-shadow: var(--shadow-md);
  }
}

/* Tarjeta 07: enlace a la sección de desarrollo, con más peso visual */
.card--feature {
  display: grid;
  gap: 1rem;
  background: linear-gradient(135deg, rgba(232, 163, 23, 0.14), rgba(196, 86, 47, 0.08)), var(--surface);
  border-color: rgba(196, 86, 47, 0.32);
}
.card--feature__cta { color: var(--clay); }
@media (hover: hover) and (pointer: fine) {
  .card--feature:hover { border-color: var(--clay); }
}


/* =============================================================
   10. DESARROLLO — bloque destacado, EN OSCURO
   Es el corte visual de la página: después de todo el papel claro,
   esta sección se va al casi negro con halos rosa y ámbar. Sirve
   para dos cosas: romper la monotonía del fondo y darle jerarquía
   propia al servicio más importante de la agencia.
   ============================================================= */
.dev {
  position: relative;
  padding-block: var(--section-y);
  background: var(--dark);
  color: var(--dark-ink);
  overflow: hidden;             /* contiene los halos decorativos */
}
/* Halo rosa arriba y ámbar abajo: la "luz" también existe en lo oscuro */
.dev::before,
.dev::after {
  content: "";
  position: absolute;
  width: min(1000px, 120%);
  aspect-ratio: 1;
  border-radius: 50%;
  pointer-events: none;
  filter: blur(20px);
}
.dev::before {
  top: -42%; left: 50%;
  transform: translateX(-50%);
  background: radial-gradient(circle, rgba(233, 168, 190, 0.22), transparent 62%);
}
.dev::after {
  bottom: -46%; right: -18%;
  background: radial-gradient(circle, rgba(232, 163, 23, 0.16), transparent 62%);
}

/* Luz que sigue al cursor, versión de esta sección: initDevLuz (main.js)
   escribe --dx/--dy (en % del propio bloque, no del viewport) sobre este
   elemento. Tonos rosa/ámbar más saturados que la capa global: sobre
   casi negro un resplandor tenue no se nota, acá tiene que notarse. */
.dev__luz {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(320px circle at var(--dx, 50%) var(--dy, 32%),
      rgba(233, 168, 190, 0.16), transparent 60%),
    radial-gradient(440px circle at calc(var(--dx, 50%) + 12%) calc(var(--dy, 32%) + 10%),
      rgba(232, 163, 23, 0.11), transparent 62%);
  transition: background 0.45s var(--ease-soft);
}

/* Sub-hero: una columna en mobile, texto + imagen en desktop */
.dev__top { display: grid; gap: clamp(2rem, 5vw, 3rem); margin-bottom: clamp(2.5rem, 6vw, 3.5rem); }
.dev__hero { max-width: 46rem; }
.dev__title { font-size: var(--fs-h2); margin-bottom: 1.25rem; }
.dev__title em { color: var(--rose); }         /* sobre oscuro el azul no rinde */
.dev__lede { color: var(--dark-soft); font-size: var(--fs-lede); }
.kicker--rose { color: var(--rose); }

.dev__figure {
  position: relative;
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 16 / 10;
  border: 1px solid var(--dark-line);
}
.dev__figure img {
  width: 100%; height: 100%;
  object-fit: cover;
  transform: scale(1.12);
  will-change: transform;
}

/* -------------------------------------------------------------
   MARQUEE — cinta de palabras en movimiento continuo.
   El track duplica la lista y se desplaza exactamente un 50%:
   así el loop es perfecto y no se nota el salto.
   ------------------------------------------------------------- */
.marquee {
  position: relative;
  overflow: hidden;
  padding-block: 1.1rem;
  border-block: 1px solid var(--dark-line);
  margin-bottom: clamp(2.5rem, 6vw, 3.5rem);
  /* Bordes desvanecidos: la cinta entra y sale sin cortarse en seco */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee__track {
  display: flex;
  width: max-content;
  gap: 2.5rem;
  animation: marqueeSlide 38s linear infinite;   /* lento: acompaña, no distrae */
}
.marquee__track span {
  font-family: var(--mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dark-mute);
  white-space: nowrap;
}
/* Punto separador entre palabras */
.marquee__track span::after { content: " ·"; color: var(--rose); margin-left: 2.5rem; }
@keyframes marqueeSlide {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.dev__grid { display: grid; gap: var(--gap); }

.devcard {
  padding: clamp(1.4rem, 3vw, 1.8rem);
  background: rgba(243, 239, 234, 0.045);
  border: 1px solid var(--dark-line);
  border-radius: var(--radius);
  transition: transform 0.4s var(--ease-out),
              border-color 0.4s var(--ease-out),
              background-color 0.4s var(--ease-out);
}
.devcard .card__icon { background: rgba(233, 168, 190, 0.14); color: var(--rose); }
.devcard__title { font-size: var(--fs-h3); margin-bottom: 0.5rem; }
.devcard p { color: var(--dark-soft); font-size: 0.94rem; }

/* Micro-beneficio: la línea que cierra cada tarjeta */
.devcard__benefit {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--dark-line);
  color: var(--rose) !important;
  font-size: 0.88rem !important;
  font-weight: 500;
}

/* Tarjeta de marca: la 8va celda de la grilla. No es un servicio, es el
   "punto" de identidad (el dot ámbar de siempre) puesto a trabajar acá
   adentro. En desktop ocupa el doble de ancho (ver breakpoint 960px)
   para que la fila de 7 servicios cierre justa, sin celdas vacías. */
.devcard--brand {
  position: relative;
  overflow: hidden;
  display: grid;
  align-content: center;
  gap: 0.7rem;
  background: linear-gradient(135deg, rgba(233, 168, 190, 0.14), rgba(232, 163, 23, 0.10));
  border-color: rgba(233, 168, 190, 0.32);
}
.devcard--brand__ring {
  position: absolute;
  top: 50%; right: -14%;
  width: 220px;
  aspect-ratio: 1;
  border: 1px solid rgba(232, 163, 23, 0.25);
  border-radius: 50%;
  transform: translateY(-50%);
  animation: bandaSpin 70s linear infinite;
  pointer-events: none;
}
.devcard--brand__dot {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--sun);
  box-shadow: 0 0 16px 3px rgba(232, 163, 23, 0.55);
}
.devcard--brand__brand {
  position: relative;
  color: var(--rose);
  letter-spacing: 0.18em;
}
.devcard--brand__quote {
  position: relative;
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.2rem;
  line-height: 1.3;
  color: var(--dark-ink);
  max-width: 30ch;
}

@media (hover: hover) and (pointer: fine) {
  .devcard:hover {
    transform: translateY(-6px) scale(1.015);
    border-color: rgba(233, 168, 190, 0.45);
    background: rgba(243, 239, 234, 0.075);
  }
}

.dev__cta {
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
  padding: clamp(1.75rem, 4vw, 2.5rem);
  display: grid;
  gap: 1.25rem;
  justify-items: start;
  background: linear-gradient(135deg, rgba(233, 168, 190, 0.14), rgba(232, 163, 23, 0.08));
  border: 1px solid rgba(233, 168, 190, 0.35);
  border-radius: var(--radius);
}
.dev__cta p { font-family: var(--serif); font-size: clamp(1.3rem, 3vw, 1.9rem); line-height: 1.15; }
/* Dentro del bloque oscuro el botón se invierte: papel sobre oscuro */
.dev__cta .btn--primary { background: var(--rose); color: #2A1520; box-shadow: 0 12px 26px -14px rgba(233, 168, 190, 0.9); }
@media (hover: hover) and (pointer: fine) {
  .dev__cta .btn--primary:hover { background: #F2BFD0; }
}


/* =============================================================
   11. PROCESO — línea de tiempo vertical
   La línea es un borde izquierdo en la lista; cada punto es un
   ::before posicionado sobre ese borde. Sin imagen: la línea de
   tiempo ocupa todo el ancho disponible, limitada para que los
   párrafos no se estiren de más en pantallas anchas.
   ============================================================= */
.proceso__inner { position: relative; display: grid; gap: clamp(2.5rem, 6vw, 4rem); max-width: 46rem; }

.timeline {
  list-style: none;
  padding: 0 0 0 1.9rem;
  margin: 0;
  border-left: 1px solid var(--line);
  display: grid;
  gap: clamp(2rem, 4.5vw, 2.75rem);
}
/* Línea "dibujada": una segunda línea de color, superpuesta a la pista
   tenue de .timeline, que initTimelineDraw (main.js) escala de 0 a 1
   con el scroll — simula que el recorrido se completa a medida que se
   lee. Por defecto (sin JS/GSAP) va completa: nunca desaparece nada. */
.timeline__progress {
  position: absolute;
  left: -1px; top: 0;
  width: 1px;
  height: 100%;
  background: var(--clay);
  transform-origin: top;
  transform: scaleY(1);
  pointer-events: none;
}
html.anim .timeline__progress { transform: scaleY(0); }

.step {
  position: relative;
  --dot-scale: 1;   /* visible por defecto; el JS lo anima desde 0 */
}
html.anim .step { --dot-scale: 0; }
.step::before {
  content: "";
  position: absolute;
  left: calc(-1.9rem - 5px);
  top: 0.45rem;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--clay);
  box-shadow: 0 0 0 5px rgba(196, 86, 47, 0.14);
  transform: scale(var(--dot-scale));
  transition: transform 0.3s var(--ease-out);
}
.step__num { display: block; color: var(--clay); margin-bottom: 0.4rem; }
.step__title { font-size: var(--fs-h3); margin-bottom: 0.45rem; }
.step p { color: var(--ink-soft); max-width: 56ch; }


/* =============================================================
   12. PÚBLICO
   ============================================================= */
.publico { background: var(--paper-2); border-block: 1px solid var(--line); overflow: hidden; }
.publico__grid { list-style: none; padding: 0; display: grid; gap: var(--gap); position: relative; z-index: 1; }

/* Canvas de la malla de puntos: de fondo, detrás de las tarjetas
   (que ya tienen su propio fondo sólido, así que nunca compite en
   legibilidad). main.js la dimensiona y anima. */
.dotwave {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.chip {
  padding: clamp(1.3rem, 3vw, 1.7rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) 65%, transparent);
  transition: transform 0.4s var(--ease-out),
              border-color 0.4s var(--ease-out),
              box-shadow 0.4s var(--ease-out);
}
.chip h3 { font-size: var(--fs-h3); margin-bottom: 0.4rem; }
.chip p { color: var(--ink-soft); font-size: 0.94rem; }

@media (hover: hover) and (pointer: fine) {
  .chip:hover {
    transform: translateY(-6px) scale(1.015);
    border-color: var(--line-strong);
    box-shadow: var(--shadow-md);
  }
}


/* =============================================================
   13. FAQ — acordeón con <details>
   Se usa <details> a propósito: es accesible de fábrica y sigue
   funcionando con JavaScript deshabilitado.
   ============================================================= */
.faq__list { display: grid; gap: 0.75rem; }

.faq__item {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: border-color 0.3s var(--ease-out);
}
.faq__item[open] { border-color: rgba(22, 50, 92, 0.35); }

.faq__item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 56px;
  padding: 1rem 1.15rem;
  cursor: pointer;
  list-style: none;
  font-family: var(--serif);
  font-size: clamp(1.24rem, 2vw, 1.4rem);
  line-height: 1.25;
}
.faq__item summary::-webkit-details-marker { display: none; }

/* Signo +/− dibujado con dos degradados sobre el summary */
.faq__item summary::after {
  content: "";
  flex: none;
  width: 13px; height: 13px;
  background:
    linear-gradient(var(--accent), var(--accent)) center/13px 1.5px no-repeat,
    linear-gradient(var(--accent), var(--accent)) center/1.5px 13px no-repeat;
  transition: transform 0.35s var(--ease-out);
}
.faq__item[open] summary::after { transform: rotate(135deg); }

.faq__body {
  display: grid;
  grid-template-rows: 1fr;
  padding: 0 1.15rem 1.15rem;
  color: var(--ink-soft);
  font-size: 0.95rem;
}


/* =============================================================
   14. CONTACTO + FORMULARIO
   ============================================================= */
.contacto { overflow: hidden; }   /* contiene el blob ámbar */
.contacto__inner { display: grid; gap: clamp(2.5rem, 6vw, 4rem); }
.contacto__lede { color: var(--ink-soft); font-size: var(--fs-lede); margin-top: 1.25rem; }

.contacto__data {
  list-style: none;
  padding: 1.5rem 0 0;
  margin-top: 1.75rem;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 0.9rem;
}
.contacto__data li { display: grid; gap: 0.15rem; }
.contacto__data a {
  font-size: 1.02rem;
  color: var(--ink);
  border-bottom: 1px solid var(--line-strong);
  justify-self: start;
  padding-block: 2px;
  transition: color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .contacto__data a:hover { color: var(--accent); border-color: var(--accent); }
}

/* El formulario vive en contextos de ancho muy distinto: a página completa
   en mobile/tablet y en media columna en desktop. Por eso NO se le fija un
   número de columnas por breakpoint (era el bug: en ~960-1150px quedaban dos
   columnas de 210px y los campos se rompían). Con auto-fit + minmax decide
   solo: dos columnas si entran de a 240px, una si no. */
.form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1rem;
  padding: clamp(1.4rem, 3.5vw, 2rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  align-content: start;
}
/* min-width:0 — sin esto, un <select> con opciones largas impone su ancho
   mínimo y desborda la celda de la grilla */
.field { display: grid; gap: 0.4rem; min-width: 0; }
.field--full { grid-column: 1 / -1; }
.form > button, .form__status { grid-column: 1 / -1; }
.field input, .field select, .field textarea { width: 100%; min-width: 0; max-width: 100%; }
.field label { font-size: 0.86rem; color: var(--ink-mute); }
.field__opt { opacity: 0.75; }

.field input,
.field select,
.field textarea {
  min-height: 48px;                 /* área táctil cómoda */
  padding: 0.7rem 0.9rem;
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: 0.97rem;
  transition: border-color 0.25s var(--ease-out), background-color 0.25s var(--ease-out);
}
.field textarea { min-height: 120px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: rgba(23, 21, 15, 0.38); }
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--accent);
  background: var(--surface);
}
/* Estado de error: lo marca main.js al validar */
.field.has-error input,
.field.has-error select,
.field.has-error textarea { border-color: #B3261E; }

.form__status { font-size: 0.9rem; min-height: 1.4rem; color: var(--ink-mute); }
.form__status.is-ok    { color: #1B6E4B; }
.form__status.is-error { color: #B3261E; }

/* Botón deshabilitado mientras se envía */
.form.is-sending button[type="submit"] { opacity: 0.6; pointer-events: none; }


/* =============================================================
   15. FOOTER + WHATSAPP FLOTANTE
   ============================================================= */
/* Footer oscuro: cierra la página en el mismo casi-negro que .dev y el
   final de .contacto, con un halo ámbar apenas insinuado para que la
   "luz" de la marca también aparezca en el último tramo del sitio. */
.footer {
  position: relative;
  padding-block: 3rem 2.5rem;
  border-top: 1px solid var(--dark-line);
  background: var(--dark);
  color: var(--dark-soft);
  overflow: hidden;
}
.footer::before {
  content: "";
  position: absolute;
  z-index: 0;
  width: min(640px, 100%);
  aspect-ratio: 1;
  top: -70%; left: 50%;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(232, 163, 23, 0.12), transparent 65%);
  pointer-events: none;
}
.footer__inner { position: relative; z-index: 1; display: grid; gap: 1.5rem; }
.footer .brand { color: var(--dark-ink); }
.footer__nav { display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; color: var(--dark-soft); font-size: 0.92rem; }
.footer__nav a { min-height: 32px; display: inline-flex; align-items: center; transition: color 0.25s var(--ease-out); }
@media (hover: hover) and (pointer: fine) { .footer__nav a:hover { color: var(--rose); } }
.footer__legal { color: var(--dark-mute); font-size: 0.85rem; }

.wa {
  position: fixed;
  right: 1rem; bottom: 1rem;
  z-index: var(--z-wa);
  width: 54px; height: 54px;
  display: grid;
  place-content: center;
  border-radius: 50%;
  background: #25D366;
  color: #FFFFFF;
  box-shadow: 0 14px 28px -10px rgba(37, 211, 102, 0.65), 0 2px 6px rgba(23, 21, 15, 0.12);
  transition: transform 0.35s var(--ease-out);
}
.wa svg { width: 27px; height: 27px; }
@media (hover: hover) and (pointer: fine) { .wa:hover { transform: scale(1.08); } }


/* =============================================================
   16. REVEALS
   Regla clave: el contenido es visible por defecto. Sólo se
   esconde si main.js confirma que GSAP cargó y va a animarlo
   (añade .anim al <html>). Así, si el JS falla o está desactivado,
   la página se lee igual.
   ============================================================= */
.reveal { opacity: 1; transform: none; }

html.anim .reveal { opacity: 0; transform: translateY(18px); }
html.anim .reveal.is-in { opacity: 1; transform: none; }

/* Defensa: un elemento con .reveal y texto partido nunca queda invisible */
.reveal[data-split] { opacity: 1; transform: none; }


/* =============================================================
   17. BREAKPOINTS (mobile-first, siempre min-width)
   540 · 720 · 960 · 1280
   ============================================================= */

/* --- 540px: teléfono grande --------------------------------- */
@media (min-width: 540px) {
  :root { --gutter: 1.75rem; }
  .hero__actions .btn { min-width: 0; }
}

/* --- 720px: tablet vertical --------------------------------- */
@media (min-width: 720px) {
  .cards         { grid-template-columns: repeat(2, 1fr); }
  .card--feature { grid-column: 1 / -1; grid-template-columns: 1fr auto; align-items: center; }
  .card--feature__cta { justify-self: end; }

  .dev__grid      { grid-template-columns: repeat(2, 1fr); }
  .publico__grid  { grid-template-columns: repeat(2, 1fr); }
  .pillars        { grid-template-columns: repeat(3, 1fr); gap: 2rem; }

  .dev__cta { grid-template-columns: 1fr auto; align-items: center; justify-items: start; }

  .footer__inner { grid-template-columns: auto 1fr; align-items: center; }
  .footer__nav   { justify-content: flex-end; }
  .footer__legal { grid-column: 1 / -1; }

  .wa { right: 1.5rem; bottom: 1.5rem; width: 58px; height: 58px; }
}

/* --- 960px: laptop chica / tablet horizontal ----------------- */
@media (min-width: 960px) {
  :root { --gutter: 2.5rem; }

  .nav__right { gap: 1.25rem; }

  /* El menú deja de ser panel y vuelve a ser barra horizontal */
  .nav__toggle { display: none; }
  .nav__menu {
    position: static;
    display: flex;
    align-items: center;
    gap: 2rem;
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    visibility: visible;
    opacity: 1;
    transform: none;
  }
  .nav__menu a {
    border-bottom: 0; padding: 0; min-height: 0; font-size: 0.92rem;
    opacity: 1; transform: none; transition-delay: 0s;
  }
  .nav__menu .btn { margin-top: 0; padding: 0.55rem 1.1rem; min-height: 42px; }

  .cards     { grid-template-columns: repeat(3, 1fr); }
  .dev__grid { grid-template-columns: repeat(3, 1fr); }
  /* 7 servicios + 1 tarjeta de marca = 8 celdas. En 3 columnas, 7 solas
     dejarían la última fila con 2 huecos vacíos; que la de marca ocupe
     el doble de ancho cierra la fila exacta (1 + 2 = 3). */
  .devcard--brand { grid-column: span 2; }
  .publico__grid { grid-template-columns: repeat(4, 1fr); }

  /* Sub-hero de desarrollo: texto | imagen */
  .dev__top { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); align-items: center; }

  /* Contacto en dos columnas: texto a la izquierda, formulario a la derecha.
     minmax(0,1fr) y no 1fr: sin el minmax, el ancho mínimo del contenido
     (opciones largas del select) desbalancea las dos columnas */
  .contacto__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 4rem; align-items: start; }

  /* Marcas de esquina del hero: sólo hay aire para que luzcan desde acá */
  .hero__marks { display: block; }

  /* Indicador de scroll: sólo desde acá, donde hay altura de sobra */
  .hero__scroll {
    display: grid;
    justify-items: center;
    gap: 0.75rem;
    position: absolute;
    right: var(--gutter);
    bottom: 2.5rem;
  }
  .hero__scroll .mono { writing-mode: vertical-rl; letter-spacing: 0.3em; }

}

/* --- 1280px: desktop ---------------------------------------- */
@media (min-width: 1280px) {
  /* En pantallas anchas el titular puede respirar más: menos líneas,
     renglones más largos y una mancha de texto más equilibrada */
  .hero__title { max-width: 18ch; }
  .hero__sub   { max-width: 46ch; }

  /* Un poco más de ancho para el formulario: recién acá le entran dos
     columnas cómodas (≈255px cada una) sin apretar los campos */
  .contacto__inner { grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); }
  .dev__grid   { grid-template-columns: repeat(3, 1fr); }
}


/* =============================================================
   18. MOVIMIENTO REDUCIDO
   Se respeta la preferencia del sistema: sin desplazamientos ni
   loops. Se conservan los cambios de color/opacidad para que la
   interfaz siga dando feedback (y no parezca rota).
   ============================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  /* Nada se mueve: las transiciones de transform se anulan */
  .btn, .card, .devcard, .chip, .wa { transition: background-color 0.3s linear, border-color 0.3s linear, color 0.3s linear, box-shadow 0.3s linear; }
  .btn:hover, .card:hover, .devcard:hover, .chip:hover, .wa:hover { transform: none; }

  /* La luz deja de perseguir al cursor: queda fija y suave */
  .luz { transition: none; }

  /* Efectos con loop infinito: se detienen (son los realmente intrusivos) */
  .blob, .banda__glow, .banda__rings-spin, .dotwave,
  .devcard--brand__ring, .hero__rings-spin { animation: none; }
  .marquee__track, .hero__pillars-track { animation: none; }
  .dev__figure img { transform: scale(1.02); }

  /* Los reveals se reducen a un fade sin desplazamiento */
  html.anim .reveal { transform: none; }

  /* Menú mobile: aparece de golpe, sin stagger ni desplazamiento */
  .nav__menu, .nav__menu a { transition-duration: 0.01s; transform: none; }
  .nav__menu.is-open a { transition-delay: 0s !important; }

  /* Línea de tiempo: siempre completa y con los puntos visibles */
  .timeline__progress { transform: scaleY(1) !important; }
  .step::before { transform: scale(1) !important; }
}
