*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #000;
  font-family: system-ui, sans-serif;
  color: #fff;
  touch-action: none;
}

/*  vanta waves background  */

#bg {
  position: fixed;
  inset: 0;
  overflow: hidden;
  z-index: 0;
}

/* fallback for browsers without WebGL: looping animated WebP of the waves */
#bg.no-webgl {
  background: #000 url('../images/bg-fallback.webp') center center / cover no-repeat;
}

/*  stagger visualizer (centered logo animation)  */

.stagger-visualizer {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 60vh;
  height: 60vh;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  z-index: 5;
  pointer-events: none;
  /* entrance: the whole logo fades + scales in on page load. The keyframe keeps
     the centring translate so it doesn't override the static transform; the
     per-tile anime.js transforms live on the children, so they don't conflict. */
  animation: kd-logo-in 1.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.stagger-visualizer div {
  position: relative;
  /* 60vh / 30 cells: keep in sync with `grid` in js/main.js */
  width: 2vh;
  height: 2vh;
  will-change: transform;
}

/* seamless full-logo overlay: covers the whole 60vh grid, shown only while the
   logo is assembled and stationary so the per-tile seams never show. */
.stagger-visualizer .logo-full {
  position: absolute;
  top: 0;
  left: 0;
  width: 60vh;
  height: 60vh;
  background-image: url('../images/logo-grid.png');
  background-size: 60vh 60vh;
  background-repeat: no-repeat;
  pointer-events: none;
  /* above the glow trace so the opaque silhouette masks the glow's inner half,
     leaving only the outer halo visible (a real glow *around* the logo) */
  z-index: 5;
  /* visible by default: on first paint the logo is assembled */
  opacity: 1;
}

/* the default look: a slice of the logo.
   Each slice bleeds 1px past its cell (right + bottom) so neighbouring tiles
   overlap and the sub-pixel rounding gaps don't show as black seams. The
   top-left stays anchored, so background-position alignment is unaffected and
   the extra pixels are just the continuation of the logo image. */
.stagger-visualizer div .slice {
  position: absolute;
  top: 0;
  left: 0;
  width: calc(100% + 1px);
  height: calc(100% + 1px);
  background-image: url('../images/logo-grid.png');
  background-size: 60vh 60vh;
  background-repeat: no-repeat;
}

/* the random-mix look: a real "K" glyph sized to the square and tinted with
   the slice's own colour (sampled from the logo, exposed via --k-color).
   Hidden until the timeline crossfades it in. */
.stagger-visualizer div .k {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: Inter, system-ui, sans-serif;
  font-weight: 800;
  line-height: 1;
  font-size: 2vh;
  color: var(--k-color, #fff);
  opacity: 0;
}

/*  projects panel  */

.projects-container {
  position: fixed;
  left: 0;
  top: 0;
  width: 33.333%;
  height: 100vh;
  padding: 24px;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  align-content: flex-start;
  gap: 20px;
  /* above the logo animation: its tiles reach zIndex 100 but are confined to
     the .stagger-visualizer stacking context (z-index 5), so keeping the
     foreground UI well above that guarantees tiles never paint over it. */
  z-index: 201;
}

.project-link {
  display: block;
  position: relative;
  text-decoration: none;
  will-change: transform;
  /* solid-black backing for the resting icon tile (opaque so the glow can't
     bleed through and wash out the square: it only shows around the edge). */
  --icon-bg: #000;
  /* vertical dodge offset (px), set by js/main.js so a column slides out of the
     way of a hovered neighbour's title. Applied as a transitioned transform so
     the slide is smooth and independent of the (optional) kd-float bob. When the
     bob IS enabled it folds the same --shift-y into its own keyframes instead. */
  --shift-y: 0px;
  transform: translateY(var(--shift-y));
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}

.project-icon {
  position: relative;
  /* above the sliding label so the label appears to emerge from behind it */
  z-index: 2;
  width: clamp(48px, 5vw, 80px);
  height: clamp(48px, 5vw, 80px);
  border-radius: 12px;
  /* overflow visible so the hover-scaled glyph and its silhouette glow aren't
     clipped; the rounded corners still apply to the (rounded) background fill. */
  overflow: visible;
  /* slight-alpha black backing for the resting tile */
  background: var(--icon-bg);
  /* anchor the bottom-left corner so the hover scale only grows up and right:
     the bottom and left stay put, the top lifts above. */
  transform-origin: bottom left;
  /* per-icon glow tint (r,g,b triplet): computed from the icon's own pixels in
     js/main.js; falls back to white until/if that fails. Used as the hover glow
     colour so the silhouette glow matches the icon. */
  --glow-color: 255, 255, 255;
  transition: transform 0.3s ease, background-color 0.3s ease, filter 0.3s ease;
  /* STATIC single drop-shadow glow (no looping animation: a continuous filter
     pulse on every icon was the performance hit). drop-shadow tracks the
     element's rendered alpha: a rounded square while the tile is opaque, morphing
     to the glyph silhouette as the tile fades on hover. At rest it's white; on
     hover it intensifies AND shifts to --glow-color, interpolated by the filter
     transition above (one icon at a time). */
  filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
}

.project-icon img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.project-link:hover .project-icon,
.project-link.is-revealed .project-icon {
  /* the tile fades out (var(--icon-bg) -> transparent), which smoothly morphs
     the glow from rounded square to silhouette, while the glyph scales up:
     growing only up and right from the anchored bottom-left corner. The single
     drop-shadow just gets brighter/wider and shifts to the icon's --glow-color,
     interpolated by the transition. .is-revealed is the touch "first tap" state. */
  transform: scale(1.2);
  background-color: transparent;
  filter: drop-shadow(0 0 8px rgba(var(--glow-color), 0.9));
}

/* The title: plain text to the right of the icon, no background of its own.
   Hidden at rest, it fades + slides in on hover. A soft shadow keeps it legible
   over the animated background. */
.project-label {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  height: 100%;               /* matches the icon height so the text is centred */
  /* clear the icon (allowing for its hover grow-right), then a comfortable gap,
     before the title text */
  padding-left: calc(clamp(48px, 5vw, 80px) + 34px);
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.75);
  white-space: nowrap;
  opacity: 0;
  /* the icon scales 1.2 from its bottom-left, lifting its centre ~10% of the
     icon height upward; translateY(-10%) re-centres the title on that scaled
     icon (label height == icon height). -12px x is the resting slide-in offset. */
  transform: translate(-12px, -10%);
  transition:
    opacity 0.3s ease,
    transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}

.project-link:hover .project-label,
.project-link.is-revealed .project-label {
  opacity: 1;
  transform: translate(0, -10%);
}

/*  top text: parallax depth  */

.top-text {
  position: fixed;
  top: 10vh;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  z-index: 200;
  pointer-events: none;
  animation: kd-fade-down 1.1s ease both;
}

.kd-parallax-container {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 60px;
}

.kd-parallax-layer {
  position: absolute;
  font-family: Inter, system-ui, sans-serif;
  font-weight: 700;
  white-space: nowrap;
  letter-spacing: 0.04em;
  pointer-events: none;
  will-change: transform;
}

.kd-parallax-layer:nth-child(1) {
  font-size: 1.65rem;
  opacity: 0.06;
  color: #00e5ff;
}

.kd-parallax-layer:nth-child(2) {
  font-size: 1.60rem;
  opacity: 0.18;
  color: #bf5fff;
}

.kd-parallax-layer.kd-parallax-active {
  font-size: 1.55rem;
  opacity: 1;
  background: linear-gradient(135deg, #00e5ff 0%, #bf5fff 50%, #ff4fc8 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/*  bottom bar  */

.bottom-bar {
  position: fixed;
  bottom: 5vh;
  left: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  z-index: 200;
  animation: kd-fade-up 1.1s ease 0.25s both;
}

.social-links {
  display: flex;
  gap: 20px;
  align-items: center;
}

.social-links a {
  position: relative;
  display: block;
  width: 28px;
  height: 28px;
  opacity: 0.55;
  transition: opacity 0.3s ease, transform 0.2s ease;
}

.social-links a:hover {
  opacity: 1;
  transform: scale(1.15);
}

.social-links img {
  position: absolute;
  inset: 0;
  width: 28px;
  height: 28px;
  object-fit: contain;
}

/* white silhouette by default (uniform tint across all icons) */
.social-links .ic-white { opacity: 1; transition: opacity 0.3s ease; }
/* full-colour version revealed on hover */
.social-links .ic-color { opacity: 0; transition: opacity 0.3s ease; }

.social-links a:hover .ic-white { opacity: 0; }
.social-links a:hover .ic-color { opacity: 1; }

.social-github:hover  .ic-color { filter: drop-shadow(0 0 6px #fff); }
.social-youtube:hover  .ic-color { filter: drop-shadow(0 0 8px #ff0000); }
.social-instagram:hover .ic-color { filter: drop-shadow(0 0 8px #e1306c); }
.social-spotify:hover  .ic-color { filter: drop-shadow(0 0 8px #1db954); }
.social-linkedin:hover  .ic-color { filter: drop-shadow(0 0 8px #0a66c2); }

.name-label {
  font-size: 0.9rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.55);
}

/*  khost fixed bottom-right  */

.khost-link {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 210;
  display: block;
  opacity: 0.85;
  animation: kd-khost-in 1.1s ease 0.5s both;
}

.khost-link img {
  width: 36px;
  height: 36px;
  object-fit: contain;
}

/*  keyframes  */

/* center logo entrance (keeps the centring translate throughout) */
@keyframes kd-logo-in {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1);   }
}

/* idle bob; the --shift-y dodge offset is folded into translateY so the two
   compose (the column can slide out of the way while still floating). */
@keyframes kd-float {
  0%, 100% { transform: translate(0px,  var(--shift-y, 0px));             }
  25%      { transform: translate(2px,  calc(var(--shift-y, 0px) - 5px)); }
  50%      { transform: translate(-2px, calc(var(--shift-y, 0px) - 2px)); }
  75%      { transform: translate(1px,  calc(var(--shift-y, 0px) - 6px)); }
}

@keyframes kd-fade-down {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: translateY(0);     }
}

@keyframes kd-fade-up {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0);    }
}

/* khost keeps its resting 0.85 opacity at the end of the entrance */
@keyframes kd-khost-in {
  from { opacity: 0;    transform: translateY(16px); }
  to   { opacity: 0.85; transform: translateY(0);    }
}

/*  ACTIVE "completed!" glow (G) 
   A quick light pulse on the logo outline; drop-shadow follows the K's alpha,
   so it glows along the shape, not a box. Triggered via .logo-pulse on each
   completed cycle. (The SVG glow-trace below is the alternative, kept beside.) */
.stagger-visualizer .logo-full.logo-pulse {
  animation: kd-logo-pulse 1.8s ease-out;
}

@keyframes kd-logo-pulse {
  0% {
    filter:
      drop-shadow(-3px 0 0 rgba(0, 229, 255, 0))
      drop-shadow(0 0 0 rgba(191, 95, 255, 0))
      drop-shadow(3px 0 0 rgba(255, 79, 200, 0));
  }
  25% {
    filter:
      drop-shadow(-3px 0 7px rgba(0, 229, 255, 0.9))
      drop-shadow(0 0 6px rgba(191, 95, 255, 0.9))
      drop-shadow(3px 0 7px rgba(255, 79, 200, 0.9));
  }
  100% {
    filter:
      drop-shadow(-3px 0 0 rgba(0, 229, 255, 0))
      drop-shadow(0 0 0 rgba(191, 95, 255, 0))
      drop-shadow(3px 0 0 rgba(255, 79, 200, 0));
  }
}

/* "completed!" glow-trace: an SVG outline of the logo's own silhouette that
   lights up while a gradient light travels around it. Adapted from a
   button-hover effect; here the path is the traced logo and it is triggered
   programmatically (.glow-active) at each completed cycle. */
.stagger-visualizer {
  --glow-line-thickness: 4px;       /* a bit wider than the 2px original */
  --glow-line-length: 22;
  --glow-blur-size: 16px;           /* a bit wider glow */
  --animation-speed: 1600ms;
}

.stagger-visualizer .glow-container {
  pointer-events: none;
  position: absolute;
  inset: 0;                 /* aligns with the logo (viewBox maps image space) */
  width: 100%;
  height: 100%;
  overflow: visible;        /* let the blur bleed past the logo box */
  opacity: 0;
  z-index: 4;               /* below the logo overlay so only the outer halo shows */
}

.stagger-visualizer .glow-blur,
.stagger-visualizer .glow-line {
  fill: none;
  /* keep stroke + dash widths in screen px regardless of the viewBox scale */
  vector-effect: non-scaling-stroke;
  stroke-dasharray: var(--glow-line-length) calc(50 - var(--glow-line-length));
}

.stagger-visualizer .glow-line {
  stroke: url(#glow-gradient);
  stroke-width: var(--glow-line-thickness);
}

.stagger-visualizer .glow-blur {
  filter: blur(var(--glow-blur-size));
  stroke: url(#glow-gradient);
  stroke-width: var(--glow-blur-size);
}

/* triggered once per completed cycle (replaces the original :hover trigger) */
.stagger-visualizer.glow-active .glow-blur,
.stagger-visualizer.glow-active .glow-line {
  animation: kd-glow-travel var(--animation-speed) ease-in;
}

.stagger-visualizer.glow-active .glow-container {
  animation: kd-glow-visibility var(--animation-speed) ease-in;
}

@keyframes kd-glow-travel {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: -80px; }
}

@keyframes kd-glow-visibility {
  0%, 100% { opacity: 0; }
  25%, 75% { opacity: 1; }
}

/*  responsive: limited VERTICAL space 
   When the viewport is short, reclaim the dead space top and bottom: lift the
   tagline toward the top edge and push the name + social links toward the
   bottom edge so the centre logo keeps room. */
@media (max-height: 680px) {
  .top-text   { top: 5vh; }
  .bottom-bar { bottom: 2.5vh; }
}

@media (max-height: 520px) {
  .top-text   { top: 2.5vh; }
  .bottom-bar { bottom: 1vh; gap: 10px; }
  .kd-parallax-container { height: 46px; }
}

/*  responsive: limited HORIZONTAL space 
   The project rail is a fixed 33% column on wide screens; on narrow ones that
   eats the page. Collapse it to just the icon width (labels only appear on
   hover, as an overlay, so they don't claim layout width) and shrink the icons
   so the centre logo and text keep the stage. */
@media (max-width: 768px) {
  .projects-container {
    width: auto;
    max-width: 42vw;
    padding: 14px 12px;
    gap: 14px;
  }

  .project-icon {
    width: clamp(38px, 11vw, 56px);
    height: clamp(38px, 11vw, 56px);
  }

  .project-label {
    font-size: 14px;
    padding-left: calc(clamp(38px, 11vw, 56px) + 22px);
  }
}
