/* ===================================
   TEAMSTERX LOADER
   A blue snake travels the logo's outline, endlessly, leaving a trail that
   fades out behind it. Only the snake and its trail are ever drawn — the rest
   of the outline is not — and the snake's own body holds one flat intensity.
   Every dash pattern is exactly one path-length long, so the lap repeats with
   no seam and the snake never closes onto its own tail.
   Tokens fall back to brand values so the very first paint
   is correct before styles.css (:root) has downloaded.
   =================================== */

.teamsterx-loader {
    position: fixed;
    inset: 0;
    background: var(--bg-body, #F5F5F7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 99999;
    transition: opacity 0.45s ease-out, visibility 0.45s ease-out;
}

/* Theme: dark-mode is applied to <html> by the boot script, but support <body> too. */
html.dark-mode .teamsterx-loader,
body.dark-mode .teamsterx-loader,
.teamsterx-loader.lock-dark {
    background: var(--bg-body, #000000);
}
.teamsterx-loader.lock-light {
    background: var(--bg-body, #F5F5F7);
}

.teamsterx-loader.hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

/* Vertical stack: mark + label */
.loader-stack {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 22px;
}

/* The mark holds the drawn logo. Sized here; the SVG scales inside it. */
.loader-mark {
    position: relative;
    width: 108px;
    height: 108px;
}

.loader-mark-svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

/* The snake and the trail it leaves. Every layer is the same path with a
   different dash length, all locked to one head position, so they read as a
   single body that fades out behind itself.
   Each dash pattern sums to 100 against the path's pathLength="100", and each
   offset falls by exactly 100 per cycle: that is what makes the lap seamless
   and what stops the snake ever meeting its own tail.
   Linear timing on purpose — easing would pulse once per lap and give away
   where the loop restarts. */
.loader-mark-bullet,
.loader-mark-t1,
.loader-mark-t2,
.loader-mark-t3,
.loader-mark-t4,
.loader-mark-t5,
.loader-mark-t6,
.loader-mark-t7,
.loader-mark-t8,
.loader-mark-t9,
.loader-mark-t10,
.loader-mark-t11,
.loader-mark-t12,
.loader-mark-t13,
.loader-mark-t14,
.loader-mark-t15,
.loader-mark-t16,
.loader-mark-t17,
.loader-mark-t18,
.loader-mark-t19,
.loader-mark-t20,
.loader-mark-t21,
.loader-mark-t22,
.loader-mark-t23,
.loader-mark-t24 {
    fill: none;
    stroke: var(--accent, #0070F3);
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* The snake itself: 20% of the path at one flat intensity nose to tail. */
.loader-mark-bullet {
    stroke-width: 3.35;
    stroke-dasharray: 20 80;
    stroke-dashoffset: 120;
    animation: loaderOrbit 2.6s linear infinite;
}

/* The trail: 24 stacked segments, each one band longer than the last, all
   sharing the snake's nose. They overlap, so the opacities below are NOT the
   visible steps — they are solved backwards from the alpha ramp we want, and
   at 0.83% of the path per band the ramp reads as one continuous fade
   rather than a stack. GENERATED by scripts/gen-loader-trail.mjs — rerun that
   script rather than editing the rules below. */
.loader-mark-t1 {
    stroke-width: 3.33;
    stroke-dasharray: 20.83 79.17;
    stroke-dashoffset: 120.83;
    opacity: 0.5;
    animation: loaderOrbitT1 2.6s linear infinite;
}
.loader-mark-t2 {
    stroke-width: 3.3;
    stroke-dasharray: 21.67 78.33;
    stroke-dashoffset: 121.67;
    opacity: 0.333;
    animation: loaderOrbitT2 2.6s linear infinite;
}
.loader-mark-t3 {
    stroke-width: 3.27;
    stroke-dasharray: 22.5 77.5;
    stroke-dashoffset: 122.5;
    opacity: 0.25;
    animation: loaderOrbitT3 2.6s linear infinite;
}
.loader-mark-t4 {
    stroke-width: 3.25;
    stroke-dasharray: 23.33 76.67;
    stroke-dashoffset: 123.33;
    opacity: 0.2;
    animation: loaderOrbitT4 2.6s linear infinite;
}
.loader-mark-t5 {
    stroke-width: 3.23;
    stroke-dasharray: 24.17 75.83;
    stroke-dashoffset: 124.17;
    opacity: 0.167;
    animation: loaderOrbitT5 2.6s linear infinite;
}
.loader-mark-t6 {
    stroke-width: 3.2;
    stroke-dasharray: 25 75;
    stroke-dashoffset: 125;
    opacity: 0.143;
    animation: loaderOrbitT6 2.6s linear infinite;
}
.loader-mark-t7 {
    stroke-width: 3.17;
    stroke-dasharray: 25.83 74.17;
    stroke-dashoffset: 125.83;
    opacity: 0.125;
    animation: loaderOrbitT7 2.6s linear infinite;
}
.loader-mark-t8 {
    stroke-width: 3.15;
    stroke-dasharray: 26.67 73.33;
    stroke-dashoffset: 126.67;
    opacity: 0.111;
    animation: loaderOrbitT8 2.6s linear infinite;
}
.loader-mark-t9 {
    stroke-width: 3.13;
    stroke-dasharray: 27.5 72.5;
    stroke-dashoffset: 127.5;
    opacity: 0.1;
    animation: loaderOrbitT9 2.6s linear infinite;
}
.loader-mark-t10 {
    stroke-width: 3.1;
    stroke-dasharray: 28.33 71.67;
    stroke-dashoffset: 128.33;
    opacity: 0.091;
    animation: loaderOrbitT10 2.6s linear infinite;
}
.loader-mark-t11 {
    stroke-width: 3.08;
    stroke-dasharray: 29.17 70.83;
    stroke-dashoffset: 129.17;
    opacity: 0.083;
    animation: loaderOrbitT11 2.6s linear infinite;
}
.loader-mark-t12 {
    stroke-width: 3.05;
    stroke-dasharray: 30 70;
    stroke-dashoffset: 130;
    opacity: 0.077;
    animation: loaderOrbitT12 2.6s linear infinite;
}
.loader-mark-t13 {
    stroke-width: 3.02;
    stroke-dasharray: 30.83 69.17;
    stroke-dashoffset: 130.83;
    opacity: 0.071;
    animation: loaderOrbitT13 2.6s linear infinite;
}
.loader-mark-t14 {
    stroke-width: 3;
    stroke-dasharray: 31.67 68.33;
    stroke-dashoffset: 131.67;
    opacity: 0.067;
    animation: loaderOrbitT14 2.6s linear infinite;
}
.loader-mark-t15 {
    stroke-width: 2.98;
    stroke-dasharray: 32.5 67.5;
    stroke-dashoffset: 132.5;
    opacity: 0.063;
    animation: loaderOrbitT15 2.6s linear infinite;
}
.loader-mark-t16 {
    stroke-width: 2.95;
    stroke-dasharray: 33.33 66.67;
    stroke-dashoffset: 133.33;
    opacity: 0.059;
    animation: loaderOrbitT16 2.6s linear infinite;
}
.loader-mark-t17 {
    stroke-width: 2.92;
    stroke-dasharray: 34.17 65.83;
    stroke-dashoffset: 134.17;
    opacity: 0.056;
    animation: loaderOrbitT17 2.6s linear infinite;
}
.loader-mark-t18 {
    stroke-width: 2.9;
    stroke-dasharray: 35 65;
    stroke-dashoffset: 135;
    opacity: 0.053;
    animation: loaderOrbitT18 2.6s linear infinite;
}
.loader-mark-t19 {
    stroke-width: 2.88;
    stroke-dasharray: 35.83 64.17;
    stroke-dashoffset: 135.83;
    opacity: 0.05;
    animation: loaderOrbitT19 2.6s linear infinite;
}
.loader-mark-t20 {
    stroke-width: 2.85;
    stroke-dasharray: 36.67 63.33;
    stroke-dashoffset: 136.67;
    opacity: 0.048;
    animation: loaderOrbitT20 2.6s linear infinite;
}
.loader-mark-t21 {
    stroke-width: 2.83;
    stroke-dasharray: 37.5 62.5;
    stroke-dashoffset: 137.5;
    opacity: 0.045;
    animation: loaderOrbitT21 2.6s linear infinite;
}
.loader-mark-t22 {
    stroke-width: 2.8;
    stroke-dasharray: 38.33 61.67;
    stroke-dashoffset: 138.33;
    opacity: 0.043;
    animation: loaderOrbitT22 2.6s linear infinite;
}
.loader-mark-t23 {
    stroke-width: 2.77;
    stroke-dasharray: 39.17 60.83;
    stroke-dashoffset: 139.17;
    opacity: 0.042;
    animation: loaderOrbitT23 2.6s linear infinite;
}
.loader-mark-t24 {
    stroke-width: 2.75;
    stroke-dasharray: 40 60;
    stroke-dashoffset: 140;
    opacity: 0.04;
    animation: loaderOrbitT24 2.6s linear infinite;
}

/* Each offset drops by exactly one path-length, so the pattern lands back on
   itself: no seam, no start frame, no end frame. The start values differ by
   each layer's dash length, which is what keeps them sharing one nose. */
@keyframes loaderOrbit {
    from { stroke-dashoffset: 120; }
    to   { stroke-dashoffset: 20; }
}
@keyframes loaderOrbitT1 {
    from { stroke-dashoffset: 120.83; }
    to   { stroke-dashoffset: 20.83; }
}
@keyframes loaderOrbitT2 {
    from { stroke-dashoffset: 121.67; }
    to   { stroke-dashoffset: 21.67; }
}
@keyframes loaderOrbitT3 {
    from { stroke-dashoffset: 122.5; }
    to   { stroke-dashoffset: 22.5; }
}
@keyframes loaderOrbitT4 {
    from { stroke-dashoffset: 123.33; }
    to   { stroke-dashoffset: 23.33; }
}
@keyframes loaderOrbitT5 {
    from { stroke-dashoffset: 124.17; }
    to   { stroke-dashoffset: 24.17; }
}
@keyframes loaderOrbitT6 {
    from { stroke-dashoffset: 125; }
    to   { stroke-dashoffset: 25; }
}
@keyframes loaderOrbitT7 {
    from { stroke-dashoffset: 125.83; }
    to   { stroke-dashoffset: 25.83; }
}
@keyframes loaderOrbitT8 {
    from { stroke-dashoffset: 126.67; }
    to   { stroke-dashoffset: 26.67; }
}
@keyframes loaderOrbitT9 {
    from { stroke-dashoffset: 127.5; }
    to   { stroke-dashoffset: 27.5; }
}
@keyframes loaderOrbitT10 {
    from { stroke-dashoffset: 128.33; }
    to   { stroke-dashoffset: 28.33; }
}
@keyframes loaderOrbitT11 {
    from { stroke-dashoffset: 129.17; }
    to   { stroke-dashoffset: 29.17; }
}
@keyframes loaderOrbitT12 {
    from { stroke-dashoffset: 130; }
    to   { stroke-dashoffset: 30; }
}
@keyframes loaderOrbitT13 {
    from { stroke-dashoffset: 130.83; }
    to   { stroke-dashoffset: 30.83; }
}
@keyframes loaderOrbitT14 {
    from { stroke-dashoffset: 131.67; }
    to   { stroke-dashoffset: 31.67; }
}
@keyframes loaderOrbitT15 {
    from { stroke-dashoffset: 132.5; }
    to   { stroke-dashoffset: 32.5; }
}
@keyframes loaderOrbitT16 {
    from { stroke-dashoffset: 133.33; }
    to   { stroke-dashoffset: 33.33; }
}
@keyframes loaderOrbitT17 {
    from { stroke-dashoffset: 134.17; }
    to   { stroke-dashoffset: 34.17; }
}
@keyframes loaderOrbitT18 {
    from { stroke-dashoffset: 135; }
    to   { stroke-dashoffset: 35; }
}
@keyframes loaderOrbitT19 {
    from { stroke-dashoffset: 135.83; }
    to   { stroke-dashoffset: 35.83; }
}
@keyframes loaderOrbitT20 {
    from { stroke-dashoffset: 136.67; }
    to   { stroke-dashoffset: 36.67; }
}
@keyframes loaderOrbitT21 {
    from { stroke-dashoffset: 137.5; }
    to   { stroke-dashoffset: 37.5; }
}
@keyframes loaderOrbitT22 {
    from { stroke-dashoffset: 138.33; }
    to   { stroke-dashoffset: 38.33; }
}
@keyframes loaderOrbitT23 {
    from { stroke-dashoffset: 139.17; }
    to   { stroke-dashoffset: 39.17; }
}
@keyframes loaderOrbitT24 {
    from { stroke-dashoffset: 140; }
    to   { stroke-dashoffset: 40; }
}

@keyframes loaderDots {
    0%   { content: ''; }
    25%  { content: '.'; }
    50%  { content: '..'; }
    75%  { content: '...'; }
    100% { content: ''; }
}

/* Reduced motion: with the animation suppressed, a lone frozen snake says
   nothing, so drop the dash pattern and let one element render the whole
   outline as a still mark. The trail has no meaning standing still. */
@media (prefers-reduced-motion: reduce) {
    .loader-mark-bullet {
        animation-name: none;
        stroke-dasharray: none;
        stroke-width: 2.8;
        filter: none;
    }
    .loader-mark-t1,
    .loader-mark-t2,
    .loader-mark-t3,
    .loader-mark-t4,
    .loader-mark-t5,
    .loader-mark-t6,
    .loader-mark-t7,
    .loader-mark-t8,
    .loader-mark-t9,
    .loader-mark-t10,
    .loader-mark-t11,
    .loader-mark-t12,
    .loader-mark-t13,
    .loader-mark-t14,
    .loader-mark-t15,
    .loader-mark-t16,
    .loader-mark-t17,
    .loader-mark-t18,
    .loader-mark-t19,
    .loader-mark-t20,
    .loader-mark-t21,
    .loader-mark-t22,
    .loader-mark-t23,
    .loader-mark-t24 {
        display: none;
    }
}

/* Mobile */
@media (max-width: 480px) {
    .loader-mark { width: 88px; height: 88px; }
}
