/* Custom styles that extend Tailwind (loaded via CDN in index.html).
   Component-level utility classes live here instead of inline <style> tags. */

html { scroll-behavior: smooth; }
body {
  background-color: #0A0A0A;
  /* Subtle dot-grid texture across the whole page - modern SaaS-style backdrop,
     kept low-opacity so it never competes with content. */
  background-image: radial-gradient(rgba(245, 245, 242, 0.05) 1px, transparent 1px);
  background-size: 28px 28px;
  position: relative;
}
/* Fine film-grain noise overlay - breaks up the flat black so large sections don't
   look like solid digital panels. Extremely subtle, fixed so it doesn't scroll-repeat oddly. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
::selection { background: #3D5CFF; color: #fff; }

/* Softer border tone used in place of hard #2E2E2E lines on cards - lower contrast,
   feels less like wireframe/dashboard boxes. */
.border-soft { border-color: rgba(245, 245, 242, 0.08) !important; }

/* Gradient "fade" divider - replaces solid border-y on section wrappers so sections
   blend into each other instead of being sliced apart by a hard rule. */
.fade-divider-top {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(245, 245, 242, 0.12) 50%, transparent);
}
.fade-divider-bottom {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(245, 245, 242, 0.12) 50%, transparent);
}

/* ---------- Interactive particle network background ---------- */
/* See js/controllers/particle-network-controller.js. A single canvas spans from the top
   of #about to the bottom of #contact - dots drift slowly and draw a connecting line to
   any neighbor within range (classic "particles.js" network effect), and the mouse acts
   as an extra particle so nearby dots link up to the cursor as it moves. Sits behind all
   section content (z-index 0) so text/cards remain fully readable on top of it. */
.particle-network-layer {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 0;
  pointer-events: none;
}
.particle-network-layer canvas {
  display: block;
  width: 100%;
  height: 100%;
}

@media (max-width: 640px) {
  .particle-network-layer { display: none; }
}

/* ---------- Custom cursor ---------- */
/* See js/controllers/custom-cursor-controller.js. A small signal-blue dot tracks the mouse
   exactly; a larger ring trails behind it with easing. Both grow/brighten on hoverable
   elements. Only active on desktop pointer devices (script bails out on touch/reduced motion),
   so the real cursor is only hidden once custom-cursor-active is added to <body>. */
.custom-cursor-active { cursor: none; }
.custom-cursor-active a,
.custom-cursor-active button,
.custom-cursor-active .gallery-thumb,
.custom-cursor-active .shot-card {
  cursor: none;
}

.custom-cursor-dot,
.custom-cursor-ring {
  position: fixed;
  top: 0;
  left: 0;
  border-radius: 50%;
  pointer-events: none;
  z-index: 10000;
  transform: translate(-9999px, -9999px);
  transition: opacity .2s ease;
}
.custom-cursor-dot {
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  background: #3D5CFF;
  box-shadow: 0 0 8px 2px rgba(61, 92, 255, 0.6);
}
.custom-cursor-ring {
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border: 1.5px solid rgba(61, 92, 255, 0.5);
  transition: width .25s ease, height .25s ease, margin .25s ease, border-color .25s ease, opacity .2s ease;
}

.custom-cursor-hover .custom-cursor-ring {
  width: 54px;
  height: 54px;
  margin: -27px 0 0 -27px;
  border-color: rgba(61, 92, 255, 0.9);
  background: rgba(61, 92, 255, 0.08);
}
/* Note: dot/ring position is driven by inline transform set in JS every frame, so the
   "pressed" state below only adjusts width/height/margin/border (properties JS never
   touches) rather than transform, which inline styles would otherwise always win over. */
.custom-cursor-down .custom-cursor-ring {
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-color: rgba(61, 92, 255, 1);
}

/* Soft ambient glow blobs - purely decorative, positioned behind section content
   via .glow-orb + inline placement classes. Keeps the same ink/signal palette. */
.glow-orb {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(61, 92, 255, 0.35), transparent 70%);
  filter: blur(40px);
  pointer-events: none;
  z-index: 0;
}

/* Gradient underline used beneath section headings for a bit of modern polish. */
.heading-underline {
  display: block;
  width: 48px;
  height: 3px;
  border-radius: 999px;
  margin-top: 14px;
  background: linear-gradient(90deg, #3D5CFF, rgba(61, 92, 255, 0));
}

/* Glass-style card: used for about stats. Softer border tone and larger radius than a
   default 1px/rounded-2xl combo so it reads as "designed" rather than a plain wireframe box. */
.glass-card {
  background: linear-gradient(145deg, rgba(28, 28, 28, 0.6), rgba(16, 16, 16, 0.6));
  border: 1px solid rgba(245, 245, 242, 0.08);
  border-radius: 20px;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.04) inset, 0 20px 40px -32px rgba(0, 0, 0, 0.6);
  transition: border-color .35s ease, transform .35s ease, box-shadow .35s ease;
}
.glass-card:hover {
  border-color: rgba(61, 92, 255, 0.45);
  transform: translateY(-4px);
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.06) inset, 0 24px 48px -24px rgba(61, 92, 255, 0.3);
}

/* ---------- Terminal-window skill cards ---------- */
/* Each skill category rendered as a mini terminal window - matches the site's existing
   "$ whoami" / "jay@dev:~$" terminal motif used in the hero, so the Skills section feels
   like a natural extension of that language instead of a generic tag cloud. */
.terminal-card {
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(245, 245, 242, 0.1);
  background: #0D0D0D;
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.05) inset, 0 24px 48px -32px rgba(0, 0, 0, 0.7);
  transition: border-color .35s ease, transform .35s ease, box-shadow .35s ease;
}
.terminal-card:hover {
  border-color: rgba(61, 92, 255, 0.4);
  transform: translateY(-4px);
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.06) inset, 0 28px 56px -28px rgba(61, 92, 255, 0.28);
}
.terminal-card-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px;
  background: rgba(245, 245, 242, 0.03);
  border-bottom: 1px solid rgba(245, 245, 242, 0.08);
}
.terminal-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.terminal-dot.red { background: #ff5f57; }
.terminal-dot.yellow { background: #febc2e; }
.terminal-dot.green { background: #28c840; }
.terminal-card-title {
  margin-left: 6px;
  font-family: "JetBrains Mono", monospace;
  font-size: 11px;
  color: rgba(245, 245, 242, 0.4);
}
.terminal-card-body { padding: 20px; }
.terminal-prompt {
  font-family: "JetBrains Mono", monospace;
  font-size: 12px;
  color: #3D5CFF;
  margin-bottom: 12px;
  display: block;
}
.terminal-prompt::before { content: '$ '; color: rgba(245, 245, 242, 0.35); }

/* ---------- Code-editor style cards ---------- */
/* Used for the Production Stack breakdown - styled like a code editor panel (file tab
   + line-numbered body) rather than a terminal window, so it reads as a distinct "artifact"
   from the Skills terminal cards while staying in the same developer-tool visual language. */
.editor-card {
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(245, 245, 242, 0.1);
  background: #0D0D0D;
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.05) inset, 0 24px 48px -32px rgba(0, 0, 0, 0.7);
  transition: border-color .35s ease, transform .35s ease, box-shadow .35s ease;
}
.editor-card:hover {
  border-color: rgba(61, 92, 255, 0.4);
  transform: translateY(-4px);
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.06) inset, 0 28px 56px -28px rgba(61, 92, 255, 0.28);
}
.editor-card-tab {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: rgba(61, 92, 255, 0.1);
  border-bottom: 1px solid rgba(245, 245, 242, 0.08);
}
.editor-card-tab svg { flex-shrink: 0; color: #3D5CFF; }
.editor-card-tab span {
  font-family: "JetBrains Mono", monospace;
  font-size: 12px;
  color: rgba(245, 245, 242, 0.75);
}
.editor-card-body { padding: 20px 20px 20px 0; }
.editor-line {
  display: flex;
  gap: 14px;
  padding: 3px 20px;
  font-size: 13px;
  line-height: 1.7;
}
.editor-line-num {
  font-family: "JetBrains Mono", monospace;
  font-size: 12px;
  color: rgba(245, 245, 242, 0.22);
  min-width: 16px;
  text-align: right;
  -webkit-user-select: none;
  user-select: none;
  flex-shrink: 0;
}
.editor-line-text { color: rgba(245, 245, 242, 0.7); }
.editor-line-text strong { color: #F5F5F2; font-weight: 600; }
.editor-line-text .tok-comment { color: rgba(245, 245, 242, 0.35); }

/* Small colored dot used as a category/list marker instead of an icon font. */
.dot-marker {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #3D5CFF;
  box-shadow: 0 0 8px 1px rgba(61, 92, 255, 0.6);
  display: inline-block;
  flex-shrink: 0;
}

/* Timeline period chip used on experience/project entries. */
.period-chip {
  font-family: "JetBrains Mono", monospace;
  font-size: 11px;
  color: rgba(245, 245, 242, 0.5);
  border: 1px solid rgba(245, 245, 242, 0.1);
  background: rgba(245, 245, 242, 0.03);
  border-radius: 999px;
  padding: 4px 10px;
  white-space: nowrap;
}

.reveal { opacity: 0; transform: translateY(24px); transition: opacity .7s ease, transform .7s ease; }
.reveal.in { opacity: 1; transform: translateY(0); }

.signal-dot {
  box-shadow: 0 0 0 0 rgba(61, 92, 255, .6);
  animation: pulse 2.4s infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(61, 92, 255, .55); }
  70% { box-shadow: 0 0 0 10px rgba(61, 92, 255, 0); }
  100% { box-shadow: 0 0 0 0 rgba(61, 92, 255, 0); }
}

.cursor-blink::after {
  content: '_';
  color: #3D5CFF;
  animation: blink 1s step-start infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* ---------- 3D tilt project cards ---------- */
/* .project-card = outer element, gives the 3D perspective context.
   .tilt-card-inner = the visible card surface, actually rotated by tilt-controller.js
   based on cursor position. .tilt-glare = a soft light that follows the cursor,
   like light hitting glass - both are common "premium" portfolio card effects. */
.project-card {
  perspective: 1200px;
}
.tilt-card-inner {
  position: relative;
  overflow: hidden;
  transform-style: preserve-3d;
  border-color: rgba(245, 245, 242, 0.08) !important;
  border-radius: 22px !important;
  background: linear-gradient(150deg, rgba(26, 26, 26, 0.6), rgba(14, 14, 14, 0.55)) !important;
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.04) inset, 0 24px 48px -36px rgba(0, 0, 0, 0.65);
  transition: transform .5s cubic-bezier(.22, 1, .36, 1), border-color .35s ease, box-shadow .35s ease;
  will-change: transform;
}
.project-card:hover .tilt-card-inner,
.project-card.tilt-active .tilt-card-inner {
  border-color: rgba(61, 92, 255, 0.45) !important;
  box-shadow: 0 1px 0 rgba(245, 245, 242, 0.06) inset, 0 32px 64px -30px rgba(61, 92, 255, 0.4);
}
.tilt-glare {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%), rgba(61, 92, 255, .3), transparent 55%);
  opacity: 0;
  transition: opacity .3s ease;
  z-index: 5;
}
.project-card:hover .tilt-glare { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .tilt-card-inner { transition: border-color .3s ease, box-shadow .3s ease !important; transform: none !important; }
}

/* ---------- Fanned screenshot stack ---------- */
/* Shows a handful of screenshots as an overlapping "hand of cards" that spreads apart
   on hover, instead of a flat thumbnail grid. Click any card to open the full lightbox
   gallery (still cycles through every screenshot for the project, not just the ones shown here). */
.shot-stack {
  position: relative;
  height: 168px;
  margin-top: 8px;
}
.shot-card {
  position: absolute;
  top: 0;
  left: 0;
  width: 60%;
  height: 100%;
  object-fit: cover;
  border-radius: 14px;
  border: 1px solid rgba(245, 245, 242, 0.12);
  box-shadow: 0 16px 32px -16px rgba(0, 0, 0, .7);
  cursor: zoom-in;
  transition: transform .45s cubic-bezier(.22, 1, .36, 1), border-color .3s ease, box-shadow .3s ease;
  transform: translateX(calc(var(--i) * 16px)) translateY(calc(var(--i) * 4px)) rotate(calc(var(--i) * 3.5deg));
  transform-origin: bottom left;
  z-index: calc(10 - var(--i));
}
.shot-stack:hover .shot-card {
  transform: translateX(calc(var(--i) * 56px)) translateY(0) rotate(calc(var(--i) * 5deg));
  border-color: rgba(61, 92, 255, 0.6);
  box-shadow: 0 20px 40px -16px rgba(61, 92, 255, .45);
}
.shot-stack:hover .shot-card:hover {
  transform: translateX(calc(var(--i) * 56px)) translateY(-10px) rotate(calc(var(--i) * 5deg)) scale(1.04);
  z-index: 20;
}
.shot-count-badge {
  position: absolute;
  bottom: 8px;
  right: 8px;
  z-index: 25;
  font-family: "JetBrains Mono", monospace;
  font-size: 11px;
  color: #F5F5F2;
  background: rgba(10, 10, 10, .85);
  border: 1px solid rgba(245, 245, 242, 0.12);
  border-radius: 999px;
  padding: 3px 9px;
  pointer-events: none;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}

.stack-chip { transition: background .25s ease, color .25s ease, border-color .25s ease; }
.stack-chip:hover { background: #3D5CFF; color: #fff; border-color: #3D5CFF; }

.skill-tag {
  border-color: rgba(245, 245, 242, 0.1) !important;
  background: rgba(245, 245, 242, 0.03);
  transition: background .2s ease, color .2s ease, border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
.skill-tag:hover {
  background: #3D5CFF;
  color: #fff;
  border-color: #3D5CFF !important;
  transform: translateY(-2px);
  box-shadow: 0 10px 20px -10px rgba(61, 92, 255, 0.6);
}

#hero-canvas { touch-action: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { transition: none !important; opacity: 1 !important; transform: none !important; }
  .signal-dot { animation: none; }
}

::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #0A0A0A; }
::-webkit-scrollbar-thumb { background: #2E2E2E; border-radius: 4px; }

a:focus-visible, button:focus-visible {
  outline: 2px solid #3D5CFF;
  outline-offset: 3px;
}

/* Project gallery lightbox (used by js/project-gallery.js) */
.gallery-thumb { transition: transform .25s ease, border-color .25s ease; cursor: zoom-in; }
.gallery-thumb:hover { transform: scale(1.03); border-color: #3D5CFF; }

/* Profile picture card styling */
.profile-card {
  position: relative;
  overflow: hidden;
  border-radius: 20px;
  border: 1px solid rgba(245, 245, 242, 0.1);
  box-shadow: 0 20px 40px -32px rgba(0, 0, 0, 0.7);
  transition: border-color .35s ease, transform .35s ease, box-shadow .35s ease;
}
.profile-card:hover {
  border-color: rgba(61, 92, 255, 0.5);
  transform: translateY(-4px);
  box-shadow: 0 0 0 1px rgba(61, 92, 255, 0.2), 0 24px 48px -20px rgba(61, 92, 255, 0.35);
}
.profile-card img {
  width: 100%;
  height: auto;
  display: block;
  transition: transform .3s ease;
}
.profile-card:hover img {
  transform: scale(1.05);
}

#lightbox { transition: opacity .2s ease; }
#lightbox.hidden-lightbox { opacity: 0; pointer-events: none; }
