/* ============ FX — Resend-style 3D objects, tilt, spotlight, glow ============
   Layered on top of existing section styles. Hero is intentionally untouched. */

/* ---------- 3D canvas mounts (Three.js objects) ---------- */
.fx-3d {
  position: absolute;
  pointer-events: none;
  z-index: 0;
}

.fx-3d canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Sections that host a floating object need a positioning context */
#experience, #technology, #contact { position: relative; }
#skills { position: relative; overflow: hidden; }

/* Experience — chrome coil, hugging the right edge behind the rows */
.fx-3d--experience {
  top: clamp(136.8px, 15.84vw, 302.4px);
  right: clamp(-64.8px, -3.6vw, -28.8px);
  width: clamp(129.6px, 12.24vw, 223.2px);
  aspect-ratio: 1;
  opacity: 0;
  transition: opacity 1.2s ease .2s;
}

/* Skills — the signature cube gets its own band ABOVE the heading.
   Keep in sync with #skills .skills-heading margin-top in skills.css. */
.fx-3d--skills {
  top: clamp(43.2px, 3.6vw, 69.12px);
  left: 50%;
  transform: translateX(-50%);
  width: clamp(216px, 25.92vw, 446.4px);
  aspect-ratio: 16 / 10;
  opacity: 0;
  transition: opacity 1.4s ease .1s;
}

/* Feather the canvas edges as insurance: even if a highlight reaches the
   bounds it fades instead of cutting a straight line. Radii stay opaque far
   past the cube's own extent (~27% from centre) so geometry is never clipped. */
.fx-3d--skills canvas {
  -webkit-mask-image: radial-gradient(ellipse 85% 85% at 50% 50%, #000 68%, transparent 100%);
          mask-image: radial-gradient(ellipse 85% 85% at 50% 50%, #000 68%, transparent 100%);
}

/* Technology — chrome tori, top right beside the heading */
.fx-3d--technology {
  top: clamp(-21.6px, -0.72vw, 0px);
  right: clamp(-14.4px, 1.44vw, 43.2px);
  width: clamp(108px, 10.08vw, 172.8px);
  aspect-ratio: 1;
  opacity: 0;
  transition: opacity 1.2s ease .2s;
}

/* Contact — faceted gem, top right clear of the Get in Touch button */
.fx-3d--contact {
  top: clamp(-7.2px, -0.22vw, 0px);
  right: clamp(21.6px, 5.04vw, 136.8px);
  width: clamp(108px, 10.08vw, 180px);
  aspect-ratio: 1;
  opacity: 0;
  transition: opacity 1.2s ease .2s;
}

/* Resend's webgl-scale-in-fade entrance: fade + scale .8 → 1 (scale property
   is separate from transform, so the skills translateX(-50%) is untouched) */
.fx-3d {
  scale: .8;
  transition: opacity 1.1s ease .15s, scale 1.1s cubic-bezier(.36, .66, .6, 1) .15s;
}

.fx-3d.is-ready { opacity: 1; scale: 1; }

/* Content must sit above the canvases */
#experience .container,
#technology .container,
#contact .container { position: relative; z-index: 1; }

/* Skills layer order: background → glow (0) → cube canvas (1) → copy (2) → grain (3) */
.fx-3d--skills { z-index: 1; }
#skills .container,
#skills .skills-heading { position: relative; z-index: 2; }

/* ---------- Skills section: Resend dark-stage treatment ---------- */
#skills {
  /* Resend's blue spotlight (#00A3FF @ 13%) + a faint pink counter-glow */
  background:
    radial-gradient(100% 50% at 50% 0%, rgba(0, 163, 255, .13) 0%, transparent 50%),
    radial-gradient(ellipse 42% 30% at 78% 6%, rgba(255, 46, 84, .06), transparent 60%),
    #050505;
}

/* Grain sits ABOVE every layer — canvas, glow and copy alike. One shared
   surface texture is what stops the cube's canvas reading as a separate
   plate pasted onto the section. */
#skills::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: .5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .04 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Coloured light-field behind the cube. Screen blending on a near-black
   stage can only ADD light — it is mathematically unable to darken, so its
   box can never show up as a lighter or darker patch. */
#skills::after {
  content: "";
  position: absolute;
  z-index: 0;
  top: clamp(4.32px, 0.72vw, 21.6px);
  left: 50%;
  width: clamp(302.4px, 36vw, 633.6px);
  height: clamp(216px, 24.48vw, 432px);
  transform: translateX(-50%);
  pointer-events: none;
  mix-blend-mode: screen;
  background:
    radial-gradient(38% 42% at 48% 52%, rgba(60, 92, 255, .55), transparent 72%),
    radial-gradient(30% 34% at 27% 42%, rgba(250, 68, 108, .32), transparent 74%),
    radial-gradient(34% 38% at 73% 46%, rgba(142, 78, 255, .36), transparent 74%);
  filter: blur(34px);
  animation: skills-glow-drift 14s ease-in-out infinite alternate;
}

@keyframes skills-glow-drift {
  from { transform: translate(-51%, 1%) scale(1); }
  to   { transform: translate(-49%, -1%) scale(1.06); }
}

@media (prefers-reduced-motion: reduce) {
  #skills::after { animation: none; }
}

/* ---------- Rainbow rotating-border pill (Resend announcement badge) ---------- */
@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@keyframes fx-angle-spin { to { --angle: 360deg; } }

#skills .skills-credit {
  align-self: flex-start;
  padding: 5.76px 12.96px;
  border: 1px solid transparent;
  border-radius: 999px;
  background:
    linear-gradient(#0b0e14, #0b0e14) padding-box,
    linear-gradient(var(--angle, 0deg), #02fcef70, #ffb52b70 50%, #a02bfe70) border-box;
  animation: fx-angle-spin 12s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  #skills .skills-credit { animation: none; }
}

/* ---------- Spotlight + border-glow cards (skills) ---------- */
#skills .skill-card {
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, .06);
  background-clip: padding-box;
}

/* cursor-tracking spotlight (JS sets --mx / --my in px) */
#skills .skill-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .5s ease;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 50%),
    rgba(255, 255, 255, .065), transparent 65%);
}

#skills .skill-card:hover::before { opacity: 1; }

/* gradient border glow that follows the cursor */
#skills .skill-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  padding: 1px;
  background: radial-gradient(240px circle at var(--mx, 50%) var(--my, 50%),
    rgba(120, 140, 255, .55), rgba(255, 255, 255, .08) 42%, transparent 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  transition: opacity .5s ease;
}

#skills .skill-card:hover::after { opacity: 1; }

/* ---------- 3D tilt cards (projects + technology) ---------- */
#projects .projects-grid,
#technology .tech-grid { perspective: 1400px; }

.fx-tilt {
  transform-style: preserve-3d;
  will-change: transform;
}

/* glare sweep that rides the tilt (JS sets --gx / --gy).
   screen on the dark projects stage so it reads as a specular highlight. */
.fx-tilt .project-media::after,
.fx-tilt .tech-media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .45s ease;
  background: radial-gradient(58% 52% at var(--gx, 50%) var(--gy, 45%),
    rgba(255, 255, 255, .20), rgba(255, 255, 255, .05) 44%, transparent 70%);
  mix-blend-mode: screen;
}

.fx-tilt:hover .project-media::after,
.fx-tilt:hover .tech-media::after { opacity: 1; }

/* the media lifts slightly off the card plane while tilted */
.fx-tilt .project-media,
.fx-tilt .tech-media { transform: translateZ(0); }

/* ---------- Scroll-linked text reveal (about statement) ---------- */
.fx-word {
  display: inline-block;
  opacity: .12;
  transition: opacity .35s linear;
}

.fx-word.is-lit { opacity: 1; }

/* ---------- Magnetic elements (contact button, explore btn) ---------- */
.fx-magnet { will-change: transform; }

/* ---------- Experience: scroll progress beam on the list ---------- */
#experience .experience-list { position: relative; }

#experience .experience-list::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  transform-origin: left;
  transform: scaleX(var(--beam, 0));
  background: linear-gradient(90deg, var(--accent), rgba(15, 181, 166, .15));
  z-index: 1;
}

/* role title picks up the accent on row hover */
#experience .experience-row:hover .experience-role h3 { color: var(--accent); }

/* ---------- Grounding shadows under floating chrome objects ---------- */
.fx-3d--experience::after,
.fx-3d--technology::after {
  content: "";
  position: absolute;
  left: 18%;
  right: 18%;
  bottom: 4%;
  height: 9%;
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(0, 0, 0, .20), transparent 72%);
  filter: blur(6px);
  z-index: -1;
}

/* ---------- Dock: magnify + tooltip micro-interactions ---------- */
.dock-item {
  position: relative;
  transition: transform .28s cubic-bezier(.34, 1.56, .64, 1);
}

.dock-item:hover { transform: scale(1.18) translateY(-5px); }

.dock-item::before {
  content: attr(aria-label);
  position: absolute;
  bottom: calc(100% + 8.64px);
  left: 50%;
  translate: -50% 4px;
  padding: 3.6px 8.64px;
  border-radius: 5.76px;
  background: #232323;
  border: 1px solid rgba(255, 255, 255, .08);
  color: #eee;
  font-family: var(--font-display);
  font-size: 13px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease, translate .25s ease;
}

.dock-item:hover::before { opacity: 1; translate: -50% 0; }

/* ---------- Reduced motion / small screens ---------- */
@media (prefers-reduced-motion: reduce) {
  .fx-3d { display: none; }
  .fx-word { opacity: 1; transition: none; }
}

@media (max-width: 820px) {
  .fx-3d--experience,
  .fx-3d--technology { width: clamp(129.6px, 24.48vw, 187.2px); opacity: .85; }
  .fx-3d--contact { display: none; }
}
