/* ==========================================================================
   MOTION
   Rules: nothing is gated behind an animation — content is visible by default
   and only hidden once JS confirms it can animate; everything respects
   prefers-reduced-motion; nothing bounces, parallaxes or autoplays except the
   maker ticker, which pauses on hover.
   ========================================================================== */

/* ---------------------------------------------------------------- reveal - */
/* Visible by default. .js-motion is added by motion.js only after it has
   confirmed IntersectionObserver support and that reduced-motion is off. */
html.js-motion [data-rise] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 700ms var(--e-out), transform 700ms var(--e-out);
  transition-delay: var(--rise-delay, 0ms);
}
html.js-motion [data-rise].is-in { opacity: 1; transform: none; }

/* section heads arrive with a little more weight */
html.js-motion [data-rise="head"],
html.js-motion .lb-shead[data-rise] { transform: translateY(26px); }

/* ------------------------------------------------------- the hero plate - */
/* The object rises into its plate and settles. One move, slow, no bounce. */
html.js-motion .lb-hero__plate img {
  opacity: 0; transform: translateY(26px) scale(.985);
  transition: opacity 900ms var(--e-out), transform 1100ms var(--e-out);
  transition-delay: 120ms;
}
html.js-motion .lb-hero__plate.is-in img { opacity: 1; transform: none; }

/* the plate's own rule draws itself in under the object */
html.js-motion .lb-hero__plate::after {
  content: ""; position: absolute; left: 0; bottom: -1px; height: 1px; right: 0;
  background: var(--ink); transform: scaleX(0); transform-origin: left;
  transition: transform 1200ms var(--e-out) 260ms;
}
html.js-motion .lb-hero__plate.is-in::after { transform: scaleX(1); }

/* --------------------------------------------------------- section rules - */
/* Every section head is underscored by a rule that draws in rather than
   simply being there. It is the page's one recurring gesture. */
html.js-motion .lb-shead { border-bottom-color: transparent; position: relative; }
html.js-motion .lb-shead::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--ink); transform: scaleX(0); transform-origin: left;
  transition: transform 1000ms var(--e-out) 120ms;
}
html.js-motion .lb-shead.is-in::after { transform: scaleX(1); }

/* ----------------------------------------------------------------- hover - */
.lb-gal__i, .lb-makers a, .lb-jcard { transition: transform var(--t-mid); }
.lb-jcard:hover { transform: translateY(-3px); }

/* the parts sheet: the row tints and its number takes the accent */
.lb-part { transition: background-color var(--t-micro); }
.lb-part:hover { background: var(--paper); }
.lb-part:hover .lb-part__n { color: var(--ink); }

/* ------------------------------------------------------ page transitions - */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: lb-fade-out 160ms var(--e-soft) both; }
::view-transition-new(root) { animation: lb-fade-in 360ms var(--e-out) both; }
@keyframes lb-fade-out { to { opacity: 0; } }
@keyframes lb-fade-in { from { opacity: 0; transform: translateY(6px); } }

/* --------------------------------------------------------------- respect - */
@media (prefers-reduced-motion: reduce) {
  html.js-motion [data-rise],
  html.js-motion [data-rise].is-in { opacity: 1 !important; transform: none !important; transition: none !important; }
  html.js-motion .lb-hero__plate img { opacity: 1 !important; transform: none !important; }
  html.js-motion .lb-hero__plate::after,
  html.js-motion .lb-shead::after { transform: scaleX(1) !important; transition: none !important; }
  html.js-motion .lb-shead { border-bottom-color: var(--ink); }
  *, *::before, *::after { transition-duration: 1ms !important; animation-duration: 1ms !important; }
  @view-transition { navigation: none; }
}


/* ==========================================================================
   SCROLL-DRIVEN MOTION
   Where the browser supports it, the reveal is driven by the element's own
   position in the scrollport rather than by a class JavaScript toggles once.
   That means it tracks the scroll continuously — it can be scrubbed backwards,
   it never fires early on a fast flick, and it costs no main-thread work.

   JavaScript remains the fallback and still owns the watchdog, so nothing here
   can leave content invisible.
   ========================================================================== */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* The ranges below are lengths, not percentages, and that is deliberate.
       A percentage of `cover` scales with the element's own height, so a
       section nearly a screen tall was still at 3% opacity with 70px of itself
       already on screen — and find-in-page could scroll a reader to a match
       inside text they could not see. A length completes the fade after the
       same amount of scrolling whatever the block's height. */

    /* the JS path stands down when the browser can do this itself */
    html.js-motion.has-sda [data-rise] {
      opacity: 1;
      transform: none;
      transition: none;
      animation: lb-rise 1s linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 240px;
    }
    @keyframes lb-rise {
      from { opacity: 0; transform: translateY(20px); }
      to   { opacity: 1; transform: none; }
    }

    /* The hero object settles as it enters, then holds. One continuous move
       tied to the scroll rather than a fire-once transition. */
    html.has-sda .lb-hero__plate img {
      animation: lb-settle 1s linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 300px;
    }
    @keyframes lb-settle {
      from { opacity: 0; transform: translateY(30px) scale(.97); }
      to   { opacity: 1; transform: none; }
    }

    /* Section rules draw themselves as the head crosses the fold. */
    html.has-sda .lb-shead::after,
    html.has-sda .lb-parts,
    html.has-sda .lb-meas__list { }
    html.has-sda .lb-shead::after {
      transform: scaleX(0);
      animation: lb-draw-x 1s linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 260px;
    }
    @keyframes lb-draw-x { to { transform: scaleX(1); } }

    /* The specification rows arrive one after another, keyed to scroll rather
       than to a timer, so the cadence follows the reader. */
    html.has-sda .lb-part,
    html.has-sda .lb-meas__r,
    html.has-sda .lb-faq__i {
      animation: lb-rise 1s linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 170px;
    }
  }
}

/* --------------------------------------------------------- sticky keys - */
/* A gallery-catalogue device: the section's number and title stay with the
   reader while its rows scroll past, so long runs never lose their heading. */
@media (min-width: 901px) {
  .lb-story__k,
  .lb-legal__k,
  .lb-shead__k { position: sticky; top: 108px; align-self: start; }
}

/* ------------------------------------------------------ counted figures - */
/* The facts are real catalogue numbers, so counting them up is reporting a
   measurement rather than decoration. Set by motion.js; the printed value is
   already correct if the script never runs. */
.lb-facts__n { font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .lb-story__k, .lb-legal__k, .lb-shead__k { position: static; }
}
