/* ==========================================================================
   Motion — reveal start states, buttons, hover variants, transitions
   Paired with js/motion.js

   The site's only easing curve is cubic-bezier(.44, 0, .56, 1) and its only
   transition duration is .4s (plus a .5s ease opacity fade on the canvas
   panels). Both read from the published stylesheet.
   ========================================================================== */

/* --- Intro sequence ------------------------------------------------------
   1. A black loading screen counts up while the hero image decodes.
   2. It lifts away, and the hero image expands out of a small centred
      rectangle to full bleed while its own scale eases back to 1.
   ------------------------------------------------------------------------- */

/* Hidden by default and only shown when JS is running. Without this gate a
   failed or blocked script would leave this full-screen black panel covering
   the site permanently, with nothing able to dismiss it. */
.intro {
  display: none;
}

.js .intro {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--c-bg);
  transition: opacity 0.6s var(--ease-site), visibility 0.6s;
}

/* Nothing below the loader should be scrollable while it is up. */
.js body.intro-active {
  overflow: hidden;
}

/* Word + number sit on one baseline. While counting they are separated by a
   small gap ("transform 100"); once the number resolves to 8 the gap closes
   so the pair reads as the wordmark ("transform8"). */
.intro__mark {
  display: inline-flex;
  align-items: baseline;
  gap: 0.375em;   /* em, so it tracks the font-size below */
  transition: gap var(--dur-base) var(--ease-site);
}

.intro__mark.is-branded {
  gap: 0;
}

/* Final beat: the 8 stays upright and simply picks up a soft glow. It used to
   rotate 90deg into an infinity glyph — that is removed; the mark holds its
   shape so it reads as the wordmark throughout. */
.intro__count {
  display: inline-block;
  transition: text-shadow 0.9s var(--ease-site);
}

.intro__mark.is-glow .intro__count {
  text-shadow:
    0 0 12px rgba(191, 207, 255, 0.55),
    0 0 30px rgba(105, 197, 238, 0.35);
}

@media (prefers-reduced-motion: reduce) {
  .intro__count { transition: none; }
}

/* Sized to match the header wordmark: the logo draws its artwork 166px wide,
   and "Transform8" in Montserrat 500 measures that width at 28.3px. */
.intro__word,
.intro__count {
  font-family: var(--f-display);
  font-size: 28.3px;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.2em;
  color: var(--c-text);
}

/* The header logo halves to a 94x40 stacked mark on phones; keep the intro
   mark from overflowing a narrow screen. */
@media (max-width: 991.98px) {
  .intro__word,
  .intro__count {
    font-size: 22px;
  }
}

/* Fixed-width digits, so 0 -> 100 does not jitter the word beside it. */
.intro__count {
  font-variant-numeric: tabular-nums;
}

.intro__bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: var(--c-accent);
}

.intro.is-done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* The hero starts clipped to a centred rectangle, its image over-scaled.
   Both resolve together, so the picture appears to zoom out of the window. */
.js .hero__bg {
  clip-path: inset(34% 37% 34% 37%);
  will-change: clip-path;
}

.js .hero__video {
  transform: scale(1.45);
  will-change: transform;
}

.js .hero__bg,
.js .hero__video {
  transition:
    clip-path 1.5s var(--ease-out-intro),
    transform 1.8s var(--ease-out-intro);
}

.js .hero.is-revealed .hero__bg {
  clip-path: inset(0% 0% 0% 0%);
}

.js .hero.is-revealed .hero__video {
  transform: scale(1);
}

/* The headline follows the image rather than arriving with it. */
.js .hero__title,
.js .hero__sub {
  transition-delay: 0.45s;
}

/* No JS, or reduced motion: show everything immediately. */
@media (prefers-reduced-motion: reduce) {
  .intro { display: none; }
  .js .hero__bg { clip-path: none; }
  .js .hero__video { transform: none; }
}

/* --- Scroll reveals ------------------------------------------------------ */

/* Start state A — fade-up (83 elements in the source).
   Gated on .js so the page renders fully without JavaScript. */
.js .reveal {
  opacity: 0;
  transform: translate3d(0, 30px, 0);
  transition:
    opacity var(--dur-reveal) var(--ease-site),
    transform var(--dur-reveal) var(--ease-site);
  will-change: transform;
}

/* Start state B — the 15 client-logo grid cells */
.js .reveal--scale {
  transform: scale(0.5);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Canvas-panel stand-ins fade opacity-only, 0.5s ease (inline in source) */
.js .fade-in {
  opacity: 0;
  transition: opacity 0.5s ease;
}

.js .fade-in.is-visible {
  opacity: 1;
}

/* --- Link colour transitions --------------------------------------------- */

/* Nav links: color .4s, hover to 70% white */
.nav__links a {
  transition: color var(--dur-base) var(--ease-site);
}

/* --- CTA button (source component: square, 1px white border, black bg,
       white fill panel that slides off to the right on hover) -------------- */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 40px;
  border: 1px solid var(--c-text);
  background: var(--c-bg);
  overflow: hidden;
  cursor: pointer;
  isolation: isolate;
  text-decoration: none;
}

/* "All Clients" / "Join US" use wider padding in the source */
.btn--wide { padding: 10px 80px; }

.btn__fill {
  position: absolute;
  inset: 0;
  background: var(--c-text);
  z-index: -1;
  /* Hover re-anchors the panel: it narrows to 151px while sliding fully
     off the right edge (left:unset; width:151px; right:-200px). */
  left: 0;
  right: 0;
  width: auto;
  transition:
    right var(--dur-base) var(--ease-site),
    width var(--dur-base) var(--ease-site),
    left var(--dur-base) var(--ease-site);
}

.btn__label {
  position: relative;
  color: var(--c-bg);
  font-family: var(--f-display);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.6em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color var(--dur-base) var(--ease-site);
}

.btn:hover .btn__fill {
  left: auto;
  width: 151px;
  right: -200px;
}

.btn:hover .btn__label {
  color: var(--c-text);
}

/* --- Underlined inline link (veawi7 preset): hover changes colour only ---- */

.link-underlined {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition:
    color var(--dur-base) var(--ease-site),
    text-decoration-color var(--dur-base) var(--ease-site),
    text-decoration-thickness var(--dur-base) var(--ease-site),
    text-underline-offset var(--dur-base) var(--ease-site);
}

.link-underlined:hover {
  color: var(--c-text-70);
}

/* --- Client logo cell ----------------------------------------------------
   Four L-shaped corner brackets (white, 50%) grow 4px -> 8px on hover.
   ------------------------------------------------------------------------- */

.logo-cell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  cursor: pointer;
}

.logo-cell__corner {
  position: absolute;
  z-index: 1;
  width: 4px;
  height: 4px;
  fill: #fff;
  opacity: 0.5;
  transition:
    width var(--dur-base) var(--ease-site),
    height var(--dur-base) var(--ease-site);
}

.logo-cell__corner:nth-of-type(1) { top: 0; left: 0; }
.logo-cell__corner:nth-of-type(2) { top: 0; right: 0; }
.logo-cell__corner:nth-of-type(3) { bottom: 0; left: 0; }
.logo-cell__corner:nth-of-type(4) { bottom: 0; right: 0; }

.logo-cell:hover .logo-cell__corner {
  width: 8px;
  height: 8px;
}

/* --- "Learn more" button -------------------------------------------------
   Label over a 1px divider that stretches full-width and fades in on hover.
   On phone the source ships the Active variant: rule permanently visible.
   ------------------------------------------------------------------------- */

.learn-more {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  cursor: pointer;
}

.learn-more__rule {
  width: 100%;
  height: 1px;
  background: var(--c-text);
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
  transition:
    opacity var(--dur-base) var(--ease-site),
    transform var(--dur-base) var(--ease-site);
}

.learn-more:hover .learn-more__rule,
.card:hover .learn-more__rule {
  opacity: 1;
  transform: scaleX(1);
}

@media (max-width: 991.98px) {
  .learn-more__rule {
    opacity: 1;
    transform: scaleX(1);
  }
}

/* --- Work card -----------------------------------------------------------
   Grayscale cover over grayscale media; hover fades the cover and lifts
   the desaturation.
   ------------------------------------------------------------------------- */

.work-card__media {
  position: relative;
  overflow: hidden;
  filter: grayscale(1);
  transition: filter 0.6s var(--ease-site);
}

/* The source layered a grayscale "Video Cover" poster over a grayscale video.
   Here the poster IS the image, so the cover element is gone — keeping it would
   just paint an opaque box over the photo. The grayscale-to-colour hover below
   reproduces the same reveal. */

.work-card:hover .work-card__media {
  filter: grayscale(0);
}


/* --- Reduced motion ------------------------------------------------------
   The original ships none at the CSS layer; this is a deliberate addition.
   ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .js .reveal,
  .js .fade-in {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}
