/* ==========================================================================
   Define IT Solution — splash.css
   The boot sequence: the brand assembling itself out of the technology.

   Structure of the overlay, back to front:
     .splash__bg        radial depth + the faint engineering grid
     canvas.splash__dust  fine particles, network paths, data packets (JS)
     .splash__logo      the lockup, built part by part
     .splash__status    a restrained system line
     .splash__skip      keyboard-reachable escape

   Three rules govern everything here:

   1. Nothing in the site's own markup may depend on this file. The overlay
      is added by JavaScript and removed by JavaScript; with JS off or broken
      the page is simply the page. That is why there is no `body { overflow:
      hidden }` in the stylesheet — the lock is applied from script, so it
      cannot outlive the script that set it.

   2. The final frame has to be the approved lockup exactly. Every animation
      here resolves to opacity 1, transform none, and full stroke — no
      animation is allowed to leave a part permanently offset or tinted.

   3. Reduced motion is not a degraded version of the sequence, it is a
      different, much shorter one: the finished logo, held briefly, then out.
   ========================================================================== */

/* Hidden unless JavaScript has announced itself. This is the whole of the
   no-JS story: the class never lands, the overlay never displays, and the
   site is just the site. Nothing else in the markup depends on this file. */
.define-splash { display: none; }
.js .define-splash { display: grid; }

/* Scroll lock, held only while the overlay is up. A class rather than an
   inline style, so releasing it cannot clobber a value something else set. */
html.splash-active,
html.splash-active body {
  overflow: hidden;
  /* Touch scroll ignores overflow on some mobile browsers. */
  touch-action: none;
}

/* Hero hand-off.

   The hero entrance is a time-based CSS animation that starts at page load,
   so under a three-second overlay it would play out entirely unseen and the
   homepage would be revealed already settled. Pausing it holds every element
   at 0% — including its stagger delay — and removing the class at the moment
   the overlay begins to fade lets the whole sequence run underneath the exit.
   That overlap is what makes it read as one continuous introduction instead
   of two animations meeting in the middle. */
html.splash-active .hero-seq > * {
  animation-play-state: paused !important;
}

.define-splash {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  background: #020408;
  /* Painted, not transparent: this sits over a page that is already laid
     out, and any translucency would show the hero sliding about underneath. */
  contain: strict;
  overscroll-behavior: contain;
  transform: scale(1);
  opacity: 1;
  transition: opacity 600ms var(--ease-out-premium, cubic-bezier(.16,1,.3,1)),
              transform 600ms var(--ease-out-premium, cubic-bezier(.16,1,.3,1));
}

/* The exit. Scale is deliberately tiny — 2% reads as the surface receding
   rather than as a zoom, and anything larger fights the hero underneath as
   it starts its own entrance. */
.define-splash.is-leaving {
  opacity: 0;
  transform: scale(1.02);
  pointer-events: none;
}

/* Depth. A dark blue wash off-centre so the field is not a flat black
   rectangle, plus a vignette to keep the edges from reading as a panel. */
.splash__bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 42%, rgba(0, 78, 200, 0.16), transparent 62%),
    radial-gradient(70% 60% at 22% 78%, rgba(98, 70, 255, 0.075), transparent 70%),
    radial-gradient(80% 70% at 82% 20%, rgba(0, 174, 239, 0.07), transparent 70%),
    #020408;
}

/* The engineering grid. 62px, and so faint it should read as texture rather
   than as lines — if you can count the squares at a glance it is too strong.
   It fades in over the first beat and never animates as a whole. */
.splash__grid {
  position: absolute;
  inset: -2px;
  pointer-events: none;
  background-image:
    linear-gradient(to right,  rgba(120, 190, 255, 0.075) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(120, 190, 255, 0.075) 1px, transparent 1px);
  background-size: 62px 62px;
  -webkit-mask-image: radial-gradient(circle at 50% 46%, #000 0%, #000 44%, transparent 82%);
          mask-image: radial-gradient(circle at 50% 46%, #000 0%, #000 44%, transparent 82%);
  opacity: 0;
  animation: splashGridIn 900ms var(--ease, cubic-bezier(.4,0,.2,1)) 120ms forwards;
}
@keyframes splashGridIn { to { opacity: 1; } }

/* Particles, network paths and data packets all share one canvas. */
.splash__dust {
  position: absolute;
  inset: 0;
  display: block;
  pointer-events: none;
}

.splash__stage {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  padding: 0 24px;
  width: min(720px, 92vw);
}

/* ===========================================================================
   The lockup, assembling

   Driven by transitions rather than keyframes, on purpose. splash.js adds one
   class per stage at a known time; every part transitions from its start
   state to its finished state and stops there. That matters more than it
   sounds: the last frame of this sequence has to be the approved logo
   exactly, and a transition to a resting value cannot leave a part offset,
   tinted or half-drawn the way a mis-filled keyframe can. Adding every stage
   class at once — which is what the short and reduced modes do — simply
   lands the finished lockup.

   Geometry is untouched. Every path, gradient stop and coordinate is the
   approved file byte for byte; only classes and two ids were added.
   =========================================================================== */
.splash-logo {
  position: relative;
  z-index: 2;
  width: min(560px, 78vw);
  margin-inline: auto;
}
.splash-logo__svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* Parts transform about their own box unless they need otherwise. */
.splash-logo__svg .lg-node,
.splash-logo__svg .lg-s,
.splash-logo__svg .lg-d,
.splash-logo__svg .lg-word,
.splash-logo__svg .lg-tag {
  transform-box: fill-box;
}

/* Every duration and delay below is multiplied by --splash-tempo, which
   splash.js sets from its TEMPO constant (1 for the short and reduced-motion
   paths). Pace the sequence from there, not from here: changing a number in
   this block alone would move one part out of step with the stage boundaries
   the script is working to. The fallback of 1 keeps the block sane if the
   stylesheet is ever used without the script. */

/* --- resting (pre-build) states ------------------------------------------ */
.lg-node {
  opacity: 0;
  transform-origin: 50% 50%;
  transform: scale(.5);
  transition: opacity calc(320ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1)),
              transform calc(380ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1));
}
/* The verticals grow up out of the baseline; the cross-bar runs in from the
   left, which is the order the eye reads the mark being constructed. */
.lg-s--1, .lg-s--3 { transform-origin: 50% 100%; transform: scaleY(0); }
.lg-s--2           { transform-origin: 0% 50%;   transform: scaleX(0); }
.lg-s {
  opacity: .001;
  transition: transform calc(420ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1)),
              opacity calc(200ms * var(--splash-tempo, 1)) linear;
}
/* The D unfurls from the stem it attaches to, not from its own middle. */
.lg-d {
  opacity: 0;
  transform-origin: 0% 50%;
  transform: scaleX(.04);
  transition: opacity calc(260ms * var(--splash-tempo, 1)) linear,
              transform calc(620ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1));
}
/* A cyan point running the outline ahead of the fill. pathLength 100 makes
   the dash arithmetic independent of the real path length. */
.lg-trace {
  opacity: 0;
  stroke-dasharray: 8 92;
  stroke-dashoffset: 8;
}
/* DEFINE wipes in from the left. clip-path on the wrapper, not a <rect> inside
   a <clipPath>: CSS transforms do not reliably reach elements inside <defs>,
   which silently clipped the whole word away. Percentages here resolve
   against the group's fill-box, so the inset needs no coordinates and cannot
   drift if the wordmark is ever re-kerned. */
.lg-word--define {
  opacity: 0;
  clip-path: inset(-12% 100% -12% -2%);
  transition: opacity calc(220ms * var(--splash-tempo, 1)) linear,
              clip-path calc(640ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1));
}
.lg-word--it {
  opacity: 0;
  transform-origin: 0% 50%;
  transform: translateX(-6px);
  transition: opacity calc(300ms * var(--splash-tempo, 1)) linear,
              transform calc(460ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1));
}
/* A touch of horizontal scale reads as tracking settling; kept small enough
   that it never looks like stretched type. */
.lg-tag {
  opacity: 0;
  transform-origin: 0% 50%;
  transform: translateY(7px) scaleX(1.035);
  transition: opacity calc(380ms * var(--splash-tempo, 1)) linear,
              transform calc(520ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1));
}

/* --- stage by stage ------------------------------------------------------ */
.is-nodes .lg-node { opacity: 1; transform: scale(1); }
.is-nodes .lg-node--blue   { transition-delay: calc(90ms * var(--splash-tempo, 1)); }
.is-nodes .lg-node--violet { transition-delay: calc(190ms * var(--splash-tempo, 1)); }

.is-structure .lg-s { opacity: 1; }
.is-structure .lg-s--1 { transform: scaleY(1); }
.is-structure .lg-s--3 { transform: scaleY(1); transition-delay: calc(90ms * var(--splash-tempo, 1)); }
.is-structure .lg-s--2 { transform: scaleX(1); transition-delay: calc(170ms * var(--splash-tempo, 1)); }

.is-curve .lg-d { opacity: 1; transform: scaleX(1); }
.is-curve .lg-trace { animation: lgTrace calc(720ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1)) forwards; }
@keyframes lgTrace {
  0%   { opacity: 0;  stroke-dashoffset: 8; }
  14%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { opacity: 0;  stroke-dashoffset: -92; }
}

/* One pulse on completion, then nothing. No permanent ring. */
.is-pulse .splash-logo { animation: lgPulse calc(760ms * var(--splash-tempo, 1)) var(--ease-out-premium, cubic-bezier(.16,1,.3,1)); }
@keyframes lgPulse {
  0%   { transform: scale(1); }
  32%  { transform: scale(1.018); }
  100% { transform: scale(1); }
}

.is-wordmark .lg-word--define { opacity: 1; clip-path: inset(-12% -2% -12% -2%); }
.is-wordmark .lg-word--it { opacity: 1; transform: translateX(0); transition-delay: calc(130ms * var(--splash-tempo, 1)); }

.is-tagline .lg-tag { opacity: 1; transform: translateY(0) scaleX(1); }
.is-tagline .lg-tag--def { transition-delay: calc(130ms * var(--splash-tempo, 1)); }

/* ---------------------------------------------------------------------------
   Status line
   Corporate, not console. One word at a time, each fading through, over a
   hairline that fills as the sequence progresses.
   --------------------------------------------------------------------------- */
.splash__status {
  position: absolute;
  left: 50%;
  bottom: clamp(38px, 9vh, 84px);
  transform: translateX(-50%);
  z-index: 3;
  display: grid;
  gap: 10px;
  justify-items: center;
  width: min(280px, 62vw);
  opacity: 0;
  animation: splashGridIn 500ms var(--ease, cubic-bezier(.4,0,.2,1)) 260ms forwards;
}

.splash__status-word {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(190, 214, 240, 0.5);
  height: 1em;
  line-height: 1;
  white-space: nowrap;
}
/* Each word is stacked in the same cell and revealed in turn by script, so
   the line never reflows and never shifts the logo above it. */
.splash__status-word span {
  display: block;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 240ms var(--ease, cubic-bezier(.4,0,.2,1)),
              transform 240ms var(--ease, cubic-bezier(.4,0,.2,1));
}
.splash__status-word span.is-on { opacity: 1; transform: none; }

.splash__rail {
  position: relative;
  width: 100%;
  height: 1px;
  background: rgba(140, 180, 225, 0.16);
  overflow: hidden;
}
.splash__rail::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 100%;
  background: linear-gradient(90deg, rgba(0, 174, 239, 0), #00AEEF 45%, #4B7BFF);
  transform: scaleX(0);
  transform-origin: 0 50%;
  animation: splashRail var(--splash-rail-dur, 2400ms) var(--ease, cubic-bezier(.4,0,.2,1)) forwards;
}
@keyframes splashRail { to { transform: scaleX(1); } }

/* ---------------------------------------------------------------------------
   Skip
   Only shown once the sequence has run long enough to be worth escaping, but
   always in the DOM and always focusable — a control that appears on a timer
   must not appear in the tab order on a timer.
   --------------------------------------------------------------------------- */
.splash__skip {
  position: absolute;
  right: clamp(16px, 3vw, 34px);
  bottom: clamp(16px, 3vh, 30px);
  z-index: 4;
  appearance: none;
  background: transparent;
  border: 1px solid rgba(140, 180, 225, 0.22);
  border-radius: 999px;
  padding: 7px 15px;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(200, 222, 245, 0.62);
  cursor: pointer;
  opacity: 0;
  transition: opacity 320ms var(--ease, cubic-bezier(.4,0,.2,1)),
              color 200ms, border-color 200ms;
}
.splash__skip.is-on { opacity: 1; }
.splash__skip:hover,
.splash__skip:focus-visible {
  opacity: 1;
  color: #EAF6FF;
  border-color: rgba(0, 174, 239, 0.6);
}
/* Focus must reveal it even before the timer would have. */
.splash__skip:focus-visible {
  outline: 2px solid #00AEEF;
  outline-offset: 3px;
}

/* ---------------------------------------------------------------------------
   Mobile
   The lockup keeps its proportions and simply gets smaller; the background
   loses density rather than detail. Tested at 430 / 390 / 360.
   --------------------------------------------------------------------------- */
@media (max-width: 560px) {
  .splash__stage { width: 92vw; padding: 0 14px; }
  .splash__grid { background-size: 48px 48px; }
  .splash__status { bottom: clamp(30px, 8vh, 60px); width: min(220px, 68vw); }
  .splash__skip { right: 14px; bottom: 14px; padding: 6px 12px; }
}

/* ---------------------------------------------------------------------------
   Reduced motion
   Not the same sequence slowed down — a different, much shorter one. The
   finished lockup, held briefly, then out. No particles, no path drawing,
   no packets; script also skips starting the canvas at all.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .splash__grid,
  .splash__status { animation: none; opacity: 1; }
  .splash__rail::after { animation: none; transform: scaleX(1); }
  .define-splash { transition-duration: 260ms; }
  .define-splash.is-leaving { transform: none; }
}

/* The finished lockup, with no part-by-part build, for both the reduced-motion
   path and a same-session refresh. Every part is forced to its resting value
   so the mark is the approved artwork with nothing in flight. */
.define-splash[data-mode="reduced"] .lg-node,
.define-splash[data-mode="reduced"] .lg-s,
.define-splash[data-mode="reduced"] .lg-d,
.define-splash[data-mode="reduced"] .lg-word,
.define-splash[data-mode="reduced"] .lg-tag {
  transition: none !important;
  animation: none !important;
  opacity: 1;
  transform: none;
}
.define-splash[data-mode="reduced"] .lg-trace,
.define-splash[data-mode="short"] .lg-trace { display: none; }
.define-splash[data-mode="reduced"] .splash-logo { animation: none !important; }
