/* ignite27 — motion & micro-interaction layer, paired with js/motion.js.
   Additive only: every rule here targets a class js/motion.js adds at
   runtime, so a page with JS disabled renders fully visible with zero
   dependency on this file. */

/* ---- scroll reveal ---- */
.ig-reveal {
  opacity: 0;
  transform: translateY(52px) scale(.97);
  transition: opacity .75s cubic-bezier(.16,1,.3,1), transform .8s cubic-bezier(.16,1,.3,1);
}
.ig-reveal.ig-revealed { opacity: 1; transform: translateY(0) scale(1); }
/* .ig-reveal's own transition (slow, meant for the one-time scroll-reveal
   entrance) never goes away afterward, and since it and .card-hover are
   both single-class selectors with .ig-reveal loading later in the
   cascade (this file loads after components.css), .ig-reveal's transition
   value wins the whole shorthand on any element that's both -- which is
   most cards site-wide, since initReveal()'s selector includes .card.
   That's why the hover tilt felt like it took the better part of a
   second instead of .card-hover's own fast, snappy transition: it was
   quite literally running on the reveal's 0.8s transform transition, not
   card-hover's 0.18s one. Once revealed, hand transform/box-shadow speed
   back to whatever .card-hover declares (higher specificity here beats
   the cascade-order issue regardless of which file loads last). */
.ig-reveal.ig-revealed.card-hover {
  transition: transform .18s var(--ig-ease), box-shadow .18s ease, opacity .3s ease;
}

/* ---- scroll progress bar ---- */
.ig-scrollbar {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%;
  background: var(--ig-gradient-logo); z-index: 500; pointer-events: none;
}

/* ---- header depth + glass intensity once scrolled ---- */
/* slightly denser background once the page has moved, so nav text stays
   readable no matter what's scrolling behind the translucent glass */
.site-header { transition: background .25s ease, box-shadow .25s ease; }
.site-header.ig-scrolled {
  background: rgba(20,19,21,.82);
  box-shadow: 0 4px 20px rgba(0,0,0,.28);
}

/* ---- tilt cards: no transition while actively tracking the cursor, so
   the card's rotation follows the mouse immediately every frame instead
   of visibly lagging/easing behind it. Leaving the card still uses
   card-hover's normal transition (see the .ig-revealed override above),
   so it settles back smoothly rather than snapping to flat.
   Two rules, not one: a revealed card (.ig-reveal.ig-revealed.card-hover,
   3 classes) has higher specificity than a plain .card-hover.ig-tilting
   (2 classes), so without a matching 4-class override here, a revealed
   card would keep using ITS transition even while tilting -- right back
   to the same "not actually instant" problem this is fixing. */
.card-hover.ig-tilting,
.ig-reveal.ig-revealed.card-hover.ig-tilting {
  transition: transform 0s;
}

/* ---- hero glow layer (added by js/motion.js) ---- */
.hero { position: relative; overflow: hidden; }
/* gently drifts the hero's multi-blob gradient so the background itself
   feels alive, independent of the 3D blob and the headline above it.
   background-size/animation are applied on .hero in index.html's own
   <style> block instead of here — that rule sets `background` (the
   shorthand) later in the cascade, which would silently reset these
   sub-properties back to their initial values if they lived here. */
@keyframes ig-hero-bg-drift {
  0%, 100% { background-position: 0% 30%; }
  50% { background-position: 100% 70%; }
}
/* must sit above .ig-hero-bleed (z-index 3), which overlaps up into the
   bottom of the hero on purpose to blend the background — without this the
   bleed's white gradient was washing out the CTA buttons and stat numbers */
.hero .wrap { position: relative; z-index: 4; }
.ig-hero-glow {
  position: absolute; top: -200px; right: -180px; width: 760px; height: 760px;
  border-radius: 50%; pointer-events: none; z-index: 0;
  /* tinted to match the hero gradient's blue stop (#0072F5) instead of
     plain white, so it reads as a highlight on the blue rather than a
     whitish wash sitting on top of it */
  background: radial-gradient(circle, rgba(0,114,245,.55), rgba(0,114,245,0) 68%);
  filter: blur(4px);
  transition: transform .3s ease-out;
}
@media (max-width: 700px) { .ig-hero-glow { width: 440px; height: 440px; } }

/* ---- hero gradient bleed: softens the hard rectangular cutoff at the
   bottom of the hero into a tall, blurred, organic fade into the white page,
   built from a multi-stop gradient (avoids a visible hard edge/banding) plus
   two offset blurred color blooms instead of one flat circle ---- */
.ig-hero-bleed {
  position: relative; z-index: 3;
  height: 300px; margin-top: -300px;
  overflow: visible; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(255,255,255,0) 0%,
    rgba(255,255,255,.28) 38%,
    rgba(255,255,255,.7) 68%,
    var(--ig-bg) 100%);
}
.ig-hero-bleed::before,
.ig-hero-bleed::after {
  content: ""; position: absolute; border-radius: 50%; pointer-events: none;
}
.ig-hero-bleed::before {
  left: 2%; right: 32%; top: -110px; bottom: -30px;
  background: var(--ig-gradient-primary);
  filter: blur(80px);
  opacity: .4;
}
.ig-hero-bleed::after {
  left: 38%; right: 0%; top: -60px; bottom: -70px;
  background: linear-gradient(120deg, #F15F22, #F89938);
  filter: blur(90px);
  opacity: .32;
}
@media (max-width: 700px) {
  .ig-hero-bleed { height: 170px; margin-top: -170px; }
  .ig-hero-bleed::before { filter: blur(46px); top: -55px; bottom: -15px; }
  .ig-hero-bleed::after { filter: blur(50px); top: -30px; bottom: -35px; }
}

/* ---- ghost numeral watermark behind the hero headline ---- */
.ig-hero-ghost {
  position: absolute; z-index: 0; pointer-events: none; user-select: none;
  font-style: italic; font-weight: 200; line-height: .8;
  font-size: clamp(220px, 34vw, 620px);
  color: rgba(255,255,255,.09);
  right: -2%; bottom: -12%;
  white-space: nowrap;
  transition: transform .15s linear, opacity .3s ease;
}
@media (max-width: 700px) { .ig-hero-ghost { display: none; } }

/* ---- 3D wireframe shape, rendered via Three.js, sitting between the flat
   glow/ghost background layers (z-index 0) and the bleed/content (3–4) so
   it reads as part of the hero background rather than floating on top ---- */
.ig-hero-3d {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%;
  pointer-events: none; display: block;
}

/* ---- TRANSFORM headline: Fugaz One, translucent per-word color ----
   Three rounds of a canvas/mask-driven backdrop-filter blur (clipped to
   the exact glyph shapes) all rendered incorrectly in practice and
   couldn't be debugged further without a live browser to inspect, so
   this keeps the reliable, guaranteed-correct version: a plain
   translucent color fill, no blur, no masking. Simple text, no custom
   rendering pipeline to get wrong. */
.hero-transform-word {
  position: relative;
  display: inline-block;
  align-self: flex-start; /* shrink to the word's own width instead of stretching full flex-column width */
  width: fit-content;
  font-family: "Fugaz One", var(--ig-font-sans);
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0;
  color: rgba(255,255,255,.55); /* overwritten per-word by JS */
  transition: color .4s ease, transform .3s ease;
  transform-style: preserve-3d;
}

/* ---- rest of the hero copy: fades/drifts up smoothly as JS updates
   opacity + transform every scroll frame ---- */
.ig-hero-fade { transition: opacity .2s ease, transform .2s ease; }

/* ---- Featured Sessions cards: start stacked/overlapping (transform set
   inline by js/motion.js), then settle into their grid line while fading
   in as the section scrolls into view ---- */
.ig-stack-reveal {
  opacity: 0;
  transition: opacity .7s cubic-bezier(.16,1,.3,1), transform .8s cubic-bezier(.16,1,.3,1);
}
/* no !important here: js/motion.js clears the inline "stacked" transform
   itself once a card is revealed (see initBentoStack), rather than
   forcing it via a stylesheet !important. An !important rule would keep
   winning against ANY later inline transform too, including the
   tilt-on-hover effect every .card-hover element gets -- which is exactly
   why these cards never actually tilted on hover before. */
.ig-stack-reveal.ig-stack-revealed { opacity: 1; }

/* ---- full-bleed marquee ticker ---- */
/* the infinite scroll loop needs overflow hidden on whatever wraps the
   moving track, but that also clips every card's own resting box-shadow
   (--ig-shadow-02 has a 20px blur, so it paints well outside the card's
   own box) plus the lighter hover tilt -- not just on hover, all the
   time, on whichever cards sit near the top/bottom/side edges.
   Padding + an equal negative margin gives the clip boundary real
   headroom on every side without changing the visible footprint... but
   only if applied on an element that ISN'T also the one participating in
   normal page flow next to its own siblings. A negative top/bottom
   margin directly on .ig-marquee would collapse with the adjoining
   margin of whatever sits before/after it, shifting real layout (this is
   exactly the "awkward gap before the sponsor CTA card" a previous fix
   here caused). So the padding/negative-margin trick lives on a separate
   inner wrapper (.ig-marquee-clip, added by js/home.js) instead: the
   outer .ig-marquee keeps a normal box (zero margin of its own, so its
   own collapsing with neighbors is unaffected) and adds display:
   flow-root purely so the inner wrapper's negative margin can't escape
   through it and collapse with those same neighbors either. */
.ig-marquee { width: 100%; display: flow-root; }
.ig-marquee-clip {
  overflow: hidden; width: 100%;
  box-sizing: border-box;
  padding: 24px 20px;
  margin: -24px -20px;
}
.ig-marquee-track {
  display: flex; width: max-content; align-items: center;
  animation: ig-marquee-scroll 26s linear infinite;
}
.ig-marquee:hover .ig-marquee-track { animation-play-state: paused; }
@keyframes ig-marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
/* cards riding inside a content marquee (speaker/sponsor strips) need a fixed
   footprint and must not shrink, since the track sizes itself to max-content */
.ig-marquee-track.ig-marquee-cards { animation-duration: 34s; }
.ig-marquee .speaker-card-compact { flex: 0 0 auto; width: 260px; margin-right: 20px; }
.ig-marquee .sponsor-pill { flex: 0 0 auto; margin-right: 14px; }

/* ---- bento grid: one large card + smaller ones ---- */
.bento-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--ig-space-6); }
.bento-grid .bento-lg { grid-column: span 2; }
.bento-grid .bento-lg .feat-card-cover { height: 220px; }
@media (max-width: 980px) {
  .bento-grid { grid-template-columns: 1fr 1fr; }
  .bento-grid .bento-lg { grid-column: span 2; }
}
@media (max-width: 640px) {
  .bento-grid { grid-template-columns: 1fr; }
  .bento-grid .bento-lg { grid-column: span 1; }
  .bento-grid .bento-lg .feat-card-cover { height: 160px; }
}

/* ---- respect reduced motion: show everything immediately, no transforms ---- */
@media (prefers-reduced-motion: reduce) {
  .ig-reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .ig-stack-reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .ig-hero-fade { opacity: 1 !important; transform: none !important; transition: none !important; }
  .ig-hero-glow { display: none !important; }
}
