/* ============================================================
 * lusion-hero.css — interactive 3D hero layer + motion polish
 * Additive only: nothing here alters existing page content.
 * Pairs with newassests/js/lusion-hero.js + lusion-motion.js
 * ============================================================ */

/* ---------- 3D stage ----------------------------------------
 * Sits inside .about-hero as an absolute layer:
 *   z-index 0 = existing aurora / grid overlays
 *   z-index 1 = this canvas
 *   z-index 2 = .ah-inner (headline, CTAs, bento) — untouched
 * If WebGL is unavailable the canvas never mounts and the
 * original gradient hero shows exactly as before.
 * ---------------------------------------------------------- */
/* While our lerp drives the scroll we must cancel the page's own
   `html { scroll-behavior: smooth }` (industry-page.css) — two smooth
   scrollers fighting stalls the page for ~800ms and then lurches. */
html.lx-smooth{scroll-behavior:auto !important;}

.lx-stage{
  position:absolute;
  inset:0;
  z-index:1;
  pointer-events:none;           /* clicks fall through to the page */
  opacity:0;
  transition:opacity 1.3s cubic-bezier(.22,.61,.36,1);
  -webkit-mask-image:radial-gradient(130% 100% at 50% 45%, #000 58%, transparent 100%);
          mask-image:radial-gradient(130% 100% at 50% 45%, #000 58%, transparent 100%);
}
/* Watermark level: the pile reads as a faint texture behind the copy
   rather than competing with it. Raise toward 1 for a louder hero. */
.lx-stage.is-live{opacity:.085;}
.lx-stage canvas{display:block;width:100%;height:100%;}

/* Readability scrim: keeps the white headline legible over the
   moving scene without touching the markup of the text column. */
.about-hero.lx-on::before{
  content:"";
  position:absolute;
  inset:0;
  z-index:1;
  pointer-events:none;
  background:
    /* light veil under the headline column only — the scene is already
       at watermark opacity, so this just guarantees text contrast */
    linear-gradient(90deg,
      rgba(6,8,22,.58) 0%,
      rgba(6,8,22,.44) 28%,
      rgba(6,8,22,.20) 48%,
      rgba(6,8,22,0)   68%),
    /* top + bottom falloff so the scene never fights the header or the fold */
    linear-gradient(180deg,
      rgba(6,8,22,.46) 0%,
      rgba(6,8,22,0)   22%,
      rgba(6,8,22,0)   66%,
      rgba(6,8,22,.52) 100%);
}
@media (max-width:900px){
  .about-hero.lx-on::before{
    background:linear-gradient(180deg, rgba(6,8,22,.52) 0%, rgba(6,8,22,.20) 42%, rgba(6,8,22,.58) 100%);
  }
}

/* The bento cards sit over the scene — a touch more backdrop so the
   numbers stay crisp. Styling only; markup untouched. */
.about-hero.lx-on .gcard{
  background:rgb(0 0 0 / 30%);
  -webkit-backdrop-filter:blur(16px) saturate(1.1);
          backdrop-filter:blur(16px) saturate(1.1);
}
.about-hero.lx-on .gcard:hover{background:rgba(20,24,52,.46);}

/* ============================================================
 * Choreographed section entrances (lusion-reveal.js)
 *
 * The page's own rule is a flat `.reveal{opacity:0;translateY(18px)}`
 * that fires on everything at once. Inside enhanced sections we
 * neutralise it and run a proper sequence instead. Overridden
 * here by specificity — industry-page.css is never edited.
 * ========================================================== */
.lx-anim .reveal{opacity:1;transform:none;transition:none;}

/* Shared easing: a firm decelerate that settles without bouncing. */
.lx-anim{--lx-ease:cubic-bezier(.22,.8,.3,1);--lx-pop:cubic-bezier(.34,1.32,.44,1);}

/* Rewind helper. When a section scrolls out it is put back into its
   entrance state so it can play again next visit — that reset must be
   instant, or a quick scroll back catches the element easing out over
   its own delay and the replay starts from the wrong place. */
.lx-noanim,
.lx-noanim *,
.lx-noanim *::before,
.lx-noanim *::after{
  transition:none !important;
  animation:none !important;
}

/* Section head drifts slightly against the scroll for depth (JS sets --py). */
.lx-anim .lx-par{transform:translate3d(0,var(--py,0px),0);will-change:transform;}

/* --- eyebrow pill: wipes open from its own centre --- */
.lx-anim .lx-pill{
  opacity:0;transform:translateY(14px) scale(.86);
  clip-path:inset(0 42% 0 42% round 999px);
  transition:opacity .55s var(--lx-ease),
             transform .72s var(--lx-pop),
             clip-path .72s var(--lx-ease);
  transition-delay:var(--d,0ms);
}
.lx-anim .lx-pill.lx-in{opacity:1;transform:none;clip-path:inset(0 0 0 0 round 999px);}

/* --- heading: each word hinges up out of a clip mask in 3D, resolving
       from a blur. The padding/margin pair keeps descenders (g, y, p)
       from being cut off by the mask. --- */
.lx-w{
  display:inline-block;overflow:hidden;vertical-align:bottom;
  padding-bottom:.2em;margin-bottom:-.2em;
  perspective:680px;
}
.lx-w > i{
  display:inline-block;font-style:inherit;
  transform:translateY(116%) rotateX(-68deg);
  transform-origin:50% 0%;
  opacity:0;
  filter:blur(5px);
  transition:transform 1.05s var(--lx-ease),
             opacity .62s var(--lx-ease),
             filter .62s var(--lx-ease);
  transition-delay:var(--d,0ms);
  will-change:transform,opacity,filter;
}
.lx-split.lx-in .lx-w > i{transform:none;opacity:1;filter:blur(0);}

/* --- lede: word-level fade so the sentence resolves left to right --- */
.lx-anim .lx-word{
  display:inline-block;
  opacity:0;transform:translateY(9px);
  filter:blur(3px);
  transition:opacity .62s var(--lx-ease),
             transform .62s var(--lx-ease),
             filter .5s var(--lx-ease);
  transition-delay:var(--d,0ms);
}
.lx-anim .lx-wordsin .lx-word{opacity:1;transform:none;filter:blur(0);}

/* --- block fallback for anything not word-split --- */
.lx-anim .lx-fade{
  opacity:0;transform:translateY(18px);
  transition:opacity .85s var(--lx-ease), transform .85s var(--lx-ease);
  transition-delay:var(--d,0ms);
}
.lx-anim .lx-fade.lx-in{opacity:1;transform:none;}

/* --- cards: hinge up from their bottom edge in perspective --- */
.lx-anim .lx-card{
  opacity:0;
  transform:perspective(1300px) rotateX(11deg) translateY(50px) scale(.955);
  transform-origin:50% 100%;
  transition:opacity .8s var(--lx-ease), transform 1.05s var(--lx-ease);
  transition-delay:var(--d,0ms);
  will-change:opacity,transform;
}
.lx-anim .lx-card.lx-in{opacity:1;transform:none;}

/* Sheen sweep as the card lands. Only on cards that already clip their
   own overflow, so nothing can spill. */
.lx-anim .feat.lx-card::before,
.lx-anim .qa-card.lx-card::before{
  content:"";position:absolute;inset:0;z-index:3;pointer-events:none;
  transform:translateX(-130%);opacity:0;
}
.lx-anim .feat.lx-card::before{
  background:linear-gradient(115deg,transparent 38%,rgba(104,60,220,.16) 50%,transparent 62%);
}
.lx-anim .qa-card.lx-card::before{
  background:linear-gradient(115deg,transparent 38%,rgba(255,255,255,.30) 50%,transparent 62%);
}
.lx-anim .lx-card.lx-in::before{
  animation:lxSheen 1.15s var(--lx-ease) forwards;
  animation-delay:calc(var(--d,0ms) + 260ms);
}
@keyframes lxSheen{
  0%  {transform:translateX(-130%);opacity:0;}
  28% {opacity:1;}
  100%{transform:translateX(130%);opacity:0;}
}

/* --- icon: pops in, then its stroke draws itself --- */
.lx-anim .lx-ico{
  opacity:0;transform:scale(.42) rotate(-24deg);
  transition:opacity .5s var(--lx-ease), transform .75s var(--lx-pop);
  transition-delay:var(--d,0ms);
}
.lx-anim .lx-ico.lx-in{opacity:1;transform:none;}
.lx-anim .lx-ico [data-lx-draw]{
  stroke-dasharray:var(--len,0);
  stroke-dashoffset:var(--len,0);
  transition:stroke-dashoffset .85s var(--lx-ease);
  transition-delay:var(--dd,0ms);
}
.lx-anim .lx-ico.lx-in [data-lx-draw]{stroke-dashoffset:0;}

/* --- tag: wipes in from the left without distorting its text --- */
.lx-anim .lx-tag{
  opacity:0;clip-path:inset(0 100% 0 0 round 999px);
  transition:opacity .35s var(--lx-ease), clip-path .7s var(--lx-ease);
  transition-delay:var(--d,0ms);
}
.lx-anim .lx-tag.lx-in{opacity:1;clip-path:inset(0 0 0 0 round 999px);}

/* --- number: drops in out of focus --- */
.lx-anim .lx-num{
  opacity:0;transform:translateY(-16px) scale(1.35);filter:blur(7px);
  transition:opacity .75s var(--lx-ease), transform .9s var(--lx-ease), filter .75s var(--lx-ease);
  transition-delay:var(--d,0ms);
}
.lx-anim .lx-num.lx-in{opacity:1;transform:none;filter:blur(0);}

/* ============================================================
 * Sticky card stack
 *
 * Each card pins at the same offset, so the next one rises from
 * below and covers it exactly — a deck being dealt. All cards
 * share one containing block (.bento), so they stay pinned under
 * each other until the whole stack scrolls past.
 *
 * The grid is converted to a block flow here; the markup and the
 * copy are untouched.
 * ========================================================== */
/* The tail is PADDING, not a margin on the last card. A bottom margin
   on the last child collapses out of .bento, so the containing block
   never grows and the sticky room never appears — the deck then breaks
   apart before the final card can pin. Padding never collapses, and
   the sticky constraint rect is the padding box, so this is the room
   the stack actually needs. */
.lx-stack.bento{display:block;}

/* An empty in-flow spacer, injected as the stack's last child.
   It has to be a real element: a sticky element is constrained by its
   containing block's CONTENT box, so padding-bottom on .bento does not
   extend it, and a margin on the last card collapses out of the parent
   entirely. Without this the final card has exactly zero stick room and
   never pins, and the cards above it release early. */
.lx-stack-tail{
  display:block;
  height:var(--lx-stack-tail,14vh);
  pointer-events:none;
}

/* The tail is the last card's entire stick room, so it cannot be zero —
   but every pixel of it is empty space once the deck releases. Keep it
   short, drop the final card's own gap, and take the section's bottom
   padding off to land near the page's normal 88 + 88 section gap. */
.lx-stack > .feat.lx-last{margin-bottom:0;}
.lx-stacked{padding-bottom:0;}

.lx-stack > .feat{
  position:sticky;
  top:var(--lx-stack-top,104px);
  /* Gap sets how far apart the cards pin; min-height makes a card fill
     enough of the frame that there is no dead space under the pinned
     one, the way the reference cards do. */
  margin-bottom:var(--lx-stack-gap,26vh);
  min-height:var(--lx-stack-min,58vh);
  width:100%;
  display:block;
  padding:40px 46px;
  background:var(--white);
  border-color:#EDF1F7;
  /* Soft ambient depth: four very low-alpha layers rather than one
     heavy drop. A single large shadow reads as a dark smudge; spreading
     it across radii keeps the edge clean and the card feeling light. */
  box-shadow:
    0 1px 2px rgba(16,31,56,.030),
    0 4px 10px rgba(16,31,56,.032),
    0 12px 26px rgba(16,31,56,.038),
    0 28px 58px rgba(16,31,56,.045);
}

/* Two columns once the illustration is in: copy left, vector right. */
.lx-stack > .feat.lx-has-vec{
  display:grid;
  grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);
  gap:46px;
  align-items:center;
}
.lx-card-text{min-width:0;}
.lx-card-vec{min-width:0;display:flex;align-items:center;justify-content:center;}
.lx-card-vec svg{width:100%;height:auto;max-height:300px;overflow:visible;}

/* Supporting points, divided like the reference cards. */
.lx-points{list-style:none;margin:20px 0 0;padding:0;}
.lx-point{
  position:relative;
  padding:11px 0 11px 26px;
  border-top:1px solid var(--line-soft);
  font-size:14.5px;line-height:1.55;font-weight:600;color:var(--ink-soft);
}
.lx-point::before{
  content:"";position:absolute;left:3px;top:19px;
  width:8px;height:8px;border-radius:50%;background:var(--grad);
}
.lx-stack > .feat:last-child{margin-bottom:0;}

/* The stylesheet's lift-on-hover would fight the pin — drop it. */
.lx-stack > .feat:hover{transform:none;}

/* ---------- card illustration animations ----------
 * All keyed off .lx-vec-in, which lusion-cards.js adds when the card
 * scrolls in. Ambient loops sit paused until then so nothing burns
 * frames off screen. */

/* strokes draw themselves */
.lx-card-vec [data-lx-draw]{
  stroke-dashoffset:var(--len,0);
  transition:stroke-dashoffset 1.25s cubic-bezier(.22,.8,.3,1);
  transition-delay:var(--dd,0ms);
}
.lx-vec-in .lx-card-vec [data-lx-draw]{stroke-dashoffset:0;}

/* elements scale in, staggered by --p */
.lx-card-vec .lx-pop{
  opacity:0;transform:scale(.68);
  transform-box:fill-box;transform-origin:50% 50%;
  transition:opacity .5s cubic-bezier(.22,.8,.3,1),
             transform .62s cubic-bezier(.34,1.32,.44,1);
  transition-delay:calc(200ms + var(--p,0) * 110ms);
}
.lx-vec-in .lx-card-vec .lx-pop{opacity:1;transform:none;}

/* bars grow up from their baseline */
.lx-card-vec .lx-bar{
  transform:scaleY(0);
  transform-box:fill-box;transform-origin:50% 100%;
  transition:transform .85s cubic-bezier(.22,.8,.3,1);
  transition-delay:calc(300ms + var(--p,0) * 110ms);
}
.lx-vec-in .lx-card-vec .lx-bar{transform:none;}

/* data travelling along the network paths */
.lx-card-vec .lx-flow{opacity:0;transition:opacity .5s ease .7s;}
.lx-vec-in .lx-card-vec .lx-flow{opacity:1;}

/* ambient loops */
.lx-card-vec .lx-ring,
.lx-card-vec .lx-float,
.lx-card-vec .lx-spin{animation-play-state:paused;}
.lx-vec-in .lx-card-vec .lx-ring,
.lx-vec-in .lx-card-vec .lx-float,
.lx-vec-in .lx-card-vec .lx-spin{animation-play-state:running;}

.lx-card-vec .lx-ring{
  opacity:0;transform-box:fill-box;transform-origin:50% 50%;
  animation:lxRing 2.8s ease-out infinite;
}
@keyframes lxRing{
  0%{opacity:.65;transform:scale(.5);}
  70%{opacity:0;transform:scale(1.55);}
  100%{opacity:0;transform:scale(1.55);}
}

.lx-card-vec .lx-float{
  transform-box:fill-box;transform-origin:50% 50%;
  animation:lxFloat 4.4s ease-in-out infinite;
}
@keyframes lxFloat{0%,100%{transform:translateY(0);}50%{transform:translateY(-9px);}}

.lx-card-vec .lx-spin{
  transform-box:view-box;
  animation:lxSpin 26s linear infinite;
}
@keyframes lxSpin{to{transform:rotate(360deg);}}

/* supporting points stagger in with the card */
.lx-point{
  opacity:0;transform:translateY(10px);
  transition:opacity .62s cubic-bezier(.22,.8,.3,1),
             transform .62s cubic-bezier(.22,.8,.3,1);
  transition-delay:calc(380ms + var(--p,0) * 95ms);
}
.lx-vec-in .lx-point{opacity:1;transform:none;}

/* Numbers sit large in the corner of a tall card. */
.lx-stack > .feat .num{top:26px;right:32px;font-size:44px;}

@media (max-width:1100px){
  .lx-stack > .feat.lx-has-vec{grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr);gap:30px;}
  .lx-card-vec svg{max-height:230px;}
}
@media (max-width:900px){
  .lx-stack > .feat{
    --lx-stack-gap:30vh;
    --lx-stack-min:0;
    --lx-stack-top:86px;
    padding:28px 24px;
  }
  /* Stack the columns: copy first, illustration beneath it. */
  .lx-stack > .feat.lx-has-vec{grid-template-columns:1fr;gap:18px;}
  .lx-card-vec{order:2;}
  .lx-card-vec svg{max-height:190px;}
  .lx-stack > .feat .num{font-size:34px;top:20px;right:22px;}
  .lx-point{font-size:13.5px;padding:9px 0 9px 22px;}
  .lx-point::before{top:16px;width:7px;height:7px;}
}
@media (max-width:560px){
  .lx-card-vec{display:none;}   /* copy wins the space on a phone */
}
/* Very short viewports cannot hold a pinned card plus its header. */
@media (max-height:560px){
  .lx-stack > .feat{position:static;margin-bottom:20px;min-height:0;}
}

@media (prefers-reduced-motion:reduce){
  .lx-anim .lx-pill,.lx-anim .lx-fade,.lx-anim .lx-card,
  .lx-anim .lx-ico,.lx-anim .lx-tag,.lx-anim .lx-num,.lx-anim .lx-word{
    opacity:1 !important;transform:none !important;transition:none !important;
    filter:none !important;clip-path:none !important;
  }
  .lx-w > i{transform:none !important;transition:none !important;filter:none !important;opacity:1 !important;}
  .lx-anim .lx-card.lx-in::before{animation:none !important;}
  .lx-anim .lx-ico [data-lx-draw]{stroke-dasharray:none !important;stroke-dashoffset:0 !important;}
  .lx-anim .lx-par{transform:none !important;}
  /* card illustrations: show the finished state, animate nothing */
  .lx-card-vec [data-lx-draw]{stroke-dashoffset:0 !important;transition:none !important;}
  .lx-card-vec .lx-pop,.lx-card-vec .lx-bar,.lx-card-vec .lx-flow,.lx-point{
    opacity:1 !important;transform:none !important;transition:none !important;
  }
  .lx-card-vec .lx-ring,.lx-card-vec .lx-float,.lx-card-vec .lx-spin{
    animation:none !important;opacity:1 !important;
  }
}

/* ============================================================
 * Flowing ribbon — soft blue stroke drifting through the light
 * sections. Canvas is injected by lusion-ribbon.js; nothing is
 * added to the page markup.
 * ========================================================== */
.lx-ribbon{
  position:absolute;
  inset:0;
  z-index:0;
  overflow:hidden;
  pointer-events:none;
  opacity:0;
  transition:opacity 1.2s cubic-bezier(.22,.61,.36,1);
}
.lx-ribbon.is-live{opacity:1;}
.lx-ribbon canvas{
  display:block;
  width:100%;
  height:100%;
  /* No blur. The reference stroke is crisp and solid — the canvas is
     rendered at device resolution and anti-aliased by the path itself,
     which is what gives the edge and the round cap their clean shape. */
}

/* Host sections need a positioning context, and every one of their
   children must sit above the ribbon — not just .container, since some
   sections also hold .marquee-wrap or a carousel. position:relative on
   a static block shifts nothing; it only creates the stacking order. */
.lx-ribbon-host{position:relative;}
.lx-ribbon-host > *:not(.lx-ribbon){position:relative;z-index:1;}

@media (prefers-reduced-motion:reduce){
  .lx-ribbon{display:none;}
}
@media print{
  .lx-ribbon{display:none !important;}
}

/* Slightly deepen the hero backdrop when the scene is live so the
   glossy pieces read against it. Colour only — no layout change. */
.about-hero.lx-on{
  background:
    radial-gradient(1200px 720px at 80% -12%, rgba(112,24,147,.38), transparent 60%),
    radial-gradient(960px 680px at 6% 22%, rgba(19,8,173,.34), transparent 55%),
    linear-gradient(165deg,#070a18 0%,#0b0e2b 46%,#140830 100%);
}
.about-hero.lx-on .aurora{opacity:.34;}
.about-hero.lx-on .grid-ov{opacity:.55;}

/* Hint that the hero responds to the pointer */
.about-hero.lx-on .ah-inner{cursor:default;}

/* ---------- scroll hint ------------------------------------- */
.lx-hint{
  position:absolute;
  left:50%;
  bottom:26px;
  transform:translateX(-50%);
  z-index:2;
  display:flex;
  align-items:center;
  gap:10px;
  font-size:11px;
  font-weight:800;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:rgba(255,255,255,.58);
  pointer-events:none;
  opacity:0;
  animation:lxHintIn .9s 1.5s cubic-bezier(.22,.61,.36,1) forwards;
}
.lx-hint i{
  width:26px;
  height:1px;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.7));
  animation:lxHintLine 2.4s ease-in-out infinite;
}
@keyframes lxHintIn{to{opacity:1;}}
@keyframes lxHintLine{0%,100%{transform:scaleX(.4);opacity:.4;}50%{transform:scaleX(1);opacity:1;}}
@media (max-width:900px){.lx-hint{display:none;}}

/* ---------- letter-roll hover ------------------------------
 * Two stacked copies of each character; the pair slides up on
 * hover with a per-character stagger. Text content is cloned
 * from the element itself, so copy never changes.
 * ---------------------------------------------------------- */
.lx-roll{
  display:inline-block;
  overflow:hidden;
  vertical-align:top;
  line-height:inherit;
}
.lx-roll__c{
  display:inline-block;
  position:relative;
  white-space:pre;
  transition:transform .52s cubic-bezier(.22,.61,.36,1);
  transition-delay:calc(var(--i) * 18ms);
  will-change:transform;
}
.lx-roll__c span{display:block;}
.lx-roll__c span:last-child{position:absolute;left:0;top:100%;}
.lx-roll:hover .lx-roll__c,
a:hover .lx-roll .lx-roll__c,
button:hover .lx-roll .lx-roll__c{transform:translateY(-100%);}

/* ---------- magnetic buttons -------------------------------- */
.lx-mag{will-change:transform;}
.lx-mag__inner{display:inline-flex;align-items:center;gap:inherit;will-change:transform;}

/* ---------- scroll-linked hero parallax --------------------- */
.about-hero.lx-on .ah-inner{
  transform:translate3d(0, var(--lx-parallax, 0px), 0);
  opacity:var(--lx-fade, 1);
  will-change:transform, opacity;
}

/* ---------- accessibility / fallbacks ----------------------- */
@media (prefers-reduced-motion:reduce){
  .lx-stage{display:none;}
  .lx-hint{animation:none;opacity:1;}
  .lx-hint i{animation:none;}
  .lx-roll__c{transition:none;}
  .about-hero.lx-on::before{display:none;}
  .about-hero.lx-on .ah-inner{transform:none;opacity:1;}
}

/* Phones get the scene at reduced quality; if it is disabled the
   stage is simply absent and the original hero is unchanged. */
@media (max-width:560px){
  .lx-stage{
    -webkit-mask-image:radial-gradient(150% 100% at 50% 40%, #000 50%, transparent 100%);
            mask-image:radial-gradient(150% 100% at 50% 40%, #000 50%, transparent 100%);
  }
}

/* Printing: never paint the stage */
@media print{
  .lx-stage,.lx-hint{display:none !important;}
  .about-hero.lx-on::before{display:none !important;}
}
