/* ============================================================
   OBSIDIAN OBSERVATORY — Pranav Pradeesh Portfolio
   Palette: void black / bone white / acid chartreuse
   Type: Syne (display) + IBM Plex Mono (body)
   ============================================================ */

:root {
  --void: #060807;
  --void-soft: #0c0f0d;
  --bone: #e8e6df;
  /* body copy sits on top of the particle canvas — dim greys drown in the
     sphere's bloom, so secondary text stays close to bone */
  --bone-dim: #c2c5bd;
  --bone-mute: #9aa096;          /* labels only — never running prose */
  --acid: #c8ff00;
  --acid-dim: rgba(200, 255, 0, 0.14);
  --line: rgba(232, 230, 223, 0.1);
  --scrim: rgba(6, 8, 7, 0.8);   /* takes --bone-dim over a lit patch of the
                                    particle field from 2.9:1 to 9.8:1 */
  --font-display: 'Syne', sans-serif;
  --font-mono: 'IBM Plex Mono', monospace;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

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

/* Lenis owns scrolling — native smooth-behavior would fight it */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }

/* clip, not hidden: hidden on the root turns the viewport into a scroll
   container and still let a stray fixed element widen the document. clip
   cannot scroll, so nothing can produce a sideways scrollbar. */
html { overflow-x: clip; }

body {
  background: var(--void);
  color: var(--bone);
  font-family: var(--font-mono);
  font-weight: 300;
  overflow-x: clip;
  cursor: none;
}

::selection { background: var(--acid); color: var(--void); }

/* ============ CANVAS ============ */
#scene {
  position: fixed;
  inset: 0;               /* sizes to the viewport on its own — 100vw would
                             include the scrollbar gutter and overflow, and
                             100vh fights mobile toolbars */
  z-index: 0;
  display: block;
}

/* ============ GRAIN ============ */
.grain {
  position: fixed;
  /* only needs enough slack to cover the 2% drift in grain-shift. At 200%
     it was the single widest box on the page — 640px wide on a 320px
     screen — and pushed the document sideways. */
  inset: -6%;
  z-index: 50;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-shift 0.9s steps(4) infinite;
}
@keyframes grain-shift {
  0% { transform: translate(0, 0); }
  25% { transform: translate(-2%, 1%); }
  50% { transform: translate(1%, -2%); }
  75% { transform: translate(-1%, 2%); }
  100% { transform: translate(2%, -1%); }
}

/* ============ LOADER ============ */
.loader {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--void);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.8s var(--ease-out), visibility 0.8s;
}
.loader.done { opacity: 0; visibility: hidden; }
.loader__inner { display: flex; flex-direction: column; gap: 14px; width: min(360px, 80vw); }
.loader__label { font-size: 0.65rem; letter-spacing: 0.35em; color: var(--bone-mute); }
.loader__bar { height: 1px; background: var(--line); overflow: hidden; }
.loader__fill { height: 100%; width: 0%; background: var(--acid); transition: width 0.3s ease; }
.loader__pct { font-size: 0.65rem; color: var(--acid); align-self: flex-end; }

/* ============ SCROLL PROGRESS ============ */
.scroll-progress {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  height: 2px;
  background: var(--acid);
  transform: scaleX(0);
  transform-origin: left;
  z-index: 150;
  box-shadow: 0 0 12px rgba(200, 255, 0, 0.5);
}

/* ============ CURSOR ============ */
.cursor, .cursor-ring {
  position: fixed;
  top: 0; left: 0;
  pointer-events: none;
  z-index: 300;
  border-radius: 50%;
  transform: translate(-50%, -50%);
}
.cursor {
  width: 6px; height: 6px;
  background: var(--acid);
}
.cursor-ring {
  width: 34px; height: 34px;
  border: 1px solid rgba(200, 255, 0, 0.4);
  transition: width 0.25s var(--ease-out), height 0.25s var(--ease-out), border-color 0.25s;
}
.cursor-ring.hovering {
  width: 60px; height: 60px;
  border-color: var(--acid);
}
@media (hover: none) {
  .cursor, .cursor-ring { display: none; }
  body { cursor: auto; }
}

/* ============ CHROME ============ */
.chrome {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* fluid: at a fixed 40px the four nav links no longer fit beside the logo
     on a 320px screen and wrapped to a second row */
  padding: clamp(14px, 3vw, 28px) clamp(12px, 3vw, 40px);
  mix-blend-mode: difference;
}
.chrome__logo {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.4rem;
  color: var(--bone);
  text-decoration: none;
  letter-spacing: -0.02em;
}
/* Fluid rather than a breakpoint: the four links measured 309px against
   276px of usable width at 320px, so they ran off the screen. These clamps
   hold the design's values from ~1100px up and shrink continuously below. */
.chrome__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(8px, 2.2vw, 32px);
  min-width: 0;
}
.chrome__nav a {
  color: var(--bone);
  text-decoration: none;
  font-size: clamp(0.55rem, 1.6vw, 0.7rem);
  letter-spacing: clamp(0.08em, 0.5vw, 0.25em);
  text-transform: uppercase;
  position: relative;
  padding: 4px 0;
}
.chrome__nav a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--acid);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.4s var(--ease-out);
}
.chrome__nav a:hover::after { transform: scaleX(1); transform-origin: left; }
.chrome__status {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.6rem;
  letter-spacing: 0.25em;
  color: var(--bone-dim);
}
.pulse-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--acid);
  animation: pulse 2s ease infinite;
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(200, 255, 0, 0.5); }
  50% { box-shadow: 0 0 0 8px rgba(200, 255, 0, 0); }
}

/* ============ SECTION INDICATOR ============ */
.section-indicator {
  position: fixed;
  right: 40px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  font-size: 0.65rem;
  letter-spacing: 0.15em;
}
.section-indicator__num { color: var(--acid); }
.section-indicator__line { width: 1px; height: 70px; background: var(--line); }
.section-indicator__total { color: var(--bone-mute); }

/* ============ PANELS ============ */
main { position: relative; z-index: 10; }

.panel {
  min-height: 100vh;
  min-height: 100svh;     /* small-viewport unit: no jump as mobile chrome
                             collapses. Ignored by browsers that lack it. */
  padding: 140px 8vw 100px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: relative;
}

.accent { color: var(--acid); }

.kicker {
  font-size: 0.65rem;
  letter-spacing: 0.4em;
  color: var(--bone-dim);
  margin-bottom: 30px;
}

/* --- hero --- */
.panel--hero { align-items: flex-start; }
.hero-title {
  font-family: var(--font-display);
  font-weight: 800;
  /* vw cap: the rem floor grows with the user's default font size and at
     24px root it pushed BUILD. past the right edge of a 390px screen. */
  font-size: min(clamp(2.6rem, 8.6vw, 8.5rem), 15vw);
  line-height: 0.95;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
.hero-title__line { display: block; }
.hero-title__line--accent {
  color: transparent;
  -webkit-text-stroke: 1.5px var(--acid);
}
/* Without text-stroke support, transparent fill means the word vanishes
   entirely. Fall back to solid acid rather than an invisible line. */
@supports not (-webkit-text-stroke: 1px currentColor) {
  .hero-title__line--accent { color: var(--acid); }
}
.hero-sub {
  margin-top: 36px;
  font-size: 0.8rem;
  letter-spacing: 0.2em;
  color: var(--bone-dim);
}
.scroll-hint {
  position: absolute;
  bottom: 48px;
  left: 8vw;
  display: flex;
  align-items: center;
  gap: 16px;
  font-size: 0.6rem;
  letter-spacing: 0.4em;
  color: var(--bone-dim);
}
.scroll-hint__track {
  width: 60px; height: 1px;
  background: var(--line);
  position: relative;
  overflow: hidden;
}
.scroll-hint__thumb {
  position: absolute;
  width: 20px; height: 100%;
  background: var(--acid);
  animation: scroll-slide 1.8s var(--ease-out) infinite;
}
@keyframes scroll-slide {
  from { left: -20px; }
  to { left: 60px; }
}

/* --- shared section layout --- */
.panel__grid {
  display: grid;
  /* min-content, not a fixed 260px: the headings are single unbreakable words
     ("BUILDING") set in the display face, and a floor narrower than the word
     lets it overflow the track and run straight over the body column. That
     collided at every width from 861px to ~1600px. */
  grid-template-columns: minmax(min-content, 1fr) 1.6fr;
  gap: 60px;
  align-items: start;
}
/* break-word, never `anywhere`: `anywhere` shrinks the min-content
   contribution to a single glyph, which defeats the track floor above and
   hyphen-less-breaks BUILDING across lines. */
.panel__title { overflow-wrap: break-word; }
.panel__index {
  font-size: 0.65rem;
  letter-spacing: 0.3em;
  color: var(--acid);
  display: block;
  margin-bottom: 18px;
}
.panel__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: min(clamp(2.2rem, 5.5vw, 4.5rem), 10.5vw);
  line-height: 1;
  letter-spacing: -0.02em;
}
.panel__head--wide { margin-bottom: 70px; }

.panel__body { display: flex; flex-direction: column; gap: 26px; position: relative; }
.panel__body p { max-width: 56ch; line-height: 1.85; font-size: 0.92rem; color: var(--bone-dim); }
.lede { color: var(--bone) !important; font-size: 1.08rem !important; }

/* --- legibility scrim ---------------------------------------------------
   The WebGL field renders behind every panel and its bright core lands right
   under the About copy. This drops a dark shape behind text blocks and blurs
   the shape itself, so it feathers out on every side instead of reading as a
   box on top of the particles.
   ---------------------------------------------------------------------- */
.panel__body::before,
.panel--contact .contact-title::before,
.panel--contact .contact-actions::before {
  content: '';
  position: absolute;
  inset: -18px -30px;
  z-index: -1;
  background: var(--scrim);
  border-radius: 60px;
  filter: blur(30px);
  pointer-events: none;
}
/* On a phone the sphere fills the viewport, so the contact heading lands on
   its brightest part — the accent word all but disappears there. */
.panel--contact .contact-actions,
.panel--contact .contact-title { position: relative; }

/* The hero copy runs under the sphere's dense edge — 2.9:1 before this, the
   worst contrast left on the page. The title is bone at 8.5rem and needs no
   help, so only the small type gets a scrim. (.scroll-hint is already
   absolutely positioned, so it must not be given position: relative.) */
.panel--hero .kicker,
.hero-sub { position: relative; }

.panel--hero .kicker::before,
.hero-sub::before,
.scroll-hint::before {
  content: '';
  position: absolute;
  inset: -14px -26px;
  z-index: -1;
  background: var(--scrim);
  border-radius: 40px;
  filter: blur(26px);
  pointer-events: none;
}

/* Second line of defence where the scrim feathers out */
.panel__body p,
.hero-sub,
.kicker,
.skill-group li,
.stat__label,
.footer { text-shadow: 0 1px 14px rgba(6, 8, 7, 0.9); }

/* --- stats --- */
.stat-row { display: flex; gap: 50px; margin-top: 22px; flex-wrap: wrap; }
.stat { display: flex; flex-direction: column; gap: 8px; }
.stat__num {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.6rem;
  color: var(--acid);
}
.stat__label { font-size: 0.6rem; letter-spacing: 0.3em; color: var(--bone-dim); }

/* --- projects --- */
.projects {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.project {
  display: block;
  text-decoration: none;
  color: inherit;
  background: rgba(6, 8, 7, 0.82);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);   /* iOS/Safari before 18 */
  padding: 44px 36px 60px;
  position: relative;
  overflow: hidden;
  transition: background 0.4s;
}
.project::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%), var(--acid-dim), transparent 65%);
  opacity: 0;
  transition: opacity 0.4s;
}
.project:hover::before { opacity: 1; }
.project__tag {
  font-size: 0.58rem;
  letter-spacing: 0.3em;
  color: var(--acid);
  border: 1px solid rgba(200, 255, 0, 0.3);
  padding: 5px 10px;
  display: inline-block;
  margin-bottom: 26px;
}
.project__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.7rem;
  margin-bottom: 14px;
  letter-spacing: -0.01em;
}
.project__desc { font-size: 0.82rem; line-height: 1.75; color: var(--bone-dim); }
.project__cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 24px;
  font-size: 0.6rem;
  letter-spacing: 0.3em;
  color: var(--bone-dim);
  transition: color 0.3s;
}
.project__arrow { transition: transform 0.3s var(--ease-out); }
.project:hover .project__cta { color: var(--acid); }
.project:hover .project__arrow { transform: translateX(6px); }
.project__num {
  position: absolute;
  bottom: 18px; right: 22px;
  font-size: 0.6rem;
  letter-spacing: 0.2em;
  color: rgba(232, 230, 223, 0.62);   /* 3.4:1 at 0.42 — under AA */
}

/* --- skills --- */
#skills .panel__body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 40px;
}
.skill-group h4 {
  font-size: 0.62rem;
  letter-spacing: 0.35em;
  color: var(--acid);
  margin-bottom: 18px;
  font-weight: 500;
}
.skill-group ul { list-style: none; }
.skill-group li {
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.85rem;
  color: var(--bone-dim);
  transition: color 0.3s, padding-left 0.3s;
}
.skill-group li:hover { color: var(--bone); padding-left: 10px; }

/* --- contact --- */
.panel--contact {
  align-items: center;
  text-align: center;
  justify-content: center;
}
.contact-title {
  font-family: var(--font-display);
  font-weight: 800;
  /* 2.2rem floor put "SOMETHING REAL" at 366px inside a 282px column */
  font-size: min(clamp(1.6rem, 6.2vw, 5.6rem), 8vw);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 24px 0 50px;
}
.contact-actions {
  display: flex;
  align-items: center;
  gap: 26px;
  flex-wrap: wrap;
  justify-content: center;
}
.contact-ghost {
  color: var(--bone-dim);
  text-decoration: none;
  /* the label is a fixed-length URL, so it grows with the user's font size
     and stops fitting narrow screens — cap against the viewport too */
  font-size: min(0.78rem, 4vw);
  letter-spacing: 0.08em;
  padding: clamp(14px, 3vw, 20px) clamp(14px, 5vw, 26px);
  max-width: 100%;
  border: 1px solid var(--line);
  transition: color 0.3s, border-color 0.3s;
}
.contact-ghost:hover { color: var(--acid); border-color: rgba(200, 255, 0, 0.4); }
.contact-link {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  color: var(--void);
  background: var(--acid);
  text-decoration: none;
  font-size: min(clamp(0.8rem, 2vw, 1.05rem), 4.2vw);   /* email address */
  font-weight: 500;
  padding: clamp(14px, 4vw, 20px) clamp(16px, 6vw, 36px);
  max-width: 100%;
  letter-spacing: 0.05em;
  transition: transform 0.3s var(--ease-out), box-shadow 0.3s;
}
.contact-link:hover {
  box-shadow: 0 0 50px rgba(200, 255, 0, 0.35);
}
.contact-link__arrow { transition: transform 0.3s var(--ease-out); }
.contact-link:hover .contact-link__arrow { transform: translateX(8px); }

.footer {
  position: absolute;
  bottom: 32px;
  left: 8vw; right: 8vw;
  display: flex;
  justify-content: space-between;
  font-size: 0.58rem;
  letter-spacing: 0.25em;
  color: var(--bone-dim);
  flex-wrap: wrap;
  gap: 10px;
}

/* ============ ACCESSIBILITY / CRAWLER FALLBACK ============ */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Copy is revealed by JS. If the module never boots — CDN blocked, JS off,
   a crawler that renders but bails — the page must still read as a document
   instead of an empty black screen. `.js` is set inline in <head> and pulled
   back off by a watchdog when the bundle fails. */
html:not(.js) .reveal { opacity: 1; transform: none; }
html:not(.js) .loader { opacity: 0; visibility: hidden; }
html:not(.js) #scene { display: none; }
/* body hides the system cursor because JS draws a replacement. No JS means
   no replacement, so the visitor is left with no pointer at all. */
html:not(.js) body { cursor: auto; }
html:not(.js) .cursor,
html:not(.js) .cursor-ring { display: none; }

/* The custom cursor removes the usual hover affordance; keyboard users had
   nothing at all. */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--acid);
  outline-offset: 3px;
}

/* ============ REVEAL ANIMATIONS ============ */
.reveal {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 1s var(--ease-out), transform 1s var(--ease-out);
}
.reveal.visible { opacity: 1; transform: translateY(0); }
.reveal.visible:nth-child(2) { transition-delay: 0.08s; }
.reveal.visible:nth-child(3) { transition-delay: 0.16s; }
.reveal.visible:nth-child(4) { transition-delay: 0.24s; }

/* ============ RESPONSIVE ============ */
@media (max-width: 860px) {
  .hero-title { position: relative; }
  .hero-title::before {
    content: '';
    position: absolute;
    inset: -12px -22px;
    z-index: -1;
    background: var(--scrim);
    border-radius: 50px;
    filter: blur(28px);
    pointer-events: none;
  }

  .chrome__status { display: none; }
  .chrome__nav { gap: 18px; }
  .section-indicator { right: 16px; }
  .panel { padding: 120px 6vw 90px; }
  .panel__grid { grid-template-columns: 1fr; gap: 36px; }
  .scroll-hint { left: 6vw; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    transition-duration: 0.001s !important;
  }
  html { scroll-behavior: auto; }
}
