/* =============================================================================
   PhotoPills · /app/ landing
   Estilos específicos de esta página. Tokens y componentes del DS:
   - .editorial-stack, .pricing-intro--v2, .pricing-faq, .premium-feature,
     .final-cta, .statement-slide, .captured-plans · vienen de common/home/
     premium-pricing (ya validados, sólo se reusan aquí).
   - .btn-store, .features-grid + .feature-tile, .review-card, .app-awards
     son NUEVOS — implementados aquí en page-CSS, candidatos a promoción al
     catálogo del DS si se reusan en otras páginas.
   ========================================================================== */


/* =============================================================================
   1 · HERO
   Layout 2-col: copy editorial a la izquierda + phone mockup a la
   derecha (con Light pollution). El phone tiene parallax suave +
   uprise scroll-driven discreto (scale 0.96 → 1, menos que Premium).
   Asimetría inversa de padding (top md, bottom lg): el section es más
   alto que el viewport (el phone se extiende por debajo, recortado por
   `overflow: clip`), así que reducimos padding-top para que el bloque
   editorial entre completo en el first fold en pantallas ~1440 (regla 8
   admite asimetría documentada en heroes).
   ========================================================================== */
.app-hero {
  position: relative;
  background: var(--gradient-soft);
  padding: var(--section-pad-y-md) var(--gutter) var(--section-pad-y-lg);
  isolation: isolate;
  overflow: clip;
}

.app-hero__grid {
  max-width: var(--max-wide);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(40px, 5vw, 72px);
  /* En desktop la fila ancla arriba: el copy parte de top y el phone
     (posicionado absolute dentro de su cell) puede extenderse hacia
     abajo libremente. En mobile el grid colapsa a 1 col y align deja
     de tener efecto. */
  align-items: start;
}

@media (min-width: 1024px) {
  .app-hero__grid {
    grid-template-columns: 1.1fr 0.9fr;
  }
}

/* Cap editorial del copy. */
.app-hero__copy {
  max-width: 56ch;
}

/* Icono PhotoPills · sello "App Store-style" arriba del título.
   La receta squircle + glass viene del componente `.app-icon` (common.css).
   Tamaño IGUALADO al de premium (`.apps-compare__icon-wrap`): mismo clamp
   (80-120px) para que sea el mismo componente a la misma medida en ambas
   páginas. Aquí solo añadimos el margen contextual al título. */
.app-hero__app-icon {
  width: clamp(80px, 26%, 96px);
  margin-bottom: clamp(16px, 1.8vw, 24px);
}

.app-hero__cta {
  margin-top: clamp(24px, 2.5vw, 36px);
  flex-wrap: wrap;
}

/* Trust line · dos bloques apilados.
   Línea 1 (.app-hero__trust-price) · mono yellow, jerarquía de precio.
   Línea 2 (.app-hero__trust-meta)  · sans gris débil, fineprint. */
.app-hero__trust {
  margin: clamp(20px, 2.5vw, 32px) 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  line-height: 1.5;
}

.app-hero__trust-price {
  font-family: var(--font-mono);
  font-size: var(--text-mono-lg);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--content-text-highlight);
}

.app-hero__trust-meta {
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  font-weight: 400;
  color: var(--content-text-default-weak);
}

/* App-rating · reusa el componente exacto de la home (.app-rating /
   .app-rating-score / .app-rating-stars / .app-rating-count, definido
   en style.css). Aquí solo ajustamos el margin-top relativo al trust. */
.app-hero__copy .app-rating {
  margin-top: clamp(12px, 1.5vw, 20px);
}

/* Phone mockup column · el phone es mucho más grande que la altura
   reservada de su cell. Está absolute-positionado dentro de un
   contenedor relativo, así puede extenderse libremente hacia abajo
   sin estirar la fila del grid. El `overflow: clip` del hero recorta
   el bottom del phone en el primer fold; al hacer scroll, el JS
   aplica un translateY negativo (rise-up) que hace que el phone suba
   más rápido que la página, revelando progresivamente la parte
   inferior de la pantalla.

   En desktop (≥1024) la cell tiene una altura reservada
   `clamp(440px, 64svh, 620px)`. El phone-mockup mide
   `max-width: 520px` (≈ alto 1098px con ratio 9:19). La diferencia
   entre el alto del phone y el de la cell es la parte cortada.

   En mobile el phone vuelve a tamaño cómodo (max-width: 320px) y se
   posiciona en flujo normal, sin cortes. */
.app-hero__phone {
  position: relative;
  width: 100%;
}

@media (min-width: 1024px) {
  .app-hero__phone {
    /* Reserva el alto vertical de la columna en el grid. El phone
       sigue siendo más alto que esto y se sale hacia abajo (visible
       hasta donde el `overflow: clip` del hero permita). */
    height: clamp(440px, 64svh, 620px);
  }
}

.app-hero__phone .phone-mockup {
  width: 100%;
  max-width: 320px;
  margin: 0 auto;
  --parallax-y: 0px;
  --uprise-progress: 0;
  transform-origin: top center;
  transform:
    translate3d(0, var(--parallax-y), 0)
    scale(calc(0.97 + 0.03 * var(--uprise-progress)));
  will-change: transform;
}

@media (min-width: 1024px) {
  .app-hero__phone .phone-mockup {
    /* En desktop, absolute dentro de la cell. `top: 30px` arranca el
       phone un poco por debajo del inicio de la cell (ajuste fino tras
       reducir el padding-top del section a `md`); el phone supera el
       alto de la cell por abajo y ese sobrante queda recortado por el
       `overflow: clip` del section. La animación de "rise" (JS) revela
       esa parte inferior conforme baja el scroll. */
    position: absolute;
    top: 30px;
    left: 0;
    right: 0;
    margin: 0 auto;
    max-width: 400px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .app-hero__phone .phone-mockup {
    --parallax-y: 0px !important;
    --uprise-progress: 1 !important;
  }
}

/* Tablet VERTICAL · el hero es la "primera pantalla": debe ocupar EXACTO
   el alto del viewport, no más. En 1 columna el phone queda debajo del
   copy y, al fijar la altura + `overflow: clip` (heredado del base),
   asoma recortado por el borde inferior — invita a hacer scroll, como el
   hero de desktop.
   Rango 721–1024 portrait · INCLUYE el iPad Pro 12.9" (1024px). A ese
   ancho el desktop pinta 2-col, así que aquí forzamos de vuelta el
   apilado en 1 columna y el phone en flujo (estas reglas ya son el
   default por debajo de 1024, donde son inocuas). */
@media (min-width: 721px) and (max-width: 1024px) and (orientation: portrait) {
  .app-hero {
    height: 100vh;   /* fallback · navegadores sin unidad svh */
    height: 100svh;
  }
  .app-hero__grid { grid-template-columns: 1fr; }
  .app-hero__phone { height: auto; }
  .app-hero__phone .phone-mockup {
    position: static;
    top: auto;
    max-width: 320px;
    /* Fijo · sin la animación scroll-driven (parallax + escala 0.97→1).
       En tablet vertical el phone se queda quieto a tamaño natural. */
    transform: none;
  }
}


/* =============================================================================
   2 · MANIFESTO  ·  Stop guessing. Start planning.
   Centered editorial-stack. Énfasis tipográfico SIN cursivas (regla 4):
   las 3 frases del medio van en weight 600 (no italic) para que destaquen
   dentro del párrafo.
   ========================================================================== */
.app-manifesto {
  background: var(--gradient-strong);
  padding: var(--section-pad-y-md) var(--gutter);
}

/* Grid 2-col · galería izquierda + copy derecha. En mobile colapsa
   a 1-col (galería arriba, copy debajo). */
.app-manifesto__grid {
  max-width: var(--max-wide);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(40px, 5vw, 80px);
  align-items: center;
}

@media (min-width: 1024px) {
  .app-manifesto__grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* Galería · UNA sola foto visible a la vez + paginador. Las slides
   están todas montadas con `position: absolute` dentro del contenedor
   `.app-manifesto__slides`, y se hace crossfade entre ellas con
   `opacity` cuando la usuaria pulsa prev/next en el `.pager`. */
.app-manifesto__gallery {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.5vw, 32px);
}

.app-manifesto__slides {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-l);
  overflow: hidden;
  background: var(--gradient-subtle);
}

.app-manifesto__slide {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  transition: opacity var(--dur-glide) var(--ease-soft);
}

.app-manifesto__slide.is-active {
  opacity: 1;
}

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

/* Copy · cap editorial canónico (48ch · mismo recipe que
   .premium-feature__text). Mantiene líneas cómodas de leer
   independiente del ancho del column. */
.app-manifesto__copy {
  max-width: 48ch;
}

/* Énfasis dentro del párrafo · sans weight 600, color highlight.
   Sustituye al italic Newsreader del briefing original (regla 4 prohíbe
   cursivas; la jerarquía se da con peso). */
.app-manifesto__emphasis {
  font-weight: 600;
  color: var(--content-text-default);
}


/* =============================================================================
   3 · ALL IN ONE APP  ·  Features grid (12 items)
   NUEVO COMPONENTE candidato al DS: `.features-grid` + `.feature-tile`.
   Patrón: 4-col grid sin gaps (hairlines entre cells dibujadas por borders
   internos), cada tile con icono amarillo + name + desc.
   ========================================================================== */
.app-features {
  background: var(--gradient-strong);
  padding: var(--section-pad-y-md) var(--gutter);
}

.app-features__header {
  max-width: var(--max-wide);
  margin: 0 auto clamp(48px, 6vw, 80px);
}

/* Features grid · cards individuales con stroke y border-radius
   (cada card es independiente, no una tabla unificada). */
.features-grid {
  max-width: var(--max-wide);
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(12px, 1.4vw, 20px);
}

/* Las feature-tiles son `.card` del DS (estáticas, sin hover · <article>) con
   `.card__icon .card__icon--mask` (icono tintado desde asset). Solo el título
   baja a body-sm porque el grid es de 4 columnas compactas (el default de la
   card es body-lg). El resto (superficie, filete, radius, icono) del DS. */
.features-grid .card__title { font-size: var(--text-body-sm); }

.app-features__more {
  max-width: var(--max-wide);
  margin: clamp(32px, 4vw, 56px) auto 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--text-mono-md);
  letter-spacing: 0.04em;
  color: var(--content-text-default-weak);
}


/* =============================================================================
   4 · CAPTURED PLANS
   Reusa el componente canónico del home (`.captured-plans` +
   `.plans-carousel` + `.plan-slide` + `.ba-slider` + `.plan-story` +
   `.author-credit` + `.badge` + `.plan-quote`) sin overrides locales.
   ========================================================================== */


/* =============================================================================
   5–9 · CAPABILITIES · Plan / Visualize / Build / Calculate / Glance
   Las 5 viven dentro de `.app-capabilities`, que:
     · Unifica el fondo (anulando la alternancia bg-bold ↔
       gradient-subtle de `.premium-feature:nth-of-type(even)`).
     · Aloja la línea SERPIENTE en SVG (z-index 0 · detrás de las
       sections que van a z-index 1). El `d` se genera por JS desde la
       posición real de los 5 `.capability-dot`. La línea pasa SIEMPRE
       detrás de los placeholders.
   El dibujado scroll-driven lo gestiona el JS via `stroke-dashoffset`
   directo en el style del path (no via CSS variable, para evitar bugs
   de normalización con `pathLength`).
   ========================================================================== */
.app-capabilities {
  position: relative;
  background: var(--gradient-strong);
}

/* Cada capability dentro del wrapper · transparente (el fondo lo
   manda el wrapper) y a z-index 1 (por encima del SVG de la línea). */
.app-capabilities .premium-feature,
.app-capabilities .premium-feature:nth-of-type(even) {
  background: transparent;
}

.app-capabilities .premium-feature {
  position: relative;
  z-index: 1;
}

/* SVG contenedor de la línea · ocupa todo el wrapper, queda detrás
   de las sections (z-index 0). `preserveAspectRatio="none"` (en el
   tag) estira el path con el alto del wrapper; el `vector-effect:
   non-scaling-stroke` (en el path) mantiene el grosor constante.
   `overflow: visible` deja que el drop-shadow del path bleed past
   los bordes del SVG sin clip. */
.capabilities-line {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 0;
}

/* Path serpiente · MISMO recipe visual que `.loop-arc` en home
   (stroke yellow-5, width 2, linecap round, drop-shadow yellow al
   35% opacity). `stroke-dasharray` y `stroke-dashoffset` los setea
   el JS directamente (usando `getTotalLength()`) — evitamos
   `pathLength` por bug de normalización con `d` inicial vacío. */
.capabilities-line__path {
  fill: none;
  stroke: var(--yellow-5);
  stroke-width: 2;
  stroke-linecap: round;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--yellow-5) 35%, transparent));
  /* Transición relajada · la línea persigue al scroll con inercia,
     se siente cinematográfica más que reactiva. `--dur-glide` (600ms)
     + `--ease-soft` da una llegada suave a cada nuevo punto del path. */
  transition: stroke-dashoffset var(--dur-glide) var(--ease-soft);
}

@media (max-width: 1024px) {
  /* Móvil/tablet · en 1 columna el serpenteo (SVG) no tiene zigzag que
     dibujar. Lo sustituimos por un TIMELINE vertical a la IZQUIERDA:
     un raíl amarillo estático + un nodo por capability alineado con su
     título. Traduce el "recorrido que conecta las 5 capacidades" al
     apilado vertical, sin depender de scroll (línea estática). */
  .capabilities-line {
    display: none;
  }

  .app-capabilities {
    --rail-gutter: 36px;
    /* Altura del nodo dentro de cada sección · centrado con el título
       (título top ~80 + media altura ~22 = ~102). Escala con el ancho. */
    --node-top: clamp(96px, 26vw, 150px);
    /* Progreso del relleno (0→1) · lo sube el JS a golpe de scroll.
       --rail-top y --rail-total (px, geometría real nodo→nodo) también
       los pone el JS midiendo los títulos. */
    --rail-progress: 0;
    padding-left: var(--rail-gutter);
  }
  /* El dot centrado bajo el phone (para el serpenteo) no va en el raíl. */
  .app-capabilities .capability-dot {
    display: none;
  }
  /* TRACK · el raíl completo (nodo→nodo) en amarillo tenue · siempre
     visible, marca el recorrido. Geometría (top/altura) del JS; fallback
     a --node-top / 0 antes de que el JS mida. */
  .app-capabilities::after {
    content: '';
    position: absolute;
    left: calc(var(--rail-gutter) / 2 - 1px);
    top: var(--rail-top, var(--node-top));
    height: var(--rail-total, 0px);
    width: 2px;
    background: color-mix(in srgb, var(--yellow-5) 22%, transparent);
    z-index: 0;
    pointer-events: none;
  }
  /* FILL · el raíl brillante que CRECE con el scroll (--rail-progress).
     Transición suave (dur-glide) → persigue al scroll con inercia, como
     el serpenteo de desktop. */
  .app-capabilities::before {
    content: '';
    position: absolute;
    left: calc(var(--rail-gutter) / 2 - 1px);
    top: var(--rail-top, var(--node-top));
    height: calc(var(--rail-total, 0px) * var(--rail-progress));
    width: 2px;
    background: var(--yellow-5);
    box-shadow: 0 0 6px color-mix(in srgb, var(--yellow-5) 35%, transparent);
    z-index: 0;
    pointer-events: none;
    transition: height var(--dur-glide) var(--ease-soft);
  }
  /* Nodo del timeline · sobre el raíl, centrado con el título.
     `--node-y` (px, centro REAL del título dentro de su sección) lo
     mide el JS — misma referencia que --rail-top/--rail-total, así el
     primer/último nodo caen EXACTAMENTE donde el raíl empieza/termina
     a cualquier ancho. Fallback a --node-top (estimate) antes de medir.
     translate(-50%,-50%) centra el nodo sobre ese Y (no cuelga por
     debajo), así el punto queda justo sobre el extremo del raíl. */
  .app-capabilities .premium-feature::before {
    content: '';
    position: absolute;
    left: calc(var(--rail-gutter) / -2);
    transform: translate(-50%, -50%);
    top: var(--node-y, var(--node-top));
    width: 12px;
    height: 12px;
    background: var(--yellow-5);
    border-radius: var(--radius-circle);
    box-shadow: var(--glow-action-soft);
    z-index: 1;
  }
}

@media (max-width: 1024px) and (prefers-reduced-motion: reduce) {
  /* Sin scroll-animation · el raíl se muestra completo y estático. */
  .app-capabilities::before {
    transition: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Sin animación scroll-driven · el JS no corre, así que el path
     queda con su `d` inicial. Aquí solo desactivamos la transición
     por consistencia. */
  .capabilities-line__path {
    transition: none;
  }
}

/* Dot · un punto amarillo bajo cada placeholder, centrado
   horizontalmente. MISMA receta visual que `.loop-stop` en la home
   (The Loop): 14×14 yellow con un drop-shadow yellow al 45% opacity.
   En HTML usamos `box-shadow` (equivalente al `drop-shadow` filter
   sobre un círculo). Vive dentro de `.premium-feature__visual` (que
   pasa a `position: relative`), así sigue al placeholder esté en la
   columna izquierda o derecha.

   Estados (por ahora todos en "unfocused" · sin trigger JS):
     .capability-dot              · UNFOCUSED · glow suave (default)
     .capability-dot.is-active    · FOCUSED   · glow intenso (futuro)
*/
.app-capabilities .premium-feature__visual {
  position: relative;
  /* Override del card chrome de `.premium-feature__visual` (bg,
     border, aspect-ratio) · ahora el visual aloja un phone mockup
     que ya tiene su propio chrome (frame, notch, pantalla), no hace
     falta caja. */
  aspect-ratio: auto;
  background: transparent;
  border: 0;
  box-shadow: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Phone mockup dentro del visual · mismo recipe que `.phone-mockup`
   de la home pero con max-width adaptado al column estrecho. */
.app-capabilities .phone-mockup {
  width: 100%;
  max-width: 280px;
}

/* Grid de cada capability · MÁS ESTRECHO que el resto de la página
   (max-width 920 vs --max-wide 1240) Y CON COLUMNAS ASIMÉTRICAS:
     - Texto · 480 px ≈ 48ch (el cap habitual del párrafo, mantiene
       las mismas roturas de línea que con el grid ancho).
     - Phone · 320 px (suficiente para el mockup de 280 + aire
       lateral).
   Reduce drasticamente el "vacío" entre el bloque de texto y el
   mockup, sin cambiar la altura del párrafo. */
.app-capabilities .premium-feature__grid {
  max-width: 920px;
}

@media (min-width: 1024px) {
  .app-capabilities .premium-feature__grid {
    grid-template-columns: 480px 320px;
    column-gap: clamp(40px, 4vw, 64px);
  }
  /* Sections con `--reverse` (Plan, Build, Glance) tienen el phone
     a la izquierda y el texto a la derecha · swapeamos los anchos
     para que el texto siga en la columna ancha (480) y el phone en
     la estrecha (320). */
  .app-capabilities .premium-feature__grid--reverse {
    grid-template-columns: 320px 480px;
  }
}

.capability-dot {
  position: absolute;
  bottom: -40px;
  left: 50%;
  transform: translateX(-50%);
  width: 14px;
  height: 14px;
  background: var(--content-text-highlight);
  border-radius: var(--radius-circle);
  pointer-events: none;
  /* Unfocused · glow suave (tokens --glow-action-* del DS). */
  box-shadow: var(--glow-action-soft);
  transition: box-shadow var(--dur-instant) var(--ease-out);
}

.capability-dot.is-active {
  /* Focused · glow más intenso, igual que .loop-stop.is-active. */
  box-shadow: var(--glow-action-strong);
}


/* =============================================================================
   10 · AWARDS
   Centered editorial-stack + display amount en yellow (uso puntual del
   ramp --text-display-* con color del DS).

   Cards desperdigadas (`.award-card`) en las franjas laterales del section,
   posicionadas en absolute alrededor de la columna editorial central
   (~600px). Rotaciones suaves -4°/+3° para sensación "polaroid lanzada
   sobre la mesa". Las cards no son interactivas (pointer-events:none en
   el contenedor) salvo los handles de Instagram (pointer-events:auto).
   En mobile y tablet se ocultan — el layout estrecho no tiene franjas
   laterales donde colocarlas sin pisar texto.
   ========================================================================== */
.app-awards {
  position: relative;
  overflow: hidden;
  background: var(--gradient-subtle);
  padding: var(--section-pad-y-md) var(--gutter);
}

.app-awards__inner {
  position: relative;
  z-index: 1;
  max-width: 600px;
  margin: 0 auto;
}

/* Contenedor full-section · las cards se anclan al centro vía
   `calc(50% - Xpx)` para mantener distancias fijas al texto central
   (~30px gap del inner card al borde de la columna de 600px) y poder
   extenderse hacia los laterales en viewports anchos. En pantallas
   estrechas las cards "outer" quedan recortadas por el `overflow: hidden`
   del section — comportamiento aceptado. */
.app-awards__cards {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

/* Polaroid en oscuro · figcaption integrada (no overlay sobre la foto).
   La rotación se aplica via CSS custom property `--rot` (definida en cada
   modifier .award-card--N), así el transform base se compone con scale
   en hover sin perder el ángulo. `pointer-events: auto` sobreescribe el
   none del contenedor para que el hover funcione card a card. */
.award-card {
  --rot: 0deg;
  position: absolute;
  width: 220px;
  margin: 0;
  background: var(--gradient-subtle);
  border-radius: var(--radius-m);
  border: 1px solid var(--border-neutral-soft);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
  pointer-events: auto;
  transform: rotate(var(--rot));
  transition:
    transform var(--dur-soft) var(--ease-out),
    box-shadow var(--dur-soft) var(--ease-out);
  will-change: transform;
}

/* Hover · sube un poco, escala discreta, sombra más fuerte y rotación
   neutralizada (la card se "endereza" al pasar el ratón). z-index muy
   alto para que la card hovered siempre pase por encima de las vecinas
   (el stack base por fila ya llega hasta z:3). */
.award-card:hover {
  transform: rotate(0deg) translateY(-6px) scale(1.06);
  box-shadow: var(--shadow-strong);
  z-index: 100;
}

@media (prefers-reduced-motion: reduce) {
  .award-card,
  .award-card:hover {
    transition: none;
    transform: rotate(var(--rot));
  }
}

.award-card__photo {
  aspect-ratio: 9 / 8;
  overflow: hidden;
}

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

.award-card__credit {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
}

.award-card__avatar {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-circle);
  object-fit: cover;
  flex-shrink: 0;
}

.award-card__meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.award-card__name {
  font-family: var(--font-sans);
  font-size: var(--text-body-xs);
  font-weight: 500;
  color: var(--content-text-default);
  line-height: 1.2;
}

.award-card__handle {
  font-family: var(--font-mono);
  font-size: var(--text-mono-xs);
  color: var(--content-text-default-weak);
  text-decoration: none;
  line-height: 1.3;
}

.award-card__handle:hover {
  color: var(--content-text-highlight);
}

/* Posiciones individuales · 16 cards repartidas en 3 columnas por lado
   con patrón 3-2-3 (alternancia "intercalada"):
     · Inner col (1-3 izq, 9-11 der): 3 cards alineadas en y=2%, 35%, 68%
     · Middle col (4-5 izq, 12-13 der): 2 cards offset a y=18%, 51%
       (entre los huecos de las columnas vecinas → ritmo masonry)
     · Outer col (6-8 izq, 14-16 der): 3 cards alineadas en y=4%, 37%, 70%
       (micro-offset respecto a inner para evitar grid percibido)

   Anclas horizontales para cards de 220px:
     · Inner:  calc(50% - 580px) · gap ~60px al borde del texto
       central (600px wide → 300px desde el centro)
     · Middle: calc(50% - 830px) · 250px más afuera (220 card + 30 gap)
     · Outer:  calc(50% - 1080px) · otros 250px más afuera

   Cards 220px wide con aspect 9/8 casi-square (1.125) → alto ~246px.
   Las 3 filas se concentran en el centro vertical del section
   (5-7% / 32-34% / 59-61%) para liberar ~40px de aire por arriba y
   ~30px por abajo. Eso produce ~55-60px de overlap entre filas
   consecutivas — aceptado, refuerza la sensación "polaroids
   apiladas". Las captions con nombre+handle siempre quedan dentro
   del area visible del section.

   En viewports estrechos las cols outer y middle se recortan por
   `overflow: hidden` del section. Col outer pide ~2100px+ para
   asomar — "hasta 3 columnas en pantallas grandes". */

/* Col 1 · INNER izq · 3 cards */
.award-card--1 { top:  5%; left: calc(50% - 580px); --rot: -3deg; }
.award-card--2 { top: 32%; left: calc(50% - 580px); --rot:  4deg; }
.award-card--3 { top: 59%; left: calc(50% - 580px); --rot: -2deg; }

/* Col 2 · MIDDLE izq · 2 cards offset */
.award-card--4 { top: 19%; left: calc(50% - 830px); --rot:  2deg; }
.award-card--5 { top: 46%; left: calc(50% - 830px); --rot: -4deg; }

/* Col 3 · OUTER izq · 3 cards */
.award-card--6 { top:  7%; left: calc(50% - 1080px); --rot:  3deg; }
.award-card--7 { top: 34%; left: calc(50% - 1080px); --rot: -3deg; }
.award-card--8 { top: 61%; left: calc(50% - 1080px); --rot:  4deg; }

/* Col 4 · INNER der · 3 cards (mirror de 1-3) */
.award-card--9  { top:  5%; right: calc(50% - 580px); --rot:  3deg; }
.award-card--10 { top: 33%; right: calc(50% - 580px); --rot: -4deg; }
.award-card--11 { top: 60%; right: calc(50% - 580px); --rot:  2deg; }

/* Col 5 · MIDDLE der · 2 cards offset */
.award-card--12 { top: 20%; right: calc(50% - 830px); --rot: -2deg; }
.award-card--13 { top: 47%; right: calc(50% - 830px); --rot:  4deg; }

/* Col 6 · OUTER der · 3 cards */
.award-card--14 { top:  7%; right: calc(50% - 1080px); --rot: -3deg; }
.award-card--15 { top: 34%; right: calc(50% - 1080px); --rot:  3deg; }
.award-card--16 { top: 61%; right: calc(50% - 1080px); --rot: -4deg; }

/* Stack z-index inverso al DOM · las cards de filas superiores tapan
   a las inferiores (no al revés). Así, cuando las fotos se solapan
   verticalmente, la caption con el nombre del fotógrafo siempre queda
   visible: la card de fila 1 cubre la card de fila 2 por su parte
   superior (la foto) pero NO la inferior (la caption), porque la
   card 2 estaba debajo en el stack.
   - Fila 1 (top) y middle upper → z 3
   - Fila 2 y middle lower       → z 2
   - Fila 3 (bottom)             → z 1 (default) */
.award-card--1,
.award-card--4,
.award-card--6,
.award-card--9,
.award-card--12,
.award-card--14 { z-index: 3; }

.award-card--2,
.award-card--5,
.award-card--7,
.award-card--10,
.award-card--13,
.award-card--15 { z-index: 2; }

/* ── Awards en VERTICAL (tablet V + móvil) ──────────────────────────
   En horizontal las polaroids enmarcan el texto por los LADOS. En
   vertical no hay franjas laterales, así que el mismo "lanzado sobre
   la mesa" se traslada a DOS BANDAS: una ARRIBA del texto (cards 1–8)
   y otra ABAJO (cards 9–16). Se anclan al top/bottom del section, se
   reparten en horizontal centradas en 50% con jitter + rotación, y el
   borde del section (overflow:hidden) recorta lo que no cabe — igual
   que en desktop recorta las columnas exteriores. La columna editorial
   deja aire arriba/abajo (padding-block) para no pisarse con las bandas. */
@media (max-width: 1024px) {
  .app-awards { padding-block: 0; }
  .app-awards__cards { display: block; }

  .award-card { width: clamp(150px, 40vw, 200px); }

  .app-awards__inner { padding-block: clamp(336px, 60vw, 376px); }

  /* BANDA SUPERIOR · cards 1–8 · ancladas arriba */
  .award-card--1,
  .award-card--2,
  .award-card--3,
  .award-card--4,
  .award-card--5,
  .award-card--6,
  .award-card--7,
  .award-card--8 { bottom: auto; right: auto; }

  .award-card--1 { top: 64px; left: calc(50% - 744px); --rot: -4deg; }
  .award-card--2 { top: 80px; left: calc(50% - 560px); --rot:  3deg; }
  .award-card--3 { top: 64px; left: calc(50% - 376px); --rot: -2deg; }
  .award-card--4 { top: 80px; left: calc(50% - 192px); --rot:  4deg; }
  .award-card--5 { top: 64px; left: calc(50% - 8px); --rot: -3deg; }
  .award-card--6 { top: 80px; left: calc(50% + 176px); --rot:  2deg; }
  .award-card--7 { top: 64px; left: calc(50% + 360px); --rot: -4deg; }
  .award-card--8 { top: 80px; left: calc(50% + 544px); --rot:  3deg; }

  /* BANDA INFERIOR · cards 9–16 · ancladas abajo */
  .award-card--9,
  .award-card--10,
  .award-card--11,
  .award-card--12,
  .award-card--13,
  .award-card--14,
  .award-card--15,
  .award-card--16 { top: auto; right: auto; }

  .award-card--9  { bottom: 64px; left: calc(50% - 744px); --rot:  3deg; }
  .award-card--10 { bottom: 80px; left: calc(50% - 560px); --rot: -3deg; }
  .award-card--11 { bottom: 64px; left: calc(50% - 376px); --rot:  4deg; }
  .award-card--12 { bottom: 80px; left: calc(50% - 192px); --rot: -2deg; }
  .award-card--13 { bottom: 64px; left: calc(50% - 8px); --rot:  3deg; }
  .award-card--14 { bottom: 80px; left: calc(50% + 176px); --rot: -4deg; }
  .award-card--15 { bottom: 64px; left: calc(50% + 360px); --rot:  2deg; }
  .award-card--16 { bottom: 80px; left: calc(50% + 544px); --rot: -3deg; }
}

/* ── Awards · MÓVIL (≤480) ──────────────────────────────────────────
   En móvil basta con 3 polaroids por banda (más grandes) para dar el
   marco. Ocultamos el resto y recentramos las 3 de cada banda alrededor
   del eje: la central entera + las dos laterales asomando (peek). Más
   padding-block que en tablet → más aire entre las cards y el texto. */
@media (max-width: 480px) {
  .award-card--4,
  .award-card--5,
  .award-card--6,
  .award-card--7,
  .award-card--8,
  .award-card--12,
  .award-card--13,
  .award-card--14,
  .award-card--15,
  .award-card--16 { display: none; }

  .award-card { width: clamp(160px, 46vw, 200px); }

  .app-awards__inner { padding-block: clamp(348px, 88vw, 384px); }

  /* 3 arriba · centradas (peek lateral) */
  .award-card--1 { left: calc(50% - 216px); }
  .award-card--2 { left: calc(50% -  86px); }
  .award-card--3 { left: calc(50% +  44px); }

  /* 3 abajo · mismo reparto */
  .award-card--9  { left: calc(50% - 216px); }
  .award-card--10 { left: calc(50% -  86px); }
  .award-card--11 { left: calc(50% +  44px); }
}

/* Display prize · número grande amarillo seguido del label. Componente
   ad-hoc, candidato a `.display-prize` si se reusa. */
.app-awards__prize {
  margin: clamp(40px, 5vw, 64px) auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

.app-awards__prize-amount {
  font-family: var(--font-sans);
  font-size: clamp(var(--text-display-md), 8vw, var(--text-display-xl));
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--content-text-highlight);
}

.app-awards__prize-label {
  font-family: var(--font-mono);
  font-size: var(--text-mono-md);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--content-text-default);
}


/* =============================================================================
   11 · REVIEWS
   NUEVO COMPONENTE candidato al DS: `.review-card` (5 estrellas + título
   + quote + autor). Distinto de `.captured-plan-card` (que mezcla foto +
   cita); este es solo testimonio de tienda.
   ========================================================================== */
.app-reviews {
  background: var(--bg-bold);
  padding: var(--section-pad-y-md) var(--gutter);
}

.app-reviews__header {
  max-width: var(--max-wide);
  margin: 0 auto clamp(40px, 5vw, 64px);
}

.app-reviews__stats {
  margin: clamp(16px, 2vw, 28px) 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono-md);
  letter-spacing: 0.02em;
  color: var(--content-text-default-weak);
  text-align: center;
}

.app-reviews__stats strong {
  color: var(--content-text-default);
  font-weight: 500;
}

.app-reviews__grid {
  max-width: var(--max-wide);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(20px, 2.5vw, 32px);
}

@media (min-width: 720px) {
  .app-reviews__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .app-reviews__grid { grid-template-columns: repeat(3, 1fr); }
}

/* Las reviews son `.card` del DS. Se conservan solo las estrellas (rating de
   dominio). A la card se le da el aire de testimonio (más gap/padding que el
   default, que es para listados densos) y el pie pegado abajo (card__text con
   flex:1 → el autor alinea abajo en todas las cards de la fila).
   Nota: el título pasa a weight 600 (regla de títulos del DS; antes 500). */
.app-reviews__grid .card__body { gap: 1.25rem; padding: clamp(24px, 2.5vw, 36px); }
.app-reviews__grid .card__text { flex: 1; }

.review-card__stars {
  display: flex;
  gap: 3px;
  color: var(--content-text-highlight);
}
.review-card__stars svg {
  width: 16px;
  height: 16px;
}

.app-reviews__footer {
  max-width: var(--max-wide);
  margin: clamp(32px, 4vw, 48px) auto 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--text-mono-sm);
  letter-spacing: 0.02em;
  color: var(--content-text-default-weak);
}


/* =============================================================================
   12 · PRICING  ·  Card single (one-time payment)
   ========================================================================== */
.app-pricing {
  background: var(--gradient-subtle);
  padding: var(--section-pad-y-md) var(--gutter);
}

.app-pricing__header {
  max-width: 720px;
  margin: 0 auto clamp(40px, 5vw, 64px);
}

/* Card "Get the app" · misma receta que `.plan-card` de
   premium-pricing (gradient-subtle + hairline + radius-l + shadow-soft)
   con el añadido del icono PhotoPills (squircle + glass highlight)
   arriba del header. El precio en blanco (no yellow): es información
   de producto, no una acción ni un acento. */
.app-pricing__card {
  max-width: 520px;
  margin: 0 auto;
  background: var(--gradient-subtle);
  border: 1px solid var(--border-neutral-soft);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-soft);
  padding: clamp(32px, 4vw, 56px);
  text-align: center;
}

/* App icon dentro de la card · receta squircle+glass del componente
   `.app-icon` (common.css). Aquí solo size + margen contextuales. */
.app-pricing__app-icon {
  width: clamp(64px, 6vw, 84px);
  margin-bottom: 24px;
}

/* Head · name + tagline (mismo recipe que .plan-card__head/__name/
   __tagline). */
.app-pricing__head {
  margin-bottom: 24px;
}

.app-pricing__name {
  margin: 0 0 4px;
  font-family: var(--font-sans);
  font-size: var(--text-heading-sm);
  font-weight: 600;
  line-height: 1.2;
  color: var(--content-text-default);
}

.app-pricing__tagline {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  line-height: 1.4;
  color: var(--content-text-default-weak);
}

/* Price block · amount + per inline (igual que .plan-card__price). */
.app-pricing__price {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  margin-bottom: 4px;
}

.app-pricing__amount {
  font-family: var(--font-sans);
  font-size: clamp(36px, 4vw, 44px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--content-text-default);
}

.app-pricing__per {
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  color: var(--content-text-default-weak);
}

.app-pricing__price-note {
  margin: 0 0 24px;
  min-height: 20px;
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  color: var(--content-text-default-weak);
}

.app-pricing__ctas {
  justify-content: center;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}

.app-pricing__fineprint {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  line-height: 1.5;
  color: var(--content-text-default-weak);
}


/* =============================================================================
   13 · FINAL CTA · overrides locales
   El componente `.final-cta` viene de home/style.css. Aquí añadimos:
   - Logo SVG ampliado (240×36 vs 160×24 default) — anchor visual.
   - Price block (amount/per/note/fineprint) mismo recipe que
     `.app-pricing__price*` (pricing card "Get the app").
   - Override de visibilidad para las words del statement-headline
     (garantiza que el texto siempre se vea aunque el observer del
     reveal cinematográfico no dispare por la altura de la sección).
   Los márgenes logo/h2 se dejan en sus defaults de la home (logo
   1rem, h2 3rem) → aire generoso entre logo y buttons.
   ========================================================================== */

/* Words del statement-headline · garantizar visibilidad siempre.
   Por defecto en `common.css`, `.statement-headline .word` arranca con
   opacity:0 + blur + translateY a la espera del IntersectionObserver
   que añade `.is-visible` al `.statement-slide` (threshold 0.45). Con
   el extra contenido del final-cta del app page (price block +
   fineprint) la sección puede no alcanzar el 45% del viewport en
   pantallas grandes — el observer nunca dispara y las palabras quedan
   invisibles ("texto invisible entre logo y botones"). Forzamos el
   estado visible; la animación cinematográfica se sacrifica a cambio
   de fiabilidad. */
.page-app .final-cta .statement-headline .word {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Price block · MISMO recipe que `.app-pricing__price/__amount/__per/
   __price-note/__fineprint`. Margin-top sobre los CTAs para abrir aire. */
.final-cta__price {
  margin-top: clamp(20px, 2.5vw, 32px);
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  margin-bottom: 4px;
}

.final-cta__amount {
  font-family: var(--font-sans);
  font-size: clamp(36px, 4vw, 44px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--content-text-default);
}

.final-cta__per {
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  color: var(--content-text-default-weak);
}

.final-cta__price-note {
  margin: 0 0 4px;
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  color: var(--content-text-default-weak);
}

.final-cta__fineprint {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-body-sm);
  line-height: 1.5;
  color: var(--content-text-default-weak);
}


/* =============================================================================
   14 · PREMIUM TEASER  ·  bridge a /premium
   Layout 2-col: copy editorial izq + laptop mockup der (mismo
   componente `.laptop-showcase` / `.laptop-mockup` que la home y
   Premium). CTA primario en yellow.
   ========================================================================== */
.app-premium-teaser {
  background: var(--gradient-soft);
  padding: var(--section-pad-y-md) var(--gutter);
  position: relative;
  isolation: isolate;
}

.app-premium-teaser__grid {
  max-width: var(--max-wide);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(56px, 6vw, 92px);
  align-items: center;
}

@media (min-width: 1024px) {
  .app-premium-teaser__grid {
    grid-template-columns: 1fr 1fr;
  }
}

.app-premium-teaser__copy {
  max-width: 48ch;
}

.app-premium-teaser__cta {
  margin-top: clamp(28px, 3vw, 40px);
}

.app-premium-teaser__visual {
  width: 100%;
  display: flex;
  justify-content: center;
}


/* =============================================================================
   `.btn-store` · MODIFIER de `.btn .btn-ghost` para los CTAs App Store /
   Google Play. Hereda el `--radius-full` del `.btn` base (pill canónico
   del DS) y solo añade el `icon + label` en una línea — coherente con
   el resto de pills de la home/Premium. El SVG va a la izquierda del
   texto, ambos en la misma baseline.
   ========================================================================== */
.btn-store {
  gap: 0.55rem;
}

.btn-store__icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

/* CTA del teaser de Premium · alineado al inicio de la columna editorial
   (left-aligned), no centrado. El equivalente para `.app-hero__cta` ya
   está cubierto arriba (column + align-items:flex-start). */
.app-premium-teaser__cta {
  justify-content: flex-start;
}


/* =============================================================================
   FAQ · "Quick questions before you download"
   Reusa `.pricing-faq` de premium-pricing (que va sobre `--gradient-strong`).
   En /app/ la queremos sobre NEGRO puro (`--bg-bold`) — override local; el
   style.css de app carga después que premium-pricing, así que gana sin
   afectar a la página de pricing.
   ========================================================================== */
main .pricing-faq {
  background: var(--bg-bold);
}


/* =============================================================================
   Responsive · narrow tweaks
   ========================================================================== */
@media (max-width: 1024px) {
  .features-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 720px) {
  .features-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  /* Hero title a 56 en móvil (suelo heading-xl), igual que Pricing /
     Home / Experiences. El markup usa `.pricing-intro__title-v2` (clase
     que no existe aquí → caía a `.section-title`, 40px); esto lo alinea
     con el resto de heroes. `.section-title` ya lleva text-wrap: balance,
     así que "PhotoPills App." reparte el corte. */
  /* Título del hero · patrón DS "punto intermedio de hero" (QA #33): a 56px
     fijo se veía muy grande en móvil, pero 40 plano rompía con el resto de
     heroes. Clamp fluido entre los tokens contiguos → ~45px @375 / ~47 @390 /
     ~52 @430, y empalma con heading-xl (56) en tablet sin salto. */
  #app-hero-title {
    font-size: clamp(var(--text-heading-lg), 12vw, var(--text-heading-xl));
  }
}

@media (max-width: 480px) {
  .app-hero,
  .app-manifesto,
  .app-features,
  .app-awards,
  .app-reviews,
  .app-pricing,
  .app-premium-teaser {
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }

  .features-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
