/* Build 3, the motion layer.

   Loads AFTER style.css and adds nothing to the layout. Every rule here is
   either a starting state for something that animates in, or an overlay.
   Delete this file and build3.js and the page is build 1 again, untouched.

   Three things carry the feel:
     1. Type and rules that arrive rather than being there already.
     2. A film grain over everything, so pure black reads as a material.
     3. A very faint lift in the black that follows the cursor (desktop only).

   Anyone with reduced motion turned on sees the finished state immediately.
   The .is-in class is added by build3.js via IntersectionObserver. */

/* ---------- 1. starting states ---------- */

/* Words are wrapped in .w > .wi by the script. The outer span clips, the
   inner one slides up from below it, so the letters come out from behind a
   hard edge rather than fading. This is the single biggest difference between
   a page that feels expensive and one that doesn't. */
.js .w {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  /* Poppins 900 has deep descenders on g, y and p. A clipping box the exact
     height of the line box cuts them off, so the box is grown 0.16em and the
     growth is pulled back out with a negative margin. Layout is unchanged. */
  padding-bottom: .16em;
  margin-bottom: -.16em;
}
.js .wi {
  display: inline-block;
  transform: translate3d(0, 108%, 0);
  transition: transform .82s cubic-bezier(.16, 1, .3, 1);
}
.js .is-in .wi { transform: translate3d(0, 0, 0); }

/* Everything that isn't a heading just lifts and fades. */
.js .rise {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity .7s ease, transform .7s cubic-bezier(.16, 1, .3, 1);
}
.js .rise.is-in { opacity: 1; transform: none; }

/* Section dividers draw themselves across instead of appearing. style.css
   puts the line on `section + section` as a border-top, so it's replaced with
   a pseudo-element here that can be scaled. */
.js section + section { border-top-color: transparent; }
.js section + section::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: var(--rule);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 1.15s cubic-bezier(.16, 1, .3, 1);
}
.js section + section.is-in::before { transform: scaleX(1); }
.js section { position: relative; }

/* The grid tiles come up one at a time, left to right. The delay is set
   per tile as an inline custom property by the script. */
.js .tile {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity .6s ease var(--d, 0ms),
              transform .6s cubic-bezier(.16, 1, .3, 1) var(--d, 0ms);
}
.js .grid.is-in .tile { opacity: 1; transform: none; }

/* The portrait circle scales up very slightly. Small enough that you feel it
   rather than see it. */
.js .portrait { opacity: 0; transform: scale(.94); transition: opacity .8s ease, transform 1s cubic-bezier(.16, 1, .3, 1); }
.js .about.is-in .portrait { opacity: 1; transform: none; }

/* ---------- 2. the grain ---------- */

/* An SVG turbulence tile, 4% opacity, over the whole page. The layer is
   oversized and shifted in 8 hard steps a second, which is how film grain
   actually behaves: it jumps, it doesn't drift. screen blend keeps it from
   muddying the blacks. */
.grain {
  position: fixed;
  top: -50%; left: -50%;
  width: 200%; height: 200%;
  z-index: 9998;
  pointer-events: none;
  opacity: .042;
  mix-blend-mode: screen;
  background-image: 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='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain 1s steps(1) infinite;
  will-change: transform;
}
@keyframes grain {
  0%   { transform: translate3d(0, 0, 0); }
  12%  { transform: translate3d(-6%, -4%, 0); }
  25%  { transform: translate3d(4%, -7%, 0); }
  37%  { transform: translate3d(-8%, 3%, 0); }
  50%  { transform: translate3d(7%, 6%, 0); }
  62%  { transform: translate3d(-3%, 8%, 0); }
  75%  { transform: translate3d(6%, -3%, 0); }
  87%  { transform: translate3d(-7%, -6%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* ---------- 3. the cursor lift ---------- */

/* A soft radial wash of near-nothing white that follows the pointer. At 3.5%
   it's invisible as a shape and its only job is to stop #000000 reading as a
   flat void. Removed entirely on touch, where there's no pointer to follow. */
.lift {
  position: fixed;
  top: 0; left: 0;
  width: 760px; height: 760px;
  margin: -380px 0 0 -380px;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle closest-side,
    rgba(232, 232, 232, .05) 0%, rgba(232, 232, 232, .022) 45%, rgba(232, 232, 232, 0) 100%);
  transition: opacity .6s ease;
  will-change: transform;
}
.lift.on { opacity: 1; }

/* Content sits above the lift. */
header, section, footer { position: relative; z-index: 1; }

/* The hero button gets a gold underline that draws on hover, so the one
   call to action on the page has a state worth noticing. */
.btn { position: relative; overflow: hidden; }
.btn::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .45s cubic-bezier(.16, 1, .3, 1);
}
.btn:hover::after { transform: scaleX(1); }

/* ---------- reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .js .wi, .js .rise, .js .tile, .js .portrait { transform: none !important; opacity: 1 !important; transition: none !important; }
  .js section + section::before { transform: scaleX(1) !important; transition: none !important; }
  .grain { animation: none; }
  .lift { display: none; }
}

@media (hover: none) {
  .lift { display: none; }
}
