/* ===========================================================================
   BLIGHT LIGHT — motion & effects
   ---------------------------------------------------------------------------
   Loading screen, custom cursor, parallax hero, animated buttons, surface
   textures and the scroll-reveal system.

   Two rules this file follows throughout:

   1. Content is visible by default. Reveals are an enhancement applied by JS
      adding .motion to <html>. If JS never runs, or the tab is hidden, or a
      crawler renders the page, everything is still there. Never gate content
      on a transition firing.
   2. Every animation has a reduced-motion answer at the bottom of the file.
   =========================================================================== */

/* ------------------------------------------------------------ layer scale */
/* Semantic, so nothing ever needs to guess at 9999. */
:root {
  --z-base:      0;
  --z-texture:   1;
  --z-raised:    10;
  --z-sticky:    20;
  --z-nav:       30;
  --z-backdrop:  40;
  --z-modal:     50;
  --z-cursor:    60;
  --z-loader:    70;

  --ease-out-quart: cubic-bezier(.25, 1, .5, 1);
  --ease-out-expo:  cubic-bezier(.16, 1, .3, 1);
  --dur-fast:  .18s;
  --dur-mid:   .34s;
  --dur-slow:  .62s;
}

/* =========================================================== loading screen */
.loader {
  position: fixed;
  inset: 0;
  z-index: var(--z-loader);
  display: grid;
  place-content: center;
  gap: 26px;
  background: var(--bg);
  transition: opacity .5s var(--ease-out-quart), visibility .5s;
}
.loader[hidden] { display: none; }
.loader.done { opacity: 0; visibility: hidden; pointer-events: none; }

.loader-mark {
  width: 74px;
  height: 74px;
  margin: 0 auto;
  animation: loader-spin 2.6s linear infinite;
}
.loader-mark circle { fill: var(--acid); transform-origin: center; }
.loader-mark circle:nth-child(1) { animation: loader-pulse 1.5s var(--ease-out-quart) infinite; }
.loader-mark circle:nth-child(2) { animation: loader-pulse 1.5s var(--ease-out-quart) .18s infinite; }
.loader-mark circle:nth-child(3) { animation: loader-pulse 1.5s var(--ease-out-quart) .36s infinite; }
.loader-mark circle:nth-child(4) { animation: loader-pulse 1.5s var(--ease-out-quart) .54s infinite; }

@keyframes loader-spin  { to { transform: rotate(360deg); } }
@keyframes loader-pulse {
  0%, 100% { opacity: .28; transform: scale(.82); }
  40%      { opacity: 1;   transform: scale(1.06); }
}

.loader-bar {
  width: 190px;
  height: 2px;
  background: var(--line);
  overflow: hidden;
  border-radius: 2px;
}
.loader-bar i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--acid);
  box-shadow: 0 0 12px var(--acid);
  transition: width .3s var(--ease-out-quart);
}
.loader-text {
  font-family: 'Chakra Petch', system-ui, sans-serif;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
}

/* ============================================================ custom cursor */
/* Applied only when JS confirms a fine pointer, so touch and keyboard users
   keep the native cursor. The real cursor stays visible on text inputs. */
html.has-cursor,
html.has-cursor a,
html.has-cursor button,
html.has-cursor [role="button"] { cursor: none; }
html.has-cursor input,
html.has-cursor textarea,
html.has-cursor select { cursor: auto; }

.cursor-dot,
.cursor-ring {
  position: fixed;
  top: 0; left: 0;
  z-index: var(--z-cursor);
  pointer-events: none;
  will-change: transform;
}
.cursor-dot {
  width: 6px; height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: var(--acid-hi);
  box-shadow: 0 0 10px var(--acid);
}
.cursor-ring {
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1px solid color-mix(in srgb, var(--acid) 55%, transparent);
  border-radius: 50%;
  transition: width var(--dur-fast) var(--ease-out-quart),
              height var(--dur-fast) var(--ease-out-quart),
              margin var(--dur-fast) var(--ease-out-quart),
              border-color var(--dur-fast) linear,
              opacity var(--dur-fast) linear;
}
/* Over something clickable the ring opens up and fills faintly. */
.cursor-ring.hot {
  width: 54px; height: 54px;
  margin: -27px 0 0 -27px;
  border-color: var(--acid);
  background: color-mix(in srgb, var(--acid) 9%, transparent);
}
.cursor-ring.press { width: 26px; height: 26px; margin: -13px 0 0 -13px; }
.cursor-dot.hide, .cursor-ring.hide { opacity: 0; }

/* ============================================================ hero parallax */
.hero-scene {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: var(--z-base);
  pointer-events: none;
  /* Fades into the page instead of ending on a hard edge. */
  mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
}
.hero-layer {
  position: absolute;
  inset: -6% -4%;
  background-repeat: no-repeat;
  background-position: center 38%;
  background-size: cover;
  will-change: transform;
}
.hero-layer.sky  { background-image: var(--hero-sky); }
.hero-layer.mid  { background-image: var(--hero-mid); }
.hero-layer.near { background-image: var(--hero-near); }

/* A slow breathing glow behind the landmark. */
.hero-bloom {
  position: absolute;
  right: 12%;
  top: 34%;
  width: 44vw;
  height: 44vw;
  max-width: 720px;
  max-height: 720px;
  transform: translate(35%, -30%);
  background: radial-gradient(circle,
              color-mix(in srgb, var(--acid) 26%, transparent) 0%,
              transparent 62%);
  filter: blur(38px);
  animation: bloom-breathe 9s ease-in-out infinite;
}
@keyframes bloom-breathe {
  0%, 100% { opacity: .55; transform: translate(35%, -30%) scale(1); }
  50%      { opacity: .95; transform: translate(35%, -30%) scale(1.09); }
}

/* Drifting motes — the only thing on the page that moves unprompted. */
.hero-motes { position: absolute; inset: 0; overflow: hidden; }
.mote {
  position: absolute;
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--acid-hi);
  opacity: 0;
  box-shadow: 0 0 8px var(--acid);
  animation: mote-drift linear infinite;
}
@keyframes mote-drift {
  0%   { opacity: 0;   transform: translate3d(0, 20px, 0) scale(.6); }
  14%  { opacity: .85; }
  76%  { opacity: .5; }
  100% { opacity: 0;   transform: translate3d(24px, -150px, 0) scale(1.2); }
}

/* ======================================================== surface textures */
.fx-grain,
.fx-scanlines {
  position: fixed;
  inset: 0;
  z-index: var(--z-texture);
  pointer-events: none;
}
.fx-grain {
  background-image: var(--tex-grain);
  background-size: 340px 340px;
  opacity: .045;
  mix-blend-mode: overlay;
  animation: grain-shift 1.1s steps(4) infinite;
}
@keyframes grain-shift {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-2%, 1%); }
  50%  { transform: translate(1%, -2%); }
  75%  { transform: translate(-1%, -1%); }
  100% { transform: translate(0, 0); }
}
.fx-scanlines {
  background-image: var(--tex-scanlines);
  background-size: auto 4px;
  opacity: .5;
}

/* ============================================================ scroll progress */
.scroll-progress {
  position: fixed;
  top: 0; left: 0;
  height: 2px;
  width: 0;
  z-index: var(--z-nav);
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--acid) 40%, transparent), var(--acid));
  box-shadow: 0 0 10px var(--acid);
  pointer-events: none;
}

/* ========================================================= animated buttons */
/* A light sweep crossing the face on hover, plus a real press response.
   No bounce, no elastic — exponential ease out only. */
.btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition: transform var(--dur-fast) var(--ease-out-quart),
              box-shadow var(--dur-mid) var(--ease-out-quart),
              background-color var(--dur-fast) linear,
              border-color var(--dur-fast) linear;
}
.btn::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(105deg, transparent 32%,
              color-mix(in srgb, #fff 26%, transparent) 50%, transparent 68%);
  transform: translateX(-130%);
  transition: transform var(--dur-slow) var(--ease-out-expo);
}
.btn:hover::after,
.btn:focus-visible::after { transform: translateX(130%); }
.btn:hover  { transform: translateY(-2px); }
.btn:active { transform: translateY(0) scale(.985); }

.btn-primary:hover {
  box-shadow: 0 0 0 1px var(--acid-hi),
              0 10px 34px color-mix(in srgb, var(--acid) 34%, transparent);
}
.btn-primary .chev { transition: transform var(--dur-mid) var(--ease-out-quart); }
.btn-primary:hover .chev { transform: translateX(4px); }

/* Focus must always be obvious — never removed. */
.btn:focus-visible,
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--acid-hi);
  outline-offset: 3px;
}

/* ============================================================== card motion */
.feature,
.slide,
.panel.hoverable {
  transition: transform var(--dur-mid) var(--ease-out-quart),
              border-color var(--dur-mid) linear,
              box-shadow var(--dur-mid) var(--ease-out-quart);
}
.feature:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--acid) 42%, var(--line));
  box-shadow: 0 14px 40px rgba(0, 0, 0, .5),
              0 0 0 1px color-mix(in srgb, var(--acid) 18%, transparent);
}
/* The icon lights up rather than the whole card shouting. */
.feature .feature-icon {
  transition: transform var(--dur-mid) var(--ease-out-quart),
              color var(--dur-mid) linear;
}
.feature:hover .feature-icon {
  transform: scale(1.1) rotate(-4deg);
  color: var(--acid-hi);
}

/* ============================================================= stat states */
.stat-value { transition: color var(--dur-mid) linear; }
.stat.is-stale .stat-value { color: var(--muted); }
/* A number that just changed gets a brief flash so live updates are visible. */
.stat-value.bumped { animation: stat-bump .7s var(--ease-out-quart); }
@keyframes stat-bump {
  0%   { color: var(--acid-hi); text-shadow: 0 0 18px color-mix(in srgb, var(--acid) 60%, transparent); }
  100% { color: inherit; text-shadow: none; }
}
.badge-live { position: relative; }
.stat.is-live .badge-live::before {
  content: '';
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 5px;
  border-radius: 50%;
  background: var(--acid);
  animation: live-pulse 1.9s var(--ease-out-quart) infinite;
}
@keyframes live-pulse {
  0%, 100% { opacity: 1;  box-shadow: 0 0 0 0 color-mix(in srgb, var(--acid) 60%, transparent); }
  70%      { opacity: .6; box-shadow: 0 0 0 7px transparent; }
}

/* ========================================================== reveal system */
/* Visible by default. .motion on <html> (added by JS) opts in to the
   animation, so no-JS and headless renders show the finished state. */
html.motion .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--dur-slow) var(--ease-out-quart),
              transform var(--dur-slow) var(--ease-out-quart);
}
html.motion .reveal.in { opacity: 1; transform: none; }

/* Staggered children — used on the feature row and the footer columns. */
html.motion .stagger > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--dur-mid) var(--ease-out-quart),
              transform var(--dur-mid) var(--ease-out-quart);
}
html.motion .stagger.in > * { opacity: 1; transform: none; }
html.motion .stagger.in > :nth-child(1) { transition-delay: .00s; }
html.motion .stagger.in > :nth-child(2) { transition-delay: .05s; }
html.motion .stagger.in > :nth-child(3) { transition-delay: .10s; }
html.motion .stagger.in > :nth-child(4) { transition-delay: .15s; }
html.motion .stagger.in > :nth-child(5) { transition-delay: .20s; }
html.motion .stagger.in > :nth-child(6) { transition-delay: .25s; }

/* The wordmark resolving out of static on load. */
html.motion .hero-title .word {
  animation: word-in 1s var(--ease-out-expo) both;
}
html.motion .hero-title .word:nth-child(2) { animation-delay: .12s; }
@keyframes word-in {
  0%   { opacity: 0; transform: translateY(22px) scale(.97); filter: blur(9px); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}

/* ========================================================= skeleton loading */
.skeleton {
  background: linear-gradient(90deg, var(--panel) 25%, var(--panel2) 50%, var(--panel) 75%);
  background-size: 200% 100%;
  animation: skeleton-slide 1.4s linear infinite;
  border-radius: 6px;
  color: transparent !important;
}
@keyframes skeleton-slide {
  to { background-position: -200% 0; }
}

/* ============================================================== responsive */
@media (max-width: 900px) {
  .hero-bloom { width: 80vw; height: 80vw; right: -10%; }
  .cursor-dot, .cursor-ring { display: none; }
}

/* ========================================================== reduced motion */
/* Everything above has an answer here. Crossfade or nothing — never a
   transform, never an infinite loop. */
@media (prefers-reduced-motion: reduce) {
  html.motion .reveal,
  html.motion .stagger > * { opacity: 1 !important; transform: none !important; }
  html.motion .hero-title .word { animation: none; }

  .loader-mark,
  .loader-mark circle,
  .hero-bloom,
  .mote,
  .fx-grain,
  .badge-live::before,
  .skeleton { animation: none !important; }

  .mote { display: none; }
  .fx-grain { opacity: .03; }

  .btn::after { display: none; }
  .btn:hover, .btn:active,
  .feature:hover { transform: none; }
  .feature:hover .feature-icon { transform: none; }

  .cursor-dot, .cursor-ring { display: none; }
  html.has-cursor, html.has-cursor a, html.has-cursor button { cursor: auto; }

  .stat-value.bumped { animation: none; }

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