/* ============================================================
   SANGAM MOTION SYSTEM
   Loads after style.css. Adds motion only; changes no existing
   colour, type or layout token.

   The idea the motion has to carry is the word itself: sangam,
   a confluence. Streams arriving from different directions and
   settling together. So reveals are directional and converging,
   not a page of identical fade-ups.

   Five behaviours, each with a job:
     converge  elements arrive from opposite sides and settle
     lines     a headline reveals line by line behind a mask
     stack     cards layer up, back to front
     flow      content travels in along the reading direction
     build     parts assemble in sequence along a path

   Everything is transform + opacity only, so it stays on the
   compositor. Nothing here animates layout properties.
   ============================================================ */

/* Off-stage reveal transforms must never widen the page. */
html { overflow-x: clip; }
body { overflow-x: clip; }

:root {
  /* durations */
  --m-xs: 180ms;
  --m-s:  320ms;
  --m-m:  560ms;
  --m-l:  820ms;

  /* easings: settle is the house curve, a soft overshoot-free landing */
  --m-settle: cubic-bezier(0.22, 0.61, 0.36, 1);
  --m-soft:   cubic-bezier(0.33, 1, 0.68, 1);
  --m-inout:  cubic-bezier(0.65, 0.05, 0.36, 1);

  /* rhythm */
  --m-stagger: 70ms;

  /* distances */
  --m-rise: 22px;
  --m-slide: clamp(18px, 4vw, 40px);
  --m-lift: -4px;
  --m-nudge: 6px;
}

/* ============================================================
   Base reveal contract
   JS adds .is-in when the element enters. Until JS runs, nothing
   is hidden: if scripting fails the page reads normally.
   ============================================================ */

.js-motion [data-reveal] {
  opacity: 0;
  transition:
    opacity var(--m-m) var(--m-settle),
    transform var(--m-m) var(--m-settle);
  transition-delay: calc(var(--i, 0) * var(--m-stagger));
  will-change: transform, opacity;
}
.js-motion [data-reveal].is-in {
  opacity: 1;
  transform: none;
}
/* release the compositor hint once the element has landed */
.js-motion [data-reveal].is-done { will-change: auto; }

/* Default rise lives on the base rule, and each named variant overrides it.
   The previous :not() chain scored (0,7,0), out-specifying .is-in at (0,3,0),
   so default reveals faded in but never travelled back to their resting
   position. Keep every selector here at or below two classes. */
.js-motion [data-reveal] { transform: translate3d(0, var(--m-rise), 0); }
.js-motion [data-reveal="converge"],
.js-motion [data-reveal="stack"],
.js-motion [data-reveal="build"],
.js-motion [data-reveal="lines"] { transform: none; }

/* ---- CONVERGENCE ------------------------------------------
   Children arrive from alternating sides and meet in the middle.
   This is the signature move; use it where the page is making
   the point that separate things belong together.            */
.js-motion [data-reveal="converge"] > * {
  opacity: 0;
  transform: translate3d(var(--from-x, 0), var(--from-y, 14px), 0);
  transition:
    opacity var(--m-l) var(--m-settle),
    transform var(--m-l) var(--m-settle);
  transition-delay: calc(var(--i, 0) * var(--m-stagger));
}
.js-motion [data-reveal="converge"].is-in > * {
  opacity: 1;
  transform: none;
}
.js-motion [data-reveal="converge"] > *:nth-child(odd)  { --from-x: calc(var(--m-slide) * -1); }
.js-motion [data-reveal="converge"] > *:nth-child(even) { --from-x: var(--m-slide); }

/* ---- LINES ------------------------------------------------
   Masked line-by-line headline reveal. JS wraps each visual line
   in .m-line > .m-line-i so the mask is per line, not per word. */
.m-line { display: block; overflow: hidden; }
.m-line-i {
  display: block;
  transform: translate3d(0, 105%, 0);
  transition: transform var(--m-l) var(--m-settle);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.js-motion [data-reveal="lines"].is-in .m-line-i { transform: none; }

/* ---- STACK ------------------------------------------------
   Cards layer up back-to-front, each slightly behind the last. */
.js-motion [data-reveal="stack"] > * {
  opacity: 0;
  transform: translate3d(0, 34px, 0) scale(0.97);
  transition:
    opacity var(--m-m) var(--m-settle),
    transform var(--m-m) var(--m-settle);
  transition-delay: calc(var(--i, 0) * var(--m-stagger));
}
.js-motion [data-reveal="stack"].is-in > * { opacity: 1; transform: none; }

/* ---- FLOW -------------------------------------------------
   Travels in along the reading direction.                     */
.js-motion [data-reveal="flow"] { transform: translate3d(calc(var(--m-slide) * -1), 0, 0); }

/* ---- BUILD ------------------------------------------------
   Parts assemble in sequence. Used where a path or line is
   drawing through them.                                       */
.js-motion [data-reveal="build"] > * {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  transition:
    opacity var(--m-s) var(--m-settle),
    transform var(--m-s) var(--m-settle);
  transition-delay: calc(var(--i, 0) * 110ms);
}
.js-motion [data-reveal="build"].is-in > * { opacity: 1; transform: none; }

/* ============================================================
   Drawn lines: the connective tissue between ideas
   A stroke that draws itself as the section arrives.
   ============================================================ */
.m-draw path,
.m-draw line,
.m-draw polyline {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: var(--len, 1000);
  transition: stroke-dashoffset 1100ms var(--m-soft);
}
.is-in .m-draw path,
.is-in .m-draw line,
.is-in .m-draw polyline { stroke-dashoffset: 0; }

/* ============================================================
   Microinteractions
   Small, consistent, and the same everywhere.
   ============================================================ */

/* links: the arrow travels */
.js-motion a .arw,
.js-motion .route-arrow,
.js-motion .cb-cta span[aria-hidden] {
  display: inline-block;
  transition: transform var(--m-xs) var(--m-settle);
}
.js-motion a:hover .arw,
.js-motion .cb-cta:hover span[aria-hidden] { transform: translateX(var(--m-nudge)); }

/* buttons: tactile press, plus a magnetic nudge on desktop (JS) */
.js-motion .btn {
  transition:
    transform var(--m-xs) var(--m-settle),
    background var(--m-xs) linear,
    color var(--m-xs) linear,
    box-shadow var(--m-xs) var(--m-settle);
}
.js-motion .btn:active { transform: scale(0.97); }

/* images: a restrained 1 -> 1.03 */
.js-motion .m-zoom { overflow: hidden; }
.js-motion .m-zoom img {
  transition: transform var(--m-l) var(--m-soft);
  will-change: transform;
}
.js-motion .m-zoom:hover img { transform: scale(1.03); }

/* ============================================================
   Reduced motion
   Keep meaning, drop movement. Everything collapses to a plain
   fade, continuous decorative motion stops, parallax is off.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .m-force [data-reveal] { }
  .js-motion:not(.m-force) [data-reveal],
  .js-motion:not(.m-force) [data-reveal="converge"] > *,
  .js-motion:not(.m-force) [data-reveal="stack"] > *,
  .js-motion:not(.m-force) [data-reveal="build"] > * {
    transform: none !important;
    transition: opacity var(--m-s) linear !important;
    transition-delay: 0ms !important;
  }
  :not(.m-force) .m-line-i {
    transform: none !important;
    transition: none !important;
  }
  .m-draw path,
  .m-draw line,
  .m-draw polyline {
    stroke-dashoffset: 0 !important;
    transition: none !important;
  }
  .js-motion .m-zoom:hover img { transform: none !important; }
  /* JS also skips parallax, magnetics and counters under this query */
}

/* ============================================================
   Word masks (used by data-reveal="lines")
   Each word gets its own mask; words sharing a visual line share
   a delay, so it reads line by line but survives nested markup.
   ============================================================ */
.m-word { display: inline-block; overflow: hidden; vertical-align: bottom; }
.m-word-i {
  display: inline-block;
  transform: translate3d(0, 110%, 0);
  transition: transform var(--m-l) var(--m-settle);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.js-motion [data-reveal="lines"].is-in .m-word-i { transform: none; }
@media (prefers-reduced-motion: reduce) {
  :not(.m-force) .m-word-i { transform: none !important; transition: none !important; }
}

/* ============================================================
   HERO: the eyebrow converges, then the headline lifts
   CREATIVITY from the left, COMMERCE from the right, CAPITAL
   from below, and the crosses fade in last, once the three
   streams have met. This is the thesis of the brand as motion.
   ============================================================ */
.eyebrow-streams { display: inline-flex; align-items: center; gap: 0.5em; flex-wrap: wrap; }
.js-motion .es-w, .js-motion .es-x { display: inline-block; opacity: 0; }
.js-motion .es-w {
  transition: opacity var(--m-l) var(--m-settle), transform var(--m-l) var(--m-settle);
}
.js-motion .es-1 { transform: translate3d(-28px, 0, 0); transition-delay: 120ms; }
.js-motion .es-2 { transform: translate3d(28px, 0, 0);  transition-delay: 260ms; }
.js-motion .es-3 { transform: translate3d(0, 16px, 0);  transition-delay: 400ms; }
.js-motion .es-x {
  transition: opacity var(--m-m) linear, transform var(--m-m) var(--m-settle);
  transform: scale(0.5) rotate(-25deg);
  transition-delay: 620ms;
}
.js-motion .hero.is-in .es-w,
.js-motion .hero.is-in .es-x { opacity: 1; transform: none; }
.js-motion .hero.is-in .es-x { opacity: 0.75; }

/* hero art drifts a little slower than the page */
.hero-deco .hd { will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  .js-motion:not(.m-force) .es-w, .js-motion:not(.m-force) .es-x { opacity: 1 !important; transform: none !important; transition: none !important; }
}

/* ============================================================
   NAV: integrated at the top, compact once you are reading
   ============================================================ */
.nav { transition: padding var(--m-s) var(--m-settle), background var(--m-s) linear, box-shadow var(--m-s) linear; }
.nav.nav-compact { box-shadow: 0 2px 18px rgba(36, 60, 144, 0.10); }
.nav.nav-compact .nav-inner { padding-top: 6px; padding-bottom: 6px; }
.nav-links a { position: relative; }
.nav-links > li > a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: 2px;
  background: currentColor; transform: scaleX(0); transform-origin: left;
  transition: transform var(--m-s) var(--m-settle);
}
.nav-links > li > a:hover::after,
.nav-links > li > a:focus-visible::after { transform: scaleX(1); }

/* dropdown opens deliberately, with the items arriving in order */
.drop-menu { transition: opacity var(--m-s) var(--m-settle), transform var(--m-s) var(--m-settle); }
.drop-menu li { transition: opacity var(--m-s) var(--m-settle), transform var(--m-s) var(--m-settle); }
.has-drop:not(:hover):not(:focus-within) .drop-menu li { opacity: 0; transform: translateY(-6px); }
.has-drop:hover .drop-menu li:nth-child(1),
.has-drop:focus-within .drop-menu li:nth-child(1) { transition-delay: 40ms; }
.has-drop:hover .drop-menu li:nth-child(2),
.has-drop:focus-within .drop-menu li:nth-child(2) { transition-delay: 80ms; }
.has-drop:hover .drop-menu li:nth-child(3),
.has-drop:focus-within .drop-menu li:nth-child(3) { transition-delay: 120ms; }
.has-drop:hover .drop-menu li:nth-child(4),
.has-drop:focus-within .drop-menu li:nth-child(4) { transition-delay: 160ms; }

/* ============================================================
   ROUTER: doors into Sangam
   ============================================================ */
.js-motion .route {
  transition: transform var(--m-s) var(--m-settle), box-shadow var(--m-s) var(--m-settle), opacity var(--m-s) linear;
}
@media (hover: hover) and (pointer: fine) {
  .js-motion .router-grid:hover .route { opacity: 0.62; }
  .js-motion .router-grid .route:hover {
    opacity: 1;
    transform: translateY(-6px) rotate(-0.8deg);
    box-shadow: 0 16px 34px rgba(36, 60, 144, 0.16);
  }
  .js-motion .route:hover .route-arrow { transform: translate(7px, -7px); }
}
.js-motion .route-arrow { transition: transform var(--m-s) var(--m-settle); }
.js-motion .route:active { transform: scale(0.99); }

/* ============================================================
   MARQUEES: slow, seamless, and they yield on hover
   ============================================================ */
.marquee:hover .marquee-track,
.cat-marquee:hover .cat-track { animation-play-state: paused; }
.marquee-track, .cat-track { will-change: transform; }
/* a later occurrence runs the other way, so the page has a current */
.marquee.marquee-rev .marquee-track { animation-direction: reverse; }

/* ============================================================
   THE GAP: talent first, then the weight of what is missing
   ============================================================ */
.js-motion .gap-left h2 .gap-dim { display: block; }
.js-motion .gap-tag {
  opacity: 0; transform: translate3d(0, 10px, 0);
  transition: opacity var(--m-m) var(--m-settle), transform var(--m-m) var(--m-settle);
  transition-delay: calc(var(--i, 0) * 110ms + 260ms);
}
.js-motion .gap.is-in .gap-tag { opacity: 1; transform: none; }
/* the thread that runs through the six missing pieces */
.gap-thread { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
.gap-right { position: relative; }

/* ============================================================
   ECOSYSTEM: a line drawn from 01 to 04 as you scroll
   Inactive stages sit back; the active one comes forward.
   ============================================================ */
.stack-path { position: relative; --progress: 0; }
.stack-path::before,
.stack-path::after {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
}
/* the track */
.stack-path::before { background: currentColor; opacity: 0.16; }
/* the filled progress */
.stack-path::after { background: var(--orange); transition: none; }

@media (min-width: 861px) {
  .stack-path::before, .stack-path::after { top: 34px; height: 2px; left: 6%; right: 6%; }
  .stack-path::after { right: auto; width: calc((100% - 12%) * var(--progress)); }
}
@media (max-width: 860px) {
  .stack-path::before, .stack-path::after { left: 26px; width: 2px; top: 10px; bottom: 10px; }
  .stack-path::after { bottom: auto; height: calc((100% - 20px) * var(--progress)); }
}

.js-motion .sp-step {
  position: relative; z-index: 1;
  opacity: 0.45;
  transform: translateY(6px);
  transition: opacity var(--m-m) var(--m-settle), transform var(--m-m) var(--m-settle);
}
.js-motion .sp-step.sp-on { opacity: 1; transform: none; }
/* The active marker cannot be a fixed colour: orange scored 1.42:1 on the
   periwinkle card. Use the card's own text colour, which is already proven
   against its own background, and mark active with weight + a ring. */
.js-motion .sp-step .sp-num { transition: opacity var(--m-s) linear; opacity: 0.7; }
.js-motion .sp-step.sp-on .sp-num { opacity: 1; }
.js-motion .sp-step.sp-on {
  box-shadow: 0 0 0 3px currentColor inset;
}
/* keyboard focus is never removed, only made obvious */
.js-motion .sp-step:focus-visible {
  outline: 3px solid var(--blue-deep);
  outline-offset: 3px;
}
.js-motion .sp-arrow { opacity: 0.3; transition: opacity var(--m-s) linear; }
.js-motion .stack-path .sp-arrow { z-index: 1; position: relative; }

@media (prefers-reduced-motion: reduce) {
  .js-motion .sp-step { opacity: 1 !important; transform: none !important; }
}

/* ============================================================
   PROOF: numbers dominate
   ============================================================ */
.stat h3 { font-variant-numeric: tabular-nums; }

/* ============================================================
   CATEGORY RAIL: hover a category, see the world behind it
   ============================================================ */
[data-rail] .cat-track > span { position: relative; cursor: default; transition: opacity var(--m-s) linear; }
@media (hover: hover) and (pointer: fine) {
  [data-rail]:hover .cat-track > span { opacity: 0.45; }
  [data-rail] .cat-track > span:hover { opacity: 1; }
}

/* ============================================================
   FOUNDER: editorial, parts arriving in reading order
   ============================================================ */
.js-motion .founder-hero .fh-portrait { will-change: transform; }
.founder-sign { position: relative; }

/* ============================================================
   TEAM: a person, not a directory row
   ============================================================ */
.js-motion .team-card { transition: transform var(--m-s) var(--m-settle); }
.js-motion .team-photo { overflow: hidden; }
.js-motion .team-photo img { transition: transform var(--m-l) var(--m-soft); }
.js-motion .team-bio2 {
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height var(--m-m) var(--m-settle), opacity var(--m-s) linear, margin-top var(--m-s) linear;
}
@media (hover: hover) and (pointer: fine) {
  .js-motion .team-card:hover { transform: translateY(-5px); }
  .js-motion .team-card:hover .team-photo img { transform: scale(1.05); }
  .js-motion .team-card:hover .team-bio2,
  .js-motion .team-card:focus-within .team-bio2 { max-height: 6em; opacity: 1; margin-top: 6px; }
}
/* touch devices never hide the personality line behind a hover */
@media (hover: none), (pointer: coarse) {
  .js-motion .team-bio2 { max-height: none; opacity: 1; overflow: visible; }
}
@media (prefers-reduced-motion: reduce) {
  .js-motion .team-bio2 { transition: opacity var(--m-s) linear; }
}

/* ============================================================
   ECOSYSTEM PAGE TRANSITION
   A short curtain in the destination's own colour.
   ============================================================ */
.m-curtain {
  position: fixed; inset: 0; z-index: 999;
  transform: scaleY(0); transform-origin: bottom;
  pointer-events: none;
  transition: transform 380ms var(--m-inout);
}
.m-curtain.is-on { transform: scaleY(1); pointer-events: all; }

/* the hero headline lands after the three streams have met */
.js-motion .hero h1 .m-word-i { transition-delay: calc(var(--i, 0) * 90ms + 560ms); }
.js-motion .hero .lede[data-reveal] { transition-delay: 900ms; }
.js-motion .hero .hero-ctas { transition-delay: 1020ms; }
@media (prefers-reduced-motion: reduce) {
  .js-motion .hero h1 .m-word-i,
  .js-motion .hero .lede[data-reveal],
  .js-motion .hero .hero-ctas { transition-delay: 0ms !important; }
}
