/* ===========================================================================
   BRAND INTRO (.bsi-*) — the octagon draws itself, the wordmark lands, and the
   mark flies into the header logo so the splash BECOMES the site chrome.

   THE CONSTRAINT THIS IS BUILT AROUND. /odds was 20-25s per request until the
   cache bug was fixed on 2026-08-11; it now answers in ~0.2s, and the owner's
   words were "as fast and clean as possble everytime". A 2-3s curtain on every
   page load would hand that back. So:

     * it plays ONCE PER SESSION, not per navigation (sessionStorage)
     * it plays only on the odds landing, which is where the site opens
     * it NEVER blocks — the page renders behind it and is already interactive
     * any click, key, scroll or touch dismisses it instantly
     * prefers-reduced-motion gets no animation at all
     * with JavaScript off it never appears (the html class is set by script)

   The overlay is display:none by default and only revealed by .bsi-on on
   <html>, which an inline head script sets BEFORE first paint. Injecting it
   later would flash the page and then slam a curtain over it, which is worse
   than having no intro.
   =========================================================================== */

.bsi { display: none; }

.bsi-on .bsi {
  display: grid; place-items: center;
  position: fixed; inset: 0; z-index: 9999;
  background:
    radial-gradient(760px 420px at 50% 44%, #0d1a24 0%, #0a0e14 62%),
    var(--bg, #0a0e14);
  /* The exit is driven from JS (.bsi-fly), not a CSS delay, so the backdrop
     fade and the logo hand-off are one synchronised move. A fixed CSS delay
     here fought the FLIP: the veil cleared before the mark had flown. */
  transition: opacity .5s ease, visibility .5s;
}
.bsi.bsi-fly { opacity: 0; visibility: hidden; }

/* the site itself must not scroll under the veil while it is up */
.bsi-on body { overflow: hidden; }

/* position:relative anchors the light sweep, which is absolutely placed over
   the mark rather than being a child of the SVG (so it can cross the whole
   lockup, glyphs included). */
.bsi-stage { display: grid; place-items: center; gap: 22px; position: relative; }

/* ---- the mark ------------------------------------------------------------ */
.bsi-mark { width: min(168px, 38vw); height: auto; display: block; overflow: visible; }

/* The octagon uses pathLength="1", so the dash maths is exact regardless of
   the real perimeter — no hardcoded 1498.1 to drift when the art is redrawn. */
.bsi-ring {
  stroke-dasharray: 1; stroke-dashoffset: 1;
  animation: bsi-draw .78s cubic-bezier(.22,.75,.3,1) .06s forwards;
  filter: drop-shadow(0 0 14px #00e5a055);
}
@keyframes bsi-draw { to { stroke-dashoffset: 0; } }

/* Wordmark glyphs rise into place after the ring closes. Staggered by row so
   BOUT lands, then SCOUT — the same two-beat rhythm as the logo's own stack. */
.bsi-g { opacity: 0; animation: bsi-rise .34s cubic-bezier(.2,.7,.3,1) forwards; }
.bsi-r1 { animation-delay: .50s; }
.bsi-r2 { animation-delay: .62s; }
.bsi-div { opacity: 0; animation: bsi-fade .3s ease .58s forwards; }
@keyframes bsi-rise {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
@keyframes bsi-fade { to { opacity: .38; } }

/* a single sweep of light across the mark, once, as it completes */
.bsi-sweep {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(105deg, transparent 38%, #00e5a038 50%, transparent 62%);
  animation: bsi-sweep .55s ease-out .60s forwards;
}
@keyframes bsi-sweep {
  0%   { opacity: 0; transform: translateX(-38%); }
  35%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(38%); }
}

/* ---- the line under it --------------------------------------------------- */
.bsi-cap {
  font: 700 10px var(--mono, monospace); letter-spacing: .26em;
  text-transform: uppercase; color: var(--dim, #7d8ca1);
  opacity: 0; animation: bsi-fade1 .3s ease .70s forwards;
  text-align: center;
}
@keyframes bsi-fade1 { to { opacity: 1; } }

.bsi-bar {
  width: min(168px, 38vw); height: 2px; border-radius: 999px;
  background: #16202c; overflow: hidden;
}
.bsi-bar i {
  display: block; height: 100%; width: 100%; border-radius: 999px;
  transform-origin: 0 50%; transform: scaleX(0);
  background: linear-gradient(90deg, #00e5a055, #00e5a0);
  /* Runs to ~2.0s so it is still filling through the longer hold and lands
     just before the hand-off at 2.5s (intro.js HOLD). A bar that completes
     early and then waits reads as stalled, not loading. */
  animation: bsi-fill 1.95s cubic-bezier(.3,.7,.4,1) .06s forwards;
}
@keyframes bsi-fill { to { transform: scaleX(1); } }

/* ---- the hand-off -------------------------------------------------------- */
/* The mark does not simply fade: it flies to the header logo's real measured
   position, so the splash resolves INTO the site rather than being wiped off
   it. The transform is set inline by intro.js (FLIP), because the header
   logo's box differs between the desktop and mobile bars. */
.bsi.bsi-fly .bsi-mark {
  transition: transform .56s cubic-bezier(.5,.05,.2,1), opacity .56s ease;
}
/* `animation: none` is load-bearing, not tidying. .bsi-cap ends on a forwards
   fill, and a filled animation outranks normal author rules AND inline styles
   in the cascade — so the opacity:0 below was silently doing nothing until the
   animation was cancelled first. Caught by pinning a mid-flight frame. */
.bsi.bsi-fly .bsi-cap,
.bsi.bsi-fly .bsi-bar {
  animation: none; transition: opacity .18s ease; opacity: 0;
}

/* ---- opt-outs ------------------------------------------------------------ */
/* Motion sensitivity is not a nice-to-have: a full-screen animated takeover is
   exactly the pattern that triggers it. No animation, no veil, no delay. */
@media (prefers-reduced-motion: reduce) {
  .bsi-on .bsi { display: none; }
  .bsi-on body { overflow: auto; }
}
