/* ============================================================
   LONGLEO PORTFOLIO — ANIMATIONS & EFFECTS
   ============================================================ */

/* ── 1. KEYFRAMES ── */

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(32px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes slideInLeft {
  from { opacity: 0; transform: translateX(-40px); }
  to { opacity: 1; transform: translateX(0); }
}

@keyframes slideInRight {
  from { opacity: 0; transform: translateX(40px); }
  to { opacity: 1; transform: translateX(0); }
}

@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.88); }
  to { opacity: 1; transform: scale(1); }
}

@keyframes pulseGlow {
  0%,
100% { box-shadow: 0 0 0 0 rgba(252, 211, 77, 0); }
  50% { box-shadow: 0 0 0 14px rgba(252, 211, 77, 0.18); }
}

@keyframes floatY {
  0%,
100% { transform: translateY(0px); }
  50% { transform: translateY(-10px); }
}

@keyframes shimmer {
  0% { background-position: -400px 0; }
  100% { background-position: 400px 0; }
}

@keyframes rotateSlow {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes borderPulse {
  0%,
100% { border-color: rgba(252,211,77,.15); }
  50% { border-color: rgba(252,211,77,.55); }
}

@keyframes typewriter {
  from { width: 0; }
  to { width: 100%; }
}

@keyframes blink {
  0%,
100% { border-color: transparent; }
  50% { border-color: rgba(252,211,77,.9); }
}

@keyframes heroOrb1 {
  0%,
100% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 0.75; }
  33% { transform: translate(80px, -60px) scale(1.15) rotate(120deg); opacity: 0.95; }
  66% { transform: translate(-40px, 40px) scale(0.9) rotate(240deg); opacity: 0.65; }
}

@keyframes heroOrb2 {
  0%,
100% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 0.65; }
  33% { transform: translate(-60px, 50px) scale(1.2) rotate(-120deg); opacity: 0.85; }
  66% { transform: translate(50px, -70px) scale(0.85) rotate(-240deg); opacity: 0.55; }
}

@keyframes heroOrb3 {
  0%,
100% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 0.55; }
  50% { transform: translate(70px, 70px) scale(1.25) rotate(180deg); opacity: 0.8; }
}

@keyframes statCount {
  from { opacity: 0; transform: translateY(20px) scale(0.85); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes pillFloat {
  0%,
100% { transform: translateY(0) rotate(-0.5deg); }
  50% { transform: translateY(-5px) rotate(0.5deg); }
}

@keyframes glowPulse {
  0%,
100% { opacity: 0.6; }
  50% { opacity: 1; }
}

@keyframes cardEntrance {
  from {
    opacity: 0;
    transform: translateY(30px) scale(0.96);
    filter: blur(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0);
  }
}

@keyframes skillBarFill {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* ── 2. SCROLL-TRIGGERED BASE ── */

.reveal {
  opacity: 0;
  transform: translateY(36px);
  transition: opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity, transform;
}

.reveal.revealed {
  opacity: 1;
  transform: translateY(0);
}

.reveal-left {
  opacity: 0;
  transform: translateX(-36px);
  transition: opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
}

.reveal-left.revealed {
  opacity: 1;
  transform: translateX(0);
}

.reveal-right {
  opacity: 0;
  transform: translateX(36px);
  transition: opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
}

.reveal-right.revealed {
  opacity: 1;
  transform: translateX(0);
}

.reveal-scale {
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

.reveal-scale.revealed {
  opacity: 1;
  transform: scale(1);
}

/* Stagger delays for children */
.stagger > *:nth-child(1) { transition-delay: 0.00s; }
.stagger > *:nth-child(2) { transition-delay: 0.08s; }
.stagger > *:nth-child(3) { transition-delay: 0.16s; }
.stagger > *:nth-child(4) { transition-delay: 0.24s; }
.stagger > *:nth-child(5) { transition-delay: 0.32s; }
.stagger > *:nth-child(6) { transition-delay: 0.40s; }
.stagger > *:nth-child(7) { transition-delay: 0.48s; }
.stagger > *:nth-child(8) { transition-delay: 0.56s; }
.stagger > *:nth-child(9) { transition-delay: 0.64s; }
.stagger > *:nth-child(10) { transition-delay: 0.72s; }
.stagger > *:nth-child(11) { transition-delay: 0.80s; }
.stagger > *:nth-child(12) { transition-delay: 0.88s; }

/* ── 3. HERO SECTION ── */

/* Animated orbs in hero background */
.hero-orb {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  filter: blur(130px);
  will-change: transform, opacity;
}

/* Performance guard: disable heavy blur orbs on mobile to prevent GPU overload */
@media (max-width: 719px) {
  .hero-orb {
    filter: blur(60px);
    opacity: 0.5;
  }
  .hero-orb-1, .hero-orb-2, .hero-orb-3 {
    animation-play-state: paused;
  }
}

.hero-orb-1 {
  width: 650px;
  height: 650px;
  top: -120px;
  left: -150px;
  background: radial-gradient(circle, rgba(251, 158, 27, 0.35), rgba(249, 115, 22, 0.15) 50%, transparent 80%);
  animation: heroOrb1 16s ease-in-out infinite;
}

.hero-orb-2 {
  width: 550px;
  height: 550px;
  bottom: -100px;
  right: -100px;
  background: radial-gradient(circle, rgba(249, 115, 22, 0.28), rgba(219, 39, 119, 0.15) 50%, transparent 80%);
  animation: heroOrb2 20s ease-in-out infinite;
}

.hero-orb-3 {
  width: 420px;
  height: 420px;
  top: 35%;
  right: 15%;
  background: radial-gradient(circle, rgba(252, 211, 77, 0.18), rgba(6, 182, 212, 0.1) 50%, transparent 80%);
  animation: heroOrb3 24s ease-in-out infinite reverse;
}

/* Hero content entrance animation */
.hero-content {
  animation: fadeUp 1s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: 0.2s;
}

.hero-visual {
  animation: slideInRight 1s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: 0.5s;
}

/* Pill: only float AFTER JS adds pill-float class (typing complete) */
.pill-float {
  animation: pillFloat 4s ease-in-out infinite;
}

/* Hero h1 letter spacing animation on load */
.hero h1 {
  animation: fadeIn 1.2s ease both;
  animation-delay: 0.25s;
}

/* Floating note gentle bob */
.floating-note {
  animation: floatY 5s ease-in-out infinite;
  will-change: transform;
}

/* Stat cards staggered entrance — start later so hero text appears first */
.stat-card {
  animation: statCount 0.8s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.stat-card:nth-child(1) { animation-delay: 1.0s; }
.stat-card:nth-child(2) { animation-delay: 1.15s; }
.stat-card:nth-child(3) { animation-delay: 1.3s; }

/* Pulse glow on brand mark.
   The old version animated box-shadow,
and .brand-mark sits in the fixed
   header — so it forced a repaint every frame for the entire visit. The ring
   is now a pseudo-element scaling and fading,
which the compositor handles. */
.brand-mark {
  position: relative;
}

.brand-mark::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 10px rgba(252, 211, 77, 0.18);
  opacity: 0;
  pointer-events: none;
  animation: brandPulse 3s ease-in-out infinite;
}

@keyframes brandPulse {
  0%,
100% { opacity: 0; transform: scale(.82); }
  50% { opacity: 1; transform: scale(1); }
}

/* ── 4. CARD HOVER UPGRADES ── */

/* Service card tilt + glow on hover */
.service-card {
  transform-style: preserve-3d;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
              box-shadow 0.35s ease,
              border-color 0.35s ease,
              background 0.35s ease;
}

.service-card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(135deg, rgba(252,211,77,0.06), transparent 60%);
  opacity: 0;
  transition: opacity 0.35s ease;
  pointer-events: none;
}

.service-card:hover::after {
  opacity: 1;
}

/* Experience card glow border */
.experience-card {
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
              border-color 0.35s ease,
              background 0.35s ease,
              box-shadow 0.35s ease;
}

.experience-card:hover {
  box-shadow: 0 0 0 1px rgba(252,211,77,.35),
              0 20px 60px rgba(0,0,0,.35),
              0 0 40px rgba(252,211,77,.08);
}

/* Portfolio thumb already has gradient overlay via .portfolio-thumb::after in styles.css */
/* Removed redundant ::before to avoid double-darkening on hover (Fix #18) */

/* Tool item shimmer on hover */
.tool-item {
  position: relative;
  overflow: hidden;
}

.tool-item::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 60%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255,255,255,0.08),
    transparent
  );
  /* translate, not `left` — `left` relayouts every frame */
  transform: translateX(-200%) skewX(-20deg);
  transition: transform var(--dur-slow) var(--ease-out);
  pointer-events: none;
}

.tool-item:hover::before {
  transform: translateX(250%) skewX(-20deg);
}

/* ── 5. SECTION TITLE UNDERLINE ANIMATION ── */

.section-title h2 {
  position: relative;
  display: inline-block;
}

/* ── 6. CONTACT SECTION HOVER UPGRADES ── */

.contact-item {
  transition: transform 0.28s ease, box-shadow 0.28s ease;
}

.contact-item:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 30px rgba(122,76,37,.12);
}

.btn-dark,
.btn-primary {
  position: relative;
  overflow: hidden;
}

.btn-dark::before,
.btn-primary::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.12), transparent);
  transform: translateX(-100%);
  transition: transform var(--dur-base) var(--ease-out);
}

.btn-dark:hover::before,
.btn-primary:hover::before {
  transform: translateX(100%);
}

/* ── 7. CURSOR SPOTLIGHT ── */

.cursor-spotlight {
  pointer-events: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 420px;
  height: 420px;
  margin: -210px 0 0 -210px; /* centre on the pointer without a second transform */
  border-radius: 50%;
  background: radial-gradient(circle, rgba(252,211,77,0.045) 0%, transparent 70%);
  z-index: var(--z-overlay);
  transition: opacity var(--dur-base) ease;
  mix-blend-mode: screen;
  will-change: transform;
}

/* Hide cursor spotlight on touch devices — no mouse to follow */
@media (hover: none) {
  .cursor-spotlight { display: none !important; }
}

/* ── 8. SKILLS + TOOLS ENTRANCE ── */

.skill-card {
  transition: transform 0.32s ease, box-shadow 0.32s ease, border-color 0.32s ease;
}

.skill-card:hover {
  transform: translateY(-4px) scale(1.02);
  box-shadow: 0 14px 40px rgba(28,25,23,.1);
  border-color: rgba(245,158,11,.4);
}

/* ── 9. SCROLL PROGRESS GLOW ── */

.scroll-progress {
  box-shadow: 0 0 12px rgba(252,211,77,.6), 0 0 4px rgba(252,211,77,.8);
}

/* ── 10. EYEBROW TAG SHIMMER ── */

.eyebrow {
  position: relative;
  overflow: hidden;
}

/* Sweep driven by transform,
not background-position. Five of these were
   repainting every frame,
forever,
including while far off screen —
   background-position is not a composited property. */
.eyebrow::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent);
  transform: translateX(-160%);
  animation: eyebrowSheen 3s ease-in-out 1s infinite;
}

@keyframes eyebrowSheen {
  0% { transform: translateX(-160%); }
  60%,
100% { transform: translateX(320%); }
}

/* ── 11. AVATAR CARD GLOW RING ── */

.about-image-inner {
  position: relative;
}

.about-image-inner::before {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: 46px;
  background: linear-gradient(135deg, rgba(252,211,77,.7), rgba(249,115,22,.4), transparent 60%);
  z-index: -1;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.about-image-inner:hover::before {
  opacity: 1;
}

/* ── 12. BACK TO TOP ── */
/* Hover handled by styles.css transform only — no rotation conflict (Fix #16) */

/* ── 13. HERO GRAIN TEXTURE ── */

.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.04'/%3E%3C/svg%3E");
  pointer-events: none;
  z-index: 1;
  opacity: 0.4;
}

/* ── 14. LINK LINE ARROW SLIDE ── */

.link-line {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transform: translateX(0);
  transition: transform var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}

.experience-card:hover .link-line {
  transform: translateX(4px);
}

/* ── 14a. SCROLL-DRIVEN MOTION ───────────────────────────────────────
   Native scroll timelines: the animation's progress *is* the scroll
   position,
computed off the main thread. No listener,
no rAF,
and it
   cannot fall behind under load the way a JS-driven equivalent does.

   Everything here is additive. Browsers without support keep the
   IntersectionObserver reveals and the JS rail,
which is why each block
   is guarded and why animations.js flips its own equivalents off when it
   sees support. */
@supports (animation-timeline: scroll()) {

  /* Scroll progress bar — hands a per-frame JS style write back to CSS. */
  .scroll-progress {
    animation: railGrowX linear;
    animation-timeline: scroll(root block);
  }

  /* Experience rail draws itself as the section crosses the viewport. */
  .experience-grid::after {
    animation: railGrow linear both;
    animation-timeline: view();
    animation-range: cover 12% cover 78%;
  }

  /* Hero recedes as it leaves rather than simply scrolling off, so the page
     reads as depth instead of a flat sheet of paper.

     Applied to the grid, NOT to .hero-content / .hero-visual: those already
     carry entrance animations, and an `animation:` shorthand there would
     replace them outright. Two animations on one element would also fight
     over `transform`, since the later one wins the whole property. */
  .hero-grid {
    animation: heroExit linear both;
    animation-timeline: view();
    animation-range: exit-crossing 0% exit-crossing 100%;
  }

  /* About portrait drifts against the scroll. Deliberately NOT applied to
     .portfolio-thumb img: a CSS animation outranks an inline style, so it
     would silently kill the hover parallax that JS writes there. Nothing
     here may touch opacity either — a scroll-driven fade that fails to
     resolve leaves real content invisible. */
  .about-image-inner {
    animation: parallaxDrift linear both;
    animation-timeline: view();
    animation-range: cover 0% cover 100%;
  }
}

@keyframes railGrow {
  from { transform: scaleY(0); }
  to { transform: scaleY(1); }
}

@keyframes heroExit {
  to { opacity: 0; transform: translateY(-40px) scale(.96); }
}

@keyframes parallaxDrift {
  from { transform: translateY(-16px); }
  to { transform: translateY(16px); }
}

@keyframes railGrowX {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* ── 14b. VIEW TRANSITION — portfolio filter change ──────────────────
   The default cross-fade is a full-page dissolve,
which is too heavy for a
   grid swap. Scoped to the grid,
shortened,
and given a slight vertical
   offset so the change reads as a cut rather than a fade-through. */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--dur-base);
  animation-timing-function: var(--ease-out);
}

::view-transition-old(root) {
  animation-name: vtOut;
}

::view-transition-new(root) {
  animation-name: vtIn;
}

@keyframes vtOut {
  to { opacity: 0; transform: translateY(-6px); }
}

@keyframes vtIn {
  from { opacity: 0; transform: translateY(10px); }
}

/* ── 15. REDUCED MOTION ──────────────────────────────────────────────

   Reduced means *less* movement,
not a dead page. What goes: looping
   ambient motion,
entrance travel,
parallax,
anything crossing the screen.
   What stays: hover and press feedback,
because that is state feedback the
   visitor needs in order to tell what is interactive — an earlier version
   blanket-reset `transform` here and silently killed every card hover on
   the site. */

/* 1. Looping / ambient animation stops outright. */

/* 2. Entrance travel collapses — these have no hover state of their own,
so resetting transform here costs nothing. */

/* 3. Visibility must never wait on a transition that may not run.
      Opacity only — deliberately not transform,
so :hover still lifts. */

/* 4. Everything else keeps a short fade instead of snapping. */

/* ── 16. PORTFOLIO CARD: fix hover vs show animation conflict ── */
/* Show animation uses opacity + translateY — hover uses translateY separately */
/* Separate transition properties prevent the conflict */
.portfolio-card {
  transition:
    opacity 0.35s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.28s ease,
    border-color 0.28s ease;
}
.portfolio-card.show:hover {
  /* Only apply hover transform when card is already visible */
  transform: translateY(-6px);
  box-shadow: 0 20px 40px rgba(245,158,11,0.16);
  border-color: rgba(245,158,11,0.4);
}

