/* ============================================================
   GX-HERO — the first three seconds.
   Four beats: (1) the lamp-smoke entrance, (2) pointer parallax,
   (3) a gold sheen riding the wordmark, (4) the startled genie.
   Everything here is namespaced .gx-hero-* / @keyframes gx-hero-*.
   Nothing in index.html is touched — gx-hero.js builds the nodes.
   ============================================================ */

:root{
  --gx-hero-ink: #141414;
  --gx-hero-gold: #FBB417;
  --gx-hero-glow: #FFE7A6;
  /* the one overshoot the whole site pops with */
  --gx-hero-pop: cubic-bezier(.2,1.4,.4,1);
}

/* ------------------------------------------------------------
   BEAT 1 — cinematic entrance
   ------------------------------------------------------------
   While the intro is "armed" the genie's own genieRise+bob are
   silenced from CSS (added on <html> before first paint, so there
   is never a flash of the old entrance). JS hands the float back
   as an inline `animation:bob ...` the moment the intro ends.     */

html.gx-hero-armed .genie{
  animation: none !important;
}
html.gx-hero-armed .genie:not(.gx-hero-genie-in){
  opacity: 0;
}
/* the genie condensing out of the smoke */
html.gx-hero-armed .genie.gx-hero-genie-in{
  transform-origin: 50% 88%;
  animation: gx-hero-materialize .8s cubic-bezier(.18,1.28,.36,1) .52s both !important;
}
@keyframes gx-hero-materialize{
  0%{
    opacity: 0;
    transform: translateY(20%) scale(.48);
    filter: blur(10px) drop-shadow(0 20px 22px rgba(20,20,20,.16));
  }
  34%{ opacity: .6; }
  70%{ filter: blur(1.5px) drop-shadow(0 20px 22px rgba(20,20,20,.16)); }
  100%{
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0) drop-shadow(0 20px 22px rgba(20,20,20,.16));
  }
}

/* the little stage that holds lamp + smoke, laid over the genie box.
   inset:0 on .genie-wrap => zero layout cost, zero CLS. */
.gx-hero-stage{
  position: absolute;
  inset: 0;
  z-index: 2;              /* smoke drifts IN FRONT of the genie, then clears */
  pointer-events: none;
  contain: layout style;
}

/* --- the lamp silhouette --------------------------------- */
.gx-hero-lamp{
  position: absolute;
  left: 50%;
  bottom: 1%;
  width: clamp(58px, 11vw, 104px);
  height: auto;
  opacity: 0;
  transform: translate(-50%, 0);
  transform-origin: 50% 100%;
  filter: drop-shadow(0 6px 0 rgba(20,20,20,.18));
  -webkit-user-drag: none;
  user-select: none;
}
.gx-hero-go .gx-hero-lamp{
  animation:
    gx-hero-lamp-in  .46s var(--gx-hero-pop) both,
    gx-hero-lamp-out .40s cubic-bezier(.5,-.4,.7,.5) 1.02s forwards;
}
@keyframes gx-hero-lamp-in{
  0%  { opacity: 0; transform: translate(-50%, 16px) scale(.35) rotate(-14deg); }
  58% { opacity: 1; transform: translate(-50%, 0) scale(1.14) rotate(4deg); }
  100%{ opacity: 1; transform: translate(-50%, 0) scale(1) rotate(0); }
}
@keyframes gx-hero-lamp-out{
  0%  { opacity: 1; transform: translate(-50%, 0) scale(1) rotate(0); }
  28% { opacity: 1; transform: translate(-50%, -3px) scale(1.1) rotate(-6deg); }
  100%{ opacity: 0; transform: translate(-50%, 8px) scale(.16) rotate(12deg); }
}

/* --- the plume: one soft column swelling out of the spout -- */
.gx-hero-plume{
  position: absolute;
  left: 50%;
  bottom: 4%;
  width: clamp(110px, 22vw, 190px);
  aspect-ratio: 3 / 4;
  margin-left: 0;
  opacity: 0;
  border-radius: 50% 50% 44% 44% / 62% 62% 38% 38%;
  background: radial-gradient(ellipse at 50% 90%,
              rgba(255,231,166,.95) 0%,
              rgba(251,180,23,.72) 34%,
              rgba(251,180,23,.22) 58%,
              rgba(251,180,23,0) 76%);
  transform: translate(-50%, 0) scale(.12, .05);
  transform-origin: 50% 100%;
}
.gx-hero-go .gx-hero-plume{
  animation: gx-hero-plume 1.18s cubic-bezier(.2,.9,.3,1) .14s both;
}
@keyframes gx-hero-plume{
  0%  { opacity: 0;  transform: translate(-50%, 0) scale(.12,.05); }
  22% { opacity: .95; }
  52% { opacity: .85; transform: translate(-50%, -14%) scale(1.05,1.12); }
  100%{ opacity: 0;  transform: translate(-50%, -34%) scale(1.3,1.42); }
}

/* --- the puffs: individual clouds pouring up and out ------- */
.gx-hero-puff{
  position: absolute;
  left: 50%;
  bottom: 6%;
  width: var(--gx-hero-pw, 60px);
  height: var(--gx-hero-pw, 60px);
  margin-left: calc(var(--gx-hero-pw, 60px) / -2);
  border-radius: 50%;
  opacity: 0;
  background: radial-gradient(circle at 38% 36%,
              rgba(255,246,214,.95) 0%,
              rgba(251,180,23,.72) 42%,
              rgba(224,154,0,.18) 68%,
              rgba(224,154,0,0) 78%);
  transform: scale(.14);
}
.gx-hero-go .gx-hero-puff{
  animation: gx-hero-puff .98s cubic-bezier(.22,.72,.3,1) var(--gx-hero-pd, 0s) both;
}
@keyframes gx-hero-puff{
  0%  { opacity: 0;   transform: translate3d(0,0,0) scale(.14) rotate(0deg); }
  16% { opacity: .92; }
  62% { opacity: .5; }
  100%{
    opacity: 0;
    transform: translate3d(var(--gx-hero-px, 0px), var(--gx-hero-py, -170px), 0)
               scale(1) rotate(var(--gx-hero-pr, 18deg));
  }
}

/* --- three comic stars, so the magic reads hand-drawn ------ */
.gx-hero-spark{
  position: absolute;
  left: 50%;
  bottom: 16%;
  width: var(--gx-hero-sw, 20px);
  height: var(--gx-hero-sw, 20px);
  margin-left: calc(var(--gx-hero-sw, 20px) / -2);
  background: var(--gx-hero-gold);
  clip-path: polygon(50% 0,60% 40%,100% 50%,60% 60%,50% 100%,40% 60%,0 50%,40% 40%);
  opacity: 0;
  transform: scale(0);
}
.gx-hero-go .gx-hero-spark{
  animation: gx-hero-spark .74s var(--gx-hero-pop) var(--gx-hero-pd, .4s) both;
}
@keyframes gx-hero-spark{
  0%  { opacity: 0; transform: translate3d(0,0,0) scale(0) rotate(-40deg); }
  35% { opacity: 1; transform: translate3d(calc(var(--gx-hero-px, 0px) * .5), calc(var(--gx-hero-py, -120px) * .45), 0) scale(1.15) rotate(0deg); }
  100%{ opacity: 0; transform: translate3d(var(--gx-hero-px, 0px), var(--gx-hero-py, -120px), 0) scale(.25) rotate(60deg); }
}

/* ------------------------------------------------------------
   BEAT 2 — pointer parallax
   The transform lives on .genie-wrap / a wrapper around .burst,
   never on .genie itself, so `bob` keeps floating underneath.   */

.gx-hero-tilt{ will-change: transform; }
/* the depth wrapper JS slips around .burst — geometrically a
   no-op (inset:0 of .hero == .burst's old containing block). */
.gx-hero-depth{
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  will-change: transform;
}

/* ------------------------------------------------------------
   BEAT 3 — gold sheen across the wordmark
   A transparent clone of the letters sits exactly on top of the
   real ones and is background-clipped to the glyphs, so the light
   band rides the letterforms and never paints a rectangle.       */

/* selector is .wordmark-scoped so it always out-ranks the page's own
   .wordmark rules, whatever order the <link> ends up in */
.wordmark .gx-hero-sheen{
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  pointer-events: none;
  user-select: none;
  color: transparent;
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 0;
  text-shadow: none;
  background-image: linear-gradient(100deg,
      rgba(255,255,255,0)    41%,
      rgba(255,255,255,.45)  46%,
      rgba(255,246,214,.92)  50%,
      rgba(255,255,255,.45)  54%,
      rgba(255,255,255,0)    59%);
  background-size: 260% 100%;
  background-repeat: no-repeat;
  background-position: -70% 0;
  -webkit-background-clip: text;
          background-clip: text;
  will-change: background-position;
  animation: gx-hero-sheen 6.6s linear 2.2s infinite;
}
/* out-specific the page's own .wordmark .g / .l rules */
.wordmark .gx-hero-sheen .g,
.wordmark .gx-hero-sheen .c{ color: transparent; -webkit-text-fill-color: transparent; }
.wordmark .gx-hero-sheen .l{ animation: none; opacity: 1; transform: none; }

@keyframes gx-hero-sheen{
  0%  { background-position: -70% 0; }
  16% { background-position: 175% 0; }
  100%{ background-position: 175% 0; }
}

/* ------------------------------------------------------------
   BEAT 4 — the startled genie on a fast scroll
   Clipped by its own fixed frame so it can never add a scrollbar. */

.gx-hero-shock{
  position: fixed;
  right: 0;
  bottom: 0;
  width: clamp(104px, 17vw, 172px);
  height: clamp(104px, 17vw, 172px);
  overflow: hidden;
  z-index: 60;
  pointer-events: none;
}
.gx-hero-shock img{
  position: absolute;
  right: -6%;
  bottom: -8%;
  width: 112%;
  height: auto;
  opacity: 0;
  transform: translate3d(0, 110%, 0) rotate(18deg);
  transform-origin: 100% 100%;
  filter: drop-shadow(-4px 4px 0 rgba(20,20,20,.22));
  -webkit-user-drag: none;
  user-select: none;
}
.gx-hero-shock.gx-hero-shock-go img{
  animation: gx-hero-shock 1.15s cubic-bezier(.2,1.35,.4,1) both;
}
@keyframes gx-hero-shock{
  0%  { opacity: 0; transform: translate3d(0,110%,0) rotate(20deg) scale(.9); }
  22% { opacity: 1; transform: translate3d(0,-6%,0)  rotate(-7deg) scale(1.06); }
  34% { transform: translate3d(0,2%,0) rotate(3deg) scale(1); }
  44% { transform: translate3d(0,0,0) rotate(-2deg) scale(1); }
  74% { opacity: 1; transform: translate3d(0,0,0) rotate(0deg) scale(1); }
  100%{ opacity: 0; transform: translate3d(0,118%,0) rotate(14deg) scale(.92); }
}

/* ------------------------------------------------------------
   Reduced motion — the module never arms itself in JS anyway, this
   is belt-and-braces if the OS setting flips mid-session.
   `.gx-hero-force` is set by JS for ?motion=1, which must still play. */
@media (prefers-reduced-motion: reduce){
  html:not(.gx-hero-force) .gx-hero-stage,
  html:not(.gx-hero-force) .gx-hero-shock{ display: none; }
  html:not(.gx-hero-force) .gx-hero-sheen{ animation: none; }
  html.gx-hero-armed:not(.gx-hero-force) .genie{ animation: bob 4.5s ease-in-out infinite !important; }
  html.gx-hero-armed:not(.gx-hero-force) .genie:not(.gx-hero-genie-in){ opacity: 1; }
}
