/* ============================================================
   ANIMATIONS — Scroll reveals, marquee, keyframes, reduced-motion
   ============================================================ */


/* --- Scroll Reveal Base --- */
.reveal {
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity var(--duration-reveal) var(--ease-out-expo),
              transform var(--duration-reveal) var(--ease-out-expo);
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Safety fallback: if JS fails, reveal everything after 2s */
@keyframes reveal-fallback {
  to { opacity: 1; transform: none; }
}
.reveal:not(.is-visible),
.reveal-fade:not(.is-visible),
.reveal-left:not(.is-visible),
.reveal-right:not(.is-visible),
.reveal-scale:not(.is-visible) {
  animation: reveal-fallback 0s 2.5s forwards;
}

/* Fade only (no movement) */
.reveal-fade {
  opacity: 0;
  transition: opacity var(--duration-reveal) var(--ease-out-expo);
}

.reveal-fade.is-visible {
  opacity: 1;
}

/* Slide from left */
.reveal-left {
  opacity: 0;
  transform: translateX(-2rem);
  transition: opacity var(--duration-reveal) var(--ease-out-expo),
              transform var(--duration-reveal) var(--ease-out-expo);
}

.reveal-left.is-visible {
  opacity: 1;
  transform: translateX(0);
}

/* Slide from right */
.reveal-right {
  opacity: 0;
  transform: translateX(2rem);
  transition: opacity var(--duration-reveal) var(--ease-out-expo),
              transform var(--duration-reveal) var(--ease-out-expo);
}

.reveal-right.is-visible {
  opacity: 1;
  transform: translateX(0);
}

/* Scale up */
.reveal-scale {
  opacity: 0;
  transform: scale(0.9);
  transition: opacity var(--duration-reveal) var(--ease-out-expo),
              transform var(--duration-reveal) var(--ease-out-expo);
}

.reveal-scale.is-visible {
  opacity: 1;
  transform: scale(1);
}


/* --- Stagger children --- */
.reveal-stagger > * {
  opacity: 0;
  transform: translateY(1rem);
  transition: opacity var(--duration-slow) var(--ease-out-expo),
              transform var(--duration-slow) var(--ease-out-expo);
  transition-delay: calc(var(--stagger-index, 0) * 0.08s);
}

.reveal-stagger.is-visible > * {
  opacity: 1;
  transform: translateY(0);
}


/* --- Marquee (logo scroll, text ticker) --- */
.marquee {
  overflow: hidden;
  position: relative;
  width: 100%;
}

.marquee__track {
  display: flex;
  gap: var(--space-4xl);
  width: max-content;
  animation: marquee-scroll 30s linear infinite;
}

.marquee--fast .marquee__track {
  animation-duration: 15s;
}

.marquee--slow .marquee__track {
  animation-duration: 45s;
}

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

@keyframes marquee-scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-50%);
  }
}


/* --- Pulse glow (disabled in V2 — no red glow on badges) --- */
.pulse-glow {
  /* animation removed */
}

/* --- Green status dot (live/announcing indicator) --- */
@keyframes dot-pulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(52, 199, 89, 0.5);
    opacity: 1;
  }
  50% {
    box-shadow: 0 0 0 0.35rem rgba(52, 199, 89, 0);
    opacity: 0.8;
  }
}

.badge--live::before {
  content: '';
  display: inline-block;
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--green-500);
  animation: dot-pulse 2s ease-in-out infinite;
  flex-shrink: 0;
}

/* --- Red status dot (hiring / open roles indicator) --- */
@keyframes dot-pulse-red {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(178, 34, 34, 0.6);
    opacity: 1;
  }
  50% {
    box-shadow: 0 0 0 0.35rem rgba(178, 34, 34, 0);
    opacity: 0.85;
  }
}

.badge--hiring::before {
  content: '';
  display: inline-block;
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--red-500, #b22222);
  animation: dot-pulse-red 2s ease-in-out infinite;
  flex-shrink: 0;
}


/* --- Gradient shimmer (hero text accent) --- */
.shimmer {
  background: linear-gradient(
    90deg,
    var(--text-primary) 0%,
    var(--accent-primary) 50%,
    var(--text-primary) 100%
  );
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: shimmer 4s linear infinite;
}

@keyframes shimmer {
  0% {
    background-position: -200% center;
  }
  100% {
    background-position: 200% center;
  }
}


/* --- Float (decorative elements) --- */
@keyframes float {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-0.75rem);
  }
}

.float {
  animation: float 6s ease-in-out infinite;
}

.float--delay-1 { animation-delay: 1s; }
.float--delay-2 { animation-delay: 2s; }
.float--delay-3 { animation-delay: 3s; }


/* --- Fade in/up on load (for hero content) --- */
@keyframes fade-in-up {
  from {
    opacity: 0;
    transform: translateY(1.5rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.animate-in {
  animation: fade-in-up var(--duration-reveal) var(--ease-out-expo) both;
}

.animate-in--delay-1 { animation-delay: 0.1s; }
.animate-in--delay-2 { animation-delay: 0.2s; }
.animate-in--delay-3 { animation-delay: 0.3s; }
.animate-in--delay-4 { animation-delay: 0.4s; }
.animate-in--delay-5 { animation-delay: 0.5s; }
.animate-in--delay-6 { animation-delay: 0.6s; }


/* --- Count-up number styling --- */
[data-count-to] {
  font-variant-numeric: tabular-nums;
}


/* --- Magnetic hover (buttons) --- */
.magnetic {
  transition: transform var(--duration-fast) var(--ease-standard);
}


/* --- Theme switch (js/main.js) ---
   The page in its old theme is held as a picture while the new one is
   uncovered in a circle that grows from the toggle; main.js animates the
   circle. The two pictures are kept from doing anything themselves: the
   default cross-fade would show both themes at half strength midway. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

/* And nothing on the page transitions on its own while the circle grows:
   what it uncovers is already in its final colours. */
.theme-switching,
.theme-switching *,
.theme-switching *::before,
.theme-switching *::after {
  transition: none !important;
}


/* --- Gradient line scan (high-tech ambient effect on sections) --- */
@keyframes scanline {
  0% { transform: translateY(-100%); }
  100% { transform: translateY(100vh); }
}

.scanline-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}

.scanline-overlay::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(178, 34, 34, 0.15) 20%,
    rgba(178, 34, 34, 0.3) 50%,
    rgba(178, 34, 34, 0.15) 80%,
    transparent 100%
  );
  animation: scanline 8s linear infinite;
  opacity: 0.5;
}

/* --- Ambient grid — fixed structural viewport frame --- */
/* Two vertical column lines at the content edges, and a crosshair "+" mark
   where each one meets the nav's bottom border. Fixed: they stay put while
   the page scrolls underneath. Nothing closes the frame along the bottom —
   a hairline pinned across the foot of the viewport read as a sticky bar. */
.ambient-grid {
  position: fixed;
  inset: 0;
  /* Above the nav (1000), not just above section content: the nav paints an
     opaque background, which was cutting the two
     vertical rails off at the nav divider instead of running them to the top
     of the viewport. The frame is 1px hairlines with pointer-events: none,
     and the nav's own gutter is grid-line-x + space-md, so they pass a full
     1rem clear of the logo and the theme toggle. */
  z-index: 1002;
  pointer-events: none;
}

/* Vertical structural columns at content padding edges */
.ambient-grid::before,
.ambient-grid::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--border-rule);
}

.ambient-grid::before {
  left: var(--grid-line-x);
}

.ambient-grid::after {
  right: var(--grid-line-x);
}

/* Fixed crosshair "+" marks where the rails meet the nav's bottom border.
   Same colour and alpha as the lines they sit on, so each one reads as a
   brightening of the intersection rather than a mark of its own. */
.ambient-grid__cross {
  position: absolute;
  width: 17px;
  height: 17px;
}

.ambient-grid__cross::before,
.ambient-grid__cross::after {
  content: '';
  position: absolute;
  background: var(--border-rule);
}

.ambient-grid__cross::before {
  /* Horizontal bar */
  top: 8px;
  left: 0;
  width: 17px;
  height: 1px;
}

.ambient-grid__cross::after {
  /* Vertical bar */
  left: 8px;
  top: 0;
  width: 1px;
  height: 17px;
}

/* The nav is border-box, so its 1px bottom border is the last row INSIDE its
   height: at y = nav-height - 1, not at y = nav-height. Subtracting only the
   8px that centres the 17px mark landed the crosshair's bar one pixel under
   the divider, which is visible as a short doubled line at each corner. */
.ambient-grid__cross--tl {
  top: calc(var(--nav-height) - 1px - 8px);
  left: calc(var(--grid-line-x) - 8px);
}

.ambient-grid__cross--tr {
  top: calc(var(--nav-height) - 1px - 8px);
  right: calc(var(--grid-line-x) - 8px);
}

/* Mobile: no frame at all — the rails and their crosshairs eat horizontal
   space that a phone screen can't spare. */
@media (max-width: 48rem) {
  .ambient-grid::before,
  .ambient-grid::after,
  .ambient-grid__cross,
  .divider-cross {
    display: none;
  }
}

/* --- Divider crosshairs — injected by JS into .ambient-grid (fixed context)
       so they align perfectly with vertical frame lines --- */
/* Divider crosshairs — same visual style as the corner crosses
   (.ambient-grid__cross) but positioned at each section-divider's
   top edge. Width/height match the corner crosses at 17px. */
.divider-cross {
  position: absolute;
  width: 17px;
  height: 17px;
  pointer-events: none;
  z-index: 101;
}

.divider-cross::before,
.divider-cross::after {
  content: '';
  position: absolute;
  background: var(--border-rule);
}

/* Horizontal bar */
.divider-cross::before {
  top: 8px;
  left: 0;
  width: 17px;
  height: 1px;
}

/* Vertical bar */
.divider-cross::after {
  left: 8px;
  top: 0;
  width: 1px;
  height: 17px;
}

.divider-cross--left {
  left: calc(var(--grid-line-x) - 8px);
}

.divider-cross--right {
  right: calc(var(--grid-line-x) - 8px);
}

/* --- Horizontal rule animation (draws from left) --- */
@keyframes draw-line {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.animated-rule {
  transform-origin: left;
  transform: scaleX(0);
}

.animated-rule.is-visible {
  animation: draw-line 1.2s var(--ease-out-expo) forwards;
}

/* --- Glow pulse on section labels --- */
@keyframes label-glow {
  0%, 100% { text-shadow: none; }
  50% { text-shadow: 0 0 1.5rem rgba(178, 34, 34, 0.15); }
}

.section-label--glow {
  animation: label-glow 4s ease-in-out infinite;
}

/* --- Typing cursor (blinking) for text emphasis --- */
@keyframes blink-cursor {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

.typing-cursor::after {
  content: '|';
  color: var(--accent-primary);
  font-weight: var(--fw-light);
  animation: blink-cursor 1s steps(1) infinite;
  margin-left: 0.125rem;
}

/* --- Card hover glow effect --- */
.card-glow {
  position: relative;
}

.card-glow::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  background: linear-gradient(135deg,
    rgba(178, 34, 34, 0.15) 0%,
    transparent 40%,
    transparent 60%,
    rgba(186, 202, 217, 0.1) 100%
  );
  opacity: 0;
  transition: opacity var(--duration-normal) var(--ease-out-expo);
  z-index: -1;
}

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

/* --- Staggered section reveal with slide direction --- */
.reveal-up {
  opacity: 0;
  transform: translateY(2.5rem);
  transition: opacity var(--duration-reveal) var(--ease-out-expo),
              transform var(--duration-reveal) var(--ease-out-expo);
}

.reveal-up.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* --- Number counter shimmer effect --- */
@keyframes data-pulse {
  0% { opacity: 0.4; }
  50% { opacity: 1; }
  100% { opacity: 0.4; }
}


/* ============================================================
   REDUCED MOTION — respect user preferences
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  /* Kill all animations */
  .reveal,
  .reveal-fade,
  .reveal-left,
  .reveal-right,
  .reveal-scale {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .reveal-stagger > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .marquee__track {
    animation-play-state: paused !important;
  }

  .pulse-glow,
  .shimmer,
  .float,
  .animate-in,
  .section-label--glow {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }

  .reveal-up {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .scanline-overlay::after {
    animation: none !important;
  }

  .badge--live::before {
    animation: none !important;
    box-shadow: none !important;
  }

  .animated-rule {
    transform: scaleX(1) !important;
  }

  .typing-cursor::after {
    animation: none !important;
  }
}
