/* Selected work cover motion options. Loaded only when <html data-motion="..."> is set.
   Shared: a real <img> still sits under the loop, so the frame is never empty; the loop fades in
   only once it is actually playing. Motion: transform / opacity / clip-path only, scroll-driven
   (animation-timeline) where supported; motion.js drives the same values elsewhere (html.sd-fb). */

/* ---------- shared: still + loop ---------- */
/* .work-media keeps layout (its aspect rules key off its style attribute, so nothing writes to it);
   .work-frame takes the gate / bars / cut, .work-lens takes the push-in */
.work-frame, .work-lens { position: absolute; inset: 0; display: block; }
html[data-motion] .work-media { background: none; }
.work-frame { overflow: hidden; background: var(--tone, #111); }
.work-still, .work-frame .preview { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* the loop is always rendered (iOS will not start or keep a video it thinks is hidden); the still sits on top
   and lifts off once the loop is really running, and comes back only if the loop fails */
.work-still { z-index: 1; transition: opacity 320ms ease; }
.work-lens.is-live .work-still { opacity: 0; }
/* the live treatment's cover rules are switched off here */
html[data-motion].js .work-media, html[data-motion].js .work-media video { animation: none; transform: none; clip-path: none; }
html[data-motion] .work-media { contain: paint; }

/* every option keys off the cover's own view timeline, so title and frame stay in step */
html[data-motion] .work { timeline-scope: --cv; }
html[data-motion] .work-media { view-timeline: --cv block; }

@keyframes mo-rise { from { clip-path: inset(0 0 100% 0); transform: translateY(0.4em); } to { clip-path: inset(-10% 0 -25% 0); transform: none; } }
@keyframes mo-follow { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* ---------- A. Aperture: a hairline of light draws, the gate opens to the full frame, the lens settles ---------- */
@keyframes ap-gate {
  0% { clip-path: inset(50% calc(50% - 1px) 50% calc(50% - 1px)); }
  22% { clip-path: inset(3% calc(50% - 1px) 3% calc(50% - 1px)); animation-timing-function: cubic-bezier(0.7, 0, 0.2, 1); }
  100% { clip-path: inset(0 0 0 0); }
}
@keyframes ap-lens { from { transform: scale(1.15); } to { transform: scale(1); } }

/* ---------- C. Letterbox: the frame arrives as a 2.39:1 band, holds, then the bars pull away to the true frame ---------- */
/* band height as a share of the frame: --arn / 2.39 (wide covers keep more, 9:16 covers start as a thin strip) */
@keyframes lb-bars {
  0% { clip-path: inset(50% 0 50% 0); }
  26% { clip-path: inset(max(0%, calc((1 - var(--arn) / 2.39) * 50%)) 0 max(0%, calc((1 - var(--arn) / 2.39) * 50%)) 0); }
  50% { clip-path: inset(max(0%, calc((1 - var(--arn) / 2.39) * 50%)) 0 max(0%, calc((1 - var(--arn) / 2.39) * 50%)) 0); animation-timing-function: cubic-bezier(0.65, 0, 0.25, 1); }
  100% { clip-path: inset(0 0 0 0); }
}
@keyframes lb-tilt { 0%, 50% { transform: translateY(5%) scale(1.08); animation-timing-function: cubic-bezier(0.65, 0, 0.25, 1); } 100% { transform: none; } }

/* ---------- B. Cut: each campaign holds the screen; the next one slides over it like a hard cut ---------- */
@keyframes cut-back { from { transform: none; } to { transform: scale(0.92) translateY(-3%); } }
@keyframes cut-dim { from { opacity: 0; } to { opacity: 0.72; } }
@keyframes cut-in { from { transform: scale(1.08); } to { transform: none; } }

@media (prefers-reduced-motion: no-preference) {
  /* layout for B (only with motion on; reduced motion keeps the normal flow) */
  html[data-motion="cut"].js:not(.no-motion) .selected .work {
    position: sticky; top: 0; min-height: 100vh; min-height: 100svh; margin-top: 0;
    align-content: center; padding-top: 64px; padding-bottom: 40px; background: var(--black);
  }
  html[data-motion="cut"].js:not(.no-motion) .selected .work--wide { display: grid; align-content: center; }
  html[data-motion="cut"].js:not(.no-motion) .selected .work + .work { margin-top: 0; }
  html[data-motion="cut"].js:not(.no-motion) .selected .work::after { content: ""; position: absolute; inset: 0; background: var(--black); opacity: 0; pointer-events: none; z-index: 3; }
  html[data-motion="cut"].js:not(.no-motion) .work--wide .work-media { width: min(100%, calc(60svh * var(--arn, 1.7778))); }
  html[data-motion="cut"].js:not(.no-motion) .work-media:where([style*="--arn:0."]) { align-self: center; justify-self: center; width: min(100%, calc(76svh * var(--arn))); }
  html[data-motion="cut"].js:not(.no-motion) .work--split, html[data-motion="cut"].js:not(.no-motion) .work--split-rev { align-items: center; }
  html[data-motion="cut"].js:not(.no-motion) .work--split .work-text, html[data-motion="cut"].js:not(.no-motion) .work--split-rev .work-text { align-self: center; }
  html[data-motion="cut"] .selected { padding-bottom: 0; }
  html[data-motion="cut"] .selected .work:nth-of-type(1) { view-timeline: --w1 block, --cw block; }
  html[data-motion="cut"] .selected .work:nth-of-type(2) { view-timeline: --w2 block, --cw block; }
  html[data-motion="cut"] .selected .work:nth-of-type(3) { view-timeline: --w3 block, --cw block; }
  html[data-motion="cut"] .selected .work:nth-of-type(4) { view-timeline: --w4 block, --cw block; }
  html[data-motion="cut"] .selected .work:nth-of-type(5) { view-timeline: --w5 block, --cw block; }
  html[data-motion="cut"] .selected .work:nth-of-type(6) { view-timeline: --w6 block, --cw block; }
  html[data-motion="cut"] .selected { timeline-scope: --w1, --w2, --w3, --w4, --w5, --w6; }
  @media (max-width: 899px) {
    html[data-motion="cut"].js:not(.no-motion) .selected .work { display: flex; flex-direction: column; justify-content: center; padding-top: 72px; padding-bottom: 32px; }
    html[data-motion="cut"].js:not(.no-motion) .work-media:where([style*="--arn:0."]) { width: auto; height: min(56svh, calc((100vw - 2 * var(--pad)) * 0.78 / var(--arn))); align-self: auto; }
    html[data-motion="cut"].js:not(.no-motion) .work-summary { margin-top: 12px; }
    html[data-motion="cut"].js:not(.no-motion) .work-link { margin-top: 8px; }
  }

  @supports (animation-timeline: view()) and (animation-range: entry) {
    /* A */
    html[data-motion="aperture"].js:not(.no-motion):not(.sd-fb) .work-frame { animation: ap-gate linear both; animation-timeline: --cv; animation-range: entry 10% cover 46%; }
    html[data-motion="aperture"].js:not(.no-motion):not(.sd-fb) .work-lens { animation: ap-lens cubic-bezier(0.3, 0, 0.2, 1) both; animation-timeline: --cv; animation-range: entry 10% cover 56%; }
    html[data-motion="aperture"].js:not(.no-motion):not(.sd-fb) .work .work-title { animation: mo-rise cubic-bezier(0.3, 0, 0.2, 1) both; animation-timeline: --cv; animation-range: cover 32% cover 50%; }
    html[data-motion="aperture"].js:not(.no-motion):not(.sd-fb) .work-text > :not(.work-title) { animation: mo-follow cubic-bezier(0.3, 0, 0.2, 1) both; animation-timeline: --cv; animation-range: cover 38% cover 56%; }

    /* C */
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) .work-frame { animation: lb-bars linear both; animation-timeline: --cv; animation-range: entry 0% cover 52%; }
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) .work-lens { animation: lb-tilt linear both; animation-timeline: --cv; animation-range: entry 0% cover 52%; }

    /* B: the outgoing campaign steps back and darkens on the incoming one's timeline */
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(1) :is(.work-frame, .work-text) { animation: cut-back linear both; animation-timeline: --w2; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(1)::after { animation: cut-dim linear both; animation-timeline: --w2; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(2) :is(.work-frame, .work-text) { animation: cut-back linear both; animation-timeline: --w3; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(2)::after { animation: cut-dim linear both; animation-timeline: --w3; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(3) :is(.work-frame, .work-text) { animation: cut-back linear both; animation-timeline: --w4; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(3)::after { animation: cut-dim linear both; animation-timeline: --w4; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(4) :is(.work-frame, .work-text) { animation: cut-back linear both; animation-timeline: --w5; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(4)::after { animation: cut-dim linear both; animation-timeline: --w5; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(5) :is(.work-frame, .work-text) { animation: cut-back linear both; animation-timeline: --w6; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work:nth-of-type(5)::after { animation: cut-dim linear both; animation-timeline: --w6; animation-range: entry 0% entry 100%; }
    /* the incoming frame lands slightly pushed in and settles as it locks */
    /* pinned panels stretch their own cover range, so B times everything on the panel's entry (the slide-in) */
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work-lens { animation: cut-in cubic-bezier(0.3, 0, 0.2, 1) both; animation-timeline: --cw; animation-range: entry 0% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work .work-title { animation: mo-rise cubic-bezier(0.3, 0, 0.2, 1) both; animation-timeline: --cw; animation-range: entry 55% entry 100%; }
    html[data-motion="cut"].js:not(.no-motion):not(.sd-fb) .work-text > :not(.work-title) { animation: mo-follow cubic-bezier(0.3, 0, 0.2, 1) both; animation-timeline: --cw; animation-range: entry 70% entry 100%; }
  }

  /* fallback (motion.js writes inline transform / clip-path / opacity each frame while a cover is on screen) */
  html[data-motion].sd-fb.js:not(.no-motion) :is(.work-frame, .work-lens, .work-text, .work-text > *) { will-change: auto; }
}

/* reduced motion or JS failed: a plain, still frame */
@media (prefers-reduced-motion: reduce) {
  html[data-motion] .work-frame, html[data-motion] .work-lens, html[data-motion] .work-text, html[data-motion] .work-text > * { animation: none !important; clip-path: none !important; transform: none !important; opacity: 1 !important; }
}
html[data-motion].no-motion :is(.work-frame, .work-lens, .work-text, .work-text > *) { animation: none !important; clip-path: none !important; transform: none !important; opacity: 1 !important; }
@media (prefers-reduced-motion: no-preference) {
  html[data-motion="cut"].sd-fb.js:not(.no-motion) .selected .work::after { opacity: var(--dim, 0); }
}

/* ---------- Words: line wipe, scroll-linked in and out (letterbox build) ----------
   Like a title card being typeset: every line of text is uncovered left to right by a hard-edged mask while it
   slides the last few pixels into place, then holds, fully set, through the middle of the screen. Near the
   header the same mask carries on and clears it left to right. Each line rides its own view timeline, so
   lines arrive and leave one after another as they reach those points. Reversible, no timers.
   motion.js cuts paragraphs into their real lines (.ln); titles, labels and links wipe as one line. */
@keyframes ln-in { from { clip-path: inset(-20% 100% -30% 0); transform: translateX(-12px); } to { clip-path: inset(-20% -4% -30% 0); transform: none; } }
@keyframes ln-out { from { clip-path: inset(-20% -4% -30% 0); transform: none; } to { clip-path: inset(-20% -4% -30% 100%); transform: translateX(10px); } }
.ln { display: block; }
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) and (animation-range: entry) {
    /* one rule per group (an :is() list would carry its heaviest selector's weight into every override) */
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) .work .work-title { animation: ln-in cubic-bezier(0.3, 0, 0.2, 1) backwards, ln-out cubic-bezier(0.4, 0, 0.6, 1) forwards; animation-timeline: view(), view(); animation-range: cover 0% cover 22%, cover 84% cover 100%; }
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) .work .work-credit { animation: ln-in cubic-bezier(0.3, 0, 0.2, 1) backwards, ln-out cubic-bezier(0.4, 0, 0.6, 1) forwards; animation-timeline: view(), view(); animation-range: cover 3% cover 24%, cover 84% cover 100%; }
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) .work .work-summary .ln,
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) .work .work-summary:not(.is-split) { animation: ln-in cubic-bezier(0.3, 0, 0.2, 1) backwards, ln-out cubic-bezier(0.4, 0, 0.6, 1) forwards; animation-timeline: view(), view(); animation-range: cover 5% cover 26%, cover 84% cover 100%; }
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) .work .work-link { animation: ln-in cubic-bezier(0.3, 0, 0.2, 1) backwards, ln-out cubic-bezier(0.4, 0, 0.6, 1) forwards; animation-timeline: view(), view(); animation-range: cover 8% cover 29%, cover 84% cover 100%; }
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) :is(.approach-body > p, .offer dd, .process dd, .about-copy p, .contact-body) :is(.ln),
    html[data-motion="letterbox"].js:not(.no-motion):not(.sd-fb) :is(.approach-body > p, .offer dd, .process dd, .about-copy p, .contact-body):not(.is-split) {
      animation: ln-in cubic-bezier(0.3, 0, 0.2, 1) backwards, ln-out cubic-bezier(0.4, 0, 0.6, 1) forwards;
      animation-timeline: view(), view(); animation-range: cover 1% cover 22%, cover 86% cover 100%;
    }
  }
}
/* forced or real fallback: motion.js owns the words (site.css's own title rise is switched off) */
html[data-motion="letterbox"].sd-fb .work .work-title, html[data-motion="letterbox"].sd-fb .work .work-title.sd-text { animation: none; }
@media (prefers-reduced-motion: reduce) {
  html[data-motion] :is(.approach-body > p, .offer dd, .process dd, .about-copy p, .contact-body, .ln) { animation: none !important; opacity: 1 !important; transform: none !important; clip-path: none !important; }
}
html[data-motion].no-motion :is(.approach-body > p, .offer dd, .process dd, .about-copy p, .contact-body, .ln) { animation: none !important; opacity: 1 !important; transform: none !important; clip-path: none !important; }
