/* MAIN VISUAL -> A NEW ERA -> CONTACT.
 *
 * Two effects in one file because they share a subtree and a scroll timeline:
 *
 *   .era-stage    the main visual dissolving into the galaxy, the GAIA unit resolving
 *                 into focus beside it, and the A NEW ERA copy wiping in
 *   .era-overlap  the CONTACT panel arriving over that stage, holding the screen, and
 *                 receding into point__2
 *
 * Driven by four custom properties, all written by js/new-era-scroll.js:
 *
 *   --new-era-p      on .era-stage   0 -> 1 across the main visual's dissolve
 *   --era-contact-p  on both         0 -> 1 as the CONTACT panel arrives
 *   --contact-out-p  on .contact     0 -> 1 as it leaves
 *   --era-exit-p     on .era-stage   retires the stage once the panel covers the screen
 *
 * The CONTACT headline and CTA are split into characters by js/contact-reveal.js, which
 * writes --shard-* on the CTA's characters and toggles .is-split / .is-on / .is-cold.
 *
 * Every default is the finished, untouched state, so the page renders correctly with no
 * JavaScript at all. See README.md for the reasoning behind the non-obvious rules.
 */

/* ---- A NEW ERA: base (all widths) -------------------------------------------------- */

.era-stage {
  position: relative;
  z-index: 0;
}

.era-unit {
  padding-top: clamp(32px, 8vw, 60px);
}

.era-unit-img {
  display: block;
  width: 70%;
  max-width: 320px;
  margin: 0 auto;
}

.new-era {
  position: relative;
}

.new-era-grid {
  display: -ms-grid;
  display: grid;
  gap: clamp(28px, 4vw, 72px);
}

.new-era-title {
  font-size: clamp(3.4rem, 6vw, 8.6rem);
  font-weight: 700;
  line-height: 1;
  text-align: center;
}

.new-era-lines {
  margin-top: clamp(20px, 3vw, 48px);
  font-size: clamp(1.4rem, 1.9vw, 2.6rem);
  line-height: 1.8;
  color: #fff;
  text-align: center;
}

/* The copy wipe: a dim copy of each line sits underneath and a full-brightness copy on top
   is revealed left to right by its clip-path. Two stacked copies rather than a colour
   transition, so the wipe has a hard edge. js/new-era-scroll.js adds .is-active. */
.new-era-line {
  position: relative;
  display: block;
  overflow: hidden;
}

.new-era-line-base {
  display: block;
  opacity: 0.45;
}

.new-era-line-fill {
  position: absolute;
  inset: 0;
  display: block;
  clip-path: inset(0 100% 0 0);
  -webkit-clip-path: inset(0 100% 0 0);
}

.new-era-line.is-active .new-era-line-fill {
  clip-path: inset(0 0 0 0);
  -webkit-clip-path: inset(0 0 0 0);
  transition: clip-path 900ms cubic-bezier(0.22, 1, 0.36, 1),
    -webkit-clip-path 900ms cubic-bezier(0.22, 1, 0.36, 1);
}

.new-era-line:nth-child(2).is-active .new-era-line-fill {
  transition-delay: 140ms;
}

.new-era-line:nth-child(3).is-active .new-era-line-fill {
  transition-delay: 280ms;
}

.new-era-lockup {
  text-align: center;
}

/* Repeats top.css's `.point .button` sizing, because the lockup is no longer inside a
   .point section. */
.new-era-lockup .button {
  display: block;
  width: 85px;
  margin: 25px auto 13px;
  padding: 3px 10px;
  font-size: 1.3rem;
  font-weight: 500;
}

/* ---- A NEW ERA: the dissolve (768px and up) ---------------------------------------- */

@media only screen and (min-width: 768px) {
  /* Three phases cut out of the one scroll progress, overlapping so the stage reads as a
     single move rather than three cues. */
  .era-stage {
    --mv-dark: clamp(0, calc(var(--new-era-p, 0) / 0.55), 1);
    --mv-fade: clamp(0, calc((var(--new-era-p, 0) - 0.3) / 0.7), 1);
    /* Defaults to 1 so the unit is visible without JS. */
    --unit-p: clamp(0, calc((var(--new-era-p, 1) - 0.2) / 0.6), 1);
  }

  /* Retires the stage behind the CONTACT panel once the panel is the whole frame. This is
     not the section tint — that is the one scrim over the galaxy in
     css/page/background.css — so at rest it is fully transparent and paints nothing. */
  .era-stage::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: rgba(0, 0, 0, calc(var(--era-contact-p, 0) * 0.7));
    pointer-events: none;
  }

  .mainvisual {
    position: relative;
    z-index: 0;
  }

  .mainvisual-video video {
    /* Two factors, because the darkening used to come from two places; see README.md,
       "The ground layer". Written as the product rather than folded into one coefficient,
       so the dissolve follows the curve it always did. */
    filter: blur(calc(var(--mv-dark) * 20px))
      brightness(
        calc((1 - var(--mv-dark) * 0.82) * (1 - var(--mv-dark) * 0.5))
      );
    opacity: calc(1 - var(--mv-fade));
    /* Melts the video's bottom edge into the galaxy instead of ending on a hard line. At
       --mv-dark 0 the gradient is fully opaque and the visual is untouched. */
    -webkit-mask-image: linear-gradient(
      to bottom,
      #000 calc(100% - var(--mv-dark) * 32%),
      transparent 100%
    );
    mask-image: linear-gradient(
      to bottom,
      #000 calc(100% - var(--mv-dark) * 32%),
      transparent 100%
    );
    will-change: filter, opacity;
  }

  /* The parked GAIA unit. An overlay rather than a grid cell, so it can stay put while the
     copy scrolls past it, and its height is derived rather than chosen: the chain below
     resolves to "as wide as the left column allows, capped at 82vh". See README.md,
     "Sizing the parked unit". */
  .era-unit {
    --iw: min(1300px, 100vw);
    --lm: calc((100vw - var(--iw)) / 2);
    --gap: clamp(28px, 4vw, 72px);
    --col: calc((var(--iw) - var(--l-gutter, 10px) * 2 - var(--gap)) / 2);
    --unit-left: clamp(40px, 4vw, 96px);
    --unit-max-w: calc(
      var(--lm) + var(--l-gutter, 10px) + var(--col) + var(--gap) -
        var(--unit-left) - 60px
    );
    --unit-h: min(82vh, calc(var(--unit-max-w) * 528 / 439));
    --unit-top: calc((100vh - 80px - var(--unit-h)) / 2);
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    /* Stops short of the stage's bottom edge, so the unit releases before A NEW ERA ends. */
    height: max(0px, calc(100% - var(--unit-top) - var(--unit-h)));
    background: none;
    padding-top: 0;
    z-index: 2;
    pointer-events: none;
  }

  /* height:0 so the sticky box takes no space and cannot push the section below. */
  .era-unit-inner {
    position: sticky;
    top: 80px;
    height: 0;
  }

  .era-unit-img {
    position: absolute;
    left: calc(var(--unit-left) - var(--lm));
    top: var(--unit-top);
    width: auto;
    max-width: none;
    /* Load-bearing: style.css sets `img { max-height: 100% }`, which collapses an image
       whose parent has height 0. */
    max-height: none;
    height: var(--unit-h);
    margin: 0;
    opacity: var(--unit-p);
    filter: blur(calc((1 - var(--unit-p)) * 18px));
    /* The only positional movement. Once --unit-p reaches 1 the unit is parked. */
    transform: translate3d(0, calc((1 - var(--unit-p)) * 30px), 0)
      scale(calc(1 + (1 - var(--unit-p)) * 0.08));
    transform-origin: 50% 50%;
    will-change: opacity, filter, transform;
  }

  .new-era {
    z-index: 3;
  }

  /* Two columns, copy on the right; the left one is empty and belongs to the parked unit. */
  .new-era-grid {
    -ms-grid-columns: minmax(0, 6fr) minmax(0, 6fr);
    grid-template-columns: minmax(0, 6fr) minmax(0, 6fr);
    min-height: clamp(600px, 92vh, 1000px);
    align-items: center;
  }

  .new-era-copy {
    grid-column: 2;
  }

  .new-era-title,
  .new-era-lines,
  .new-era-lockup {
    text-align: left;
  }

  .new-era-lockup .button {
    display: inline-block;
    width: auto;
    margin: 25px 0 13px;
    padding: 6px 15px;
    font-size: 2rem;
  }
}

/* Pinning the visual needs room for the copy to travel over it, which only exists once the
   layout is two columns and tall enough. */
@media only screen and (min-width: 1024px) {
  .mainvisual {
    position: sticky;
    top: 80px;
  }
}

/* ---- CONTACT: the panel (all widths) ----------------------------------------------- */

/* z-index 0 keeps the layer order inside (stage 0, panel 5) local, so it cannot outrank
   the fixed header. */
.era-overlap {
  position: relative;
  z-index: 0;
}

/* Its own night sky: solid black rather than top.css's #242424, with a star field behind
   the CTA. At every width, so the section reads as one thing on phones too. */
.era-overlap .contact {
  position: relative;
  overflow: hidden;
  background: #000;
}

/* Scoped inside the panel rather than fixed to the page, so the drift belongs to this
   section and cannot paint over anything else. Sized and drawn by js/contact-stars.js. */
.contact-stars {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0.85;
  pointer-events: none;
}

.era-overlap .contact > .l-section {
  position: relative;
  z-index: 1;
}

/* ---- CONTACT: the lockup ----------------------------------------------------------- */

/* Sized off the viewport rather than the breakpoint so each line stays on one line at
   every width; the clamp's ceiling stops the longer line short of the 1300px gutter. */
.contact-title {
  font-size: clamp(2.2rem, 7vw, 9.6rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.01em;
  text-align: center;
}

.contact-line,
.contact-line-inner {
  display: block;
}

.contact-cta {
  margin-top: clamp(28px, 4vh, 64px);
}

/* The pieces js/contact-reveal.js cuts the type into. Words hold their characters together
   so a line that has to wrap still breaks between words; characters keep their advance
   width whether or not they have arrived, so nothing reflows mid-reveal. */
.contact-word {
  display: inline-block;
  white-space: nowrap;
}

.contact-char {
  display: inline-block;
}

/* The reveal itself is js/contact-reveal.js (GSAP + ScrollTrigger). Nothing here hides
   anything: the hidden states live in that file's .from() tweens, so if GSAP fails to load
   the lockup renders as ordinary, finished type. See README.md, "The CONTACT reveal". */

/* ---- CONTACT: the panel overlapping the stage (1024px and up) ----------------------- */

/* 1024px, not 768px: below it the stage comes to rest before the dissolve has finished, and
   the panel would arrive on top of a transition still in progress. There both stay in
   ordinary flow. */
@media only screen and (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  /* How far the panel's top edge is pulled up into the stage. Eats the section's bottom
     padding, nothing else. */
  .era-overlap {
    --era-contact-lead: clamp(60px, 10vh, 140px);
  }

  /* The pin target (index.html). It carries two things that have to move off the panel
     once ScrollTrigger pins this element:

     margin-top — the overlap into the stage. A fixed element does not collapse margins
     with its children, so left on the panel the -90px would displace the lockup inside
     the pinned box instead of pulling the box up into the stage.

     z-index — a position:fixed element always forms a stacking context, so the panel's
     own z-index:5 stops competing with the stage's layers and gets scoped inside this
     wrapper. Without the 5 here the sticky GAIA unit (z-index 2) paints over the CONTACT
     lockup for the whole pin. */
  .contact-pin {
    position: relative;
    z-index: 5;
    margin-top: calc(var(--era-contact-lead) * -1);
  }

  /* --era-exit-p retires the stage once the panel covers the screen. Without it the stage
     stays parked there, and every bit of transparency the panel takes on during its exit
     puts A NEW ERA back on screen behind the CTA. See README.md, "Why A NEW ERA does not
     show through". */
  .era-overlap .era-stage {
    position: static;
    opacity: calc(1 - var(--era-exit-p, 0));
  }

  /* The stage goes out of focus as the panel comes forward, so the two are never both
     legible at once. */
  .era-overlap .new-era {
    filter: blur(calc(var(--era-contact-p, 0) * 12px))
      brightness(calc(1 - var(--era-contact-p, 0) * 0.45));
  }

  .era-overlap .new-era-copy {
    transform: scale(calc(1 + var(--era-contact-p, 0) * 0.035));
  }

  /* Carries its own entrance blur as well, because a second `filter` would replace it. */
  .era-overlap .era-unit-img {
    filter: blur(
        calc((1 - var(--unit-p)) * 18px + var(--era-contact-p, 0) * 12px)
      )
      brightness(calc(1 - var(--era-contact-p, 0) * 0.45));
  }

  /* The foreground panel. It resolves rather than simply arriving: it starts fractionally
     transparent, soft, a little low and a little small, and lands opaque and sharp before
     it covers the screen. Every property below carries both halves of its life —
     --era-contact-p brings it forward, --contact-out-p takes it back — because the exit is
     meant to read as the entrance run backwards.

     Defaults of 1 and 0 mean that wherever the script does not run, the panel is its plain
     opaque self. */
  .era-overlap .contact {
    position: relative;
    z-index: 5;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-align: center;
    align-items: center;
    /* 100vh of panel plus 24vh of trailing sky. The extra opens the stretch where the panel
       is the only thing on screen, which is where --era-exit-p runs. As padding, so the
       lockup still lands dead centre of the viewport. */
    min-height: 124vh;
    padding-bottom: 24vh;
    box-shadow: 0 -60px 120px
      rgba(0, 0, 0, calc(0.18 + var(--era-contact-p, 1) * 0.32));
    opacity: calc(
      (0.72 + var(--era-contact-p, 1) * 0.28) *
        (1 - var(--contact-out-p, 0) * 0.24)
    );
    filter: blur(
        calc(
          (1 - var(--era-contact-p, 1)) * 18px + var(--contact-out-p, 0) * 14px
        )
      )
      brightness(calc(1 - var(--contact-out-p, 0) * 0.22));
    /* Entrance only. Displacing this element on the way out opens a lit strip along the
       seam with point__2 — the receding is done by the star field instead. See README.md,
       "The CONTACT panel's seam". */
    transform: translate3d(0, calc((1 - var(--era-contact-p, 1)) * 80px), 0)
      scale(calc(0.96 + var(--era-contact-p, 1) * 0.04));
    /* The panel's bottom edge dissolves into the section below. Both sides stand on the
       same ground now — one scrim over the shared galaxy — so the mask can simply fade to
       transparent. Fixed length, because a fade proportional to the exit would be at its
       shortest exactly when the edge first appears. */
    -webkit-mask-image: linear-gradient(
      to bottom,
      #000 calc(100% - clamp(150px, 24vh, 300px)),
      transparent
    );
    mask-image: linear-gradient(
      to bottom,
      #000 calc(100% - clamp(150px, 24vh, 300px)),
      transparent
    );
    will-change: opacity, filter, transform;
  }

  /* The lockup leads the panel out by a hair: content and background leaving on the same
     frame reads as a cut. */
  .era-overlap .contact > .l-section {
    width: 100%;
    opacity: calc(1 - var(--contact-out-p, 0) * 0.5);
    transform: translate3d(0, calc(var(--contact-out-p, 0) * -28px), 0);
  }

  /* The exit's whole sense of depth lives here, because this layer is inset:0 inside the
     panel and can pull back as far as it likes without exposing an edge. Origin above
     centre, so the vanishing point sits behind the CTA. The drift slows to match on the
     canvas side (js/contact-stars.js). */
  .contact-stars {
    opacity: calc(0.85 * (1 - var(--contact-out-p, 0) * 0.5));
    filter: blur(calc(var(--contact-out-p, 0) * 6px));
    transform: translate3d(0, calc(var(--contact-out-p, 0) * -40px), 0)
      scale(calc(1 - var(--contact-out-p, 0) * 0.06));
    transform-origin: 50% 45%;
  }

  /* Softens the panel's leading edge so it does not arrive as a hard rectangle. Sits above
     the panel, over the stage, and is long — a short ramp on a panel this large is itself a
     line. */
  .era-overlap .contact::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: clamp(240px, 38vh, 460px);
    background: linear-gradient(
      to top,
      #000 0%,
      rgba(0, 0, 0, 0.86) 32%,
      rgba(0, 0, 0, 0.38) 68%,
      rgba(0, 0, 0, 0) 100%
    );
    opacity: calc(0.45 + var(--era-contact-p, 1) * 0.55);
    pointer-events: none;
  }
}

/* ---- Reduced motion: the stacked, finished layout ----------------------------------- */

/* Nothing pinned, nothing overlaid. Without this the unit stays an absolute overlay and
   floats on top of the (correctly untouched) main visual. */
@media (prefers-reduced-motion: reduce) {
  .mainvisual {
    position: static;
  }

  .mainvisual-video video {
    filter: none;
    opacity: 1;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .era-stage::after {
    display: none;
  }

  .era-unit {
    position: static;
    height: auto;
    padding-top: clamp(32px, 6vw, 80px);
  }

  .era-unit-inner {
    position: static;
    height: auto;
  }

  .era-unit-img {
    position: static;
    width: min(560px, 60vw);
    height: auto;
    max-width: 100%;
    margin: 0 auto;
    opacity: 1;
    filter: none;
    transform: none;
  }

  .new-era-grid {
    grid-template-columns: 1fr;
    min-height: 0;
  }

  .new-era-copy {
    grid-column: auto;
  }

  .new-era-title,
  .new-era-lines,
  .new-era-lockup {
    text-align: center;
  }

  .new-era-line-fill {
    clip-path: inset(0 0 0 0);
    -webkit-clip-path: inset(0 0 0 0);
    transition: none;
  }
}
