/* ==========================================================================
   loader.css — the intro loader.

   Layout is Figma node 14395:24530 ("Use this"), a 1920x1080 frame, taken
   verbatim. Every left/top below is that frame's value, unscaled:

     logo             left 0    top 0     30 x 30
     "design is dead" left 200  top 11
     white rule       left 346  top 10    5 x 20
     "who killed it?" left 355  top 11    gap 8
     roles            left 200  top 142   gap 6, w 91
     "*** / loading / 2026"   left 200  top 523   gap 10, w 119
     "14"             left 200  bottom 200
     "do not enjoy"   left 288  bottom 200
     red line         left 0    bottom 0  3px tall

   The type is the SITE'S system, inverted: PP Supply Sans, 10px,
   line-height .5, letter-spacing -0.4px, uppercase, left:200px — the same
   values .col already uses in ui.css, which is why the loader's copy sits on
   exactly the grid the page underneath it uses.
   ========================================================================== */

:root{
  /* The site's transition easing, as a CSS variable so the loader can use it
     without GSAP. Identical curve to CustomEase "osmo" registered in
     js/transition.js: 0.625, 0.05, 0, 1. */
  --ease-osmo: cubic-bezier(0.625, 0.05, 0, 1);

  /* The site's other easing — the one .anim-line already uses in ui.css. Its
     fast start is what the flood and the cover need: `osmo` is deliberately
     slow off the mark (0.625, 0.05), which is lovely for copy gathering speed
     as it leaves, and useless for a panel that has to take the screen. With
     osmo on the flood the red had barely lifted off the bottom before the
     white started covering it, and the whole step went unseen. Measured. */
  --ease-out: cubic-bezier(.22, .61, .36, 1);

  --loader-ink: #ffffff;
  --loader-ground: #000000;
}

/* ---------------- the layer ------------------------------------------------
   z-index 900: above the page and above the page-transition overlay (100),
   below the film grain (999) so the grain rides over the loader exactly as it
   rides over everything else. */
.loader{
  position: fixed;
  inset: 0;
  z-index: 900;
  display: none;
  background: var(--loader-ground);
  color: var(--loader-ink);
  font-size: 10px;
  line-height: .5;
  letter-spacing: -0.4px;
  text-transform: uppercase;
  overflow: hidden;

  /* It SHOULD swallow input: it is an opaque full-screen layer over a page
     that is not ready to be used yet. What it must not do is inherit the
     cursor of whatever sits underneath — the sphere viewport sets `grab`, so
     the loader was showing a drag cursor over a screen with nothing to drag. */
  cursor: default;
}
html.is-loading .loader{ display: block; }

.loader p{ margin: 0; }

/* ---------------- copy blocks ----------------------------------------------
   Every block is its own transform context so the exit can stagger them
   independently. translate3d keeps the move on the compositor. */
.loader__block{
  position: absolute;
  will-change: transform;
}
.loader{ --exit-x: calc(100vw - 526px); }
.loader.is-exiting .loader__block{
  /* Figma 14395:24636 is the END state: every block moves from left:200 to
     left:1594 on the 1920 frame — one uniform +1394px shift, and it STAYS
     there. It is a move across the screen, not an exit off it.

     1394 = 1920 - 526, where 526 is the 200px start inset plus the 119px
     status block plus the 207px it ends up from the right edge. Expressed
     against 100vw so the same relationship holds at any width. */
  transform: translate3d(var(--exit-x), 0, 0);
  transition: transform var(--exit-dur, 1.1s) var(--ease-osmo);
  transition-delay: var(--exit-delay, 0s);
}

/* ---------------- logo -----------------------------------------------------
   Figma 14395:24560: left 0, top 0, 30x30. Hard into the corner, against both
   edges — NOT centred. The supplied Logo.svg, whole: its two paths are
   interlocking hooks that each cross the centre line, so it must never be
   clipped or split into halves.

   It blurs in with the copy but does NOT carry .loader__block, so it takes no
   part in the exit and holds the corner while everything else leaves. */
.loader__logo{
  position: absolute;
  left: 0; top: 0;
  width: 30px; height: 30px;
  background: url('../assets/logo.svg') 50% 50% / contain no-repeat;
}

/* ---------------- top-left copy --------------------------------------------
   The frame carries a SECOND "design is dead" at top 78. That was a duplicate
   left in the Figma by mistake, confirmed by the client — it is not in the
   markup. */
.loader__dead1{ left: 200px; top: 11px; width: 71px; }

.loader__rule{
  left: 346px; top: 10px;
  width: 5px; height: 20px;
  background: var(--loader-ink);
}
.loader__lines{
  left: 355px; top: 11px; width: 71px;
  display: flex; flex-direction: column; gap: 8px;
}

/* ---------------- the roles ------------------------------------------------
   Anchored to the TOP, 100px below the "***" that ends the "who killed it?"
   block: that block runs 11 -> 42 (three 5px lines, gap 8), so 42 + 100 = 142.

   Figma exports this as `top: calc(50% - 346px)` with a -50% translate, i.e.
   centred on y194 — which is the same 142 on a 1080-tall frame and drifts
   everywhere else. On a short window it walked straight up into the top-left
   copy and the two blocks rendered on top of each other. Anchoring to the top
   ties it to the block it belongs under, at any viewport height. */
.loader__roles{
  left: 200px;
  top: 142px;
  width: 91px;
  display: flex; flex-direction: column; gap: 6px;
  white-space: nowrap;
}

/* ---------------- status ---------------------------------------------------- */
.loader__status{
  left: 200px; top: 523px; width: 119px;
  display: flex; flex-direction: column; gap: 10px;
}

/* ---------------- bottom pair ----------------------------------------------
   Figma exports "do not enjoy" as left: calc(50% - 672px) — a centre
   constraint on a 1920 frame. That resolves to 288px there and to a NEGATIVE
   number on anything narrower than 1344px, which would put it off-screen.
   Pinned to the 288px it was drawn at instead, which holds the designed 88px
   gap from the counter at every width. */
.loader__pct{
  left: 200px; bottom: 200px; width: 60px;
  font-variant-numeric: tabular-nums;
}
.loader__enjoy{ left: 288px; bottom: 200px; white-space: nowrap; }

/* ---------------- progress line --------------------------------------------
   Figma 14395:24533: left 0, bottom 0, height 3px. Width tracks the
   percentage; at 100% it grows upward and floods the screen.

   The fill is the site's --ui-red (#e8271c) rather than the frame's literal
   `red` (#ff0000) — the brief was to reuse the site's existing colours, and
   #e8271c is the red the rail's active tab and the copied-email state already
   use. One token to change if the pure red was deliberate. */
.loader__line{
  position: absolute;
  left: 0; bottom: 0;
  height: 3px;
  width: 0;
  background: var(--ui-red, #e8271c);
  will-change: width, height;
}
.loader.is-flooding .loader__line{
  height: 100%;
  transition: height .65s var(--ease-out);
}

/* ---------------- the white ------------------------------------------------
   Step 4's second half: while the red is still going up, the white goes over
   it. It carries .stage's gradient verbatim, so at the moment the loader is
   removed the real page behind it is pixel-identical and there is no seam —
   the hand-off is invisible and the page's own reveal simply begins.

   background-size / -position are pinned to the viewport so the gradient does
   not slide or restretch as the panel's own height animates; without them the
   ramp is recomputed every frame against a growing box and the white visibly
   shifts tone as it rises. */
.loader__white{
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 0;
  background: linear-gradient(to bottom, var(--c-white, #fff) 0%, var(--c-fog, #afafaf) 162.59%)
              bottom center / 100vw 100vh no-repeat;
  will-change: height;
}
.loader.is-covering .loader__white{
  height: 100%;
  transition: height .55s var(--ease-out);
}

/* ---------------- holding the page's own animations ------------------------
   The page mounts and builds behind the loader — that is the point, the wheel
   is warm and its photos are decoded by the time it appears. But its REVEAL
   animations would also have played out behind the black, so they are paused
   until hand-off and released in js/loader.js. Pausing (rather than delaying)
   means they resume from frame zero at exactly the right moment. */
html.is-loading [data-barba="container"] .anim-line{ animation-play-state: paused; }

/* Scoped to the container ON PURPOSE. `html.is-loading .anim-line` looks
   equivalent and is not: the loader's own copy uses .anim-line too — that is
   the whole point, step 1 is the site's existing reveal — so the unscoped
   selector pauses the loader's own text at opacity 0 and the loader renders
   as an empty black screen with a red line. Measured; do not "simplify". */

/* ---------------- grain: OFF during the loader ------------------------------
   The loader used to carry its own inverted grain — `screen` instead of
   `overlay`, because overlay barely moves the dark end and against black it
   did nothing, which is why the loader first looked like it had none at all.
   That whole override is gone: the loader is a clean black ground now.

   Hidden rather than tuned to zero, so it costs no compositing while the
   intro runs, and scoped to html.is-loading so the site's own grain is
   untouched and returns the instant the class comes off. */
html.is-loading .grain{ display: none; }
@media (prefers-reduced-motion: reduce){
  /* The head script does not set is-loading at all under reduced motion, so
     this is belt and braces: no loader, no hand-off, straight to the page. */
  .loader{ display: none !important; }
}
