/* pages.css - shared motion + image layer for every treated page EXCEPT the
   home page (which keeps home.css/home.js) and about.html (which owns its own
   earth.mp4 scroll engine). Loads AFTER /styles.css, which stays the single
   owner of the shared system: tokens, nav, section rhythm, typography, footer
   columns, forms.

   This file carries only what /styles.css does not: the one-shot reveal
   system, the CSS-only page-hero entrance, the hairline draw utility, the
   full-bleed image band, the image-drift and reading-progress hosts, and the
   large footer wordmark.

   Page-specific composition (grid columns, art ratios, per-image cropping)
   stays in each page's own inline <style> block. This file stays generic.

   Design contract: DESIGN Alex Bogo 01.md
   Zero box-shadow, zero gradients except scrims laid over photographs, no
   font-weight 700, radius 0 outside pill buttons, positive display tracking. */

:root {
  /* Expensive easing + slow durations, same values as the home layer. */
  --ease-cine: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-reveal: 800ms;
  --dur-draw: 700ms;
  /* Static SVG turbulence. Not a gradient - a photographic grain plate. */
  --noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* The full-bleed band and the 9.4vw wordmark can both exceed the viewport
   mid-motion. Scoped to treated pages so untreated ones are untouched. */
body.pv { overflow-x: hidden; }
.pv img { max-width: 100%; }
.pv :focus-visible { outline: 1px solid var(--ink); outline-offset: 4px; }

/* =========================================================
   REVEAL SYSTEM
   One-shot, gated on the .js class the page sets synchronously in <head>.
   Without JS the gate never applies and every target renders at its
   resting state, so a no-JS page is a complete page.
   ========================================================= */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur-reveal) var(--ease-cine),
              transform var(--dur-reveal) var(--ease-cine);
  transition-delay: calc(var(--i, 0) * var(--stagger, 80ms));
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

/* Fade In Text - per word, resting at 0.15 so the copy stays readable
   even if the observer never fires. */
.js .w {
  display: inline-block;
  opacity: 0.15;
  transform: translateY(4px);
  transition: opacity 500ms var(--ease-cine), transform 500ms var(--ease-cine);
  transition-delay: calc(var(--i) * 25ms);
}
.js .is-in .w { opacity: 1; transform: none; }

/* =========================================================
   PAGE HERO ENTRANCE - CSS only, load-time, no JS involved.
   Applies when <body> carries the .pv class. Mirrors the home hero:
   the kicker tracks in, the headline blur-reveals, the lede and the CTA
   rise. Nothing here waits on an observer, so the fold is never blank.
   ========================================================= */
@keyframes pvKicker {
  from { opacity: 0; letter-spacing: -0.02em; }
  to   { opacity: 1; letter-spacing: 2.4px; }
}
@keyframes pvLine {
  from { filter: blur(8px); transform: translateY(12px); }
  to   { filter: blur(0); transform: translateY(0); }
}
@keyframes pvRiseSoft {
  from { transform: translateY(12px); }
  to   { transform: translateY(0); }
}
@keyframes pvRise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

.pv .page-hero__eyebrow { animation: pvKicker 900ms var(--ease-cine) 100ms both; }
.pv .page-hero__title   { animation: pvLine 950ms var(--ease-cine) 320ms both; }
.pv .page-hero__lede,
.pv .page-hero__sub     { animation: pvRiseSoft 900ms var(--ease-cine) 560ms both; }
.pv .page-hero__cta     { animation: pvRise 900ms var(--ease-cine) 760ms both; }
/* A hero byline or meta row sits between lede and CTA on the article pages. */
.pv .page-hero__byline  { animation: pvRise 900ms var(--ease-cine) 660ms both; }

/* =========================================================
   [data-line] - hairline draw utility
   The element paints its own 1px top rule and scales it out from the left
   on reveal. Without JS the rule is simply there, drawn full width.
   Generalised from the home page dev-row pattern.
   ========================================================= */
[data-line] { position: relative; }
[data-line]::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 1px;
  background: var(--hairline);
  transform-origin: left center;
  pointer-events: none;
}
.js [data-line]::before {
  transform: scaleX(0);
  transition: transform var(--dur-draw) var(--ease-cine);
  transition-delay: calc(var(--i, 0) * var(--stagger, 90ms));
}
.js [data-line].is-in::before { transform: scaleX(1); }
/* Closing rule on the last row of a drawn list. */
[data-line-close]:last-child::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--hairline);
  transform-origin: left center;
  pointer-events: none;
}
.js [data-line-close]:last-child::after {
  transform: scaleX(0);
  transition: transform var(--dur-draw) var(--ease-cine);
  transition-delay: calc(var(--i, 0) * var(--stagger, 90ms));
}
.js [data-line-close].is-in:last-child::after { transform: scaleX(1); }

/* =========================================================
   .pv-art - a framed still inside a content column.
   The image is laid 110% tall and pulled up 5%, which is the headroom the
   +/-4% drift travels in. Nothing is ever scaled, so nothing softens.
   ========================================================= */
.pv-art {
  position: relative;
  overflow: hidden;
  background: var(--surface-soft);
  border-radius: 0;
  aspect-ratio: var(--pv-art-ratio, 3 / 2);
}
.pv-art picture { position: absolute; inset: 0; display: block; }
.pv-art img {
  position: absolute;
  left: 0; top: -5%;
  width: 100%; height: 110%;
  object-fit: cover;
  object-position: var(--pv-art-pos, center);
  display: block;
  border-radius: 0;
  will-change: transform;
}

/* =========================================================
   .pv-band - full-bleed cinematic band.
   The caption carries its own local scrim; the frame itself is never
   covered edge to edge. A gradient is allowed here and only here,
   because it sits over a photograph.
   ========================================================= */
.pv-band {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  height: clamp(320px, 56vh, 620px);
  overflow: hidden;
  background: var(--surface-soft);
}
.pv-band picture { position: absolute; inset: 0; display: block; }
.pv-band img {
  position: absolute;
  left: 0; top: -5%;
  width: 100%; height: 110%;
  object-fit: cover;
  object-position: var(--pv-art-pos, center);
  display: block;
  will-change: transform;
}
.pv-band__caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  padding: 28px var(--sp-xxl);
  font-family: var(--font-mono);
  font-weight: var(--w-body);
  font-size: 10px;
  letter-spacing: 2.4px;
  text-transform: uppercase;
  color: var(--body);
}
.pv-band__caption::before {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 260%;
  background: linear-gradient(to top, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.55) 45%, rgba(0,0,0,0) 100%);
  pointer-events: none;
  z-index: -1;
}

/* =========================================================
   [data-progress] - reading progress hairline.
   Sits directly under the fixed nav. Ink, never gold: the gold budget
   for any viewport belongs to that viewport's one real CTA.
   ========================================================= */
.pv-progress {
  position: fixed;
  top: var(--nav-height);
  left: 0; right: 0;
  height: 1px;
  z-index: 49;
  pointer-events: none;
}
.pv-progress__bar {
  display: block;
  height: 1px;
  width: 100%;
  background: var(--ink);
  opacity: 0.55;
  transform: scaleX(0);
  transform-origin: left center;
}

/* =========================================================
   LARGE FOOTER WORDMARK - ported from the home footer.
   The visible text is always ALEX BOGOMOLOV. It is a mark, not a claim.
   A separate class from .footer__wordmark so the small mono mark that
   /styles.css already puts in .footer__bottom is left alone.
   ========================================================= */
.pv .footer { overflow: hidden; }
.pv-footer {
  margin-top: 40px;
  margin-bottom: -40px;   /* cancels the 40px bottom padding of .footer */
  overflow: hidden;
  line-height: 0.8;
}
.pv-footer__wordmark {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--w-display);
  /* 9.4vw keeps ALEX BOGOMOLOV inside the padded viewport at every width. */
  font-size: clamp(36px, 9.4vw, 200px);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--surface-elevated);
  transform: translateY(14%);
  user-select: none;
}
.pv-footer__wordmark .l {
  display: inline-block;
  color: var(--surface-elevated);
  transition: color 420ms var(--ease-cine);
  transition-delay: calc(var(--d, 0) * 60ms);
}
.pv-footer__wordmark.is-lit .l { color: var(--ink); }

@media (max-width: 920px) {
  .pv-band__caption { padding: 20px var(--sp-lg); }
}

/* =========================================================
   PRINT - the page is a document again. Everything readable is visible,
   every decorative device is gone.
   ========================================================= */
@media print {
  .js [data-reveal],
  .js [data-reveal].is-in,
  .js [data-words],
  .js .w {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
    animation: none !important;
  }
  [data-line]::before,
  [data-line-close]:last-child::after,
  .js [data-line]::before,
  .js [data-line-close]:last-child::after {
    transform: scaleX(1) !important;
    transition: none !important;
  }
  .pv .page-hero__eyebrow,
  .pv .page-hero__title,
  .pv .page-hero__lede,
  .pv .page-hero__sub,
  .pv .page-hero__byline,
  .pv .page-hero__cta { animation: none !important; opacity: 1 !important; filter: none !important; }
  .pv-band,
  .pv-footer,
  .pv-progress { display: none !important; }
  .pv .footer { overflow: visible; }
}

/* =========================================================
   REDUCED MOTION - everything lands in its final state immediately.
   ========================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js [data-reveal],
  .js [data-reveal].is-in {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .js .w { opacity: 1 !important; transform: none !important; transition: none !important; }
  [data-line]::before,
  [data-line-close]:last-child::after,
  .js [data-line]::before,
  .js [data-line-close]:last-child::after {
    transform: scaleX(1) !important;
    transition: none !important;
  }
  .pv .page-hero__eyebrow,
  .pv .page-hero__title,
  .pv .page-hero__lede,
  .pv .page-hero__sub,
  .pv .page-hero__byline,
  .pv .page-hero__cta {
    animation: none !important;
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }
  .pv-art img, .pv-band img { transform: none !important; }
  .pv-footer__wordmark .l { transition: none !important; color: var(--muted-soft); }
  .pv-footer__wordmark { color: var(--muted-soft); }
  .pv-progress__bar { transform: scaleX(0) !important; }
  .pv * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; }
}
