/* ============================================================
   BIN CLEANING SUMMIT 2027 — MOTION LAYER
   Loaded after site.css. Behaviour only: states, transitions,
   keyframes. Delete this file and the site still renders and works.

   Curves (defined in site.css):
     --bez         general — header states, card lift, hero
     --bez-btn     button position
     --bez-scroll  anything scroll-driven, and follow-the-pointer
   ============================================================ */

/* ------------------------------------------------------------
   1. SPLASH — once per browser session
   ------------------------------------------------------------ */
/* Only exists when JS is there to clear it, and is skipped entirely once
   it has been seen this session (the inline head script sets no-splash),
   so moving between pages never replays it. */
.splash { display: none; }

html.js:not(.no-splash) .splash {
  display: grid;
  position: fixed;
  inset: 0;
  z-index: 999;
  place-items: center;
  background: var(--bg);
  opacity: 1;
  transition: opacity 520ms var(--bez), visibility 0s linear 520ms;
}
.splash__mark {
  width: min(220px, 46vw);
  height: auto;
  opacity: 0;
  transform: scale(0.97);
  animation: splash-mark 700ms var(--bez) forwards;
}
@keyframes splash-mark {
  0%   { opacity: 0; transform: scale(0.97); }
  45%  { opacity: 1; transform: scale(1); }
  100% { opacity: 1; transform: scale(1); }
}
.splash.is-done { opacity: 0; visibility: hidden; pointer-events: none; }

html.js:not(.no-splash) body.is-loading { overflow: hidden; }

/* Hold the hero entrance until the overlay lifts, or it plays unseen. */
html.js:not(.no-splash) body.is-loading .hero__lockup,
html.js:not(.no-splash) body.is-loading .hero__credit,
html.js:not(.no-splash) body.is-loading .hero__eyebrow,
html.js:not(.no-splash) body.is-loading .page-head > .shell > * {
  animation-play-state: paused;
}

/* ------------------------------------------------------------
   2. HEADER — open at the top, glass once you scroll
   ------------------------------------------------------------ */
.site-header__bar {
  transition:
    max-width 1000ms var(--bez),
    height    1000ms var(--bez),
    padding   1000ms var(--bez);
}
.glass__fill,
.site-header__bar::before,
.site-header__bar::after  { transition: opacity 1200ms var(--bez); }
.site-header__logo img    { transition: width 1000ms var(--bez); }

/* Hover: the label brightens and the rectangle fades in under it. */
.nav__link { transition: color 250ms var(--bez); }
.nav__link::before,
.mobile-nav__link::before { transition: opacity 250ms var(--bez); }

/* Press: the link squeezes in and a brighter plate flashes behind it,
   then both spring back. motion.js starts it on pointerdown and it
   always runs its full course, so even a quick click is seen. The plate
   is its own layer (::after), so the hover rectangle never pops. */
.nav__link,
.mobile-nav__link { --press-scale: 0.94; }
.mobile-nav__link { --press-scale: 0.975; }   /* full-width row: less travel */

.nav__link::after,
.mobile-nav__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: rgba(150, 205, 255, 0.16);
  box-shadow: inset 0 0 0 1px rgba(150, 205, 255, 0.24);
  opacity: 0;
  pointer-events: none;
}
.nav__link.is-pressed,
.mobile-nav__link.is-pressed { animation: nav-press 380ms var(--bez-btn); }
.nav__link.is-pressed::after,
.mobile-nav__link.is-pressed::after { animation: nav-press-glow 380ms var(--bez-btn); }

@keyframes nav-press {
  0%   { transform: scale(1); }
  30%  { transform: scale(var(--press-scale)); }
  100% { transform: scale(1); }
}
@keyframes nav-press-glow {
  0%   { opacity: 0; }
  25%  { opacity: 1; }
  100% { opacity: 0; }
}

/* The pill's slide after a click wants a fast start (a slow one reads
   as lag): the scroll curve, which leaves at once and settles softly. */
.nav__indicator {
  transition:
    transform 420ms var(--bez-scroll),
    width     420ms var(--bez-scroll),
    opacity   250ms var(--bez);
}


/* ------------------------------------------------------------
   3. SCROLL CUE (home intro only)
   ------------------------------------------------------------ */
.scroll-cue {
  position: fixed;
  left: 50%;
  bottom: 32px;
  z-index: 60;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  margin-left: -21px;
  padding: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 42% 18%, rgba(96, 197, 254, 0.22) 0%, rgba(29, 129, 255, 0.04) 70%);
  box-shadow: inset 0 0 0 1px rgba(96, 197, 254, 0.3), inset 0 1px 0 rgba(180, 225, 255, 0.25), inset 1px 0 0 rgba(180, 225, 255, 0.08);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  cursor: pointer;
  transition: opacity 700ms var(--bez), transform 700ms var(--bez), box-shadow 300ms var(--bez);
}
html.is-intro .scroll-cue { pointer-events: auto; }
html.is-intro .scroll-cue:hover,
html.is-intro .scroll-cue:focus-visible { box-shadow: inset 0 0 0 1px rgba(96, 197, 254, 0.6), inset 0 1px 0 rgba(180, 225, 255, 0.34), inset 1px 0 0 rgba(180, 225, 255, 0.10); }
.scroll-cue svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: var(--blue-2);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
html.is-intro .scroll-cue {
  animation:
    cue-in  700ms var(--bez) 1100ms forwards,
    cue-bob 2200ms var(--bez) 1800ms infinite;
}
html:not(.is-intro) .scroll-cue { opacity: 0; transform: translateY(12px); }
@keyframes cue-in  { to { opacity: 1; } }
@keyframes cue-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }

/* ------------------------------------------------------------
   4. SEAM — draws out from the centre; a soft highlight trails the
   pointer along it (--seam-x, written by motion.js)
   ------------------------------------------------------------ */
.seam {
  background: linear-gradient(90deg,
    rgba(96, 197, 254, 0)    calc(var(--seam-x, 50%) - 48%),
    rgba(96, 197, 254, 0.16) calc(var(--seam-x, 50%) - 25%),
    rgba(160, 226, 255, 0.6) var(--seam-x, 50%),
    rgba(96, 197, 254, 0.16) calc(var(--seam-x, 50%) + 25%),
    rgba(96, 197, 254, 0)    calc(var(--seam-x, 50%) + 48%));
  transform: scaleX(0.2);
  opacity: 0;
  transition: transform 1100ms var(--bez-scroll), opacity 800ms var(--bez-scroll);
}
/* A soft blue bloom under the brightest point. No sideways bleed — a
   bleed past the seam widened the page on narrow screens in v1. */
.seam::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 120px;
  transform: translateY(-50%);
  pointer-events: none;
  background: radial-gradient(ellipse 28% 32% at var(--seam-x, 50%) 50%,
    rgba(29, 129, 255, 0.09) 0%,
    rgba(29, 129, 255, 0.03) 40%,
    rgba(29, 129, 255, 0) 72%);
}
.seam.is-in { transform: scaleX(1); opacity: 1; }

/* ------------------------------------------------------------
   5. SCROLL REVEAL
   A keyframe animation, not a transition: components declare their
   own `transition` for hover, which would replace a reveal transition
   wholesale (transform animates, opacity snaps). `backwards` fill only,
   so hover transforms take over once the rise has finished.
   ------------------------------------------------------------ */
[data-reveal] { opacity: 0; }
[data-reveal].is-in {
  opacity: 1;
  animation: reveal-rise 800ms var(--bez-scroll) var(--reveal-delay, 0ms) backwards;
}
@keyframes reveal-rise {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Subpage heads arrive on load rather than on scroll. */
.page-head > .shell > * {
  opacity: 0;
  transform: translateY(14px);
  animation: rise-in 800ms var(--bez-scroll) forwards;
}
.page-head > .shell > :nth-child(1) { animation-delay: 80ms; }
.page-head > .shell > :nth-child(2) { animation-delay: 160ms; }
.page-head > .shell > :nth-child(3) { animation-delay: 240ms; }
.page-head > .shell > :nth-child(4) { animation-delay: 320ms; }
.page-head > .shell > :nth-child(5) { animation-delay: 400ms; }

/* ------------------------------------------------------------
   6. HERO (home)
   ------------------------------------------------------------ */
.hero__lockup,
.hero__credit,
.hero__eyebrow {
  opacity: 0;
  transform: translateY(10px);
  animation: rise-in 700ms var(--bez) forwards;
}
.hero__lockup  { animation-delay: 140ms; }
.hero__credit  { animation-delay: 260ms; }
.hero__eyebrow { animation-delay: 380ms; }

@keyframes rise-in { to { opacity: 1; transform: none; } }

/* --intro-shift centres the lockup group in the viewport (measured by
   motion.js) and is released on the first downward scroll. A fast
   start, so it travels with the scroll that released it. */
.hero__intro {
  transform: translateY(var(--intro-shift, 0px));
  transition: transform 1000ms var(--bez-scroll);
}
html:not(.is-intro) .hero__intro { transform: none; }

/* Headline, sub and actions wait for the release. */
html.is-intro .hero__word,
html.is-intro .hero__sub,
html.is-intro .hero__actions,
html.is-intro .hero__micro,
html.is-intro .hero__title::before { opacity: 0; }

/* The reveal (html.is-reveal, added on release), about 1.6s end to end
   (was 3s; Chris, 19 Oct: "too slowly, i scroll past it").
   The page scrolls freely the whole time.
     1. Headline, word by word in reading order (For, Operators., By,
        Operators.): each rises into place and sharpens out of a blur
        on --bez-reveal. Order is --i in the markup.      0 – ~1.1s
     2. Paragraph: uncovered through a soft mask that sweeps from its top
        left corner, on the same curve.                   0.35 – 1.15s
     3. Buttons: their rise-and-sharpen.                  0.6 – 1.25s
     4. Micro line: the same top-left mask.               0.8 – 1.5s
   The blurred ones fill BACKWARDS only: once finished they drop the
   animation, so no filter stays on the text. Held at blur(0), iOS
   Safari kept the words soft with magenta seams (19 Oct). */
html.is-reveal .hero__word {
  animation: word-in 850ms var(--bez-reveal) backwards;
  animation-delay: calc(var(--i, 0) * 90ms);
}
html.is-reveal .hero__sub            { animation: mask-in 800ms var(--bez-reveal) 350ms both; }
html.is-reveal .hero__actions        { animation: rise-in-blur 650ms var(--bez-impact) 600ms backwards; }
html.is-reveal .hero__micro          { animation: mask-in 700ms var(--bez-reveal) 800ms both; }
html.is-reveal .hero__title::before  { animation: glow-in 2200ms var(--bez) 300ms both; }

/* The mask: opaque toward the top left, clear toward the bottom right,
   with a wide feather between, drawn at 2.5x the element. Sliding it
   from the far corner to 0 0 carries the feathered edge across the
   text from its top left corner. At 0 0 the element sees only the
   opaque part, so the finished text is untouched. */
html.is-reveal .hero__sub,
html.is-reveal .hero__micro {
  -webkit-mask-image: linear-gradient(135deg, #000 0%, #000 42%, transparent 58%, transparent 100%);
          mask-image: linear-gradient(135deg, #000 0%, #000 42%, transparent 58%, transparent 100%);
  -webkit-mask-size: 250% 250%;
          mask-size: 250% 250%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

@keyframes word-in {
  from { opacity: 0; transform: translateY(0.6em); filter: blur(0.14em); }
  to   { opacity: 1; transform: none;              filter: none; }
}
@keyframes mask-in {
  from { -webkit-mask-position: 100% 100%; mask-position: 100% 100%; }
  to   { -webkit-mask-position: 0% 0%;     mask-position: 0% 0%; }
}
@keyframes rise-in-blur {
  from { opacity: 0; transform: translateY(16px); filter: blur(6px); }
  to   { opacity: 1; transform: none;             filter: none; }
}
@keyframes glow-in {
  from { opacity: 0; }
  to   { opacity: 0.45; }
}

html.is-intro body { overflow-x: hidden; }

/* ------------------------------------------------------------
   7. AMBIENT DRIFT — slow enough to never read as movement
   ------------------------------------------------------------ */
.ambient__glow {
  will-change: transform;
  animation: drift var(--drift-dur, 48s) var(--bez) infinite alternate;
  animation-delay: var(--drift-delay, 0s);
}
.ambient__glow--tl { --drift-dur: 46s; --drift-delay: -4s;  }
.ambient__glow--tr { --drift-dur: 53s; --drift-delay: -12s; animation-name: drift-alt; }
.ambient__glow--mr { --drift-dur: 61s; --drift-delay: -22s; }
.ambient__glow--ml { --drift-dur: 49s; --drift-delay: -8s;  animation-name: drift-alt; }
.ambient__glow--bl { --drift-dur: 67s; --drift-delay: -30s; }
.ambient__glow--br { --drift-dur: 58s; --drift-delay: -17s; animation-name: drift-alt; }

@keyframes drift {
  from { transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(1);            opacity: 1; }
  50%  {                                                                             opacity: 0.96; }
  to   { transform: translate(-50%, -50%) translate3d(3.5%, 2.5%, 0) scale(1.06);   opacity: 1; }
}
@keyframes drift-alt {
  from { transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(1.04);         opacity: 0.96; }
  50%  {                                                                             opacity: 1; }
  to   { transform: translate(-50%, -50%) translate3d(-3%, -2.5%, 0) scale(1);      opacity: 0.96; }
}

/* ------------------------------------------------------------
   8. CARDS — lift + a spotlight that trails the cursor
   The lift is on `translate` (not transform) so it can take its own
   750ms on --bez. The spotlight position is eased per-frame in JS;
   a CSS transition on a value rewritten every frame never plays.
   ------------------------------------------------------------ */
.card {
  translate: 0 0;
  transition:
    translate        750ms var(--bez),
    box-shadow       420ms var(--bez),
    background-color 420ms var(--bez);
}
/* Light from the pointer (motion.js writes --mx/--my and --near,
   0 far away to 1 on the card). The face glow can sit outside the card,
   so a neighbour catches it on its near side. */
.card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: var(--near, 0);
  background: radial-gradient(
    340px circle at var(--mx, 50%) var(--my, 50%),
    rgba(120, 195, 255, 0.09),
    rgba(120, 195, 255, 0) 70%);
}
/* ...and the rim brightens where it faces the pointer. Layered over the
   overhead light; the ring mask keeps it to the 1px edge. */
.card::before {
  background:
    radial-gradient(240px circle at var(--mx, 50%) var(--my, 50%),
      rgba(170, 220, 255, calc(var(--near, 0) * 0.85)) 0%,
      rgba(120, 190, 255, calc(var(--near, 0) * 0.35)) 35%,
      rgba(120, 190, 255, 0) 70%),
    var(--lit-rim);
}

/* Lifting toward the light: the rim's dim side brightens and the shadow
   drops further below. */
@media (hover: hover) and (pointer: fine) {
  .card:hover {
    translate: 0 -4px;
    box-shadow:
      inset 0 0 0 1px rgba(122, 190, 255, 0.22),
      4px 20px 34px -14px rgba(0, 2, 10, 0.8);
  }
}

.card:focus-within { box-shadow: inset 0 0 0 1px var(--line-3), var(--lit-cast); }

/* ------------------------------------------------------------
   9. BUTTONS
   ------------------------------------------------------------ */
.btn {
  transition:
    transform  300ms var(--bez-btn),
    filter     300ms var(--bez-btn),
    background-color 300ms var(--bez-btn),
    border-color     300ms var(--bez-btn);
}
.btn:hover { transform: translateY(-1px); }
.btn--primary:hover { filter: brightness(1.05); }
.btn--ghost:hover   { background-color: rgba(29, 129, 255, 0.2); border-color: rgba(143, 207, 255, 0.62); }
.btn--dark:hover    { filter: brightness(1.25); }
.btn:active { transform: translateY(0); filter: brightness(0.97); }

.btn__arrow { transition: transform 300ms var(--bez-btn); }
.btn:hover .btn__arrow,
.link-arrow:hover .btn__arrow { transform: translateX(3px); }
.link-arrow .btn__arrow { transition: transform 300ms var(--bez-btn); }

/* ------------------------------------------------------------
   10. LINKS & ROWS
   ------------------------------------------------------------ */
.site-footer__col a,
.mobile-nav__link,
.fact__sub a,
.cta-panel p a { transition: color 300ms var(--bez); }
.site-footer__col a:hover { color: var(--blue-2); }
.fact__sub a:hover, .cta-panel p a:hover { color: var(--text); }

/* ------------------------------------------------------------
   11. STICKY CTA — slides up from the bottom edge
   ------------------------------------------------------------ */
.cta-bar { transition: transform 520ms var(--bez); }

/* ------------------------------------------------------------
   12. REDUCED MOTION — hover states still change; travel goes
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .splash, .splash__mark { animation: none !important; transition: opacity 1ms linear !important; }
  .splash__mark { opacity: 1; transform: none; }

  .hero__lockup, .hero__credit, .hero__eyebrow,
  .page-head > .shell > * { animation: none !important; opacity: 1; transform: none; }

  [data-reveal] { opacity: 1 !important; transform: none !important; animation: none !important; }
  .hero__intro { transform: none !important; }
  html.is-intro .hero__word,
  html.is-intro .hero__sub,
  html.is-intro .hero__actions,
  html.is-intro .hero__micro { opacity: 1 !important; transform: none !important; }
  .seam { opacity: 1 !important; transform: scaleX(1) !important; }

  .ambient__glow { animation: none !important; }
  .scroll-cue { animation: none !important; transform: none !important; }
  html.is-intro .scroll-cue { opacity: 1; }

  .card:hover { translate: none !important; }
  .btn:hover { transform: none !important; }

  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}
