/* =================================================================== PAGE LOADER — full-screen, ASCII progress bar (global css) ===================================================================

   Built to `loader.pdf`, one 1920x1080 artboard. Every number below was read
   out of that file's content stream or measured off its ink extents at 1:1,
   never off a screenshot:

     wordmarks     wliq  x100 -> 778  (679 x 124), managedcoder x1027 -> 1469
                   (443 x 112), both centred on y161.5, divider at x~894,
                   135 tall. That is 3.10x the header's marks, exactly.
     ASCII bar     x100 -> 1410 (1311 wide), y860 -> 979 (120 tall)
                   ELEVEN rows, row pitch 11.24, ~230 columns
     percentage    x1537 -> 1834, y861 -> 976 — 160px, the display face, and
                   the PDF's own TJ kerning is -0.0298em, i.e. --track-display
     colours       background 12/255 = --ink exactly; marks and the number pure
                   --white; the bar's glyphs peak at 160/255 over ink, which is
                   white at .6 = --muted-invert

   ⚠️ THE BAR'S TWO HALVES ARE THE SAME COLOUR. Measured: fill peaks at 160,
   track at 143, and the 17 is thin-stroke antialiasing, not opacity — `-+=`
   simply have less ink than `%#&`. So progress here is carried by CHARACTER
   DENSITY, not by brightness, which is the same thing §8.0.5 says about the
   drawings: the image IS the density. Do not "help" it with two opacities.

   assets/js/global/loader.js drives it. The two files are a SET: this one
   holds the overlay off the page until `html.js` is present, so with JS off
   the loader never paints and cannot strand a visitor behind it.
   =================================================================== */

.loader {
    /* Above --z-cursor (400), which is the highest thing vendor.css defines.
       Local because vendor.css is closed (CLAUDE.md §5.2). */
    --z-loader: 500;

    --loader-mark-h: 40px;         /* wliq, measured */
    --loader-mark-partner-h: 36px; /* managedcoder, measured */
    --loader-pct: 160px;
    --loader-char: 10px;            /* the bar's font-size */
    --loader-row: 1.124;            /* 11.24 / 10 — the measured row pitch */

    position: fixed;
    inset: 0;
    z-index: var(--z-loader);
    display: none;                  /* html.js turns it on, see below */
    flex-direction: column;
    justify-content: space-between;
    padding: var(--sp100) var(--gutter);
    padding-top: 37px;
    background: var(--ink);
    /* it is doing the same job as a dialog, so it takes the whole surface */
    overscroll-behavior: contain;
    transition: opacity var(--ease-out), visibility var(--ease-out);
}

/* ⚠️ Only ever shown when JS is running. A full-screen overlay that nothing
   can dismiss is the worst failure mode this file has, so it is switched on by
   the same `html.js` flag the header uses (set by the inline head script, so it
   cannot flash) rather than being visible by default. */
html.js .loader { display: flex; }

/* The dismissal. `visibility` rather than `display` so the fade can run, and
   `pointer-events` released on the same frame so a fast reader is never
   clicking through a ghost. */
.loader.is-done {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

/* Scroll lock while it is up — the same job vendor.js's Lenis stop does for
   the request popup (§12.4), on the element rather than the library so it
   holds with Lenis absent (reduced motion). */
html.is-loading, html.is-loading body { overflow: hidden; }

.loader__brands {
    display: flex;
    align-items: center;
    /* measured 116 and 133 either side of the divider; one token covers both */
    gap: var(--sp30);
}

.loader__logo {
    display: block;
    width: auto;
    height: var(--loader-mark-h);
}

.loader__logo--partner { height: var(--loader-mark-partner-h); }

/* 135 tall on the artboard, 1px of --line-invert like the header's */
.loader__divider {
    flex: none;
    width: 1px;
    height: 40px;
    background: var(--line-invert);
}

/* The bar and the number share a bottom edge on the artboard — bar to y979,
   number to y976 — so the row aligns to `end`, not centre. */
.loader__progress {
    display: flex;
    align-items: end;
    gap: var(--sp120);
}

/* ⚠️ The bar is LIVE TEXT, not artwork. Every other ASCII drawing in this
   build ships as a *-glyphs.svg (§8.0.5) because it never changes; this one's
   whole job is that the boundary between the two character sets moves, so it
   has to be typeset at runtime. loader.js writes the rows.

   Monospace matters twice: the columns have to line up vertically, and the
   column COUNT is derived from one character's measured width so the block
   fills the box exactly at any width. (The artboard's own glyphs are
   proportional — advances of 4.13 to 7.27 — which no live text can reproduce
   and which a progress bar does not want anyway.) */
.loader__bar {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--loader-char);
    line-height: var(--loader-row);
    color: var(--muted-invert);
    white-space: pre;
    overflow: hidden;
    /* ⚠️ The rows arrive from JS, so the <pre> starts empty and 0 tall. This
       reserves the block's exact height — 11 rows of the measured pitch — so
       the row does not jump when they land. */
    min-height: calc(var(--loader-char) * var(--loader-row) * 11);
    /* it says nothing the percentage does not */
    user-select: none;
}

/* ⚠️ THE FIELD IS RESERVED FOR THE WIDEST VALUE IT WILL EVER HOLD, and that
   is not a nicety. `max-content` was the first cut, and because the bar beside
   it is `flex: 1` the whole bar stretched every time the number gained a digit
   — measured at 160px with --track-display and tabular figures: "8%" is 210.7,
   "99%" 304.5, "100%" 398.3, so the bar jumped twice on the way up, by 94px
   each time. 2.49em is that 398.3 at 160px, so the box is fixed in ems and
   holds at every step of --loader-pct. Right-aligned, so the digits grow
   leftward into the reserved space and the number's right edge never moves.

   ⚠️ The cost, stated: the artboard draws the bar 1311 wide with the number's
   ink 298 (it shows "99%", a two-digit frame). Reserving three digits leaves
   the bar 1202 at 1920 — 109 narrower than the drawing. Matching the artboard
   exactly means either a bar that resizes as the number counts, or one that
   the number overlaps at 100%; a fixed field is worth 109px. */
.loader__pct {
    flex: none;
    min-width: 2.49em;
    text-align: right;
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--loader-pct);
    font-weight: var(--fw-display);
    line-height: 1;
    letter-spacing: var(--track-display);
    color: var(--text-invert);
    /* the digits change every few frames; without this the whole row reflows
       as 1 becomes 8 */
    font-variant-numeric: tabular-nums;
    /* ⚠️ `align-items: end` aligns the BOXES, and a digits-only string leaves
       the descender space of a `line-height: 1` box empty — measured, the
       number's ink floated 20px above the bar's last row where the artboard
       has them flush (ink to y976 and y979). This pulls the box down by that
       gap. In em, so it holds at every step of --loader-pct. */
    margin-bottom: -0.125em;
}

/* =================================================================== responsive =================================================================== */
/* The artboard is 1920. Both the marks and the number are display-scale, so
   they step down hard, the way --f100 does (§7.1). The bar keeps its 10px
   type at every width — it is a texture, and shrinking it turns it to mush;
   what changes is how many columns fit, which loader.js measures. */
@media (max-width: 1599.98px) {
    .loader {
        --loader-mark-h: 100px;
        --loader-mark-partner-h: 90px;
        --loader-pct: 130px;
    }
    .loader__divider { height: 110px; }
}

@media (max-width: 1365.98px) {
    .loader {
        --loader-mark-h: 84px;
        --loader-mark-partner-h: 76px;
        --loader-pct: 110px;
    }
    .loader__divider { height: 92px; }
}

@media (max-width: 1199.98px) {
    .loader {
        --loader-mark-h: 70px;
        --loader-mark-partner-h: 63px;
        --loader-pct: 90px;
    }
    .loader__divider { height: 76px; }
}

@media (max-width: 991.98px) {
    .loader {
        --loader-mark-h: 56px;
        --loader-mark-partner-h: 50px;
        --loader-pct: 72px;
    }
    .loader__brands { gap: var(--sp60); }
    .loader__divider { height: 60px; }
}

@media (max-width: 767.98px) {
    .loader {
        --loader-mark-h: 44px;
        --loader-mark-partner-h: 40px;
        --loader-pct: 56px;
    }
    .loader__brands { gap: var(--sp40); }
    .loader__divider { height: 48px; }
    .loader__progress { gap: var(--sp40); }
}

@media (max-width: 575.98px) {
    .loader {
        --loader-mark-h: 30px;
        --loader-mark-partner-h: 27px;
        --loader-pct: 44px;
    }
    .loader__brands { gap: var(--sp20); }
    .loader__divider { height: 34px; }
    .loader__progress { gap: var(--sp20); }
}

/* Not decoration — it is the thing standing between the reader and the page —
   so it still appears and still dismisses. What comes off is the ramp: the
   number goes straight to each milestone instead of easing there, and the
   fade becomes a cut. loader.js reads the same query. */
@media (prefers-reduced-motion: reduce) {
    .loader { transition: none; }
}
