/* ==========================================================================
   BON · motion layer
   Everything the JS in assets/js/motion.js needs to draw itself.
   Tokens are inherited from each page (:root), so this file never defines
   colour — it only arranges it.
   ========================================================================== */

/* ---------------------------------------------------------------- boot gate
   mx-prep is added by the tiny inline head script before first paint, so
   animated elements never flash in their start state. The head script also
   removes it on a timer, and without JS it is never added at all. */

html.mx-prep [data-mx],
html.mx-prep .mx-logo { opacity: 0 !important; }

html.mx-prep body { overflow: hidden; }

/* ---------------------------------------------------------------- curtain
   The page transition. Slats are brand-gradient bars skewed off the grid; they
   wipe in from the bottom-left, hold while the mark assembles in the middle,
   then wipe out to the top-right. */

.mx-curtain {
  position: fixed;
  inset: 0;
  /* Above the portfolio loader (z 10000) and every sticky header: the
     transition is the last thing on screen and the first thing off it. */
  z-index: 100000;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  contain: strict;
}

.mx-curtain.is-armed { opacity: 1; visibility: visible; }
.mx-curtain.is-live { pointer-events: all; }

/* No gaps and a 2 px overlap: the slats still read as slats, but once they are
   in place the frame is a solid cover. A gap between them showed the page
   through the transition, which looked like a mistake rather than a wipe. */
.mx-curtain__slats {
  position: absolute;
  inset: -14%;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0;
  transform: skewX(-9deg) translate3d(0, 0, 0);
}

.mx-curtain__slats > i {
  display: block;
  position: relative;
  margin-right: -2px;
  background: linear-gradient(160deg, var(--crimson) 0%, var(--pink) 52%, var(--gold) 108%);
  will-change: transform;
  box-shadow: 0 0 60px rgba(255, 99, 200, 0.25), inset -2px 0 0 rgba(10, 2, 8, 0.55);
}

/* Each slat carries its own slice of the gradient, so a full cover still reads
   as seven moving panels rather than one flat wash. */
.mx-curtain__slats > i:nth-child(1) { background-position: 0% 50%; }
.mx-curtain__slats > i:nth-child(2) { background-position: 16% 50%; }
.mx-curtain__slats > i:nth-child(3) { background-position: 32% 50%; }
.mx-curtain__slats > i:nth-child(4) { background-position: 48% 50%; }
.mx-curtain__slats > i:nth-child(5) { background-position: 64% 50%; }
.mx-curtain__slats > i:nth-child(6) { background-position: 80% 50%; }
.mx-curtain__slats > i:nth-child(7) { background-position: 100% 50%; }

/* The middle slat reads as a solid brand plate so the mark has ground. It has
   to stay opaque: it is the one panel the lockup sits on. */
.mx-curtain__slats > i:nth-child(4) {
  background: linear-gradient(180deg, #12040e 0%, #1b0512 60%, #0d0209 100%);
  box-shadow: inset -2px 0 0 rgba(10, 2, 8, 0.55);
}

.mx-curtain__mark {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  opacity: 0;
}

.mx-curtain__mark svg { width: clamp(34px, 7vw, 54px); height: auto; overflow: visible; }
.mx-curtain__mark .mx-logo__word { font-size: clamp(1.5rem, 5vw, 2.4rem); }
.mx-curtain__mark .mx-logo__spark { display: none; }

/* ---------------------------------------------------------------- logo
   The brand lockup: a hairline rule, the crescent mark, BON set per glyph so
   each letter can rise through its own mask, and a spark that spins in. */

.mx-logo {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  color: var(--text-1, #fff);
  font-family: var(--font-display, sans-serif);
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1;
  isolation: isolate;
  touch-action: manipulation;
}

.mx-logo__rule {
  position: absolute;
  left: -0.35em;
  right: -0.35em;
  bottom: -0.32em;
  height: 2px;
  border-radius: 2px;
  transform-origin: 50% 50%;
  background: var(--grad-brand, linear-gradient(135deg, #ff3b4e, #ff63c8 55%, #ffd18a));
  box-shadow: 0 0 14px rgba(255, 99, 200, 0.55);
  scale: 0 1;
}

.mx-logo__mark {
  position: relative;
  display: grid;
  place-items: center;
  width: 1.35em;
  height: 1.35em;
  border-radius: 32%;
  background: linear-gradient(150deg, rgba(255, 59, 78, 0.95), rgba(255, 99, 200, 0.9) 55%, rgba(255, 209, 138, 0.9));
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14), 0 6px 22px rgba(255, 59, 78, 0.4);
  overflow: hidden;
  flex: none;
}

.mx-logo__mark svg {
  width: 74%;
  height: 74%;
  display: block;
  color: #18040f;
}

.mx-logo__mark::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(60% 60% at 28% 22%, rgba(255, 255, 255, 0.55), transparent 70%);
  mix-blend-mode: overlay;
}

.mx-logo__word {
  position: relative;
  display: inline-flex;
  font-size: 1em;
  white-space: nowrap;
}

.mx-logo__word b {
  font: inherit;
  display: block;
  transform-origin: 50% 100%;
}

/* Per-glyph mask: each letter is clipped by its own box and rises through it,
   the way the reel's title rises through the rule. The vertical padding gives
   room for descenders without changing the line box. */
.mx-glyph,
.mx-w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding: 0.16em 0.02em 0.18em;
  margin: -0.16em -0.02em -0.18em;
}

.mx-glyph > b,
.mx-w > .mx-ch {
  display: block;
  will-change: transform;
}

.mx-logo__word.is-grad b {
  background: linear-gradient(100deg, var(--crimson), var(--pink) 40%, var(--gold) 72%, var(--pink));
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.mx-logo__spark {
  display: grid;
  place-items: center;
  font-size: 0.62em;
  color: var(--gold, #ffd18a);
  text-shadow: 0 0 16px rgba(255, 209, 138, 0.7);
  transform-origin: 50% 50%;
  flex: none;
}

/* The bloom that lands under the word on the reveal. */
.mx-logo__bloom {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 3.2em;
  height: 3.2em;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 99, 200, 0.85), rgba(255, 59, 78, 0.3) 55%, transparent 70%);
  filter: blur(14px);
  opacity: 0;
  pointer-events: none;
  z-index: -1;
}

.mx-logo--lg { font-size: clamp(2rem, 9vw, 2.8rem); }
.mx-logo--lg .mx-logo__mark { width: 1.05em; height: 1.05em; }

/* Nav-sized variant: the mark carries the identity once the word is small. */
.mx-logo--nav { font-size: 1.35rem; }

/* ---------------------------------------------------------------- cursor
   Dot tracks the pointer exactly, ring lags behind it. Both are decorative:
   they never appear on coarse pointers and never take pointer events. */

.mx-cursor {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 2147483000;
  display: none;
}

@media (pointer: fine) {
  .mx-cursor { display: block; }
}

.mx-cursor__dot,
.mx-cursor__ring {
  position: absolute;
  top: 0;
  left: 0;
  border-radius: 50%;
  will-change: transform;
}

/* Centring is part of the transform rather than a negative margin, so the ring
   can grow from a 34 px circle into a pill that hugs its label and still sits
   on the pointer. */
.mx-cursor__dot {
  width: 7px;
  height: 7px;
  background: var(--pink, #ff63c8);
  box-shadow: 0 0 14px rgba(255, 99, 200, 0.9);
  opacity: 0;
}

.mx-cursor__ring {
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1.5px solid rgba(255, 99, 200, 0.5);
  background: rgba(255, 99, 200, 0.05);
  display: grid;
  place-items: center;
  overflow: hidden;
  opacity: 0;
  transition:
    width 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    height 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    padding 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    border-radius 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    border-color 0.3s,
    background 0.3s;
}

.mx-cursor__ring.is-labelled {
  width: auto;
  min-width: 34px;
  padding: 0 15px;
  border-radius: 17px;
}

.mx-cursor__label {
  font-family: var(--font-display, sans-serif);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fff;
  opacity: 0;
  white-space: nowrap;
  line-height: 1;
}

.mx-cursor.is-on .mx-cursor__dot { opacity: 1; }
.mx-cursor.is-on .mx-cursor__ring { opacity: 1; }

/* ---------------------------------------------------------------- particles */

.mx-particles {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  contain: strict;
}

/* ---------------------------------------------------------------- reveal
   Shared primitives. Nothing is promoted to its own layer up front: a blanket
   will-change on fifty elements costs more memory and compositing than the
   reveals themselves do. anime.js adds it for the duration of each animation. */

[data-mx-split] .mx-ch { will-change: transform, opacity; }

/* Card sheen used by the magnetic cards. */
.mx-sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(220px circle at var(--mx-sx, 50%) var(--mx-sy, 50%), rgba(255, 255, 255, 0.16), transparent 62%);
  opacity: 0;
  transition: opacity 0.4s ease;
}

[data-mx-sheen]:hover > .mx-sheen { opacity: 1; }

/* A hairline that draws itself under a section head. */
.mx-underline {
  position: relative;
  display: inline-block;
}

.mx-underline::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.12em;
  height: 2px;
  border-radius: 2px;
  transform-origin: 0 50%;
  scale: 0 1;
  background: var(--grad-brand, linear-gradient(135deg, #ff3b4e, #ff63c8 55%, #ffd18a));
}

/* Counters land on a small punch when they finish. */
.mx-count.is-hit { animation: mx-hit 0.55s cubic-bezier(0.22, 1, 0.36, 1); }
@keyframes mx-hit {
  0% { transform: scale(1); }
  35% { transform: scale(1.16); color: var(--gold, #ffd18a); text-shadow: 0 0 26px rgba(255, 209, 138, 0.75); }
  100% { transform: scale(1); }
}

/* Interactive elements opt into the magnetic pull. */
[data-mx-magnetic] { will-change: transform; }

/* ---------------------------------------------------------------- loader
   The opening slate on portfolio: the lockup assembles, the hairline fills,
   then the whole plate is cut away from the top so the page is revealed rather
   than faded in. */

.loader .loader-mark { display: flex; justify-content: center; margin-bottom: 1.6rem; }
.loader .loader-mark .mx-logo { font-size: clamp(2.4rem, 9vw, 3.4rem); }
.loader .loader-bar span { animation: none !important; }

/* ---------------------------------------------------------------- a11y */

@media (prefers-reduced-motion: reduce) {
  .mx-curtain { display: none !important; }
  .mx-cursor { display: none !important; }
  .mx-particles { display: none !important; }
  html.mx-prep [data-mx],
  html.mx-prep .mx-logo { opacity: 1 !important; }
  .mx-glyph > b,
  .mx-logo__rule,
  .mx-underline::after { transform: none !important; scale: 1 1 !important; }
}
