/* ============================================================================
   nt-motion.css — the shared motion system for nikotrenchev.com
   ----------------------------------------------------------------------------
   One curve, mask reveals instead of fades, blur-up photographs, and a real
   reduced-motion branch. Loaded by every public page BEFORE its own stylesheet
   so page CSS can override, and so the tokens are available everywhere.

   The house rule, in four lines:
     1. One curve everywhere:  cubic-bezier(.24, 1, .52, 1)
     2. Text enters by sliding out of a mask. It never fades.
     3. Long duration, tiny displacement. scale(1.025) over 1.3s.
     4. Persistent UI is white + mix-blend-mode: difference.

   Everything here is namespaced `nt-` so it cannot collide with the existing
   `h-` (home), `gt-` (galleries) or `mm-` (menu) class families.
   ========================================================================= */

:root {
  /* --- curves ------------------------------------------------------------ */
  /* The house curve. y1 = 1, so it reaches full velocity almost immediately
     and then spends the whole remaining duration decelerating. Nothing on this
     site eases IN. Replacing every ease-in-out with this is most of the job. */
  --nt-ease: cubic-bezier(0.24, 1, 0.52, 1);
  --nt-ease-quint: cubic-bezier(0.23, 1, 0.32, 1);
  --nt-ease-util: ease;   /* small utility transitions only */

  /* --- durations, scaled to the size of the thing that moves -------------- */
  --nt-t-micro: 0.2s;   /* icon states, tiny opacity                         */
  --nt-t-small: 0.3s;   /* button opacity, small colour, underlines          */
  --nt-t-panel: 0.4s;   /* sheets, toggles, whole-page opacity               */
  --nt-t-text: 0.6s;    /* captions, headline colour, focus-pull             */
  --nt-t-veil: 1s;      /* full-screen overlay opacity                       */
  --nt-t-mask: 1.2s;    /* mask reveals (logo, headlines, corner labels)     */
  --nt-t-image: 1.3s;   /* large photograph transforms  <- the signature     */
  --nt-t-fade: 0.8s;    /* LQIP -> sharp photograph crossfade                */

  --nt-stagger: 0.05s;
  --nt-stagger-lg: 0.1s;

  /* --- surface ------------------------------------------------------------
     This site is dark. The reference is white. We keep our own ground and
     take only the mechanics. */
  --nt-ground: #0a0a0a;
  --nt-ink: #ffffff;
  --nt-scrim-caption: rgba(0, 0, 0, 0.5);
  --nt-blur-lqip: 20px;

  --nt-gutter: 30px;
  --nt-gutter-sm: 18px;
}

/* ============================================================================
   1. MASK REVEAL — the house entrance. Text NEVER fades.
   ----------------------------------------------------------------------------
   <span class="nt-mask"><span>WORD</span></span>
   then add .is-in to play it, .is-out to dismiss it.
   105% (not 100%) so descenders never poke out of the slot for a frame.
   -130% on exit so leaving reads as dismissal, not as reversal.
   ========================================================================= */
.nt-mask {
  display: block;
  overflow: hidden;
}
.nt-mask > * {
  display: block;
  transform: translateY(105%);
  transition: transform var(--nt-t-mask) var(--nt-ease);
  will-change: transform;
}
.nt-mask.is-in > * { transform: translateY(0); }
.nt-mask.is-out > * { transform: translateY(-130%); }

/* Hand-authored stagger ladder — deliberately not linear. */
.nt-stagger > .nt-mask:nth-child(1) > * { transition-delay: 0s; }
.nt-stagger > .nt-mask:nth-child(2) > * { transition-delay: 0.05s; }
.nt-stagger > .nt-mask:nth-child(3) > * { transition-delay: 0.10s; }
.nt-stagger > .nt-mask:nth-child(4) > * { transition-delay: 0.18s; }
.nt-stagger > .nt-mask:nth-child(5) > * { transition-delay: 0.28s; }

/* Inline variant, for a mask that sits inside a line of text. */
.nt-mask-inline { display: inline-block; overflow: hidden; vertical-align: bottom; }
.nt-mask-inline > * {
  display: inline-block;
  transform: translateY(105%);
  transition: transform var(--nt-t-mask) var(--nt-ease);
}
.nt-mask-inline.is-in > * { transform: translateY(0); }

/* ============================================================================
   2. BLUR-UP PHOTOGRAPH — colour and composition before the pixels land
   ----------------------------------------------------------------------------
   <div class="nt-photo">
     <img class="nt-photo__lqip" src="/api/images/lqip/x.jpg" alt="" aria-hidden="true">
     <img class="nt-photo__full" src="/api/images/thumb/x.jpg" alt="...">
   </div>

   The LQIP is a ~32px, sub-kilobyte smear. scale(1.06) on it is what stops the
   blur bleeding a soft halo into the tile edge. The sharp copy is revealed only
   after decode(), so the fade never stutters against the decoder.
   ========================================================================= */
.nt-photo {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #161616;
}
.nt-photo__lqip,
.nt-photo__full {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.nt-photo__lqip {
  filter: blur(var(--nt-blur-lqip));
  transform: scale(1.06);
  /* Held until the sharp copy has actually painted, then faded out underneath
     it — never removed from flow, so there is no reflow at the swap. */
  transition: opacity var(--nt-t-fade) var(--nt-ease);
}
.nt-photo__full {
  opacity: 0;
  transition: opacity var(--nt-t-fade) var(--nt-ease);
}
.nt-photo.is-loaded .nt-photo__full { opacity: 1; }
.nt-photo.is-loaded .nt-photo__lqip { opacity: 0; }

/* The lean: long duration, tiny displacement. 1.025 over 1.3s. Applied to the
   wrapper, not the <img>, so object-fit stays honest. */
.nt-lean { transition: transform var(--nt-t-image) var(--nt-ease); }
.nt-lean-host:hover .nt-lean { transform: scale(1.025); }

/* ============================================================================
   3. SCROLL REVEAL HOOK
   ----------------------------------------------------------------------------
   Elements marked [data-nt-reveal] get .is-in once, when they first cross into
   view. One-way — nothing re-animates on scroll-back.
   ========================================================================= */
[data-nt-reveal] { }

/* ============================================================================
   4. REDUCED MOTION — the thing the reference site gets wrong
   ----------------------------------------------------------------------------
   The original has no prefers-reduced-motion handling anywhere in 224KB of CSS,
   for a 1.2s intro, hijacked scroll and a pinned horizontal scrub. We do not
   copy that. Here everything lands in its final state instantly: no intro
   travel, no blur-up crossfade, no lean, no smooth scroll.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  /* Masked text is simply present. */
  .nt-mask > *,
  .nt-mask-inline > * { transform: none !important; }

  /* Photographs arrive sharp, with no blur stage at all. */
  .nt-photo__full { opacity: 1 !important; }
  .nt-photo__lqip { display: none !important; }

  /* No lean on hover. */
  .nt-lean-host:hover .nt-lean { transform: none !important; }

  /* The preloader must never hold the page hostage. */
  .nt-pre { display: none !important; }
}
