/* ---------------------------------------------------------------
   Helper styles that don't belong in Tailwind utility classes.
   Keep everything here transform/opacity friendly (60fps rule).
------------------------------------------------------------------ */

::selection {
  background: rgba(139, 124, 246, 0.3);
}

/* Custom scrollbar (Webkit) — purely cosmetic, no layout impact */
::-webkit-scrollbar {
  width: 10px;
}
::-webkit-scrollbar-track {
  background: #0a0a0c;
}
::-webkit-scrollbar-thumb {
  background: #26262e;
  border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover {
  background: #3a3a44;
}

/* Hero line reveal masks: overflow hidden wrapper + inner span animated via transform */
.hero-line-inner {
  will-change: transform;
}

/* Bento / reveal / project cards get will-change only while GSAP marks them active */
[data-bento],
[data-reveal],
[data-project] {
  will-change: transform, opacity;
}

/* Parallax media layer must be slightly larger than its container so translateY never reveals edges */
.project-media {
  isolation: isolate;
}
.project-media-inner {
  width: 100%;
  height: 130%;
  top: -15%;
  will-change: transform;
}

/* Project card hover: scale via transform only, color/border via transition (composited, cheap) */
.project-card {
  transition: border-color 300ms ease;
}
.project-card:hover {
  border-color: rgba(139, 124, 246, 0.4);
}

/* Bento skill cards get the same subtle hover language as project cards: border
   brightens + a small lift, both cheap (color transition + transform), so the
   whole site feels consistently interactive instead of only the project grid. */
.card[data-bento] {
  transition: border-color 300ms ease, transform 300ms ease;
}
.card[data-bento]:hover {
  border-color: rgba(139, 124, 246, 0.4);
  transform: translateY(-4px);
}

/* Social icons: true-to-brand colors on hover only (stay neutral/muted at rest to
   match the site's restrained palette). GitHub's mark has no real brand color —
   it just goes full white/opaque. LinkedIn uses its blue. Instagram uses its
   official gradient via background-clip on the glyph itself. */
/* Bumped to `a.social-btn-*` (adds the element-type selector) so these definitely
   outrank the equal-class-specificity `.social-btn:hover` rule that Tailwind's
   browser JIT bakes in from the @apply block, regardless of which stylesheet
   the CDN compiler ends up inserting later in the document. */
a.social-btn-github:hover {
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.25);
}
a.social-btn-linkedin:hover {
  color: #0a66c2;
  border-color: rgba(10, 102, 194, 0.4);
  background-color: rgba(10, 102, 194, 0.1);
}
a.social-btn-instagram:hover {
  border-color: rgba(238, 42, 123, 0.4);
  background-color: rgba(238, 42, 123, 0.08);
}
a.social-btn-instagram:hover i {
  background: linear-gradient(45deg, #f9ce34, #ee2a7b, #6228d7);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Raised/floating screenshot mockup ("timbul"): full image always visible (no crop — sized to its
   own intrinsic aspect ratio inside a padded panel), sitting above the card on a static shadow so it
   reads as an elevated card. The shadow itself never animates (box-shadow triggers repaint, so per the
   60fps rule it's set once and left alone); only `transform` moves on hover. */
.shot {
  /* max-width/max-height (not a flat width:100%) so the image scales up against
     whichever dimension is tightest and actually fills the panel, instead of
     only ever sizing off the width and floating small inside a taller box. */
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  display: block;
  border-radius: 0.875rem;
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.75), 0 10px 25px -10px rgba(0, 0, 0, 0.5);
  transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform;
}
.project-card:hover .shot {
  transform: translateY(-6px) scale(1.02);
}

/* Wide screenshot "peek reveal": the .project-media container is deliberately given an
   aspect-ratio a little wider (shorter) than the screenshot's own ratio, so the image
   (rendered at its natural height from width:100%) is always a consistent ~6% taller than
   its box at ANY viewport width — proportional math, not a fixed pixel overscan, so it
   never runs out of room to pan on narrow screens. Top-anchored by default (shows the
   header/most recognizable part); hovering the card pans it up by that same ~6% to reveal
   the rest. Pure transform, no layout cost. */
.media-pan {
  height: auto;
  transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform;
}
.project-card:hover .media-pan {
  transform: translateY(-6.5%);
}

/* Magnetic elements need a transform origin + will-change while interacted with */
[data-magnetic] {
  will-change: transform;
}

/* Nav gets this once the page scrolls past the hero, set via ScrollTrigger toggleClass */
.nav-scrolled {
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.4);
}

/* Liquid glass sheen: a soft diagonal highlight baked onto cards/panes to read as curved glass
   catching light, purely decorative background — no layout or paint cost beyond first render. */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.09), transparent 45%);
  pointer-events: none;
}

/* Marquee row label: centered text flanked by two thin gradient dividers,
   built with flex + pseudo-elements so no extra markup is needed. Purely
   decorative and static — the divider lines don't animate, only the rows
   of chips below them do. */
.marquee-label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
}
.marquee-label::before,
.marquee-label::after {
  content: "";
  height: 1px;
  width: 100%;
  max-width: 5rem;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25));
}
.marquee-label::after {
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.25), transparent);
}

/* Tech stack marquee — pure CSS transform loop, no JS/GSAP work per frame.
   Track is the chip list duplicated once in the markup; animating to -50%
   loops seamlessly back to an identical starting frame. Once a user clicks
   a pagination arrow, main.js swaps this for direct GSAP control of `x`. */
.marquee-row {
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, black 4%, black 96%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, black 4%, black 96%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  will-change: transform;
  animation: marquee-left 40s linear infinite;
}

.marquee-track-reverse {
  animation-name: marquee-right;
  animation-duration: 36s;
}

@keyframes marquee-left {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@keyframes marquee-right {
  from { transform: translateX(-50%); }
  to { transform: translateX(0); }
}

/* Respect reduced motion: disable smooth scroll + let JS skip entrance timelines */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .marquee-track {
    animation: none;
  }
}
