/* ===========================================================================
   RTD Services · motion.css (v3, 2026-10-08)
   Loaded after styles.css. Owns the scroll-driven pieces only: the pinned
   phone hero film, the pinned phone peak scene, the peak crop, the steps line
   and the gallery scroller. Colours, type and resting layout stay in
   styles.css. Every rule here is keyed off a hook the markup contract fixes
   (#hero, #hero-video, #peak, [data-peak-stage], [data-steps], [data-step],
   [data-steps-line], [data-steps-finale], [data-rail]).
   ========================================================================= */

/* --------------------------------------------------- peak: the crop --- */
/* The bottom ~12% of the 16:9 scene is sidewalk. The scene box is made 1/0.88
   of the stage's height and top-aligned inside [data-peak-stage], which clips
   it: the canvas and the poster keep their own size and cover fit (the engine
   sizes its drawing buffer from the canvas's client box), only the window
   onto them is shorter. The stage itself still fills the pinned viewport, so
   there is no empty band. Phones load a scene set whose sidewalk is already
   cropped off (media/peak-m/), so the phone rules below undo this. */
@media (min-width: 861px) {
  [data-peak-stage] {
    position: absolute; inset: 0;
    overflow: hidden;
  }
  [data-peak-stage] > .scene {
    inset: 0 0 auto 0;
    height: calc(100% / 0.88);
  }
}

/* No WebGL: the lit poster page.js lays over the unlit one. Its opacity is
   written inline from scroll. */
.scene__poster--lit { z-index: 1; pointer-events: none; }

/* -------------------------------------------------- steps: the line --- */
/* page.js writes --steps-progress (0..1) on [data-steps]. The line is drawn
   at full size by styles.css and revealed with a scale, so the fill never
   relays out. data-steps-axis="x" (set by page.js when the line is wider
   than tall) runs it across instead. */
[data-steps-line] {
  transform-origin: 50% 0;
  transform: scaleY(var(--steps-progress, 0));
  transition: transform 140ms linear;
  box-shadow: 0 0 10px 1px color-mix(in oklab, var(--sc-accent, #e7c077) 45%, transparent);
  will-change: transform;
}
[data-steps-axis="x"] [data-steps-line] {
  transform-origin: 0 50%;
  transform: scaleX(var(--steps-progress, 0));
}
html:not(.js) [data-steps-line],
html.is-static [data-steps-line] { transform: none; }
[data-step],
[data-steps-finale] {
  transition: color 420ms ease, border-color 420ms ease, background-color 420ms ease,
              box-shadow 420ms ease, opacity 420ms ease, filter 420ms ease;
}
@media (prefers-reduced-motion: reduce) {
  [data-steps-line] { transform: none; transition: none; }
  [data-step], [data-steps-finale] { transition: none; }
}

/* ------------------------------------------------ gallery: the rail --- */
/* A plain horizontal scroller on every breakpoint: no pin, no translate, no
   scroll capture. Vertical wheel over it scrolls the page; touch, trackpads
   and the arrow keys (once focused) scroll it; page.js adds mouse drag. */
[data-rail] {
  width: auto; max-width: 100%;
  overflow-x: auto; overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: var(--sc-gutter, 1.25rem);
  -webkit-overflow-scrolling: touch;
  transform: none !important;
  will-change: auto;
}
[data-rail] > * { scroll-snap-align: start; }
[data-rail]:focus-visible { outline: 2px solid var(--sc-accent, #e7c077); outline-offset: 4px; }
@media (hover: hover) and (pointer: fine) {
  [data-rail] { cursor: grab; }
  [data-rail].is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; -webkit-user-select: none; }
  [data-rail].is-dragging * { pointer-events: none; }
}

/* ====================================================== phones, moving == */
/* 860px and below with motion allowed: the hero is a pinned film again and
   the peak is a pinned scene. These heights match the spans page.js gives
   ScrollCraft (hero 2.6, peak 1.6) and apply from first paint, so mounting
   moves nothing. With reduced motion, ?static=1 (page.js) or Save-Data
   (html.hero-still, hero only), styles.css's stacked phone layout applies. */
@media (max-width: 860px) and (prefers-reduced-motion: no-preference) {

  /* ------------------------------------------------------- hero film --- */
  html.js:not(.hero-still):not(.is-static) #hero.video-hero { height: 260svh !important; min-height: 0; }
  html.js:not(.hero-still):not(.is-static) #hero .video-hero__stage {
    position: sticky; top: 0;
    display: block;
    height: 100vh; height: 100svh;
    overflow: clip;
    background: #070b16;
  }
  html.js:not(.hero-still):not(.is-static) #hero .video-hero__poster,
  html.js:not(.hero-still):not(.is-static) #hero-video {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    /* The film is 16:9 and shows a narrow slice on a phone; 42% keeps the
       house centred through the whole push-in. */
    object-position: 42% 50%;
  }
  html.js:not(.hero-still):not(.is-static) #hero-video {
    display: block;
    z-index: 0;
    opacity: 0;
    transition: opacity 450ms ease;
  }
  html.js:not(.hero-still):not(.is-static) #hero.is-video-ready #hero-video { opacity: 1; }
  /* The poster stays under the film for the fade, then leaves the compositor. */
  html.js:not(.hero-still):not(.is-static) #hero.is-video-ready .video-hero__poster { visibility: visible; }
  html.js:not(.hero-still):not(.is-static) #hero.is-video-settled .video-hero__poster { visibility: hidden; }
  html.js:not(.hero-still):not(.is-static) #hero.is-video-poster #hero-video { display: none; }
  html.js:not(.hero-still):not(.is-static) #hero .video-hero__snow { display: none; }
  html.js:not(.hero-still):not(.is-static) #hero .video-hero__veil { display: block; position: absolute; inset: 0; }
  html.js:not(.hero-still):not(.is-static) #hero .video-hero__shade {
    position: absolute; inset: 0; height: auto;
    background: linear-gradient(180deg,
      rgb(7 11 22 / 0.86) 0%, rgb(7 11 22 / 0.62) 30%, rgb(7 11 22 / 0) 56%,
      rgb(7 11 22 / 0) 80%, rgb(7 11 22 / 0.5) 100%);
  }
  html.js:not(.hero-still):not(.is-static) #hero .video-hero__copy {
    position: absolute; z-index: 4;
    top: calc(var(--bar-h, 60px) + clamp(14px, 3svh, 28px));
    left: var(--sc-gutter, 1.25rem); right: var(--sc-gutter, 1.25rem);
    width: auto; margin: 0; padding: 0;
  }

  /* -------------------------------------------------------- peak scene --- */
  html.js:not(.is-static) #peak.act--peak { height: 160svh !important; min-height: 0; }
  html.js:not(.is-static) #peak > [data-sc-stage] {
    position: sticky; top: 0;
    display: flex; flex-direction: column;
    height: 100vh; height: 100svh;
    min-height: 0;
    overflow: clip;
    padding: calc(var(--bar-h, 60px) + 8px) 0 calc(var(--mbar-h, 65px) + 10px);
    background: #050810;
  }
  html.js:not(.is-static) #peak [data-peak-stage] {
    position: relative; inset: auto;
    flex: 1 1 auto;
    min-height: 0;
    order: 1;
  }
  html.js:not(.is-static) #peak [data-peak-stage] > .scene {
    position: absolute; inset: 0;
    width: auto; height: auto;
    aspect-ratio: auto;
  }
  html.js:not(.is-static) #peak .scene__canvas { display: block; }
  html.js:not(.is-static) #peak .scene:not(.is-live) .scene__poster { opacity: 1; visibility: visible; }
  html.js:not(.is-static) #peak .scene.is-live .scene__poster { opacity: 0; visibility: hidden; }
  html.js:not(.is-static) #peak .peak__rail {
    position: relative; inset: auto;
    order: 2;
    flex: 0 0 auto;
    width: auto; max-width: none;
    margin: 10px var(--sc-gutter, 1.25rem) 0;
  }
}

/* The film's first frame and the portrait poster are the same picture: the
   poster is frame 0 cropped to x 262..838 of 1280, full height. While both
   are height-limited (any screen narrower than 4:5) this puts the poster
   exactly where that crop sits inside the film at object-position 42%, so the
   crossfade changes nothing but the moving image. 0.42 * 1280/720 = 74.667
   and 262/720 = 36.389, in svh of the 100svh stage. */
@media (max-width: 860px) and (max-aspect-ratio: 4/5) and (prefers-reduced-motion: no-preference) {
  html.js:not(.hero-still):not(.is-static) #hero .video-hero__poster {
    object-position: calc(42vw - 74.667svh + 36.389svh) 50%;
  }
}

/* Landscape phones: the scene fills the stage and the plate sits on it, top
   left over the tree and sky, so the house in the middle stays in view
   between the header and the phone bar. */
@media (max-width: 860px) and (max-height: 560px) and (orientation: landscape) and (prefers-reduced-motion: no-preference) {
  html.js:not(.is-static) #peak > [data-sc-stage] { display: block; padding: 0; }
  html.js:not(.is-static) #peak [data-peak-stage] { position: absolute; inset: 0; }
  html.js:not(.is-static) #peak .peak__rail {
    position: absolute; z-index: 20;
    left: var(--sc-gutter, 1.25rem); right: auto;
    top: calc(var(--bar-h, 60px) + 8px); bottom: auto;
    width: min(40%, 21rem);
    margin: 0;
  }
}
