/* ==========================================================================
   Luke Carrington — Counter-Strike cold open
   --------------------------------------------------------------------------
   A one-shot intro: a tactical HUD draws in over near-black, a bomb timer counts
   the site in, and a flashbang white-out hands over to the page.

   intro.js owns the timeline — it adds `.intro--play`, `.intro--flash` and
   `.intro--done` at the right moments. This file only describes those three
   states, so the two can never argue about timing.

   Anything generic is a token in index.css instead (`--z-intro`, `--font-mono`,
   `--ease-out-expo`, `--tracking-label`); only the intro's own palette lives here.
   ========================================================================== */

/* The crosshair converges by animating a shared gap length. Registering it as a
   real `<length>` is what makes it interpolate; without `@property` a browser
   simply snaps to the end state, which is a harmless degradation. */
@property --intro-gap {
  syntax: "<length>";
  inherits: true;
  initial-value: 3rem;
}

/* The velocity-driven spread is written by intro.js as a real length, so the
   ticks can add it to their resting gap with `calc()`. Registering it keeps it
   a length rather than an opaque token, and gives it a 0 fallback. */
@property --intro-spread {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

/* The gate that precedes the cold open shares this shell's palette and its
   full-screen near-black, so the two read as the same screen: the click cuts
   straight from one to the other. */
.intro,
.intro-gate {
  /* Scoped palette — tactical HUD on near-black with a hot alert red, so the
     bomb countdown is the loudest thing on screen. */
  --intro-hud: #cfe8b4;
  --intro-hud-dim: rgba(207, 232, 180, 0.45);
  --intro-panel: rgba(207, 232, 180, 0.16);
  --intro-alert: #ff4b3e;
  --intro-inset: clamp(1rem, 3.5vw, 2.5rem);
  --intro-callsign-offset: clamp(4.5rem, 12vh, 7rem);
  --intro-time-left: 1;

  position: fixed;
  inset: 0;
  z-index: var(--z-intro);
  display: grid;
  place-items: center;
  overflow: hidden;
  background-color: var(--color-ink);
}

/* Dropped by intro.js once the white-out has fully taken over. */
.intro--done {
  display: none;
}

/* --------------------------------------------------------------------------
   Gate — the intro to the intro
   --------------------------------------------------------------------------
   Browsers refuse to make noise until the visitor has interacted, so the whole
   sequence waits behind one click. Painted in the HUD's own language so it
   looks like part of the cold open rather than a dialog in front of it.
   -------------------------------------------------------------------------- */

.intro-gate {
  /* The whole screen is the target, not just the button inside it. */
  cursor: pointer;
  /* The gate shares the overlay's shell rule, but it has to sit *over* the
     overlay: the two are the same z-index, and the overlay is the later sibling,
     so it would otherwise paint its near-black straight over the gate. */
  z-index: var(--z-gate);
  animation: fade-in 0.5s ease both;
}

/* Dropped by intro.js on the click, on the same frame the timeline starts. */
.intro-gate--done {
  display: none;
}

.intro-gate__button {
  display: grid;
  justify-items: center;
  gap: 0.9rem;
  padding: clamp(1.6rem, 4vw, 2.6rem) clamp(2.2rem, 9vw, 5rem);
  font-family: var(--font-mono);
  font-weight: var(--font-weight-medium);
  text-transform: uppercase;
  text-align: center;
  color: var(--intro-hud);
  background: transparent;
  border: 1px solid var(--intro-panel);
  border-radius: 0.35rem;
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-out-expo),
    background-color var(--duration-fast) var(--ease-out-expo),
    box-shadow var(--duration-fast) var(--ease-out-expo);
}

/* The focus ring is deliberately left alone — the glow is an addition to it,
   not a replacement for it. */
.intro-gate__button:hover,
.intro-gate__button:focus-visible {
  border-color: var(--intro-hud);
  background: rgba(207, 232, 180, 0.06);
  box-shadow: 0 0 2.2rem rgba(207, 232, 180, 0.2);
}

.intro-gate__eyebrow,
.intro-gate__hint {
  font-size: clamp(0.62rem, 1.05vw, 0.72rem);
  letter-spacing: var(--tracking-label);
  color: var(--intro-hud-dim);
}

.intro-gate__label {
  font-size: clamp(1.05rem, 2.6vw, 1.5rem);
  letter-spacing: 0.18em;
  /* Tracking adds a trailing space after the last glyph, which visually shunts
     the line left; matching padding on the other side puts it back on centre. */
  padding-left: 0.18em;
  text-shadow: 0 0 1.8rem rgba(207, 232, 180, 0.3);
}

/* Blinking console caret, reusing the HUD's own blink. */
.intro-gate__hint::after {
  content: "_";
  animation: intro-blink 1s step-end infinite;
}

.intro__scene {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

/* --------------------------------------------------------------------------
   HUD
   -------------------------------------------------------------------------- */

.intro__hud {
  position: absolute;
  font-family: var(--font-mono);
  font-size: clamp(0.68rem, 1.05vw, 0.82rem);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--intro-hud);
  opacity: 0;
}

/* Radar — survey grid in the top-left, with a turning sweep and live contacts. */
.intro__hud--radar {
  top: var(--intro-inset);
  left: var(--intro-inset);
  width: clamp(4.5rem, 11vw, 7.5rem);
  aspect-ratio: 1;
  border: 1px solid var(--intro-panel);
  border-radius: 0.35rem;
  background-image:
    repeating-linear-gradient(0deg, var(--intro-panel) 0 1px, transparent 1px 25%),
    repeating-linear-gradient(90deg, var(--intro-panel) 0 1px, transparent 1px 25%);
  overflow: hidden;
}

/* Sweep: a cone of light projected from the centre of the scope. The gradient
   holds its hard leading edge at 0deg (12 o'clock) and falls away *behind* it —
   the stops run clockwise, so the fade occupies the 296deg–360deg arc, which is
   the counter-clockwise side of the beam. The element's own rotation then
   carries that edge around clockwise, and the ramp trails it the whole way,
   which is what makes the sweep read as a rotation rather than as a wedge
   sliding sideways. The end colour is the HUD green at zero alpha rather than
   `transparent`, so the ramp does not pass through grey on its way out. */
.intro__hud--radar::after {
  content: "";
  position: absolute;
  inset: -100%;
  border-radius: inherit;
  background: conic-gradient(from 0deg,
      rgba(207, 232, 180, 0) 0deg,
      rgba(207, 232, 180, 0) 296deg,
      rgba(207, 232, 180, 0.05) 306deg,
      rgba(207, 232, 180, 0.22) 336deg,
      rgba(207, 232, 180, 0.45) 354deg,
      rgba(207, 232, 180, 0.6) 360deg);
}

.intro__blip {
  position: absolute;
  width: 0.35rem;
  height: 0.35rem;
  background: var(--intro-alert);
  box-shadow: 0 0 0.5rem rgba(255, 75, 62, 0.7);
  opacity: 0;
}

.intro__blip--a {
  top: 26%;
  left: 58%;
}

.intro__blip--b {
  top: 62%;
  left: 34%;
}

.intro__blip--c {
  top: 44%;
  left: 78%;
}

/* Bomb readout — top centre, with a bar that empties in step with the digits. */
.intro__hud--timer {
  top: var(--intro-inset);
  left: 50%;
  /* `translate` rather than `transform`, so the entrance animation can own
     `transform` without fighting the centring. */
  translate: -50% 0;
  display: grid;
  justify-items: center;
  gap: 0.45rem;
  text-align: center;
}

.intro__timer {
  font-size: clamp(1.5rem, 4.4vw, 2.85rem);
  line-height: 1;
  letter-spacing: 0.04em;
  /* Tabular figures stop the digits jittering as they count down. */
  font-variant-numeric: tabular-nums;
  color: var(--intro-alert);
  text-shadow: 0 0 1.6rem rgba(255, 75, 62, 0.45);
}

/* The last couple of seconds: the readout starts pulsing. */
.intro__timer--hot {
  animation: intro-pulse 0.42s ease-in-out infinite alternate;
}

.intro__timer-bar {
  display: block;
  width: clamp(7rem, 22vw, 14rem);
  height: 2px;
  background: var(--intro-panel);
  overflow: hidden;
}

.intro__timer-fill {
  display: block;
  height: 100%;
  background: var(--intro-alert);
  /* Written by intro.js as the countdown drains. */
  transform: scaleX(var(--intro-time-left));
  transform-origin: left center;
}

.intro__timer-label {
  font-size: 0.62em;
  letter-spacing: var(--tracking-label);
  color: var(--intro-hud-dim);
}

/* Callsign — the name card, tucked just below the crosshair. */
.intro__callsign {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% calc(-50% + var(--intro-callsign-offset));
  display: grid;
  justify-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-weight: var(--font-weight-medium);
  text-align: center;
  text-transform: uppercase;
  color: var(--intro-hud);
  opacity: 0;
}

.intro__tag {
  font-size: 0.72em;
  letter-spacing: var(--tracking-label);
  color: var(--intro-hud-dim);
  border: 1px solid var(--intro-panel);
  border-radius: 0.2rem;
  padding: 0.35em 0.7em;
}

.intro__name {
  font-size: clamp(1.15rem, 3.6vw, 2.35rem);
  letter-spacing: 0.16em;
  /* Tracking adds a trailing space after the last glyph, which visually shunts
     the line left; matching padding on the other side puts it back on centre. */
  padding-left: 0.16em;
  text-shadow: 0 0 2rem rgba(207, 232, 180, 0.28);
}

.intro__status {
  font-size: 0.8em;
  letter-spacing: 0.28em;
  color: var(--intro-hud-dim);
}

/* Blinking console caret. */
.intro__status::after {
  content: "_";
  animation: intro-blink 1s step-end infinite;
}

/* Vitals and ammo — the bottom corners. */
.intro__hud--vitals {
  bottom: var(--intro-inset);
  left: var(--intro-inset);
  display: flex;
  gap: 1.1rem;
}

.intro__hud--ammo {
  bottom: var(--intro-inset);
  right: var(--intro-inset);
  font-size: clamp(1rem, 2.6vw, 1.6rem);
  letter-spacing: 0.06em;
}

.intro__readout {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4em;
}

.intro__stat-value {
  color: var(--intro-hud);
  font-variant-numeric: tabular-nums;
}

.intro__stat-label {
  font-size: 0.72em;
  letter-spacing: 0.2em;
  color: var(--intro-hud-dim);
}

/* --------------------------------------------------------------------------
   Crosshair — a zero-sized node at the exact centre, four ticks around it
   -------------------------------------------------------------------------- */

.intro__crosshair {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  opacity: 0;
}

.intro__tick {
  position: absolute;
  top: 0;
  left: 0;
  background: var(--intro-hud);
  box-shadow: 0 0 0.45rem rgba(207, 232, 180, 0.6);
}

/* Each tick is nudged outwards from the centre along its own axis. Two lengths
   add up here: `--intro-gap` is the resting offset, which shrinks from 3rem to
   0.6rem in the `intro-aim` keyframes, and `--intro-spread` is the extra
   distance intro.js opens them by while the crosshair is travelling fast. They
   are separate variables precisely because the keyframes animate one of them —
   an animation outranks an inline style, so a single shared variable could
   never be written from JavaScript. */
.intro__tick--up {
  width: 2px;
  height: 0.85rem;
  translate: -50% calc(-100% - var(--intro-gap) - var(--intro-spread));
}

.intro__tick--down {
  width: 2px;
  height: 0.85rem;
  translate: -50% calc(var(--intro-gap) + var(--intro-spread));
}

.intro__tick--left {
  width: 0.85rem;
  height: 2px;
  translate: calc(-100% - var(--intro-gap) - var(--intro-spread)) -50%;
}

.intro__tick--right {
  width: 0.85rem;
  height: 2px;
  translate: calc(var(--intro-gap) + var(--intro-spread)) -50%;
}

.intro__dot {
  position: absolute;
  top: 0;
  left: 0;
  width: 0.16rem;
  height: 0.16rem;
  border-radius: 50%;
  background: var(--intro-hud);
  translate: -50% -50%;
}

/* --------------------------------------------------------------------------
   Screen treatment — scanlines and a vignette, so it reads as a monitor
   -------------------------------------------------------------------------- */

.intro__grain {
  position: absolute;
  inset: 0;
  opacity: 0;
  background-image: repeating-linear-gradient(0deg,
      rgba(255, 255, 255, 0.05) 0 1px,
      transparent 1px 4px);
  mix-blend-mode: screen;
}

.intro__vignette {
  position: absolute;
  inset: 0;
  opacity: 0;
  background: radial-gradient(circle at 50% 50%,
      transparent 26%,
      rgba(0, 0, 0, 0.74) 100%);
}

/* --------------------------------------------------------------------------
   The payoff — shockwave, flashbang, recoil
   -------------------------------------------------------------------------- */

.intro__shockwave {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(8rem, 22vw, 16rem);
  aspect-ratio: 1;
  border: 2px solid rgba(255, 255, 255, 0.85);
  border-radius: 50%;
  translate: -50% -50%;
  scale: 0.2;
  opacity: 0;
}

.intro__flash {
  position: absolute;
  top: 50%;
  left: 50%;
  /* Wide enough that even at a third of full scale it reaches every corner. */
  width: 160vmax;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle,
      #ffffff 0%,
      #ffffff 42%,
      rgba(255, 255, 255, 0.86) 58%,
      rgba(255, 255, 255, 0) 74%);
  translate: -50% -50%;
  scale: 0.05;
  opacity: 0;
}

/* --------------------------------------------------------------------------
   Staged animation — everything waits for `.intro--play`, which intro.js adds on
   the first frame, so no beat can start before the stylesheet has settled. The
   delays are tuned against the timeline in intro.js (FLASH_AT); they were spread
   out when the timeline was lengthened, so the HUD still fills the screen well
   before the bang rather than arriving all at once.
   -------------------------------------------------------------------------- */

.intro--play .intro__hud--radar {
  animation: intro-hud-in 0.7s var(--ease-out-expo) both;
  animation-delay: 0.15s;
}

.intro--play .intro__hud--radar::after {
  animation: intro-sweep 3.6s linear infinite;
  animation-delay: 0.6s;
}

.intro--play .intro__hud--timer {
  animation: intro-hud-in 0.7s var(--ease-out-expo) both;
  animation-delay: 0.25s;
}

.intro--play .intro__hud--vitals {
  animation: intro-hud-in 0.7s var(--ease-out-expo) both;
  animation-delay: 0.35s;
}

.intro--play .intro__hud--ammo {
  animation: intro-hud-in 0.7s var(--ease-out-expo) both;
  animation-delay: 0.45s;
}

.intro--play .intro__grain {
  animation: fade-in 0.9s ease both;
  animation-delay: 0.25s;
}

.intro--play .intro__vignette {
  animation: fade-in 1.1s ease both;
}

.intro--play .intro__blip {
  animation: intro-blip 2.4s ease-out infinite;
}

.intro--play .intro__blip--a {
  animation-delay: 1s;
}

.intro--play .intro__blip--b {
  animation-delay: 1.9s;
}

.intro--play .intro__blip--c {
  animation-delay: 2.6s;
}

.intro--play .intro__callsign {
  animation: fade-in 0.7s var(--ease-out-expo) both;
  animation-delay: 0.7s;
}

/* The name opens out of a wide tracking into its resting spacing. */
.intro--play .intro__name {
  animation: intro-tracking 1.1s var(--ease-out-expo) both;
  animation-delay: 0.85s;
}

.intro--play .intro__crosshair {
  animation: intro-aim 1.3s var(--ease-out-expo) both;
  animation-delay: 0.6s;
}

.intro--play .intro__dot {
  animation: intro-dot 0.4s var(--ease-out-expo) both;
  animation-delay: 1.5s;
}

/* --------------------------------------------------------------------------
   The bang. Once the flash is up, intro.js has already released the page's own
   entrance, so the overlay fading away simply hands the screen over.
   -------------------------------------------------------------------------- */

.intro--flash {
  animation: intro-dismiss 1.1s ease-in both;
  animation-delay: 0.4s;
}

.intro--flash .intro__scene {
  animation: intro-recoil 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

.intro--flash .intro__crosshair {
  /* Held converged, so the ticks fly apart on their own scale rather than
     snapping back out to the initial gap first. */
  --intro-gap: 0.6rem;
  animation: intro-crosshair-out 0.3s ease-out both;
}

.intro--flash .intro__shockwave {
  animation: intro-shockwave 0.85s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.intro--flash .intro__flash {
  animation: intro-flashbang 0.6s ease-out both;
}

/* --------------------------------------------------------------------------
   Keyframes
   -------------------------------------------------------------------------- */

@keyframes intro-hud-in {
  from {
    opacity: 0;
    transform: translateY(0.6rem) scale(0.96);
    filter: blur(0.35rem);
  }

  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

@keyframes intro-sweep {
  from {
    rotate: 0deg;
  }

  to {
    rotate: 360deg;
  }
}

@keyframes intro-blip {
  0% {
    opacity: 0;
    scale: 0.4;
  }

  20% {
    opacity: 1;
    scale: 1;
  }

  70% {
    opacity: 0.8;
    scale: 1;
  }

  100% {
    opacity: 0;
    scale: 1.5;
  }
}

@keyframes intro-tracking {
  from {
    opacity: 0;
    letter-spacing: 0.55em;
  }

  to {
    opacity: 1;
    letter-spacing: 0.16em;
  }
}

@keyframes intro-blink {

  0%,
  49% {
    opacity: 1;
  }

  50%,
  100% {
    opacity: 0;
  }
}

@keyframes intro-pulse {
  from {
    opacity: 1;
  }

  to {
    opacity: 0.55;
  }
}

/* Ticks fall inwards from well outside the centre; the gap interpolates because
   `--intro-gap` is registered with `@property` at the top of this file. */
@keyframes intro-aim {
  0% {
    --intro-gap: 3rem;
    opacity: 0;
  }

  25% {
    opacity: 1;
  }

  100% {
    --intro-gap: 0.6rem;
    opacity: 1;
  }
}

@keyframes intro-dot {
  from {
    opacity: 0;
    scale: 0;
  }

  to {
    opacity: 1;
    scale: 1;
  }
}

@keyframes intro-crosshair-out {
  from {
    opacity: 1;
    scale: 1;
  }

  to {
    opacity: 0;
    scale: 2.6;
  }
}

@keyframes intro-shockwave {
  from {
    opacity: 0.9;
    scale: 0.2;
  }

  to {
    opacity: 0;
    scale: 3;
  }
}

@keyframes intro-flashbang {
  from {
    opacity: 0;
    scale: 0.05;
  }

  18% {
    opacity: 1;
    scale: 0.55;
  }

  to {
    opacity: 1;
    scale: 1;
  }
}

@keyframes intro-recoil {
  0% {
    translate: 0 0;
    scale: 1;
  }

  15% {
    translate: 0 -0.5rem;
    scale: 1.03;
  }

  35% {
    translate: 0.35rem 0.2rem;
    scale: 1.012;
  }

  55% {
    translate: -0.25rem 0.15rem;
    scale: 1.006;
  }

  100% {
    translate: 0 0;
    scale: 1;
  }
}

@keyframes intro-dismiss {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

/* --------------------------------------------------------------------------
   Holding the page back, and letting it in
   -------------------------------------------------------------------------- */

/* The page's own entrance is frozen at frame zero while the intro plays.
   intro.js removes `intro-pending` from <html> on the same frame the flashbang
   blooms, so the page's entrance and the white-out overlap instead of butting up
   against each other. The <noscript> block in index.html overrides this to
   `running` for anyone without JavaScript. */
.intro-pending .name__word,
.intro-pending .hero__subtitle,
.intro-pending .backdrop,
.intro-pending .corner-art__image {
  animation-play-state: paused;
}

/* --------------------------------------------------------------------------
   Reduced motion — skip the cold open and its gate entirely, and go straight to
   the page
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {

  .intro,
  .intro-gate {
    display: none;
  }
}