/* ==========================================================================
   STEMX — effects layer

   Vanilla-CSS equivalents of the 21st.dev component library's signature
   effects (aurora backgrounds, spotlight cards, glow borders, shimmer text,
   bento grids, marquees). 21st.dev ships React + Tailwind + Framer Motion;
   this site is static HTML, so the effects are rebuilt natively — same look,
   no framework, no build step, no external requests.

   Everything here is decorative. The reduced-motion block at the bottom of
   style.css already neutralises the animations.
   ========================================================================== */

@property --fx-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

:root {
  /* Whisper-quiet tints. At these opacities they read as white-on-black
     depth rather than colour, which keeps the SpaceX palette intact. */
  --glow-cool: rgba(139, 78, 255, 0.32);
  --glow-warm: rgba(196, 140, 255, 0.16);
  --glow-pure: rgba(255, 255, 255, 0.13);
}

/* ==========================================================================
   Aurora background — slow-drifting blurred light fields
   ========================================================================== */
.aurora {
  position: absolute;
  inset: -25% -15%;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  filter: blur(80px);
  opacity: 0.85;
}

.aurora i {
  position: absolute;
  display: block;
  border-radius: 50%;
  mix-blend-mode: screen;
  will-change: transform;
}

.aurora i:nth-child(1) {
  width: 46vw; height: 46vw; top: -6%; left: 4%;
  background: radial-gradient(circle, var(--glow-cool) 0%, transparent 68%);
  animation: auroraA 26s var(--ease) infinite alternate;
}
.aurora i:nth-child(2) {
  width: 38vw; height: 38vw; top: 22%; right: 2%;
  background: radial-gradient(circle, var(--glow-pure) 0%, transparent 66%);
  animation: auroraB 32s var(--ease) infinite alternate;
}
.aurora i:nth-child(3) {
  width: 52vw; height: 34vw; bottom: -14%; left: 26%;
  background: radial-gradient(circle, var(--glow-warm) 0%, transparent 70%);
  animation: auroraC 38s var(--ease) infinite alternate;
}

@keyframes auroraA {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(9vw, 6vh, 0) scale(1.18); }
}
@keyframes auroraB {
  from { transform: translate3d(0, 0, 0) scale(1.1); }
  to   { transform: translate3d(-11vw, 9vh, 0) scale(0.92); }
}
@keyframes auroraC {
  from { transform: translate3d(0, 0, 0) scale(0.95); }
  to   { transform: translate3d(7vw, -7vh, 0) scale(1.22); }
}

/* ==========================================================================
   Dot grid — masked so it fades out toward the edges
   ========================================================================== */
.dotgrid {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: radial-gradient(rgba(255, 255, 255, 0.16) 1px, transparent 1px);
  background-size: 34px 34px;
  -webkit-mask-image: radial-gradient(ellipse 78% 62% at 50% 42%, #000 0%, transparent 100%);
  mask-image: radial-gradient(ellipse 78% 62% at 50% 42%, #000 0%, transparent 100%);
  opacity: 0.55;
}

.dotgrid--tight { background-size: 22px 22px; opacity: 0.4; }

/* ==========================================================================
   Beam — a single light sweep travelling down a section edge
   ========================================================================== */
.beam {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255, 255, 255, 0.7) 50%,
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 20%, #000 80%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 20%, #000 80%, transparent);
  opacity: 0.5;
  animation: beamSlide 7s var(--ease) infinite;
}

@keyframes beamSlide {
  0%   { transform: translateX(-60%) scaleX(0.4); opacity: 0; }
  35%  { opacity: 0.65; }
  100% { transform: translateX(60%) scaleX(0.4); opacity: 0; }
}

/* ==========================================================================
   Spotlight — a highlight that tracks the cursor across a card
   Driven by --mx / --my, set from JS on pointermove.
   ========================================================================== */
.spotlight {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.spotlight::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.45s var(--ease);
  background: radial-gradient(
    340px circle at var(--mx, 50%) var(--my, 50%),
    rgba(139, 78, 255, 0.20),
    transparent 62%
  );
}

.spotlight:hover::before,
.spotlight:focus-within::before { opacity: 1; }

.spotlight > * { position: relative; z-index: 1; }

/* the hairline itself brightens where the cursor is */
.spotlight::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border: 1px solid transparent;
  opacity: 0;
  transition: opacity 0.45s var(--ease);
  background: radial-gradient(
    280px circle at var(--mx, 50%) var(--my, 50%),
    var(--purple-lit),
    transparent 60%
  ) border-box;
  -webkit-mask:
    linear-gradient(#000 0 0) padding-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) padding-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.spotlight:hover::after { opacity: 1; }

/* ==========================================================================
   Glow border — a conic gradient rotating around the card edge
   ========================================================================== */
.glow-card {
  position: relative;
  isolation: isolate;
}

/* The ring sits on the inner edge rather than outside it, so that cards which
   are also .spotlight (and therefore overflow:hidden) do not clip it away. */
.glow-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
  background: conic-gradient(
    from var(--fx-angle),
    transparent 0%,
    var(--purple-lit) 12%,
    transparent 26%,
    transparent 100%
  );
  -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;
  animation: fxSpin 4s linear infinite;
}

.glow-card:hover::before,
.glow-card:focus-within::before { opacity: 1; }

@keyframes fxSpin {
  to { --fx-angle: 360deg; }
}

/* ==========================================================================
   Shimmer text — a light pass over the letterforms
   ========================================================================== */
.shimmer {
  background: linear-gradient(
    100deg,
    #6f6f6f 20%,
    var(--purple-lit) 34%,
    #ffffff 46%,
    #ffffff 50%,
    #6f6f6f 72%
  );
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: shimmerPass 6.5s linear infinite;
}

@keyframes shimmerPass {
  from { background-position: 180% 0; }
  to   { background-position: -80% 0; }
}

/* ==========================================================================
   Shimmer button — the 21st.dev signature, rebuilt on the site's pill
   ========================================================================== */
.pill--shimmer,
.btn--shimmer { position: relative; overflow: hidden; }

.pill--shimmer::after,
.btn--shimmer::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    105deg,
    transparent 35%,
    rgba(255, 255, 255, 0.55) 50%,
    transparent 65%
  );
  transform: translateX(-120%);
  animation: pillShimmer 4.5s var(--ease) infinite;
}

.pill--solid.pill--shimmer::after,
.btn--solid.btn--shimmer::after {
  background: linear-gradient(
    105deg,
    transparent 35%,
    rgba(0, 0, 0, 0.16) 50%,
    transparent 65%
  );
}

@keyframes pillShimmer {
  0%   { transform: translateX(-120%); }
  55%  { transform: translateX(120%); }
  100% { transform: translateX(120%); }
}

/* ==========================================================================
   Bento grid
   ========================================================================== */
.bento {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1px;
  background: var(--hairline-soft);
  border: var(--edge) solid var(--hairline-soft);
}

.bento-cell {
  position: relative;
  background: #000;
  padding: clamp(26px, 3vw, 44px);
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 210px;
  transition: background 0.45s var(--ease);
}

.bento-cell:hover { background: #0a0a0a; }

.bento-cell h3 {
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  letter-spacing: var(--track-mid);
}

.bento-cell p {
  color: var(--ash);
  font-size: 0.9rem;
  line-height: 1.8;
}

.bento-cell .cell-tag {
  font-size: 10px;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--slate);
}

.cell--wide  { grid-column: span 4; }
.cell--half  { grid-column: span 3; }
.cell--third { grid-column: span 2; }
.cell--tall  { grid-row: span 2; }

.bento-cell .big-figure {
  font-size: clamp(2.4rem, 5vw, 4rem);
  font-weight: 300;
  letter-spacing: -0.02em;
  line-height: 1;
  margin-top: auto;
}

/* ==========================================================================
   Marquee — an endless strip of terms
   ========================================================================== */
.marquee {
  position: relative;
  overflow: hidden;
  border-block: 1px solid var(--hairline-soft);
  padding: 22px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  gap: 0;
  animation: marqueeSlide 42s linear infinite;
}

.marquee:hover .marquee-track { animation-play-state: paused; }

.marquee-track > span {
  display: inline-flex;
  align-items: center;
  gap: 46px;
  padding-right: 46px;
  font-size: 12px;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--slate);
  white-space: nowrap;
}

.marquee-track > span::after {
  content: "";
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--hairline);
  flex: none;
}

@keyframes marqueeSlide {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* ==========================================================================
   Animated WebGL gradient — the hero backdrop
   Rendered by assets/js/animated-gradient.js into [data-animated-gradient].
   ========================================================================== */
.hero-shader {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.9;

  /* The headline is white and sits on the left, so the shader is masked into
     the upper right and falls away before it reaches the text or the horizon
     arc at the bottom. One radial mask does both jobs — compositing two masks
     needs -webkit-mask-composite, whose legacy keywords differ from the
     standard ones and are not worth the branch. */
  -webkit-mask-image: radial-gradient(ellipse 108% 92% at 70% 20%, #000 0%, rgba(0,0,0,0.58) 40%, transparent 80%);
  mask-image: radial-gradient(ellipse 108% 92% at 70% 20%, #000 0%, rgba(0,0,0,0.58) 40%, transparent 80%);
}

.hero-shader canvas { display: block; width: 100%; height: 100%; }

/* .has-shader is added by the script only after WebGL is confirmed working,
   so everything below degrades to the original hero when it is not. */

/* Two soft light fields on top of each other read as haze, so the CSS aurora
   steps aside once the real gradient is running. */
.hero.has-shader .aurora { display: none; }

/* The stock hero paints its own white radial bloom; with the shader live the
   light comes from the gradient instead. */
.hero.has-shader {
  background:
    radial-gradient(ellipse 120% 80% at 50% 115%, rgba(255,255,255,0.10), transparent 60%),
    #000;
}

/* Tablet width: the copy stacks wider, so pull the gradient up and out of its
   way. Below 700px the script does not mount the gradient at all, which is why
   this range starts there. */
@media (min-width: 700px) and (max-width: 900px) {
  .hero-shader {
    opacity: 0.85;
    -webkit-mask-image: radial-gradient(ellipse 118% 56% at 78% 4%, #000 0%, rgba(0,0,0,0.5) 42%, transparent 82%);
    mask-image: radial-gradient(ellipse 118% 56% at 78% 4%, #000 0%, rgba(0,0,0,0.5) 42%, transparent 82%);
  }
}

/* ==========================================================================
   Assorted small polish
   ========================================================================== */

/* frosted panel used by the auth cards and dashboard tiles */
.glass {
  background: rgba(12, 12, 12, 0.62);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border: var(--edge) solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

/* hairline that brightens from the centre out */
.rule--lit {
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.35) 50%,
    transparent
  );
}

/* a small status dot with a breathing halo */
.live-dot {
  position: relative;
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  flex: none;
}
.live-dot::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.45);
  animation: livePulse 2.6s var(--ease) infinite;
}
@keyframes livePulse {
  0%   { transform: scale(0.6); opacity: 1; }
  100% { transform: scale(1.5); opacity: 0; }
}

/* subtle lift shared by interactive surfaces */
.lift { transition: transform 0.45s var(--ease), border-color 0.45s var(--ease); }
.lift:hover { transform: translateY(-3px); }

@media (prefers-reduced-motion: reduce) {
  /* The shimmer's resting gradient position renders the text grey, so with the
     animation frozen it would read dimmer than the lines around it. */
  .shimmer {
    background: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    color: var(--white);
  }
  .aurora, .beam { display: none; }
  .marquee-track { transform: none; }
}

@media (max-width: 700px) {
  /* On a narrow screen the hero copy grows tall enough to collide with the
     horizon arc, so drop the limb further below the fold. */
  .hero-horizon { bottom: -36vh; height: 52vh; }
}

@media (max-width: 900px) {
  .bento { grid-template-columns: 1fr; }
  .cell--wide, .cell--half, .cell--third { grid-column: span 1; }
  .cell--tall { grid-row: span 1; }
  .aurora { filter: blur(60px); opacity: 0.6; }
}
