/* Vulogic - page loader ("container doors").
   The screen is a pair of charcoal container doors. The brush-stroke V paints itself on the left
   leaf, the divider of the logo lockup draws in as the seam between the leaves, and VULOGIC /
   FREIGHT slide out from behind it. Below, the little container truck from the header drives a
   route line that fills as the page loads. When the page is ready the seam runs crimson from top
   to bottom and the doors part, each leaf carrying its half of the logo off screen.

   Timeline (loader.js adds .vl-js, then .is-driving, then .is-leaving when the page is ready):
     0.00s  seam draws out from its centre            0.15s  V paints: left stroke down, right up
     0.20s  route line draws, truck rolls in          0.50s  VULOGIC slides out of the seam
     0.62s  FREIGHT follows                           1.20s  earliest exit (hard cap 1.9s)
     exit:  +0.00s truck finishes the route, route fades     +0.26s seam runs crimson, top to bottom
            +0.56s doors unlatch, hold, then part (0.9s)   +0.75s page header and hero text enter
            +1.50s loader removed from the DOM
   Without JS (blocked, failed) the same exit runs from CSS alone at 2.3s and the loader is
   hidden at 4s whatever happens. With JS disabled, a <noscript> style hides it outright.
   Only transform, opacity, clip-path and stroke-dashoffset are animated. */

.vl-loader {
  --D: clamp(84px, 9vw, 128px);                /* height of the lockup divider; everything scales with it */
  --seam: calc(50% - .7455 * var(--D));        /* x of the seam: keeps the lockup optically centred */
  --cy: calc(50% - .16 * var(--D));            /* y of the divider's centre */
  --x: 2.3s;                                   /* when the exit starts: CSS fallback timing */
  --door-ease: cubic-bezier(.77, 0, .18, 1);
  --out: cubic-bezier(.16, 1, .3, 1);
  position: fixed; inset: 0; z-index: 2147483000;
  overflow: hidden; pointer-events: none;
  contain: strict;
  animation: vl-gone 0s linear 4s forwards;    /* fail-safe: never covers the page for long */
}
.vl-loader.vl-js { --x: 0s; animation-delay: 6s; }  /* JS removes it by 3.5s at the latest; this is the backstop */
@keyframes vl-gone { to { visibility: hidden; opacity: 0; } }

/* Page entrance waits behind the doors: the header drops in and the hero text rises as they open.
   The body fade-in is replaced by the loader itself. */
html.motion body { animation: none; }
html.vl-hold .header-dock,
html.vl-hold .hero-in,
html.vl-hold .pt-in,
html.vl-hold .pt-hero-media { animation-play-state: paused; }

/* ---------- Doors ---------- */
.vl-door {
  position: absolute; top: 0; bottom: 0;
  overflow: hidden; pointer-events: auto;
  --rib: calc(.5 * var(--D));                  /* corrugation pitch */
  background-color: #1e1e1e;
  /* corrugated steel, kept very quiet: soft trapezoid ribs and a little top light */
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.04), rgba(0,0,0,0) 40%, rgba(0,0,0,.3)),
    repeating-linear-gradient(90deg,
      rgba(255,255,255,.022) 0, rgba(255,255,255,.022) calc(var(--rib) * .3),
      rgba(0,0,0,.11) calc(var(--rib) * .4), rgba(0,0,0,.11) calc(var(--rib) * .72),
      rgba(255,255,255,.022) calc(var(--rib) * .82), rgba(255,255,255,.022) var(--rib));
  will-change: transform;
}
/* the leaves meet with a hairline gap */
.vl-door-l { left: 0; width: calc(var(--seam) + 1px); box-shadow: inset -1px 0 0 rgba(0,0,0,.5); }
.vl-door-r { left: var(--seam); right: 0; box-shadow: inset 1px 0 0 rgba(255,255,255,.06); }

/* ---------- The brush-stroke V (left leaf) ---------- */
.vl-v {
  position: absolute; overflow: visible;
  width: calc(.868 * var(--D)); height: calc(1.3 * var(--D));
  right: calc(.115 * var(--D)); top: calc(var(--cy) - .7 * var(--D));
}
.vl-ink { fill: #D0003F; }
.vl-brush {
  fill: none; stroke: #fff; stroke-width: 17;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 1 1; stroke-dashoffset: 1;
  animation: vl-paint .95s .15s both;
}
/* left stroke eases down into the point, then a quick confident sweep up to the hook */
@keyframes vl-paint {
  0%   { stroke-dashoffset: 1;    animation-timing-function: cubic-bezier(.45, 0, .7, .9); }
  40%  { stroke-dashoffset: .62;  animation-timing-function: cubic-bezier(.3, .2, .2, 1); }
  100% { stroke-dashoffset: 0; }
}
/* An unmasked copy settles in once the paint is done: crisp edges, and the V still appears in any
   browser that will not repaint an animated mask. */
.vl-ink-set { opacity: 0; animation: vl-fade-in .2s 1.05s forwards; }
@keyframes vl-fade-in { to { opacity: 1; } }

/* ---------- Wordmark (right leaf), slides out from behind the seam ---------- */
.vl-w { position: absolute; display: block; fill: #fff; overflow: visible; }
.vl-w1 {
  left: calc(.11 * var(--D)); top: calc(var(--cy) - .356 * var(--D));
  width: calc(2.364 * var(--D)); height: calc(.41 * var(--D));
  animation: vl-slide .9s var(--out) .5s both;
}
.vl-w2 {
  left: calc(.177 * var(--D)); top: calc(var(--cy) + .151 * var(--D));
  width: calc(1.403 * var(--D)); height: calc(.244 * var(--D));
  opacity: .92;
  animation: vl-slide .9s var(--out) .62s both;
}
@keyframes vl-slide { from { transform: translateX(calc(-100% - .25 * var(--D))); } to { transform: none; } }

/* ---------- Seam: the lockup's divider, later the crack between the doors ---------- */
.vl-seam {
  position: absolute; top: 0; bottom: 0;
  left: var(--seam); width: max(2px, calc(.03 * var(--D))); margin-left: min(-1px, calc(-.015 * var(--D)));
  background: #fff;
  clip-path: inset(calc(var(--cy) - .5 * var(--D)) 0 calc(100% - var(--cy) - .5 * var(--D)) 0);
  animation: vl-seam-in .55s var(--out) both;
}
.vl-seam::after { content: ""; position: absolute; inset: 0; background: #D0003F; opacity: 0; }
@keyframes vl-seam-in {
  from { clip-path: inset(var(--cy) 0 calc(100% - var(--cy)) 0); }
  to   { clip-path: inset(calc(var(--cy) - .5 * var(--D)) 0 calc(100% - var(--cy) - .5 * var(--D)) 0); }
}

/* ---------- Route and truck ---------- */
.vl-route {
  --tw: calc(.38 * var(--D));                 /* truck width; .38 / 3.457 = .1099 of the route */
  position: absolute; height: 2px;
  left: calc(50% - 1.7285 * var(--D)); width: calc(3.457 * var(--D));
  top: calc(var(--cy) + .98 * var(--D));
}
.vl-route::before {
  content: ""; position: absolute; inset: 0; border-radius: 2px;
  background: rgba(255, 255, 255, .16);
  transform-origin: 0 50%;
  animation: vl-line-in .7s var(--out) .2s both;
}
@keyframes vl-line-in { from { transform: scaleX(0); } }
.vl-fill {
  position: absolute; inset: 0; border-radius: 2px; background: #D0003F;
  transform-origin: 0 50%; transform: scaleX(.1099);
  animation: vl-show .3s .35s both;
}
.vl-car {
  position: absolute; left: 0; bottom: 0; width: 100%; height: 0;
  animation: vl-show .3s .35s both;
}
@keyframes vl-show { from { opacity: 0; } }
.vl-truck {
  position: absolute; left: 0; bottom: 1px;
  width: var(--tw); height: calc(var(--tw) * .375);
}
.vl-truck .truck { display: block; width: 100%; height: 100%; overflow: visible; }
.vl-loader .truck .wheel { animation: vl-wheel .5s linear infinite; }
@keyframes vl-wheel { to { transform: rotate(360deg); } }

/* CSS-only drive (no JS): steady to the end of the line */
.vl-loader:not(.vl-js) .vl-car  { animation: vl-show .3s .35s both, vl-drive 2.1s cubic-bezier(.35, .1, .3, 1) .35s both; }
.vl-loader:not(.vl-js) .vl-fill { animation: vl-show .3s .35s both, vl-fill 2.1s cubic-bezier(.35, .1, .3, 1) .35s both; }
@keyframes vl-drive { from { transform: translateX(0); } to { transform: translateX(calc(100% - var(--tw))); } }
@keyframes vl-fill  { from { transform: scaleX(.1099); } to { transform: scaleX(1); } }

/* JS drive: eases towards 82% while the page loads, then finishes the route on .is-leaving */
.vl-js .vl-car  { transition: transform 2.6s cubic-bezier(.2, .55, .3, 1); }
.vl-js .vl-fill { transition: transform 2.6s cubic-bezier(.2, .55, .3, 1); }
.vl-js.is-driving .vl-car  { transform: translateX(calc(.82 * (100% - var(--tw)))); }
.vl-js.is-driving .vl-fill { transform: scaleX(.8398); }
.vl-js.is-leaving .vl-car  { transform: translateX(calc(100% - var(--tw))); transition: transform .3s cubic-bezier(.45, 0, .25, 1); }
.vl-js.is-leaving .vl-fill { transform: scaleX(1); transition: transform .3s cubic-bezier(.45, 0, .25, 1); }

/* ---------- Exit: same choreography for the JS path (--x: 0s) and the CSS fallback (--x: 2.3s) ---------- */
.vl-loader:not(.vl-js) .vl-route,
.vl-loader.is-leaving .vl-route { animation: vl-fade-out .2s ease calc(var(--x) + .24s) forwards; }

.vl-loader:not(.vl-js) .vl-seam,
.vl-loader.is-leaving .vl-seam {
  animation: vl-seam-in .55s var(--out) both,
             vl-seam-run .32s cubic-bezier(.65, 0, .25, 1) calc(var(--x) + .26s) forwards,
             vl-fade-out .16s ease calc(var(--x) + .72s) forwards;
}
.vl-loader:not(.vl-js) .vl-seam::after,
.vl-loader.is-leaving .vl-seam::after { animation: vl-fade-in .2s ease calc(var(--x) + .22s) forwards; }
@keyframes vl-seam-run {
  from { clip-path: inset(calc(var(--cy) - .5 * var(--D)) 0 calc(100% - var(--cy) - .5 * var(--D)) 0); }
  to   { clip-path: inset(0 0 0 0); }
}
@keyframes vl-fade-out { to { opacity: 0; } }

/* the leaves unlatch (a few pixels, a short hold, crimson light in the gap), then swing clear */
.vl-loader:not(.vl-js) .vl-door-l,
.vl-loader.is-leaving .vl-door-l { animation: vl-open-l .9s linear calc(var(--x) + .56s) forwards; }
.vl-loader:not(.vl-js) .vl-door-r,
.vl-loader.is-leaving .vl-door-r { animation: vl-open-r .9s linear calc(var(--x) + .56s) forwards; }
@keyframes vl-open-l {
  0%   { transform: translateX(0);    animation-timing-function: cubic-bezier(.2, .7, .3, 1); }
  13%  { transform: translateX(-7px); animation-timing-function: linear; }
  21%  { transform: translateX(-7px); animation-timing-function: var(--door-ease); }
  100% { transform: translateX(-101%); }
}
@keyframes vl-open-r {
  0%   { transform: translateX(0);   animation-timing-function: cubic-bezier(.2, .7, .3, 1); }
  13%  { transform: translateX(7px); animation-timing-function: linear; }
  21%  { transform: translateX(7px); animation-timing-function: var(--door-ease); }
  100% { transform: translateX(101%); }
}
.vl-loader.is-leaving .vl-door { pointer-events: none; }
