/* =====================================================================
   SERRES DRIVE — css/preloader.css
   "SERRES → DRIVE" split-overlay preloader.

   Sequence: a speed wipe lays the SERRES logotype down left to right →
   DRIVE slides in to its right and completes the lockup → the lockup
   tightens → the overlay splits through its horizontal centre (top half
   peels up, bottom half peels down) and the untouched page is revealed
   underneath.

   ---------------------------------------------------------------------
   WHY THE OVERLAY SPLITS INSTEAD OF THE PAGE BEING CLIPPED
   The usual way to build this is to wrap the whole page in a container
   and animate a clip-path on it. That is not survivable here: the home
   page runs a position:fixed WebGL stage (.sd-model), a fixed backdrop,
   Lenis smooth scroll and ScrollTrigger-pinned scrollytelling. A
   transformed/clipped ancestor re-parents every fixed child and desyncs
   every trigger. So the PAGE IS NEVER TOUCHED — the overlay itself is
   two halves that peel apart. Visually identical, zero layout risk.

   ---------------------------------------------------------------------
   WHY PERCENTAGE INSETS AND NOT 50svh/50vh
   The brief asked for 50svh with a 50vh fallback for mobile URL-bar
   correctness. Percentage insets are strictly better at the same job:
   .pl is position:fixed inset:0, so it IS the visual viewport whatever
   the URL bar is doing, and bottom:50% / top:50% split that box exactly
   in two. No unit ever has to guess the viewport height, the halves can
   never fail to cover it, and — critically — the two halves can never
   disagree about where the centre is, which is what keeps the wordmark
   seamless across the seam.

   ---------------------------------------------------------------------
   NOTHING HERE CAN HIDE PAGE CONTENT
   Every selector is scoped under .pl / .pl-*, which only ever exist
   inside the overlay js/preloader.js builds at runtime. If JS is off or
   throws, this stylesheet matches nothing at all. The one exception is
   html.pl-lock (the scroll lock) — a class only preloader.js adds, and
   which it removes on every exit path including its own watchdog.

   All motion lives under .pl--css (the no-GSAP fallback). When GSAP is
   present JS drives inline transforms instead, and there must be no CSS
   transition competing with it for the same property — hence the gate.
   ===================================================================== */


/* ---------- Scroll lock ----------
   Held only while the overlay is on screen. preloader.js also calls
   lenis.stop() when Lenis happens to exist, but this class is the part
   that must work on every page, with or without it. */
html.pl-lock,
html.pl-lock body{overflow:hidden}


/* ---------- Root ----------
   z-index 9999 clears the site's ladder (.nav 60, .mobile-menu 55,
   .wa-float 50). pointer-events stay live so a click anywhere skips —
   a preloader must never be able to trap someone. */
.pl{
  position:fixed;
  inset:0;
  z-index:9999;
  pointer-events:auto;
  touch-action:none;
  user-select:none;
  -webkit-user-select:none;
  -webkit-tap-highlight-color:transparent;
  cursor:default;
}

/* Skip / bail-out fade. Deliberately NOT gated on .pl--css: it is the
   one transition that has to work in the GSAP path too, and GSAP never
   animates opacity on the root, so nothing can fight it. */
.pl--out{opacity:0;pointer-events:none;transition:opacity .28s linear}


/* ---------- The two halves ----------
   Absolute children of a fixed inset:0 parent, so both are measured
   against the identical box. box-shadow on the top half paints a 1px
   skirt of --bg just past the seam, which swallows any sub-pixel
   hairline between the two without changing either one's layout (and
   therefore without shifting the wordmark by even a fraction). */
.pl-half{
  position:absolute;
  left:0;right:0;
  overflow:hidden;
  background:var(--bg,#0a0a0b);
  will-change:transform;
  backface-visibility:hidden;
}
.pl-half--top{top:0;bottom:50%;box-shadow:0 1px 0 0 var(--bg,#0a0a0b)}
.pl-half--bot{top:50%;bottom:0}


/* ---------- Viewport-coordinate layer ----------
   The heart of the seam trick. Each half contains one .pl-vp that is
   exactly 200% of its half — i.e. exactly one viewport tall — anchored
   to the outer edge. The top half's .pl-vp therefore spans viewport
   rows 0→100% and so does the bottom half's; each is simply clipped to
   its own half. Consequence: the two halves hold IDENTICAL markup at
   IDENTICAL viewport coordinates, so anything placed at top:50% is cut
   perfectly in two by the seam, and tag words can be positioned in
   plain viewport percentages. */
.pl-vp{position:absolute;left:0;width:100%;height:200%}
.pl-half--top .pl-vp{top:0}
.pl-half--bot .pl-vp{bottom:0}


/* ---------- Lockup ----------
   Three nested transform layers, one job each, so they can never
   overwrite one another:
     .pl-lockup-pos  centres it on the seam           (translateY(-50%))
     .pl-shift       holds SERRES centred pre-DRIVE   (translateX)
     .pl-lockup      the settle/tighten               (scale) */
.pl-lockup-pos{
  position:absolute;
  left:0;right:0;
  top:50%;
  transform:translateY(-50%);
  display:flex;
  justify-content:center;
  /* --brand-h is the logotype's height and the single scale knob for the
     whole lockup, exactly as in the nav (.brand in styles.css). Everything
     else — DRIVE's size, the word gap, the measured shift — is derived from
     it, so the mark and the type can never drift out of proportion.

     The mark is ~11.2:1, far wider per unit height than the six characters
     of type it replaces, so --brand-h is much smaller than the old
     font-size: at 62px the artwork alone is ~693px across.

     font-size lives HERE, not on .pl-lockup, so that .pl-shift below
     inherits it and can express its offset in em. Because font-size is a
     fixed multiple of --brand-h, the em ratio --pl-shift depends on stays
     constant at every viewport width. See --pl-shift. */
  --brand-h:clamp(20px,5.6vw,58px);
  font-size:calc(var(--brand-h) * .82);
}
.pl-shift{
  display:flex;
  /* --pl-shift is measured at runtime = (lockup width − SERRES width)/2,
     which puts SERRES on the optical centre before DRIVE arrives.

     It is published in em, NOT px, and that is load-bearing. The offset
     is a fixed proportion of the type size, and the type size is
     viewport-relative (11vw) — so a px value measured at load is wrong
     the instant the device is rotated, leaving SERRES visibly off
     centre. In em the ratio holds at every width for free, with no
     resize listener to register, debounce or tear down.

     Defaults to 0, so a failed measurement is a slightly off-centre
     word — never a broken or invisible one. */
  transform:translateX(var(--pl-shift,0em));
}
.pl-lockup{
  display:flex;
  /* centre, not baseline: the mark is artwork with no baseline to sit on.
     Matches the nav lockup. */
  align-items:center;
  gap:.75em;                 /* = .62 x --brand-h, same optical gap as the nav */
  font-family:var(--font-head,"Barlow Condensed","Arial Narrow",system-ui,sans-serif);
  font-weight:700;
  text-transform:uppercase;
  letter-spacing:.02em;
  line-height:1;
  white-space:nowrap;
  transform-origin:50% 50%;
}
/* Deliberately NOT flex. letter-spacing only applies between inline
   boxes — turning a word into a flex container blockifies its glyphs and
   silently drops the .02em tracking. Inline-block keeps the glyphs in a
   line box where the tracking still lands between them. */
.pl-word{display:inline-block;line-height:1;white-space:nowrap}


/* ---------- The logotype ----------
   Sized off --brand-h, aspect preserved by the SVG's own viewBox. The
   width/height attributes on the <img> give it the right aspect before it
   has loaded, so the lockup never reflows underneath the measurement. */
.pl-mark{
  display:block;
  height:var(--brand-h);
  width:auto;
}
/* The wipe. Declared in CSS so there is no frame where the mark is
   visible un-wiped; the GSAP path re-asserts the identical value with
   gsap.set() before tweening, so both engines start the same.

   Guarded on @supports: where clip-path is unavailable this rule never
   applies and the mark is simply present from the start. It must never
   be the reason the logotype cannot be seen. */
@supports (clip-path:inset(0 0 0 0)){
  .pl-word--serres{clip-path:inset(0% 100% 0% 0%)}
}

/* ---------- Brushed chrome fill ----------
   The gradient sits on the CHARACTER, not on the word. background-clip:
   text on a word whose children are individually transformed is the
   fragile case — the clipped background is painted by the ancestor and
   does not follow a promoted, transformed descendant, so the fill can
   detach or vanish. Giving each character its own background makes the
   fill part of the moving element and immune to that. --chrome-grad is
   176deg (4deg off vertical) and every character box is the same
   height, so per-character and per-word rendering are indistinguishable.

   @supports below is the last line of defence: if background-clip:text
   is unavailable the text falls back to solid --white. The wordmark is
   never allowed to end up invisible. */
.pl-char{
  display:inline-block;
  /* Every inline box in the lockup aligns to its line-box BOTTOM rather
     than to a baseline. Baseline alignment of nested inline-blocks with
     overflow:hidden is defined differently per level and lets SERRES and
     DRIVE drift a pixel or two apart; bottom alignment is unambiguous
     and, since every box shares one font-size and line-height:1, it puts
     both words on the same baseline by construction. */
  vertical-align:bottom;
  background:var(--chrome-grad,linear-gradient(176deg,#fdfdfe 0%,#cfcfd6 32%,#8d8d97 52%,#f0f0f4 72%,#a6a6b0 100%));
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
  -webkit-text-fill-color:transparent;
}
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .pl-char{
    background:none;
    color:var(--white,#fdfdfe);
    -webkit-text-fill-color:currentColor;
  }
}

/* DRIVE slides in as one word, masked at its own edge. Tracked out as a
   descriptor exactly as in the nav lockup; the negative right margin
   cancels the trailing space the tracking adds after the final E, so the
   lockup is optically centred rather than sitting left of its box. */
.pl-drive-mask{
  display:inline-block;
  overflow:hidden;
  padding-top:.12em;
  margin-top:-.12em;
}
.pl-word--drive{
  vertical-align:bottom;
  transform:translateX(105%);
  letter-spacing:.30em;
  margin-right:-.30em;
}


/* (The auxiliary tag words — Barcelona / Flota premium / Entrega a
   domicilio — were removed on request: the overlay shows the SERRES DRIVE
   lockup and nothing else.) */


/* =====================================================================
   NO-GSAP FALLBACK
   js/preloader.js adds .pl--css only when window.gsap is absent, then
   toggles the beat classes on a timer. Gating every transition on that
   class means the GSAP path has no CSS transition to fight with.
   Easings mirror the JS ones; the split uses the literal "hop" curve.
   ===================================================================== */

/* t=0.2 — the speed wipe lays SERRES down */
.pl--css .pl-word--serres{transition:clip-path 1.15s cubic-bezier(.65,0,.35,1)}
.pl--css.is-serres .pl-word--serres{clip-path:inset(0% 0% 0% 0%)}

/* t=1.55 — DRIVE arrives and the lockup re-centres around it */
.pl--css .pl-word--drive,
.pl--css .pl-shift{transition:transform .8s cubic-bezier(.16,1,.3,1)}
.pl--css.is-drive .pl-word--drive{transform:translateX(0)}
.pl--css.is-drive .pl-shift{transform:translateX(0)}

/* t=2.7 — the assembled lockup settles/tightens */
.pl--css .pl-lockup{transition:transform .6s cubic-bezier(.45,.05,.25,1)}
.pl--css.is-settle .pl-lockup{transform:scale(.94)}

/* t=3.3 — THE SPLIT */
.pl--css .pl-half{transition:transform 1s cubic-bezier(.8,0,.3,1)}
.pl--css.is-split .pl-half--top{transform:translateY(-100%)}
.pl--css.is-split .pl-half--bot{transform:translateY(100%)}


/* =====================================================================
   REDUCED MOTION
   No stagger, no slide, no split. preloader.js adds .pl--reduced and
   shows the finished lockup statically for ~600ms, then cross-fades the
   whole overlay out via .pl--out. Degrading to a STATIC READABLE state,
   never a blank one.
   ===================================================================== */
.pl--reduced .pl-word--drive,
.pl--reduced .pl-shift,
.pl--reduced .pl-lockup{transform:none}
/* the wipe must be fully open, or reduced motion would show a blank mark */
.pl--reduced .pl-word--serres{clip-path:none}

@media (prefers-reduced-motion:reduce){
  /* Belt and braces: even if the JS branch were somehow missed, nothing
     inside the overlay may animate. The root's opacity fade is left
     alone on purpose — it is how the overlay gets out of the way. */
  .pl *{animation:none !important}
  .pl--css .pl-word--serres,
  .pl--css .pl-word--drive,
  .pl--css .pl-shift,
  .pl--css .pl-lockup,
  .pl--css .pl-half{
    transition:none !important;
    transform:none !important;
    opacity:1 !important;
  }
  /* transform:none above cannot open a clip-path — say it explicitly, or
     the mark stays wiped out under reduced motion. */
  .pl-word--serres{clip-path:none !important}
}


/* =====================================================================
   BREAKPOINTS — the site uses 1080 / 860 / 620, no tokens exist.
   ===================================================================== */
@media (max-width:620px){
  .pl-lockup{gap:.6em}
}
