/* ============================================================
   ART DE VIVRE — HERO MOTION LAYER
   Loads after main.css, so equal-specificity rules here win.

   The home hero is an aerial of Copacabana at golden hour — already
   graded, already beautiful. So this layer is restrained on purpose
   (Charlie's "deliberately understated" note on .hero__sunset): it
   adds lens and atmosphere, not colour.

     0. Vignette       — makes the wide aerial read as a frame
     1. Media life     — a slow drift so the loop never sits still
     2. Ambient light  — drifting sun haze (hero-motion.js)
     3. Specular rake  — one soft pass of light, every ~16s
     4. Film grain     — texture, and it kills gradient banding
     5. Kinetic type   — per-line mask reveal on the headline
     6. Scroll dissolve

   TWO CLASSES DRIVE IT, both added by hero-motion.js:
     .hm-armed  "JS is alive" — only now may anything be hidden, so a
                JS failure leaves the hero fully visible as before.
     .hm-in     the reveal. Named hm-in, not is-revealed, because
                is-revealed already belongs to main.js's [data-reveal]
                observer.
   ============================================================ */

.hero--motion {
  --hm-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --hm-px: 0px;   /* pointer parallax x */
  --hm-py: 0px;   /* pointer parallax y */
  --hm-sy: 0px;   /* scroll dissolve lift */
  --hm-op: 1;     /* scroll dissolve fade */
  isolation: isolate;   /* contain the screen blend to this section */
}

.hero--motion .content          { z-index: 6; }
.hero--motion .hero-scroll-hint { z-index: 6; }

/* ── 0. Vignette ───────────────────────────────────────────────
   Survives reduced motion and small screens — it is doing real work,
   seating the headline over a bright, high-key frame. */
.hero--motion::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(
    132% 108% at 50% 34%,
    rgba(0, 0, 0, 0) 58%,
    rgba(10, 10, 12, 0.15) 84%,
    rgba(8, 8, 10, 0.33) 100%
  );
}

/* ── 1. Media life ─────────────────────────────────────────────
   The video already carries data-parallax="0.22" (GSAP writes
   yPercent), so the drift goes on .bg-wrap, which nothing else
   touches, and composes with it instead of fighting it. */
.hero--motion.hm-in .bg-wrap {
  will-change: transform;
  animation: hmDrift 44s ease-in-out infinite alternate;
}
@keyframes hmDrift {
  from { transform: scale(1.01) translate3d(0, 0, 0); }
  to   { transform: scale(1.075) translate3d(-0.9%, -0.6%, 0); }
}

/* ── 2. Ambient light field ────────────────────────────────────
   A deliberately tiny canvas, stretched. The browser's own upscaling
   is the blur, which is far cheaper than blurring a full-viewport
   layer every frame. */
.hero--motion .hero-aurora {
  position: absolute;
  inset: -14%;
  width: 128%;
  height: 128%;
  z-index: 2;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transform: translate3d(calc(var(--hm-px) * 2.6), calc(var(--hm-py) * 2.6), 0);
  transition: opacity 2.4s ease 0.2s;
}
.hero--motion.hm-in .hero-aurora { opacity: 0.40; }

/* ── 3. Specular rake ──────────────────────────────────────────
   One slow pass of light. Slower and fainter than Cavmir's — this
   footage is backlit already. */
.hero--motion .hero-rake {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  overflow: hidden;
}
.hero--motion .hero-rake::after {
  content: '';
  position: absolute;
  top: -60%;
  left: 0;
  width: 34%;
  height: 220%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 244, 226, 0.05) 42%,
    rgba(255, 248, 236, 0.10) 50%,
    rgba(255, 244, 226, 0.05) 58%,
    rgba(255, 255, 255, 0) 100%
  );
  transform: rotate(14deg) translate3d(-80vw, 0, 0);
  opacity: 0;
}
.hero--motion.hm-in .hero-rake::after {
  animation: hmRake 16s cubic-bezier(0.42, 0, 0.28, 1) 4s infinite;
}
@keyframes hmRake {
  0%        { transform: rotate(14deg) translate3d(-80vw, 0, 0); opacity: 0; }
  8%        { opacity: 1; }
  46%       { opacity: 1; }
  58%, 100% { transform: rotate(14deg) translate3d(190vw, 0, 0); opacity: 0; }
}

/* ── 4. Film grain ─────────────────────────────────────────────
   feTurbulence baked to a data URI: one small tile, no request. */
.hero--motion .hero-grain {
  position: absolute;
  inset: -60px;
  z-index: 4;
  pointer-events: none;
  opacity: 0.042;
  mix-blend-mode: overlay;
  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.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}
.hero--motion.hm-in .hero-grain { animation: hmGrain 0.9s steps(5) infinite; }
@keyframes hmGrain {
  0%   { transform: translate3d(0, 0, 0); }
  20%  { transform: translate3d(-14px, 8px, 0); }
  40%  { transform: translate3d(9px, -12px, 0); }
  60%  { transform: translate3d(-7px, -6px, 0); }
  80%  { transform: translate3d(12px, 10px, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* ── 5. Kinetic type ───────────────────────────────────────────
   Pointer parallax and scroll dissolve share one transform, composed
   from custom properties so neither overwrites the other. */
.hero--motion.hm-armed .content {
  transform: translate3d(var(--hm-px), calc(var(--hm-py) + var(--hm-sy)), 0);
  opacity: var(--hm-op);
}

/* No eyebrow here: hero eyebrows were pulled sitewide as a distraction
   (Dan 2026-06-16, see main.css). The headline reveal carries the
   opening beat on its own.

   Each headline line rises out of its own mask. The padding /
   negative-margin pair keeps descenders from clipping. */
.hero--motion .hm-line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.1em;
  margin-bottom: -0.1em;
}
.hero--motion .hm-line > span { display: block; }
.hero--motion.hm-armed .hm-line > span {
  transform: translate3d(0, 112%, 0);
  opacity: 0;
  filter: blur(8px);
  transition:
    transform 1.25s var(--hm-ease),
    opacity 0.85s ease,
    filter 0.9s ease;
}
.hero--motion.hm-in .hm-line > span { transform: none; opacity: 1; filter: none; }
.hero--motion.hm-in .hm-line:nth-of-type(2) > span { transition-delay: 0.12s; }
.hero--motion.hm-in .hm-line:nth-of-type(3) > span { transition-delay: 0.24s; }

/* ── 6. Scroll cue ─────────────────────────────────────────────
   main.css already animates .hero-scroll-hint .l; only the fade-in
   of the whole cue is ours. */
.hero--motion.hm-armed .hero-scroll-hint {
  opacity: 0;
  transition: opacity 1s ease 1.1s;
}
.hero--motion.hm-in .hero-scroll-hint { opacity: 0.7; }

/* ── 7. Reduced motion ─────────────────────────────────────────
   Everything lands in its finished state; nothing loops. The
   vignette stays — it is composition, not motion. */
@media (prefers-reduced-motion: reduce) {
  .hero--motion .hero-aurora,
  .hero--motion .hero-rake,
  .hero--motion .hero-grain { display: none; }

  .hero--motion.hm-in .bg-wrap { animation: none; transform: none; }

  .hero--motion.hm-armed .content { transform: none; opacity: 1; }

  .hero--motion.hm-armed .hero-scroll-hint { opacity: 0.7; transform: none; transition: none; }

  .hero--motion.hm-armed .hm-line > span { transform: none; opacity: 1; filter: none; transition: none; }
}

/* ── 8. Phones ─────────────────────────────────────────────────
   Trim the most expensive layers and drop the pointer parallax. */
@media (max-width: 700px) {
  .hero--motion .hero-rake { display: none; }
  .hero--motion.hm-in .hero-aurora { opacity: 0.32; }
  .hero--motion .hero-grain { opacity: 0.034; }
  .hero--motion.hm-armed .content { transform: translate3d(0, var(--hm-sy), 0); }
}
