/* ============================================================================
   ANIMATION

   This was a 1,595-line animate.css-style library with 80 keyframes, of which
   the site used exactly two effects. It is now just those two.

       <div class=" delay250">
   <div class="animated blurInUpSmall delay250">

   Everything here is inert under prefers-reduced-motion — the old library had
   no such guard, so all 91 entrance animations played regardless.
   ============================================================================ */

@layer utilities {

.animated {
	animation-duration: 1s;
	animation-fill-mode: both;

	&.fast { animation-duration: 0.5s; }
}

@keyframes fadeInUpSmall {
	from { opacity: 0; transform: translate(0, 10px); }
	to   { opacity: 1; transform: none; }
}
.fadeInUpSmall { animation-name: fadeInUpSmall; }

/* The exit-direction reverse of fadeInUpSmall — used by the page-content view
   transition below, not as an .animated entrance class (nothing on the site
   needs to animate itself away on its own page). */
@keyframes fadeOutDownSmall {
	from { opacity: 1; transform: none; }
	to   { opacity: 0; transform: translate(0, 10px); }
}

@keyframes blurInUpSmall {
	from { opacity: 0; filter: blur(10px); transform: translate(0, 20px); }
	to   { opacity: 1; filter: blur(0);    transform: none; }
}
.blurInUpSmall { animation-name: blurInUpSmall; }

.delay100 { animation-delay: 0.1s; }
.delay200 { animation-delay: 0.2s; }
.delay250 { animation-delay: 0.25s; }
.delay300 { animation-delay: 0.3s; }
.delay400 { animation-delay: 0.4s; }
.delay500 { animation-delay: 0.5s; }

@media (prefers-reduced-motion: reduce) {
	.animated,
	.fadeInUpSmall,
	.blurInUpSmall {
		animation: none !important;
		opacity: 1;
		filter: none;
		transform: none;
	}
}

/* --- Page-content view transition -------------------------------------------
   The header opts itself out of the default root crossfade in 06-nav because
   it wants to hold still (it names itself site-header there, which cuts it
   out of the root capture below). Everything else on the page — <main> and
   the footer alike — is the opposite case: still opted out of the *default*
   crossfade (a plain fade reads as a fade, not a "page"), but replaced with
   a real exit/entrance pair instead of silenced outright.

   This targets the implicit "root" name rather than naming <main> or the
   footer individually. Every element the page doesn't name itself falls into
   that one capture automatically, so one rule covers the two-region site
   (chrome that holds still vs. everything that doesn't) without having to
   track which elements exist on which of the 17 pages.

   Only the old/new crossfade animation is overridden; the group's default
   animation (which interpolates the container's size and position between
   the outgoing and incoming rects) is left alone and just given a matching
   duration, because a case study and a short page like /about are genuinely
   different heights and that's the part of the default behaviour that
   handles it — reimplementing it here would be redoing work the browser
   already does for free. */
::view-transition-group(root) { animation-duration: 150ms; }
::view-transition-old(root)   { animation: 150ms ease-in both fadeOutDownSmall; }
::view-transition-new(root)   { animation: 150ms 75ms ease-out both fadeInUpSmall; }

/* --- Shared media morphs -----------------------------------------------------
   Two pages often show the same picture at a different size and position: a
   work-card thumbnail and its case study's cover, the small studio portrait on
   the home page and the big one at the head of /about. Giving both elements
   the same view-transition-name pulls that one element out of the root
   crossfade above into its own group, and the browser's default group
   animation — the same size/position interpolation root relies on — morphs one
   into the other instead of the two crossfading in place.

   The pairing is named where the content identity actually lives: a
   `style="view-transition-name: …"` attribute on the element itself, on both
   pages. That's the same idiom as `data-slot` — per-instance identity belongs
   with the markup, not with a stylesheet that would need a new rule every time
   a case study is added or featured on another page. The name only ever has to
   be unique per document, so the same "work-cover-west-music" reused across
   three source pages pointing at the one destination is correct, not a
   collision.

   Names in use today:

     work-cover-<slug>   a .workCardMedia and the matching .csHero
                         .mediaFrameInner — home, /work and /for-agencies all
                         feature the same cards
     studio-portrait     the .artifactPlate inside .portraitPlate, on the home
                         page's practice band and at the head of /about

   Every pair also carries `view-transition-class: media-morph`, so this one
   rule times every current and future pairing without being touched again.
   Browsers without view-transition-class support (newer than the base API)
   just fall back to the default per-group crossfade — still a reasonable
   result, so no fallback rule is needed. */
::view-transition-group(.media-morph),
::view-transition-old(.media-morph),
::view-transition-new(.media-morph) {
	animation-duration: 400ms;
}
::view-transition-group(.media-morph) {
	animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}

/* The cross-document view transition opted into in 01-reset crossfades the
   whole page by default (the persistent header's own group is already
   silenced in 06-nav, unconditionally — it's not motion, it's meant to hold
   still). This is the same guard pattern as above, just aimed at the
   browser-generated transition pseudo-elements instead of the .animated
   classes. */
@media (prefers-reduced-motion: reduce) {
	::view-transition-group(*),
	::view-transition-old(*),
	::view-transition-new(*) {
		animation: none;
	}
}

} /* @layer utilities */


@layer utilities {

/* --- Responsive visibility helpers ------------------------------------------
   .excludeFromX hides an element at that breakpoint; .includeInX shows it only
   at that breakpoint. Breakpoints: mobile < 768 <= tablet <= 1024 < desktop.  */

@media only screen and (min-width: 1025px) {
	/* desktop */
	.excludeFromDesktop { display: none; }
	.includeInTablet,
	.includeInTabletLandscape,
	.includeInTabletPortrait,
	.includeInMobile,
	.includeInMobilePortrait,
	.includeInMobileLandscape { display: none; }
}

@media only screen and (min-width: 768px) and (max-width: 1024px) {
	/* tablet, portrait and landscape */
	.excludeFromTablet { display: none; }
	.includeInDesktop,
	.includeInMobile,
	.includeInMobilePortrait,
	.includeInMobileLandscape { display: none; }
}

@media only screen and (min-width: 960px) and (max-width: 1024px) {
	/* tablet landscape */
	.excludeFromTabletLandscape { display: none; }
	.includeInTabletPortrait { display: none; }
}

@media only screen and (min-width: 768px) and (max-width: 959px) {
	/* tablet portrait */
	.excludeFromTabletPortrait { display: none; }
	.includeInTabletLandscape { display: none; }
}

@media only screen and (max-width: 767px) {
	/* mobile, portrait and landscape */
	.excludeFromMobile { display: none; }
	.includeInDesktop,
	.includeInTablet,
	.includeInTabletPortrait,
	.includeInTabletLandscape { display: none; }
}

@media only screen and (min-width: 480px) and (max-width: 767px) {
	/* mobile landscape */
	.excludeFromMobileLandscape { display: none; }
	.includeInMobilePortrait { display: none; }
}

@media only screen and (max-width: 479px) {
	/* mobile portrait */
	.excludeFromMobilePortrait { display: none; }
	.includeInMobileLandscape { display: none; }
}

} /* @layer utilities */
