/* ==========================================================================
   Visual effects layer.
   Everything here is decorative. Each effect is switched off wholesale by
   `prefers-reduced-motion` at the bottom of this file, and none of it is
   required for the page to be readable or navigable.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Ambient background
   -------------------------------------------------------------------------- */

/* Particle constellation canvas — painted by js/particles.js */
#fx-canvas {
  position: fixed;
  inset: 0;
  /* A canvas is a replaced element: `inset: 0` alone leaves it at its
     intrinsic 300x150. The explicit size is what actually stretches it. */
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s var(--ease);
}
#fx-canvas.ready { opacity: 1; }

/* Lift page content above the canvas.

   .nav-bar and .progress are deliberately NOT in this list. They are already
   `position: fixed` with their own stacking order in main.css, and including
   them here overrode that to `relative` — which detached the header from the
   viewport and let it scroll away with the page. Only flow content needs
   lifting; anything already fixed sits above the canvas by its own z-index. */
main, .foot { position: relative; z-index: 1; }

/* Aurora mesh behind the hero */
.aurora {
  position: absolute;
  inset: -30% -10% auto;
  height: 78rem;
  z-index: 0;
  pointer-events: none;
  filter: blur(70px);
  opacity: 0.55;
}
.aurora i {
  position: absolute;
  display: block;
  border-radius: 50%;
  mix-blend-mode: screen;
  animation: drift 26s var(--ease) infinite alternate;
}
.aurora i:nth-child(1) {
  width: 38rem; height: 38rem;
  top: 4rem; left: -6rem;
  background: radial-gradient(circle, rgba(34,211,238,.5), transparent 66%);
}
.aurora i:nth-child(2) {
  width: 34rem; height: 34rem;
  top: 0; right: 2rem;
  background: radial-gradient(circle, rgba(177,77,255,.42), transparent 66%);
  animation-delay: -9s;
}
.aurora i:nth-child(3) {
  width: 28rem; height: 28rem;
  top: 16rem; left: 42%;
  background: radial-gradient(circle, rgba(124,140,255,.34), transparent 66%);
  animation-delay: -17s;
}
@keyframes drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(3rem, -2rem, 0) scale(1.14); }
  100% { transform: translate3d(-2.5rem, 2.5rem, 0) scale(0.94); }
}
[data-theme="light"] .aurora { opacity: 0.34; }

/* Perspective grid floor under the hero */
.grid-floor {
  position: absolute;
  inset: auto 0 0;
  height: 22rem;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right,  var(--hairline) 1px, transparent 1px),
    linear-gradient(to bottom, var(--hairline) 1px, transparent 1px);
  background-size: 3.5rem 3.5rem;
  transform: perspective(22rem) rotateX(62deg);
  transform-origin: bottom center;
  mask-image: linear-gradient(to top, #000 5%, transparent 85%);
  -webkit-mask-image: linear-gradient(to top, #000 5%, transparent 85%);
  opacity: 0.7;
}

/* Film grain — a static SVG noise tile, no JS, no image request */
.grain {
  position: fixed;
  inset: -50%;
  z-index: 3;
  pointer-events: none;
  opacity: 0.16;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  animation: grain 0.7s steps(4) infinite;
}
@keyframes grain {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-2%, 1%); }
  50%  { transform: translate(1%, -2%); }
  75%  { transform: translate(-1%, -1%); }
  100% { transform: translate(2%, 2%); }
}
[data-theme="light"] .grain { opacity: 0.07; mix-blend-mode: multiply; }

/* Scanline sweep across the viewport */
.scanline {
  position: fixed;
  inset: 0 0 auto;
  height: 12rem;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(34,211,238,.045), transparent);
  animation: sweep 9s linear infinite;
}
@keyframes sweep {
  0%   { transform: translateY(-14rem); }
  100% { transform: translateY(100vh); }
}

/* --------------------------------------------------------------------------
   1b. Pixel runner strip
   -------------------------------------------------------------------------- */

#runner {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  /* Height is set from JS to fit the rex plus its full jump — a fixed value
     here clipped the top of its body once the pixel scale reached 3. The
     value below is only a pre-JS placeholder. */
  height: var(--runner-h, 7.5rem);
  z-index: 4;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.8s var(--ease);
  /* Keep the pixels crisp rather than letting the browser smooth them. */
  image-rendering: pixelated;
}
#runner.ready { opacity: 1; }

/* The footer only has to clear the band the rex runs in, not the airspace
   above it — the jump happens over transparent canvas. */
.has-runner .foot { padding-bottom: calc(var(--runner-floor, 4rem) + 1.25rem); }

/* --------------------------------------------------------------------------
   2. Custom cursor (fine pointers only)
   -------------------------------------------------------------------------- */

/* The ring trails the real pointer as a halo. The native cursor is never
   hidden: replacing it meant that any failure of this layer — a slow frame,
   a lost pointer event, an unsupported browser — left the visitor with no
   pointer at all. Decoration is not worth that. The solid dot that used to
   stand in for the cursor is gone for the same reason. */
#cursor { display: none; }

#cursor-ring {
  position: fixed;
  top: 0; left: 0;
  z-index: 200;
  pointer-events: none;
  border-radius: 50%;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1px solid var(--hairline-2);
  opacity: 0;
  transition: opacity 0.3s var(--ease), width 0.28s var(--ease-out),
              height 0.28s var(--ease-out), margin 0.28s var(--ease-out),
              border-color 0.28s var(--ease), background 0.28s var(--ease);
}
body.cursor-on #cursor-ring { opacity: 0.9; }
body.cursor-on.cursor-hot #cursor-ring {
  width: 54px; height: 54px;
  margin: -27px 0 0 -27px;
  border-color: var(--c1);
  background: rgba(34, 211, 238, 0.09);
}

/* --------------------------------------------------------------------------
   3. Boot sequence
   -------------------------------------------------------------------------- */

.boot {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-content: center;
  gap: 1.25rem;
  padding: var(--gutter);
  background: var(--void);
  font-family: var(--font-mono);
  font-size: var(--t--1);
  transition: opacity 0.6s var(--ease), visibility 0.6s var(--ease);
}
.boot[hidden] { display: none; }
.boot.done { opacity: 0; visibility: hidden; }

.boot-log { display: grid; gap: 0.4rem; min-width: min(26rem, 82vw); }
.boot-log li { color: var(--muted); opacity: 0; }
.boot-log li.in { animation: bootline 0.32s var(--ease-out) forwards; }
.boot-log li b { color: var(--ok); font-weight: 500; }
.boot-log li i { color: var(--faint); font-style: normal; }
@keyframes bootline {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: none; }
}

.boot-bar {
  height: 2px;
  border-radius: 2px;
  background: var(--surface-2);
  overflow: hidden;
}
.boot-bar span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--grad);
  box-shadow: 0 0 12px var(--glow);
  transition: width 0.25s linear;
}
.boot-skip {
  justify-self: center;
  font-size: var(--t--2);
  color: var(--faint);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.boot-skip:hover { color: var(--c1); }

/* --------------------------------------------------------------------------
   4. Reveal on scroll
   -------------------------------------------------------------------------- */

/* Reveal animates `translate`, not `transform`.
   These rules override .pcard's own transition (same specificity, later file),
   so anything they animate is taken away from the element's own use of it.
   Using the independent `translate` property leaves `transform` entirely free
   for the pointer tilt — otherwise the tilt inherits this 0.65s ease *and* the
   per-child stagger delay below, which makes every card after the first feel
   completely dead. border-color/background are listed so cards keep the hover
   easing that .pcard asked for. */
.rv {
  opacity: 0;
  translate: 0 20px;
  transition: opacity 0.75s var(--ease-out), translate 0.75s var(--ease-out),
              border-color 0.35s var(--ease), background 0.35s var(--ease);
}
.rv.in { opacity: 1; translate: none; }

/* Staggered children.

   The stagger delay is carried by --rv-delay and applied ONLY to the two
   entrance properties. Using bare `transition-delay` here would delay every
   property in the rule, including the hover transitions — which made each
   card respond to the pointer later than the one before it, so only the
   first card (0s) felt responsive. Entrance staggers; interaction never does. */
.rv-stagger > * {
  opacity: 0;
  translate: 0 18px;
  transition:
    opacity     0.65s var(--ease-out) var(--rv-delay, 0s),
    translate   0.65s var(--ease-out) var(--rv-delay, 0s),
    border-color 0.35s var(--ease),
    background   0.35s var(--ease);
}
.rv-stagger.in > * { opacity: 1; translate: none; }
.rv-stagger > *:nth-child(1) { --rv-delay: 0.00s; }
.rv-stagger > *:nth-child(2) { --rv-delay: 0.07s; }
.rv-stagger > *:nth-child(3) { --rv-delay: 0.14s; }
.rv-stagger > *:nth-child(4) { --rv-delay: 0.21s; }
.rv-stagger > *:nth-child(5) { --rv-delay: 0.28s; }
.rv-stagger > *:nth-child(6) { --rv-delay: 0.35s; }
.rv-stagger > *:nth-child(7) { --rv-delay: 0.42s; }
.rv-stagger > *:nth-child(8) { --rv-delay: 0.49s; }

/* Hero line mask-reveal */
.hero h1 .line { overflow: hidden; }
.hero h1 .line > span {
  display: block;
  transform: translateY(105%);
  animation: rise 1s var(--ease-out) forwards;
}
.hero h1 .line:nth-child(1) > span { animation-delay: 0.05s; }
.hero h1 .line:nth-child(2) > span { animation-delay: 0.15s; }
.hero h1 .line:nth-child(3) > span { animation-delay: 0.25s; }
@keyframes rise { to { transform: none; } }

/* --------------------------------------------------------------------------
   5. Scramble text
   -------------------------------------------------------------------------- */

.scramble { font-variant-ligatures: none; }
.scramble .gh { color: var(--c1); opacity: 0.85; }

/* --------------------------------------------------------------------------
   6. Marquee
   -------------------------------------------------------------------------- */

.marquee {
  position: relative;
  display: flex;
  overflow: hidden;
  border-block: 1px solid var(--hairline);
  padding-block: 1.1rem;
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee-track {
  display: flex;
  flex-shrink: 0;
  gap: 2.75rem;
  padding-right: 2.75rem;
  animation: marquee 34s linear infinite;
}
.marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-track span {
  font-family: var(--font-mono);
  font-size: var(--t--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
  transition: color 0.25s var(--ease);
}
.marquee-track span:hover { color: var(--c1); }
@keyframes marquee { to { transform: translateX(-100%); } }

/* --------------------------------------------------------------------------
   7. Gradient border wrapper
   -------------------------------------------------------------------------- */

.gborder {
  position: relative;
  border-radius: var(--r-lg);
}
.gborder::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--grad);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0.45;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   8. Command palette
   -------------------------------------------------------------------------- */

.palette {
  position: fixed;
  inset: 0;
  z-index: 250;
  display: grid;
  align-items: start;
  justify-items: center;
  padding: clamp(3rem, 10vh, 8rem) var(--gutter) var(--gutter);
  background: rgba(4, 4, 12, 0.68);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.24s var(--ease), visibility 0.24s var(--ease);
}
.palette[data-open="true"] { opacity: 1; visibility: visible; }

.palette-box {
  width: min(100%, 34rem);
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline-2);
  background: var(--bg-2);
  box-shadow: 0 32px 90px -30px rgba(0, 0, 0, 0.85);
  overflow: hidden;
  transform: translateY(-10px) scale(0.985);
  transition: transform 0.28s var(--ease-out);
}
.palette[data-open="true"] .palette-box { transform: none; }

.palette-input {
  width: 100%;
  padding: 1.1rem 1.3rem;
  font: inherit;
  font-size: var(--t-0);
  color: var(--text);
  background: none;
  border: none;
  border-bottom: 1px solid var(--hairline);
}
.palette-input:focus { outline: none; }
.palette-input::placeholder { color: var(--faint); }

.palette-list { max-height: 22rem; overflow-y: auto; padding: 0.5rem; }
.palette-list li + li { margin-top: 2px; }
.palette-item {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  width: 100%;
  padding: 0.7rem 0.85rem;
  border-radius: var(--r-sm);
  font-size: var(--t--1);
  color: var(--muted);
  text-align: left;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.palette-item svg { width: 1rem; height: 1rem; flex-shrink: 0; color: var(--faint); }
.palette-item .sub { margin-left: auto; font-family: var(--font-mono); font-size: var(--t--2); color: var(--faint); }
.palette-item[aria-selected="true"], .palette-item:hover {
  background: var(--surface-2);
  color: var(--text);
}
.palette-item[aria-selected="true"] svg { color: var(--c1); }
.palette-empty { padding: 1.5rem 0.85rem; text-align: center; color: var(--faint); font-size: var(--t--1); }
.palette-foot {
  display: flex;
  gap: 1.1rem;
  padding: 0.7rem 1.3rem;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: var(--t--2);
  color: var(--faint);
}

/* --------------------------------------------------------------------------
   9. Magnetic / tilt helpers
   -------------------------------------------------------------------------- */

.magnetic { will-change: transform; }
[data-tilt] { will-change: transform; }

/* --------------------------------------------------------------------------
   10. Reduced motion — the master switch
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html.smooth { scroll-behavior: auto; }

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

  /* Show content that animation would otherwise have revealed. */
  .rv, .rv-stagger > * {
    opacity: 1 !important;
    translate: none !important;
    transform: none !important;
  }
  .hero h1 .line > span { transform: none !important; }

  /* Remove ambient motion entirely. */
  .grain, .scanline, #fx-canvas, .aurora i, #runner { display: none !important; }
  .has-runner .foot { padding-bottom: 2.5rem; }
  #cursor, #cursor-ring { display: none !important; }
  .marquee-track { animation: none !important; }
  .pulse-dot::after { display: none !important; }
}
