/* ==================================================================
   Motion system
   One timing scale, one easing pair, applied consistently. Everything
   is opt-in through a class or a data attribute so nothing animates by
   accident, and the whole file collapses to "no motion" under
   prefers-reduced-motion.
   ================================================================== */

:root{
  --m-fast:170ms;
  --m-base:320ms;
  --m-slow:620ms;
  --m-out:cubic-bezier(.16,1,.3,1);   /* decelerating: entrances */
  --m-inout:cubic-bezier(.4,0,.2,1);  /* symmetric: state changes */
  --m-stagger:70ms;
}

/* ---------- page entrance -----------------------------------------
   Runs once, after the preloader hands over. The order is deliberate:
   ground, then chrome, then the words in reading order.
   ------------------------------------------------------------------ */
.bl-enter .hdr{opacity:0;transform:translateY(-8px)}
.bl-enter .hero__in > *{opacity:0;transform:translateY(14px)}

.bl-entered .hdr{
  animation:m-drop var(--m-base) var(--m-out) 40ms both;
}
.bl-entered .hero__in > *{
  animation:m-rise var(--m-slow) var(--m-out) both;
}
.bl-entered .hero__in > *:nth-child(1){animation-delay:140ms}
.bl-entered .hero__in > *:nth-child(2){animation-delay:220ms}
.bl-entered .hero__in > *:nth-child(3){animation-delay:300ms}
.bl-entered .hero__in > *:nth-child(4){animation-delay:380ms}

/* Inner pages had no entrance at all - only the homepage hero did - so every
   h1 on the site simply appeared. Same treatment, one beat quicker, because
   an inner page is usually a second click rather than a first impression. */
.bl-enter .pg__hero .wrap > *{opacity:0;transform:translateY(14px)}
.bl-entered .pg__hero .wrap > *{
  animation:m-rise var(--m-slow) var(--m-out) both;
}
.bl-entered .pg__hero .wrap > *:nth-child(1){animation-delay:90ms}
.bl-entered .pg__hero .wrap > *:nth-child(2){animation-delay:160ms}
.bl-entered .pg__hero .wrap > *:nth-child(3){animation-delay:230ms}
.bl-entered .pg__hero .wrap > *:nth-child(4){animation-delay:300ms}

@keyframes m-drop{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
@keyframes m-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}

/* ---------- scroll reveal ------------------------------------------
   opacity + lift + a touch of scale, staggered by --i within a group.
   ------------------------------------------------------------------ */
[data-reveal]{
  opacity:0;
  transform:translateY(20px) scale(.988);
  transition:opacity var(--m-slow) var(--m-out),
             transform var(--m-slow) var(--m-out);
  transition-delay:calc(var(--i, 0) * var(--m-stagger));
}
/* No will-change here on purpose. It was promoting all 64 revealable elements
   to their own compositor layers at once, for the whole life of the page —
   far more expensive than the transition it was meant to smooth. Opacity and
   transform composite well enough without the hint. */
[data-reveal].is-in{opacity:1;transform:none}

/* images get a mask wipe rather than a fade */
[data-reveal-img]{
  clip-path:inset(0 0 100% 0);
  transition:clip-path var(--m-slow) var(--m-out);
}
[data-reveal-img].is-in{clip-path:inset(0 0 0 0)}

/* ---------- buttons --------------------------------------------------
   The fill rises from the bottom while the label slides up and its copy
   takes its place. Both halves share one duration, so it reads as a
   single movement rather than two effects that happen to coincide.

   The copy is a real element, not a pseudo-element, so it can be hidden
   from screen readers — and it carries the colour the filled state
   needs, which is why text and fill can never disagree mid-transition.

   Only buttons the script has wrapped opt in through .btn--fx, so a
   button without JS keeps a plain, legible hover.
   ------------------------------------------------------------------ */
.btn{transition:background var(--m-fast) var(--m-inout),
                color var(--m-fast) var(--m-inout),
                border-color var(--m-base) var(--m-out),
                transform var(--m-base) var(--m-out)}
.btn:hover{transform:translateY(-2px)}
.btn:active{transform:translateY(0)}

.btn--fx{position:relative;overflow:hidden;isolation:isolate}
.btn--fx:hover{opacity:1;filter:none}
.btn--fx::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:var(--gold);
  transform:translateY(100%);
  transition:transform var(--m-base) var(--m-out);
}
.btn--fx:hover::before,.btn--fx:focus-visible::before{transform:translateY(0)}
.btn--fx:hover{border-color:var(--gold)}
.btn--ghost.btn--fx::before{background:var(--ink)}
.btn--gold.btn--fx::before{background:var(--ink)}
.btn--ghost.btn--fx:hover,.btn--gold.btn--fx:hover{border-color:var(--ink)}

.btn__l{position:relative;display:block;overflow:hidden;line-height:1.25}
.btn__t{display:block;white-space:nowrap;transition:transform var(--m-base) var(--m-out)}
.btn__t--in{position:absolute;left:0;top:0;width:100%;transform:translateY(105%);color:#1A1A18}
.btn--ghost.btn--fx .btn__t--in,.btn--gold.btn--fx .btn__t--in{color:var(--ground)}
.btn--fx:hover .btn__t,.btn--fx:focus-visible .btn__t{transform:translateY(-105%)}
.btn--fx:hover .btn__t--in,.btn--fx:focus-visible .btn__t--in{transform:translateY(0)}

/* icon buttons get the same idea at a smaller amplitude */
.icon-btn{transition:color var(--m-fast) var(--m-inout),transform var(--m-fast) var(--m-out)}
.icon-btn:hover{transform:translateY(-1px)}

/* cards lift very slightly; 1–2% is enough at this size */
.card,.rev,.fact{
  transition:transform var(--m-base) var(--m-out),
             box-shadow var(--m-base) var(--m-out),
             border-color var(--m-base) var(--m-out);
}
.grid .card:hover,.cli .rev:hover{
  transform:translateY(-3px) scale(1.008);
  box-shadow:0 10px 30px rgba(26,26,24,.07);
}
.svc__row{transition:background var(--m-fast) var(--m-inout)}
.svc__row:hover{background:color-mix(in srgb,var(--gold) 4%,transparent)}

/* small icon nudge */
.bl-trust__icon,.vow__n,.card__k{transition:transform var(--m-base) var(--m-out),color var(--m-base) var(--m-out)}
.card:hover .card__k{transform:translateX(3px);color:var(--gold)}

/* faq rows */
.faq summary{transition:color var(--m-fast) var(--m-inout)}
.faq summary:hover{color:var(--gold-text)}
.faq summary::after{transition:transform var(--m-base) var(--m-out),color var(--m-fast) var(--m-inout)}
.faq details[open] summary::after{transform:rotate(180deg)}

/* ---------- hero atmosphere -----------------------------------------
   A very slow gradient drift plus a pointer-following highlight. Both
   sit under the veil, so they read as light rather than as objects.
   ------------------------------------------------------------------ */
.hero__glow{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(52rem 30rem at var(--mx,70%) var(--my,30%),
      color-mix(in srgb,var(--gold) 13%,transparent), transparent 68%),
    radial-gradient(40rem 26rem at 12% 82%,
      color-mix(in srgb,var(--gold) 7%,transparent), transparent 70%);
  transition:background-position var(--m-slow) var(--m-out);
  animation:m-drift 26s ease-in-out infinite alternate;
}
@keyframes m-drift{
  from{transform:translate3d(-1.5%,-1%,0) scale(1)}
  to{transform:translate3d(1.5%,1.5%,0) scale(1.04)}
}

/* ---------- animated statistics -------------------------------------- */
.fact__n{font-variant-numeric:tabular-nums}

/* ---------- calculator motion ---------------------------------------- */
.cost__col{transition:border-color var(--m-fast) var(--m-inout),
                      background var(--m-fast) var(--m-inout),
                      transform var(--m-fast) var(--m-inout),
                      box-shadow var(--m-fast) var(--m-inout)}

/* ---------- page transitions -----------------------------------------
   A short cross-fade on the body, so following a link does not flash.
   The class is removed on pageshow, which also covers back/forward.
   ------------------------------------------------------------------ */
.bl-leaving{opacity:0;transition:opacity var(--m-fast) var(--m-inout)}

@media (prefers-reduced-motion:reduce){
  .bl-enter .hdr,.bl-enter .hero__in > *,
  .bl-entered .hdr,.bl-entered .hero__in > *{animation:none;opacity:1;transform:none}
  [data-reveal],[data-reveal-img]{opacity:1;transform:none;clip-path:none;transition:none}
  .hero__glow{animation:none}
  .btn:hover,.grid .card:hover,.cli .rev:hover{transform:none}
  .bl-leaving{opacity:1;transition:none}
}

/* ---------- a cell that comes alive under the pointer ----------------
   The movement is contained by the cell: a soft gold light drifts across
   it and a hairline draws along the top edge. Both are transform and
   opacity only, so hovering a grid of seventeen cards costs nothing, and
   overflow:hidden guarantees nothing leaks into the neighbour.
   -------------------------------------------------------------------- */
.card--link,.character__item{position:relative;overflow:hidden;isolation:isolate}
.card--link::before,.character__item::before{
  content:"";position:absolute;z-index:-1;
  inset:-40% -10%;
  background:radial-gradient(38% 46% at 30% 30%,
    color-mix(in srgb,var(--gold) 26%,transparent), transparent 70%);
  opacity:0;transform:translate3d(-18%,12%,0) scale(.9);
  transition:opacity var(--m-base) var(--m-out),transform var(--m-slow) var(--m-out);
}
.card--link:hover::before,.card--link:focus-visible::before,
.character__item:hover::before{
  opacity:1;transform:translate3d(12%,-8%,0) scale(1.12);
}
/* the top edge draws itself in from the left */
.card--link::after,.character__item::after{
  content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:var(--gold);transform:scaleX(0);transform-origin:left;
  transition:transform var(--m-base) var(--m-out);
}
.card--link:hover::after,.card--link:focus-visible::after,
.character__item:hover::after{transform:scaleX(1)}

/* the commitment cells already carry a gold rule; theirs slides instead */
.character__item--vow::before{z-index:-1}

@media (prefers-reduced-motion:reduce){
  .card--link::before,.character__item::before,
  .card--link::after,.character__item::after{transition:none}
  .card--link:hover::before,.character__item:hover::before{opacity:.6;transform:none}
}

/* ---------- custom cursor ------------------------------------------
   A soft ring that trails the real pointer with a slight lag. Fine-pointer
   desktop only — matmedia(pointer:fine) plus a JS check, since Windows
   convertible laptops and some desktops report hover:hover with a touch
   screen attached. The native cursor is only hidden once the ring is
   actually live, so nothing goes invisible if the script fails to run.
   -------------------------------------------------------------------- */
.bl-cursor{
  position:fixed;top:0;left:0;z-index:200;
  /* One fixed size, and it stays that size. The earlier version doubled its
     width over anything clickable, which meant the pointer changed shape
     dozens of times crossing a page of links — restless rather than
     helpful. Hover feedback belongs to the elements themselves, which
     already have it. */
  width:22px;height:22px;margin:-11px 0 0 -11px;
  border-radius:50%;pointer-events:none;
  /* A ring, not a wash: an outlined disc with a small solid centre stays
     legible over body copy, photographs and the dark footer alike, where a
     14%-opacity fill was effectively invisible. No mix-blend-mode — that is
     what turned the gold to navy on the light ground. */
  background:color-mix(in srgb,var(--gold) 26%,transparent);
  /* 2px, not 1.5: at the display scaling this project is reviewed on the
     browser resolved 1.5px down to 0.8px, which is below the threshold where
     a hairline ring stays legible over body copy. */
  border:2px solid var(--gold);
  /* a faint halo of page colour, so the ring separates from dark sections
     and photographs without needing mix-blend-mode */
  box-shadow:0 0 0 1.5px color-mix(in srgb,var(--ground) 60%,transparent);
  /* transform is deliberately NOT transitioned: js/motion-2026.js already
     eases position toward the pointer every frame via requestAnimationFrame.
     A CSS transition on the same property was chasing a target that was
     itself still easing, and the two competing easings is what read as the
     cursor "running around chaotically" rather than following smoothly. */
  transform:translate3d(-100px,-100px,0);
  transition:opacity .2s var(--ease);
  opacity:0;
}
.bl-cursor-on .bl-cursor{opacity:1}
.bl-cursor-on{cursor:none}
.bl-cursor-on a,.bl-cursor-on button,.bl-cursor-on input,.bl-cursor-on textarea,
.bl-cursor-on select,.bl-cursor-on summary,.bl-cursor-on [role="button"]{cursor:none}

@media (prefers-reduced-motion:reduce),(pointer:coarse){
  .bl-cursor{display:none}
  .bl-cursor-on{cursor:auto}
}
