/* ==========================================================================
   LB Automations — shared motion & polish layer
   Loaded AFTER each page's inline <style> so equal-specificity rules here win.
   Purely additive: no selectors that the login modal / forms / inline scripts
   depend on are renamed or restructured. Safe to remove without breaking JS
   (motion.js feature-detects everything).
   `.lb-motion` is set on <html> by motion.js only when the visitor does NOT
   prefer reduced motion — all moving parts hang off it.
   ========================================================================== */

:root{
  --lb-ease-out:cubic-bezier(.16,1,.3,1);        /* ease-out-expo: fast start, soft landing */
  --lb-ease-spring:cubic-bezier(.34,1.4,.64,1);  /* gentle overshoot for playful elements */
}

/* ---- brand selection + scrollbar polish ------------------------------- */
::selection{background:rgba(59,130,246,.35);color:#fff}
@supports (scrollbar-width:thin){
  html{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.18) transparent}
}
::-webkit-scrollbar{width:10px}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:8px;border:2px solid #07080F}
::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.24)}
::-webkit-scrollbar-track{background:transparent}

/* ---- scroll progress bar (injected by motion.js) ----------------------- */
.lb-progress{
  position:fixed;top:0;left:0;right:0;height:2px;z-index:200;
  background:var(--grad);transform-origin:0 50%;transform:scaleX(0);
  pointer-events:none;opacity:.9;
}

/* ---- background atmosphere --------------------------------------------
   The glows live inside .glow-wrap (position:fixed; overflow:hidden) — that
   containment is REQUIRED for the iOS zoom fix, so we only ever move the
   children with transforms; the wrap itself never changes. */
.lb-motion .glow{will-change:transform}

/* aurora disc, injected behind the glows — rotation is driven by motion.js
   (time + scroll position), NOT a CSS animation: a CSS animation's transform
   would override the inline scroll-reactive transform in the cascade. */
.lb-aurora{
  position:absolute;left:50%;top:42%;width:130vmax;height:130vmax;
  margin:-65vmax 0 0 -65vmax;border-radius:50%;
  background:conic-gradient(from 0deg,
    rgba(59,130,246,.10),rgba(34,201,160,.06),rgba(192,38,211,.05),
    rgba(37,99,235,.08),rgba(59,130,246,.10));
  filter:blur(70px);opacity:.45;pointer-events:none;
  will-change:transform;
}

/* floating brand shapes (injected by motion.js) — hexagons echo the logo.
   They live inside .glow-wrap so the iOS containment still applies; most sit
   below the first viewport and parallax up into view as you scroll. */
.lb-shape{position:absolute;pointer-events:none;line-height:0}
.lb-shape svg{display:block;width:100%;height:100%}
.lb-shape--blue{color:#3E8BFF}
.lb-shape--teal{color:#1FE0A6}
.lb-shape--violet{color:#8B5CF6}
.lb-shape--dot{border-radius:50%;background:radial-gradient(circle,currentColor 0%,transparent 70%)}
.lb-shape--ring{border:1.5px solid currentColor;border-radius:50%}
.lb-shape--soft{filter:blur(2px)}          /* reads as further away */
.lb-shape--glow{filter:drop-shadow(0 0 7px currentColor)}
@media(max-width:680px){
  .lb-shape--lg{display:none}              /* fewer, smaller shapes on phones */
}

/* faint dot grid fading from the top — gives the glass cards texture to sit on */
.lb-grid{
  position:absolute;inset:-20% 0;pointer-events:none;opacity:.55;
  background-image:radial-gradient(rgba(255,255,255,.05) 1px,transparent 1.4px);
  background-size:34px 34px;
  -webkit-mask-image:radial-gradient(ellipse 90% 60% at 50% 12%,#000 25%,transparent 72%);
  mask-image:radial-gradient(ellipse 90% 60% at 50% 12%,#000 25%,transparent 72%);
}
@media(max-width:680px){
  .lb-aurora{display:none} /* save GPU/battery on phones — glows carry the look */
}

/* ---- rising dust motes (injected by motion.js, time-based) -------------- */
.lb-mote{
  position:absolute;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,rgba(157,185,255,.85),rgba(157,185,255,0) 70%);
}

/* ---- occasional comet streak (injected + scheduled by motion.js) -------- */
.lb-comet{
  position:absolute;left:0;top:0;width:160px;height:2px;border-radius:2px;
  background:linear-gradient(90deg,transparent,rgba(150,200,255,.15) 35%,rgba(190,225,255,.95));
  filter:drop-shadow(0 0 6px rgba(140,190,255,.8));
  opacity:0;pointer-events:none;will-change:transform,opacity;
}
.lb-comet--go{animation:lbComet 1.7s ease-out forwards}
@keyframes lbComet{
  0%{opacity:0;transform:var(--lbc-from) rotate(var(--lbc-rot))}
  10%{opacity:.9}
  100%{opacity:0;transform:var(--lbc-to) rotate(var(--lbc-rot))}
}

/* ---- auto-entrances (motion.js tags cards that lack a .reveal) -----------
   Classes are REMOVED again right after the entrance finishes so the
   elements return to their pristine CSS state (hover transforms intact). */
.lb-motion .lb-rv{opacity:0;transition:opacity .7s var(--lb-ease-out),transform .7s var(--lb-ease-out)}
.lb-motion .lb-rv--up{transform:translateY(26px)}
.lb-motion .lb-rv--left{transform:translateX(-30px)}
.lb-motion .lb-rv--right{transform:translateX(30px)}
.lb-motion .lb-rv--scale{transform:scale(.92)}
.lb-motion .lb-rv.lb-in{opacity:1;transform:none}

/* ---- smoother reveals ---------------------------------------------------
   Overrides the per-page reveal timing with a longer ease-out-expo curve.
   motion.js adds a per-card transition-delay for stagger and clears it after
   the entrance so hover transforms stay instant. */
html.js .reveal{
  opacity:0;transform:translateY(30px) scale(.985);
  transition:opacity .9s var(--lb-ease-out),transform .9s var(--lb-ease-out);
}
html.js .reveal.in{opacity:1;transform:none}

/* dashboard bars only grow once their card has revealed */
html.js .reveal .bar{animation-play-state:paused}
html.js .reveal.in .bar{animation-play-state:running}

/* ---- hero entrance ------------------------------------------------------
   Pages gate JS-dependent styling behind html.js (set by the inline script),
   so a no-JS visitor sees everything instantly. Not gated behind
   prefers-reduced-motion (owner's call — Windows machines often ship with
   animation effects off, which was hiding the whole layer). */
html.js .hero-copy>*{animation:lbRise .9s var(--lb-ease-out) backwards}
html.js .hero-copy>*:nth-child(1){animation-delay:.05s}
html.js .hero-copy>*:nth-child(2){animation-delay:.14s}
html.js .hero-copy>*:nth-child(3){animation-delay:.23s}
html.js .hero-copy>*:nth-child(4){animation-delay:.32s}
html.js .hero-copy>*:nth-child(5){animation-delay:.41s}
html.js .hero-visual{animation:lbRiseScale 1.05s var(--lb-ease-out) .3s backwards}
html.js .page-hero>*{animation:lbRise .9s var(--lb-ease-out) backwards}
html.js .page-hero>*:nth-child(1){animation-delay:.05s}
html.js .page-hero>*:nth-child(2){animation-delay:.14s}
html.js .page-hero>*:nth-child(3){animation-delay:.23s}
html.js .page-hero>*:nth-child(4){animation-delay:.32s}

/* gentle shimmer across gradient headline text */
html.js .hero h1 .gt,html.js .page-hero h1 .gt{
  background-size:220% 100%;
  animation:lbShimmer 7s ease-in-out infinite;
}
@keyframes lbRise{from{opacity:0;transform:translateY(26px)}}
@keyframes lbRiseScale{from{opacity:0;transform:translateY(30px) scale(.96)}}
@keyframes lbShimmer{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}

/* ---- buttons: press feedback + sheen sweep ----------------------------- */
.lb-motion .btn{position:relative;overflow:hidden}
.lb-motion .btn:active{transform:translateY(0) scale(.97);transition-duration:.08s}
.lb-motion .btn-primary::after{
  content:"";position:absolute;top:0;bottom:0;left:0;width:36%;
  background:linear-gradient(105deg,transparent,rgba(255,255,255,.32),transparent);
  transform:translateX(-160%) skewX(-18deg);pointer-events:none;
}
.lb-motion .btn-primary:hover::after{
  transform:translateX(340%) skewX(-18deg);
  transition:transform .65s ease;
}
.btn:focus-visible,.faq-q:focus-visible,a:focus-visible{
  outline:2px solid #5B9BFF;outline-offset:3px;border-radius:6px;
}
.btn:focus-visible{border-radius:999px}

/* ---- richer card hovers (transform/shadow only — no layout shift) ------ */
.lb-motion .svc:hover,.lb-motion .case:hover{
  box-shadow:0 24px 60px -28px rgba(0,0,0,.85),0 0 0 1px rgba(91,155,255,.10);
}
.lb-motion .plan{transition:transform .35s var(--lb-ease-out),box-shadow .35s,border-color .3s}
.lb-motion .plan:hover{transform:translateY(-5px);box-shadow:0 26px 60px -30px rgba(43,130,246,.45)}
.lb-motion .vrow{transition:border-color .3s,transform .35s var(--lb-ease-out),box-shadow .35s}
.lb-motion .vrow:hover{box-shadow:0 18px 44px -24px rgba(0,0,0,.8)}
.lb-motion .float-card{animation-duration:7s}

/* ---- nav: animated underline on hover (absolute — no layout shift) ----- */
.lb-motion .nav-links a{position:relative}
.lb-motion .nav-links a:not(.active)::after{
  content:"";position:absolute;left:0;right:0;bottom:-7px;height:2px;border-radius:2px;
  background:var(--grad-soft);opacity:.8;
  transform:scaleX(0);transform-origin:left center;
  transition:transform .35s var(--lb-ease-out);
}
.lb-motion .nav-links a:not(.active):hover::after{transform:scaleX(1)}
@media(max-width:680px){
  .lb-motion .nav-links a::after{display:none} /* no underline in mobile drawer */
}

/* ---- header: soft shadow once scrolled --------------------------------- */
.lb-motion header.scrolled{box-shadow:0 12px 32px -20px rgba(0,0,0,.7)}

/* ---- twinkle stars (injected by motion.js, CSS animation only) ------------ */
.lb-twinkle{
  position:absolute;width:2px;height:2px;border-radius:50%;
  pointer-events:none;
  background:rgba(200,220,255,.95);
  box-shadow:0 0 5px rgba(180,210,255,.7),0 0 10px rgba(140,180,255,.4);
  animation:lbTwinkle var(--lbt-dur,4s) ease-in-out var(--lbt-del,0s) infinite;
}
@keyframes lbTwinkle{
  0%,100%{opacity:0;transform:scale(.4)}
  45%,55%{opacity:1;transform:scale(1.5)}
}
@media(max-width:680px){
  .lb-twinkle:nth-child(odd){display:none} /* half density on phones */
}

/* ---- enhanced card auto-entrances ----------------------------------------
   Bigger transforms than the original so the pop-in is clearly visible.
   Classes are still REMOVED after the entrance so hover transforms work clean. */
.lb-motion .lb-rv{
  opacity:0;
  transition:opacity .75s var(--lb-ease-out),transform .75s var(--lb-ease-out);
}
.lb-motion .lb-rv--up   {transform:translateY(44px) scale(.95)}
.lb-motion .lb-rv--left {transform:translateX(-44px) scale(.95)}
.lb-motion .lb-rv--right{transform:translateX(44px) scale(.95)}
.lb-motion .lb-rv--scale{transform:scale(.82) translateY(18px)}
.lb-motion .lb-rv.lb-in {opacity:1;transform:none}

/* ---- mobile lite mode (html.lb-mobile, set by motion.js) -----------------
   Phones get ~10 statically placed shapes drifting on a cheap CSS float
   instead of the rAF parallax loop — a handful of compositor-only
   animations instead of dozens of per-frame style writes. */
.lb-mobile .lb-shape{
  animation:lbFloat var(--lbf-dur,10s) ease-in-out var(--lbf-del,0s) infinite alternate;
}
@keyframes lbFloat{
  from{transform:translateY(0) rotate(0deg)}
  to  {transform:translateY(-18px) rotate(7deg)}
}

/* Mobile perf: per-shape blur/drop-shadow filters force each animated shape
   to re-rasterise — plain shapes composite for free. And the headline shimmer
   animates background-position, which repaints the gradient text every frame
   forever; phones get the static gradient instead. */
@media(max-width:680px){
  .lb-shape--soft{filter:none}
  .lb-shape--glow{filter:none}
  html.js .hero h1 .gt,html.js .page-hero h1 .gt{animation:none;background-size:100% 100%}
}
