/* Motion for /workshop.html only, kept out of site.css on purpose: the rest
   of the site should not carry it, and backing the whole thing out is a
   matter of deleting this file, its script, and the two tags that load them.

   One rule governs everything here. The reveal system in site.css already
   owns the `transform` property on almost every element on the page - it
   writes `transform: none` at .rv.is-in with a specificity these selectors
   cannot beat, and a fight over it ends with the reveal winning. So every
   effect below drives the INDIVIDUAL properties instead: `translate`,
   `scale`, `rotate`. Those compose with `transform` rather than replacing
   it, which means motion and reveal can both be true at the same time. The
   only places using `transform` are wrappers this file introduces, which no
   other stylesheet touches. */

/* ---------------------------------------------------------
   1. The headline arrives a word at a time
   Each word rides up out of a clipped line. The mask is the
   line box itself, so a word that wraps still arrives clean.
   --------------------------------------------------------- */
.fx-word {
  display: inline-block;
  clip-path: inset(-.25em 0 -.15em 0);
}
.fx-word > span {
  display: inline-block;
  transform: translateY(110%) rotate(2deg);
  opacity: 0;
  transition:
    transform 1.15s cubic-bezier(.16, .84, .3, 1),
    opacity .7s ease;
  transition-delay: var(--d, 0ms);
}
.fx-lit .fx-word > span { transform: none; opacity: 1; }

/* The bar under the closing phrase is painted on the span, not on the words
   inside it, so it was sitting there at full width while its own words were
   still below the mask - a crimson line under nothing, arriving first. It
   draws itself in from the right instead, and the script times the start to
   the last word that belongs to it, so the line follows the phrase rather
   than announcing it.

   The class is written by the script at the moment it splits the headline,
   so a page with no JS and a visitor who asked for less motion both get the
   plain bar they always had. */
/* The start state is written from a class the <head> puts on <html> before
   this sheet is even fetched, not from one the script adds after it splits
   the words. Added later, the bar was painted at its full width for the
   ~95ms it took the script to run, so the page opened on a finished
   headline that then took itself apart - the very flash this is meant to
   remove. The head script skips the class under prefers-reduced-motion and
   never runs at all without JS, and in both of those cases the bar is
   simply there, as it always was. */
.fx-on .lp-h1 .mark {
  background-position: 100% calc(100% - .04em);
  background-size: 0 .16em;
}
/* The transition is declared on the LIT state and not on the start state,
   which is the whole trick. Declared on the start state, the collapse from
   full width down to nothing is itself a transition - and a delayed one -
   so the bar sat at full width for the length of the delay and was already
   back at full width by the time it was asked to grow. It never moved.
   Here the collapse is instant, because nothing is transitioned yet, and
   only the growth is animated. */
.fx-on .lp-h1.fx-lit .mark {
  background-size: 100% .16em;
  transition: background-size .5s cubic-bezier(.22, .61, .36, 1);
  transition-delay: var(--md, 0ms);
}

/* ---------------------------------------------------------
   2. Drift
   Elements travel a few pixels against the scroll. The value
   is written to `translate` from script, so the reveal keeps
   its own transform untouched.
   --------------------------------------------------------- */
[data-fx-drift] { will-change: translate; }

/* ---------------------------------------------------------
   3. The portrait leans toward the pointer
   A wrapper this file owns, so `transform` is free here. The
   sheen is a second layer whose position follows the cursor.
   --------------------------------------------------------- */
.fx-tilt {
  position: relative;
  transform-style: preserve-3d;
  transition: transform .5s cubic-bezier(.22, .61, .36, 1);
  /* This wrapper takes the portrait's place as the flex child, and without
     its own flex:none it shrinks where the row is tight - which then caps
     the image through the global img{max-width:100%} and leaves the square
     portrait squashed to a slice. The rule the image carries cannot help:
     it is no longer the item being flexed. */
  flex: none;
}
.fx-tilt.is-live { transition: transform .08s linear; }
.fx-tilt::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .35s ease;
  background: radial-gradient(
    30% 30% at var(--mx, 50%) var(--my, 50%),
    rgba(255, 255, 255, .34),
    rgba(255, 255, 255, 0) 70%);
}
.fx-tilt.is-live::after { opacity: 1; }
.fx-tilt > img { display: block; }

/* ---------------------------------------------------------
   4. The running order fills as it is read
   A rail down the side of the list, and a bar inside it whose
   height tracks how far through the section the reader is.
   --------------------------------------------------------- */
.agenda { position: relative; }
.fx-rail {
  /* the duration column sits at the far LEFT of each row, which in an RTL
     page is the inline END - the rail has to stand on the clocks, not
     opposite them */
  position: absolute; inset-block: 6px; inset-inline-end: 7px;
  width: 2px; border-radius: 2px;
  background: var(--hairline);
  overflow: hidden;
  pointer-events: none;
}
.fx-rail i {
  position: absolute; inset-inline: 0; top: 0;
  height: var(--fill, 0%);
  background: linear-gradient(180deg, #FF6E9C, #A45BF0 55%, #5B8CFF);
  border-radius: 2px;
}
/* The rail is absolutely positioned, so it painted over the rows - and the
   clock faces are drawn with fill:none, which left the bar visible straight
   through the middle of each one. The rows are lifted above it and the faces
   given the ground colour behind them, measured off the rendered page rather
   than guessed, so each icon now sits on the rail like a bead on a string. */
.agenda li { position: relative; z-index: 1; }
.agenda .mins-ico circle { fill: #1F0A2B; }

/* The row the reader is level with says so, quietly. The resting state dims
   the drawing and not the element: `opacity` on the <svg> dimmed the filled
   face along with the outline, and the bar came straight back through it -
   measured at 55% of the bar's own colour inside the face. `stroke-opacity`
   reaches the outline and the hands and leaves the fill alone, so the face
   covers the rail in every state. */
.agenda li .mins-ico {
  transition: scale .35s cubic-bezier(.22, .61, .36, 1), stroke-opacity .35s ease;
  stroke-opacity: .55;
}
.agenda li.is-now .mins-ico { scale: 1.25; stroke-opacity: 1; }
.agenda li .what { transition: color .35s ease; }
.agenda li.is-now .what { color: #fff; }
@media (max-width: 560px) { .fx-rail { display: none; } }

/* ---------------------------------------------------------
   5. The main buttons rise toward the pointer
   `translate` again, so the lift on :hover and the press on
   :active keep working on top of it.
   --------------------------------------------------------- */
[data-fx-magnet] {
  will-change: translate;
  /* the easing is most of what makes this feel gentle - without it the
     button tracks the cursor one to one and reads as twitchy. The script
     only ever writes a vertical, upward value here, so what composes with
     the -2px on :hover is a lift and nothing else. */
  transition: translate .34s cubic-bezier(.22, .61, .36, 1);
}

/* ---------------------------------------------------------
   Everything above is motion for its own sake, so all of it
   is off when the visitor has asked for less of it. The words
   simply start where they finish.
   --------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .fx-word > span { transform: none; opacity: 1; transition: none; }
  .fx-tilt, .fx-tilt::after { transition: none; }
  .fx-tilt::after { display: none; }
  .fx-rail i { transition: none; }
  .agenda li .mins-ico, .agenda li .what { transition: none; }
}
