@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;600;700&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Pinyon+Script&display=swap');

/* ------------------------------------------------------------------
   Kunal Arora — a portfolio, bound as a book.
------------------------------------------------------------------ */

:root {
  --bg:      #eeece1;
  --bg-deep: #e4e3d4;
  --ink:     #3f372c;
  --faint:   #8d8471;
  --umber:   #8a6a43;
  --ochre:   #c39a5c;
  --rust:    #a8623f;
  --hair:    rgba(63, 55, 44, 0.16);

  --serif: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --hand:  'Caveat', 'Segoe Script', cursive;
  --calig: 'Pinyon Script', 'Segoe Script', cursive;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  min-height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--serif);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Watercolour ground: a few soft washes plus a fine paper grain. */
body::before,
body::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

body::before {
  background:
    radial-gradient(58vw 46vw at 8% 4%,   rgba(141, 150, 120, 0.20), transparent 62%),
    radial-gradient(46vw 40vw at 96% 10%, rgba(195, 154, 92, 0.18),  transparent 64%),
    radial-gradient(52vw 48vw at 92% 94%, rgba(141, 150, 120, 0.17), transparent 63%),
    radial-gradient(44vw 40vw at 4% 92%,  rgba(168, 98, 63, 0.11),   transparent 66%),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-deep) 100%);
}

body::after {
  opacity: 0.5;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.16'/%3E%3C/svg%3E");
}

/* ---------------- stage ---------------- */

#stage {
  position: fixed;
  inset: 0;
  /* Above #ground, which is itself above the wash and its grain. */
  z-index: 2;
}

#gl {
  display: block;
  width: 100%;
  height: 100%;
}

#spacer { position: relative; z-index: 0; }

/* ---------------- the ground behind the book ---------------- */

/* Aristotle's celestial spheres, turning, and a quill lying at rest.
   The layer sits above the paper wash *and its grain* on purpose: the
   grain is a multiply blend across the whole viewport, so keeping the
   moving parts above it means the blend's backdrop never changes and
   the browser can cache it instead of recompositing it every frame.
   Everything here animates transform or opacity only, which keeps it
   on the compositor and off the book's frame budget. */
#ground {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  contain: layout paint style;
}

/* --- the spheres --- */

.cosmos {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.cosmos circle {
  fill: none;
  stroke: rgba(63, 55, 44, 0.13);
  vector-effect: non-scaling-stroke;
}

.cosmos .mark { fill: rgba(138, 106, 67, 0.34); stroke: none; }

.cosmos .orrery,
.cosmos .ring,
.cosmos .turn,
.cosmos .orbit { transform-box: view-box; transform-origin: 50% 50%; }

/* Each ring is hung at its own inclination, then squashed across that
   axis — a circle narrowing along one axis reads as a ring turning in
   space, and costs a single composited transform to do it. */
.cosmos .ring { transform: rotate(var(--tilt)); }
/* No will-change here on purpose. A running transform gets composited
   anyway, whereas pinning it would hold a layer the size of the whole
   viewport for each of the five rings — tens of megabytes of GPU memory
   sat next to the book, which already budgets its page textures. The
   barbs below are small enough for it to be worth having. */
.cosmos .turn { animation: sphere-turn var(--p) ease-in-out infinite; }
.cosmos .orbit  { animation: revolve var(--p) linear infinite; }
.cosmos .orrery { animation: revolve 620s linear infinite; }

/* Periods are deliberately incommensurate, so the assembly never
   returns to a pose you have already seen. */
@keyframes sphere-turn {
  0%, 100% { transform: scaleX(1); }
  50%      { transform: scaleX(var(--sx)); }
}
@keyframes revolve { to { transform: rotate(360deg); } }

/* --- the quill --- */

.quill {
  position: absolute;
  right: clamp(1rem, 3vw, 3.5rem);
  bottom: clamp(0.5rem, 3vh, 2rem);
  height: clamp(220px, 36vh, 400px);
  width: auto;
}

/* a faint body under the barbs, so the feather reads as a surface
   rather than as a row of bristles once it drops to background alpha */
.quill .vane  { fill: rgba(63, 55, 44, 0.075); stroke: none; }
.quill .shaft { fill: rgba(63, 55, 44, 0.26); stroke: none; }
.quill .slit  { fill: none; stroke: rgba(63, 55, 44, 0.30); stroke-width: 1.1; stroke-linecap: round; }

.quill .barbs {
  fill: none;
  stroke: rgba(63, 55, 44, 0.175);
  stroke-width: 1.5;
  stroke-linecap: round;
  transform-box: view-box;
  transform-origin: var(--ox) var(--oy);
  will-change: transform;
}

/* The shaft never moves — it is lying on a desk. Only the vane stirs,
   each band on its own timing, loosest at the free end. A whole quill
   swaying reads as a metronome; this reads as a draught in the room. */
@keyframes stir {
  0%, 100% { transform: rotate(calc(var(--a) * -1)); }
  50%      { transform: rotate(var(--a)); }
}
.quill .b0 { --a: 1.20deg; animation: stir  9.4s ease-in-out  -0.0s infinite; }
.quill .b1 { --a: 0.95deg; animation: stir 11.7s ease-in-out  -1.8s infinite; }
.quill .b2 { --a: 0.70deg; animation: stir  8.3s ease-in-out  -3.1s infinite; }
.quill .b3 { --a: 0.50deg; animation: stir 13.1s ease-in-out  -0.7s infinite; }

/* --- dust --- */

.motes i {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s);
  height: var(--s);
  border-radius: 50%;
  background: rgba(138, 106, 67, 0.9);
  opacity: 0;
  animation: mote var(--p) linear var(--d) infinite;
}

@keyframes mote {
  0%   { transform: translate3d(0, 0, 0); opacity: 0; }
  18%  { opacity: var(--o); }
  82%  { opacity: var(--o); }
  100% { transform: translate3d(var(--dx), var(--dy), 0); opacity: 0; }
}

/* --- room to breathe --- */

/* The caption plate spans the width on narrower screens, and the quill
   has nowhere left to lie without sitting under the type. */
@media (max-width: 1000px), (max-height: 620px) { .quill { display: none; } }
@media (max-width: 760px) { .motes { display: none; } }

@media (prefers-reduced-motion: reduce) {
  #ground .orrery,
  #ground .turn,
  #ground .orbit,
  #ground .barbs,
  #ground .motes i { animation: none; }
  #ground .motes i { opacity: var(--o); }
}

/* Without WebGL the mirror becomes a plain document; a turning orrery
   behind body copy is just noise. */
body.no-webgl #ground { display: none; }

/* ---------------- top bar ---------------- */

.topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(0.9rem, 2.2vw, 1.7rem) clamp(1rem, 3.4vw, 2.6rem);
  pointer-events: none;
}

.topbar > * { pointer-events: auto; }

.mark {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  text-decoration: none;
  color: var(--ink);
}

.mark .monogram {
  font-family: var(--calig);
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  line-height: 1;
  color: var(--umber);
}

.mark .who {
  font-size: clamp(0.6rem, 1.1vw, 0.72rem);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--faint);
}

.topnav {
  display: flex;
  gap: clamp(0.8rem, 2vw, 1.8rem);
  font-size: clamp(0.6rem, 1.1vw, 0.72rem);
  letter-spacing: 0.26em;
  text-transform: uppercase;
}

.topnav a {
  color: var(--faint);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  transition: color 0.25s, border-color 0.25s;
}

.topnav a:hover,
.topnav a:focus-visible { color: var(--ink); border-bottom-color: var(--ochre); }

/* ---------------- chrome around the book ---------------- */

.chrome {
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.turn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: clamp(2.6rem, 5vw, 3.6rem);
  height: clamp(2.6rem, 5vw, 3.6rem);
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--faint);
  font-family: var(--serif);
  font-size: clamp(1.6rem, 3.4vw, 2.4rem);
  line-height: 1;
  cursor: pointer;
  pointer-events: auto;
  transition: color 0.25s, background 0.25s, opacity 0.25s;
}

.turn:hover:not(:disabled) { color: var(--ink); background: rgba(63, 55, 44, 0.06); }
.turn:disabled { opacity: 0.22; cursor: default; }
.turn.left  { left:  clamp(0.4rem, 2.5vw, 2.4rem); }
.turn.right { right: clamp(0.4rem, 2.5vw, 2.4rem); }

.plate {
  position: absolute;
  left: 50%;
  bottom: clamp(1rem, 3.4vh, 2.4rem);
  transform: translateX(-50%);
  width: min(92vw, 46rem);
  text-align: center;
  pointer-events: auto;
}

#chapter {
  margin: 0 0 0.5rem;
  font-size: clamp(0.55rem, 1vw, 0.66rem);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--umber);
  opacity: 0.85;
  min-height: 1em;
}

#caption {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(0.82rem, 1.5vw, 1.02rem);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--ink);
}

#links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem 1.4rem;
  margin-top: 0.9rem;
  min-height: 1.2rem;
}

#links a {
  font-family: var(--hand);
  font-size: clamp(1rem, 1.7vw, 1.2rem);
  color: var(--rust);
  text-decoration: none;
  border-bottom: 1px solid rgba(168, 98, 63, 0.42);
  transition: border-color 0.25s, color 0.25s;
}

#links a:hover { color: var(--ink); border-bottom-color: var(--ink); }

#counter {
  margin-top: 1rem;
  font-size: clamp(0.56rem, 1vw, 0.66rem);
  letter-spacing: 0.3em;
  color: var(--faint);
}

/* ---------------- scroll hint ---------------- */

/* Tucked into the empty lower-left corner rather than over the book. */
#hint {
  position: fixed;
  z-index: 5;
  left: clamp(1rem, 3vw, 2.6rem);
  bottom: clamp(1.2rem, 4vh, 2.6rem);
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--hand);
  font-size: clamp(0.95rem, 1.5vw, 1.18rem);
  color: var(--faint);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

#hint .arrow {
  display: inline-block;
  width: 1px;
  height: 1.7rem;
  background: linear-gradient(to bottom, transparent, var(--faint));
  animation: drift 2.1s ease-in-out infinite;
}

@keyframes drift {
  0%, 100% { transform: translateY(-3px); opacity: 0.4; }
  50%      { transform: translateY(3px);  opacity: 1; }
}

#hint.gone { opacity: 0; transform: translateY(8px); }

/* ---------------- loader ---------------- */

#loader {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 1.4rem;
  background: var(--bg);
  transition: opacity 0.8s ease, visibility 0.8s;
}

#loader .monogram {
  font-family: var(--calig);
  font-size: clamp(3.4rem, 9vw, 5.6rem);
  color: var(--umber);
  line-height: 1;
}

#loader .word {
  font-size: 0.64rem;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--faint);
}

#loader .bar {
  width: clamp(7rem, 18vw, 11rem);
  height: 1px;
  background: var(--hair);
  overflow: hidden;
}

#loader .bar i {
  display: block;
  width: 40%;
  height: 100%;
  background: var(--umber);
  animation: sweep 1.5s ease-in-out infinite;
}

@keyframes sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}

#loader.gone { opacity: 0; visibility: hidden; }

/* ---------------- accessible mirror ---------------- */

#mirror {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

#fallback {
  position: relative;
  z-index: 6;
  max-width: 42rem;
  margin: 0 auto;
  padding: 4rem 1.5rem 1rem;
  font-family: var(--hand);
  font-size: 1.3rem;
  color: var(--ink);
  text-align: center;
}

/* Without WebGL the mirror becomes the page. */
body.no-webgl #stage,
body.no-webgl .chrome,
body.no-webgl #hint,
body.no-webgl #spacer { display: none; }

body.no-webgl #mirror {
  position: relative;
  width: auto; height: auto;
  margin: 0 auto;
  clip-path: none;
  white-space: normal;
  max-width: 44rem;
  /* Clear the fixed top bar. */
  padding: 5.5rem 1.5rem 6rem;
  z-index: 6;
}

body.no-webgl #fallback + #mirror { padding-top: 1rem; }

body.no-webgl #mirror h1 {
  font-family: var(--calig);
  font-size: clamp(3rem, 9vw, 4.6rem);
  font-weight: 400;
  margin: 0 0 0.2em;
}

body.no-webgl #mirror h2 {
  font-family: var(--calig);
  font-size: clamp(2rem, 5vw, 2.8rem);
  font-weight: 400;
  margin: 2.6rem 0 0.3em;
}

body.no-webgl #mirror h3 {
  font-family: var(--serif);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.86rem;
  margin: 1.8rem 0 0.4em;
}

body.no-webgl #mirror p,
body.no-webgl #mirror li,
body.no-webgl #mirror dd {
  font-family: var(--hand);
  font-size: 1.24rem;
  line-height: 1.5;
  color: #4a4133;
}

body.no-webgl #mirror dt {
  font-family: var(--serif);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-size: 0.7rem;
  color: var(--umber);
  margin-top: 1rem;
}

body.no-webgl #mirror dd { margin: 0.15rem 0 0; }
body.no-webgl #mirror a  { color: var(--rust); }
body.no-webgl #mirror section { border-top: 1px solid var(--hair); padding-top: 0.4rem; }

/* ---------------- short viewports ---------------- */

@media (max-height: 620px) {
  #links, #counter { display: none; }
}

/* No free corner once the caption plate spans the width. */
@media (max-width: 900px), (max-height: 620px) {
  #hint { display: none; }
}

@media (max-width: 640px) {
  #links { gap: 0.3rem 1rem; }
  .topbar { padding: 0.8rem 0.9rem; }
  .topnav { gap: 0.85rem; }
  .topnav a { letter-spacing: 0.16em; }
  .mark .who { letter-spacing: 0.22em; }
}

@media (max-width: 400px) {
  .mark .who { display: none; }
}
