/* ==========================================================================
   MOTION — the typographic layer

   Everything here animates TEXT, and everything here is CSS driving elements
   that JavaScript only ever creates once. No library, no runtime, no frames
   being computed in script.

   Three ideas, in the order a reader meets them:

     1. words rise out of their own line, one after the next
     2. the accented word in a heading draws its own rule underneath it
     3. the beds behind the sections drift against the scroll

   The restraint is the point. Every one of these is a single gesture per
   element, at editorial speed — nothing bounces, nothing springs, nothing
   arrives from off-screen left. The brief calls that slop and it is right.

   WP Phase 2 target: enqueued with the rest, no PHP of its own.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. WORDS RISE

   A heading is split into words by site-ui.js — once, at load, preserving
   the <em> inside it. Each word is a box that clips, holding a box that
   moves. The stagger is per WORD, not per letter: letters are a party trick
   and they wreck the wrap law, because a line can then break mid-word.

   The padding/margin pair is load-bearing. `overflow: hidden` on the clip box
   would otherwise shave descenders and the overhang of the italic — so the
   box is grown and pulled back by the same amount, which clips the rise
   without clipping the letterforms.

   BOTH AXES. It was vertical only, and that was wrong: an italic leans out
   past the end of its own advance width, and the clip box was cut to the
   advance width. The 'd' at the end of "hard" lost its lean; so did every
   other italic word that happened to end in a letter that leans. 0.12em each
   side covers the overhang of this face at display sizes, and the negative
   margin takes it straight back off so no word moves.
   -------------------------------------------------------------------------- */

.u-word {
  display: inline-block;
  overflow: hidden;
  padding-block: 0.16em;
  margin-block: -0.16em;
  padding-inline: 0.12em;
  margin-inline: -0.12em;
  /* Keeps the word sitting on the same baseline it had before it was split. */
  vertical-align: bottom;
}

.u-word__in {
  display: inline-block;
  transform: translateY(var(--word-y, 0));
}

/* A comma or full stop that followed an <em> is moved inside that em's last
   word so the line cannot break in front of it. That means it inherits the
   accent's italic and colour, which it should not — it is not part of the
   accented phrase, it is the punctuation after it. */
.u-punct {
  font-style: normal;
  color: var(--c-ink);
}

@media (prefers-reduced-motion: no-preference) {
  .is-split .u-word__in {
    transition:
      transform var(--dur-slow) var(--ease-out),
      opacity var(--dur-slow) var(--ease-out);
    transition-delay: calc(var(--i, 0) * 48ms);
  }

  /* Before it is shown. 118% rather than 100% so the descenders of the line
     above cannot peek under the mask. */
  .is-split:not(.is-shown) .u-word__in {
    --word-y: 118%;
    opacity: 0;
  }
}

/* --------------------------------------------------------------------------
   2. THE ACCENT DRAWS ITS OWN RULE

   The italic word in every heading is the one thing the eye is meant to
   catch. It gets a rule that draws itself left to right once the word has
   landed — a printer's flourish, not a highlighter.

   `scaleX` on a pseudo-element rather than `background-size`, because a
   transform is composited and a background repaint is not.
   -------------------------------------------------------------------------- */

.c-head__title em,
.c-shop__title em,
.c-consult__title em,
.c-contact__title em,
.c-about__title em,
.c-hero__title-line em {
  position: relative;
}

@media (prefers-reduced-motion: no-preference) {
  .c-head__title em::after,
  .c-shop__title em::after,
  .c-consult__title em::after,
  .c-contact__title em::after,
  .c-about__title em::after,
  .c-hero__title-line em::after {
    content: "";
    position: absolute;
    inset-block-end: -0.06em;
    inset-inline: 0;
    block-size: var(--bw-rule);
    background: currentColor;
    opacity: 0.5;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.9s var(--ease-out);
    /* After the words: the longest heading is around eight words, so this
       clears the last of them. */
    transition-delay: 0.5s;
  }

  .is-shown em::after { transform: scaleX(1); }
}

/* --------------------------------------------------------------------------
   3. THE EYEBROW SETTLES

   The mono labels above each heading open out of a tighter tracking. It is
   two hundredths of an em — invisible as a movement, felt as the label
   arriving rather than being there.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .c-head__index,
  .c-about__eyebrow,
  .c-shop__eyebrow {
    transition:
      letter-spacing 1s var(--ease-out),
      opacity var(--dur-slow) var(--ease-out);
  }

  .is-armed .c-head__index,
  .is-armed .c-about__eyebrow,
  .is-armed .c-shop__eyebrow {
    letter-spacing: calc(var(--tr-mono-wide) + 0.22em);
  }
}

/* --------------------------------------------------------------------------
   4. THE BEDS DRIFT AGAINST THE SCROLL

   Native scroll-driven animation: `animation-timeline: view()` ties the
   progress of the animation to the element's own passage through the
   viewport. No scroll listener, no rAF loop, no layout read on every frame —
   the compositor runs it.

   Behind `@supports`, because Firefox does not have it yet. Where it is
   missing the bed simply sits still, which is what it did before.
   -------------------------------------------------------------------------- */

/* WITHDRAWN. The washes are static now, and this is where they were being
   animated from.
   
   Both animations were applied to `.c-bed__wash`, which carries a large blur
   — see the note in base.css. The scroll-driven parallax moved a 1.58
   million pixel blurred layer in response to scrolling, and the hero's drift
   SCALED a 1.79 million pixel one, which invalidates the rasterised blur on
   every frame rather than merely moving it.
   
   Reported as stutter on a MacBook in Chrome, and this was the largest part
   of it. What is lost is imperceptible: an abstract colour field at 0.3-0.42
   opacity, under a scrim, drifting six per cent over 46 seconds.
   
   The parallax and Ken Burns you can actually SEE are untouched — those are
   on `.c-bed__media` and `.c-entry__image`, which are unfiltered images and
   are exactly the case a compositor handles for free. */

@keyframes bed-parallax {
  from { translate: 0 -6%; }
  to   { translate: 0 6%; }
}

/* --------------------------------------------------------------------------
   5. THE TIMELINE'S RULE DRAWS DOWN

   The hairline between video entries grows as the list is read, so the
   column reads as one continuous thing rather than five detached rows.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .c-entry {
      animation: entry-rule linear both;
      animation-timeline: view();
      animation-range: entry 10% entry 70%;
    }
  }
}

@keyframes entry-rule {
  from { opacity: 0.35; translate: 0 1.5rem; }
  to   { opacity: 1; translate: 0 0; }
}

/* --------------------------------------------------------------------------
   6. PHOTOGRAPHS MOVE INSIDE THEIR FRAMES

   The tour cards and the video stills drift a little against the scroll,
   inside a frame that does not move. It is the difference between a picture
   printed on a page and a picture seen through a window.

   The image is oversized by the same amount it travels, so the frame is
   never uncovered at either end of the range. 108% for a 4% drift each way
   leaves a percent in hand.

   Scroll-driven, so there is no listener and no frame loop: the compositor
   advances it against the scroll position and nothing runs on the main
   thread. Behind @supports, and behind reduced-motion.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .c-tours__frame,
    .c-entry__media {
      overflow: clip;
    }

    .c-tours__frame img,
    .c-entry__image {
      block-size: 108%;
      object-fit: cover;
      animation: photo-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}

@keyframes photo-drift {
  from { translate: 0 -4%; }
  to   { translate: 0 4%; }
}

/* --------------------------------------------------------------------------
   7. THERE IS NO READING RULE UNDER THE SECTION HEAD

   There was, briefly: a hairline that filled as each section was crossed.
   It had to go. The accent word in every heading already draws its own rule,
   and the section rule landed NINE PIXELS below it — two horizontal lines
   under one heading, which is the double underline it looked like.

   One rule per heading. The accent keeps it, because it marks the word that
   matters rather than the block it sits in.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   THE ACCENT RULE NEEDS SOMEWHERE TO LIVE

   Reported in the guide-book heading on a phone: the rule under "over coffee"
   was printing through "written down." on the line beneath it.

   Not the rule's fault. `--lh-display` is 0.88 — deliberately tighter than
   the glyphs themselves, which is what makes a two-line display heading look
   set rather than typed. At two lines the descenders have the space; at
   three, on a narrow column, consecutive lines overlap by about a fifth of an
   em and anything drawn below a word lands on the next one.

   So the leading opens up only where headings actually reach three lines.
   1.02 is still tight — it is below the font's own box — but it is enough
   that the rule clears.
   -------------------------------------------------------------------------- */

@media (max-width: 48rem) {
  .c-head__title,
  .c-shop__title,
  .c-consult__title,
  .c-contact__title,
  .c-about__title {
    line-height: 1.02;
  }
}

/* --------------------------------------------------------------------------
   6. THE ARROWS KEEP MOVING

   Every "watch", "book this", "open the booking page" carries an arrow, and
   every one of them only moved on hover — which is nothing at all on a
   phone, where most of this is read. They nudge on their own now.

   Out, back, and a long rest. The rest is two thirds of the cycle on
   purpose: a mark that moves constantly stops being a signal and becomes
   wallpaper. Hover still wins and holds the arrow forward, so pointing at
   one feels like catching it rather than fighting it.

   The delay is staggered off the row index. Twelve arrows moving in lockstep
   reads as a screensaver; the same twelve a third of a second apart read as
   a page that is awake.
   -------------------------------------------------------------------------- */

@keyframes u-nudge {
  0%, 62%, 100% { transform: translateX(0); }
  20%           { transform: translateX(0.4rem); }
  40%           { transform: translateX(0); }
}

@media (prefers-reduced-motion: no-preference) {
  .c-action svg,
  .c-entry__foot svg {
    animation: u-nudge 2.6s var(--ease-out) infinite;
  }

  .c-entry:nth-child(3n + 2) .c-entry__foot svg { animation-delay: 0.35s; }
  .c-entry:nth-child(3n + 3) .c-entry__foot svg { animation-delay: 0.7s; }

  .c-service__inner .c-action svg { animation-delay: 0.5s; }

  /* Hover takes it off the loop and holds it forward. */
  .c-action:hover svg,
  .c-entry__link:hover .c-entry__foot svg,
  .c-entry__link:focus-visible .c-entry__foot svg {
    animation: none;
  }
}


/* --------------------------------------------------------------------------
   7. THE ACCENT RULE IS LIT ALONG ITS LENGTH, OVER AND OVER

   The rule under an accented word draws itself once and then sits there.
   Asked to loop it — and the obvious loop, drawing and un-drawing, is wrong:
   a rule that vanishes every few seconds reads as a rendering fault, not as
   a flourish.

   So the rule stays drawn and a brighter length travels along it instead.
   Only the gradient's position animates; the draw is still the transform
   transition above and still happens once, when the heading arrives.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .is-shown em::after {
    background: linear-gradient(
      90deg,
      currentColor 0%,
      currentColor 34%,
      color-mix(in oklab, currentColor 22%, transparent) 50%,
      currentColor 66%,
      currentColor 100%);
    background-size: 320% 100%;
    animation: u-rule-travel 7s linear 1.6s infinite;
  }
}

@keyframes u-rule-travel {
  from { background-position: 160% 0; }
  to   { background-position: -160% 0; }
}
