/* ============================================================
   personal-laser — shared stylesheet
   One file, plain CSS, no build step.
   Sections:
     1. reset + page shell
     2. background blobs / grids (the "laser" glow)
     3. chrome heading + reflection
     4. shared text + link styles
     5. per-page bits (home, essays, projects, art, contact, adios)
   ============================================================ */

/* ---------- 1. reset + page shell ---------- */
* { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --red:   #ff1a1a;
  --cyan:  #00e5ff;
  --green: #19ff9b;
  --text:  #cfd8dc;
  --mono:  "Share Tech Mono", ui-monospace, "DejaVu Sans Mono", monospace;
  --display: "Audiowide", "Trebuchet MS", sans-serif;
}

html, body { height: 100%; }

/* the default scrollbar reads as a grey stripe on a black page */
* { scrollbar-width: thin; scrollbar-color: #1d2b30 #000; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: #000; }
*::-webkit-scrollbar-thumb { background: #1d2b30; border-radius: 4px; }

body {
  background: #000;
  color: var(--text);
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.06em;
  line-height: 1.6;
  overflow-x: hidden;
}

/* everything readable sits above the glow layer */
.page { position: relative; z-index: 2; min-height: 100vh; padding: 48px 32px 80px; }

/* "paused" freezes every animation on the page (the ▮▮ button on Contact) */
body.paused *, body.paused *::before, body.paused *::after {
  animation-play-state: paused !important;
}

/* ---------- 2. background glow ----------
   Every background shape is one of four primitives, all plain gradients.
   The stop positions below are the real ones, read out of the Figma file
   (Creative-personal-site, HtBwAzYCPOtrmqEPQvFUQc) rather than eyeballed —
   node ids are noted per primitive. Offsets are quoted as the same fraction
   of the gradient radius that Figma uses, so the outer part of each box is
   deliberately empty; that is what makes the shapes float.

   The mock-up's ramp is always the same journey: a saturated cyan core
   (#00C0EA) through teal into red (#FF0000), then darkening to nothing.
   --c is the main colour, --hot the core; the mid-tones are derived.

     .glow    cyan core through red, fading out       (soft light)
     .lens    dark core, gap, then a red ring; add .eye for the cyan dot
     .streak  the same ramp mirrored into a bar       (the diagonal beams)
     .sphere  a black ball with a red glow and rim

   Each instance sets its own size, position and colour inline, e.g.
     <div class="shape lens" style="--c:#ff2b2b;width:520px;height:300px;right:-8%;top:40%"></div>
   -------------------------------------------------------------------------- */
.bg { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }

.shape {
  position: absolute;
  --c: #ff0000;                    /* main colour  */
  --hot: #00c0ea;                  /* core         */
  animation: drift 26s ease-in-out infinite alternate;
}

/* Rectangle 15 (113:1812): cyan .105, teal .22, red .335, half-dark .41, out .485 */
.glow {
  border-radius: 50%;
  background: radial-gradient(closest-side,
      var(--hot)                                10.5%,
      color-mix(in srgb, var(--hot) 65%, #000)    22%,
      var(--c)                                  33.5%,
      color-mix(in srgb, var(--c) 50%, #000)      41%,
      transparent                               48.5%);
  filter: blur(12px);              /* anti-banding only; the stops do the work */
  opacity: 0.9;
}

/* Rectangle 70 (114:2031): cyan dot, dark core to .085, gap to .18,
   red ring peaking at .235, gone by .545.
   The ring sits on ::before so its blur does not smear the .eye dot. */
.lens { border-radius: 50%; opacity: 0.95; }
.lens::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  background: radial-gradient(closest-side,
      transparent                                 18%,
      var(--c)                                  23.5%,
      color-mix(in srgb, var(--c) 65%, #000)    33.5%,
      color-mix(in srgb, var(--c) 29%, #000)    43.5%,
      transparent                               54.5%);
  filter: blur(6px);
}
/* cyan core, 8.5% of the gradient radius; a percentage box tracks the parent */
.lens.eye::after {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 8.5%; height: 8.5%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--hot) 47%, transparent 100%);
  filter: blur(2px) drop-shadow(0 0 12px var(--hot));
}

/* Rectangle 16 (113:1813) colours: cyan .035, teal .155, red .289, .467, .645.
   That node is corner-anchored; here the ramp is mirrored about a bar centre.
   Rotate with --rot */
.streak {
  --rot: 0deg;
  border-radius: 50%;
  background: linear-gradient(90deg,
      transparent                                  0%,
      color-mix(in srgb, var(--c) 50%, #000)    17.75%,
      color-mix(in srgb, var(--c) 75%, #000)    26.65%,
      var(--c)                                  35.55%,
      color-mix(in srgb, var(--hot) 65%, #000)  42.25%,
      var(--hot)                                48.25%,
      var(--hot)                                51.75%,
      color-mix(in srgb, var(--hot) 65%, #000)  57.75%,
      var(--c)                                  64.45%,
      color-mix(in srgb, var(--c) 75%, #000)    73.35%,
      color-mix(in srgb, var(--c) 50%, #000)    82.25%,
      transparent                                100%);
  filter: blur(8px);
  opacity: 0.8;
  animation: streak-drift 24s ease-in-out infinite alternate;
}

/* Ellipse 27 (94:62): black circle, red drop shadow (dy 4, stdDeviation 24.5)
   and red inner shadow (dx -20, dy -2, stdDeviation 11.05).
   SVG stdDeviation doubles to a CSS blur radius. */
.sphere {
  border-radius: 50%;
  background: #000;
  box-shadow:
    0 4px 49px var(--c),
    inset -20px -2px 22px var(--hot);
  opacity: 0.95;
}

/* Rectangle 43 (95:1609) — the Contact "floaty clouds". A different ramp from
   everything above: no cyan core and no blur at all. Opaque --c at the centre
   interpolating straight out to #d9d9d9 at zero alpha, so it lightens as it
   fades rather than just dimming. The gradient ellipse is wider and much
   flatter than its box (58.8% x 24.7%), and the whole layer sits at 50%.
   Each instance sets its own animation-duration — the mock-up notes the reds
   "pulsing growing/contracting over screen at different rates". */
.cloud {
  background: radial-gradient(58.8% 24.7% at 50% 50%,
      var(--c) 0%,
      color-mix(in srgb, color-mix(in srgb, var(--c) 87.5%, #d9d9d9) 87.5%, transparent) 12.5%,
      color-mix(in srgb, color-mix(in srgb, var(--c) 75%,   #d9d9d9) 75%,   transparent)   25%,
      color-mix(in srgb, color-mix(in srgb, var(--c) 62.5%, #d9d9d9) 62.5%, transparent) 37.5%,
      color-mix(in srgb, color-mix(in srgb, var(--c) 50%,   #d9d9d9) 50%,   transparent)   50%,
      transparent 100%);
  opacity: 0.5;
  animation: pulse 19s ease-in-out infinite alternate;
}
@keyframes pulse {
  from { scale: 1; }
  to   { scale: 1.18; }
}

@keyframes drift {
  from { transform: translate3d(0,0,0) scale(1); }
  to   { transform: translate3d(40px,-30px,0) scale(1.15); }
}
/* streaks keep their angle while they drift */
@keyframes streak-drift {
  from { transform: rotate(var(--rot)) translateX(0) scale(1); }
  to   { transform: rotate(var(--rot)) translateX(30px) scale(1.08); }
}

/* full-bleed wireframe canvases (Contact floor grid, Adios line web) */
.wire { position: absolute; inset: 0; width: 100%; height: 100%; }

/* ---------- 3. chrome heading + reflection ---------- */
.chrome-wrap { text-align: center; line-height: 1; user-select: none; }

.chrome {
  display: inline-block;
  font-family: var(--display);
  font-size: 54px;
  letter-spacing: 0.02em;
  /* mock-up title 114:12125: near-white fill, cyan bloom thrown downward.
     em units, authored at 64px, so the bloom scales with the heading. */
  color: rgba(255, 255, 255, 0.9);
  text-shadow:
    0       0.06em 0.06em #0ff,
    0       0.06em 0.06em #00d1ff,
    0.06em  0.28em 0.26em #00d1ff,
    0.11em  0.80em 0.60em rgba(0, 209, 255, 0.72),
    0.16em  1.55em 0.80em rgba(0, 209, 255, 0.45);
  filter: blur(0.015em);
}

/* mirrored red-tinted echo underneath, faded out */
.chrome-echo {
  display: inline-block;
  font-family: var(--display);
  font-size: 54px;
  letter-spacing: 0.02em;
  background: linear-gradient(0deg, rgba(255,43,43,0) 10%, #ff2b2b 55%, #ffd0d0 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  transform: scaleY(-0.5);
  transform-origin: top;
  margin-top: 0.3em;
  opacity: 0.6;
  filter: blur(1.5px);
}

/* ---------- 4. shared text + links ---------- */
a { color: var(--cyan); text-decoration: none; }
a:hover { color: #fff; text-shadow: 0 0 8px var(--cyan); }

.back { position: fixed; top: 20px; left: 24px; z-index: 5; font-size: 12px; }

.prose { max-width: 62ch; }

/* a column that scrolls inside a page that does not — used on Essays, so the
   background shapes stay exactly where they are while the words move */
.page.fixed-page { height: 100vh; overflow: hidden; }
.scroll-col { overflow-y: auto; overscroll-behavior: contain; padding-right: 14px; }
.prose p { margin-bottom: 18px; }

/* chromatic-aberration text (used by the home page digits) */
/* the soft shadow replaces a per-element blur: a shadow costs no extra layer */
.aberrate {
  text-shadow: -2px 0 rgba(255,0,0,0.9), 2px 0 rgba(0,170,255,0.9),
               0 0 2px currentColor;
}

/* ---------- 5a. home ---------- */
.home { display: flex; align-items: center; min-height: 100vh; padding-left: 8vw; }
.home .chrome, .home .chrome-echo { font-size: 73px; }
.menu { list-style: none; counter-reset: item; margin-top: 40px; }
.menu li { counter-increment: item; margin-bottom: 10px; font-size: 24px; }
.menu li::before { content: counter(item) ". "; color: var(--text); }
.menu a { color: var(--text); }
.menu a:hover { color: #fff; text-shadow: 0 0 10px var(--cyan); }

/* the scattered green clock read-outs on the right of the home page */
#digits { position: fixed; inset: 0; z-index: 1; pointer-events: none; }
.digit {
  position: absolute;
  top: 0;
  font-family: var(--mono);
  font-size: 22px;          /* JS varies this per read-out */
  color: var(--green);
  white-space: nowrap;
  /* each one falls forever; JS sets --rot, the duration and a negative delay
     so they start mid-fall instead of all dropping in together */
  animation: fall linear infinite;
}

@keyframes fall {
  from { transform: translateY(-20vh) rotate(var(--rot, 0deg)); }
  to   { transform: translateY(120vh) rotate(var(--rot, 0deg)); }
}

/* ---------- 5b. essays ---------- */
.essay-list { list-style: none; margin: 40px 0 30px; }
.essay-list li { font-size: 17px; cursor: pointer; }
.essay-list li:hover { color: #fff; }
.essay-list li.active { color: var(--red); text-shadow: 0 0 10px rgba(255,0,0,0.6); }
.essay-meta { color: var(--red); font-size: 12px; margin-bottom: 26px; }

/* ---------- 5c. projects ---------- */
.cards { max-width: 620px; margin: 40px auto 0; }
.card {
  border: 2px solid var(--cyan);
  border-radius: 14px;
  padding: 14px 18px;
  margin-bottom: 18px;
  background: rgba(0, 20, 26, 0.75);
  box-shadow: 0 0 18px rgba(0, 229, 255, 0.35), inset 0 0 24px rgba(0, 229, 255, 0.08);
  cursor: pointer;
}
.card:hover { box-shadow: 0 0 28px rgba(0, 229, 255, 0.7); }
.card h2 { font-family: var(--mono); font-weight: normal; font-size: 16px; color: var(--cyan); margin-bottom: 8px; }
.card dl { display: grid; grid-template-columns: 86px 1fr; font-size: 12px; row-gap: 2px; }
.card dt { color: var(--cyan); }

/* floating tilted plates in the projects background */
.plate {
  position: absolute;
  width: 150px; height: 210px;
  border-radius: 8px;
  filter: blur(7px);
  opacity: 0.85;
  animation: float 18s ease-in-out infinite alternate;
}
/* each plate is drawn three times; the copies sit slightly off the original and
   are blurred further, so they overlap into a soft glow instead of a hard card */
.plate.copy-1 { filter: blur(18px); opacity: 0.55; translate: 14px -12px; animation-duration: 22s; }
.plate.copy-2 { filter: blur(34px); opacity: 0.35; translate: -18px 16px; animation-duration: 27s; }
.plate::after {
  content: ""; position: absolute; inset: 20% 24%;
  background: radial-gradient(closest-side, #ffffff 0%, #ffffff 20%, rgba(255,255,255,0.5) 42%, rgba(255,255,255,0) 74%);
  filter: blur(5px);
}
@keyframes float {
  from { transform: rotate(var(--rot, 20deg)) translateY(0); }
  to   { transform: rotate(var(--rot, 20deg)) translateY(-40px); }
}

/* project detail panel */
.detail {
  display: none;
  max-width: 720px; margin: 26px auto 0;
  border: 2px solid #fff;
  background: #000;
  box-shadow: 0 0 26px rgba(255,255,255,0.35);
  padding: 18px;
  gap: 18px;
}
.detail.open { display: flex; }
.detail .shots { display: flex; flex-direction: column; gap: 10px; flex: 0 0 200px; }
.detail .shot {
  height: 110px; border: 1px solid #900;
  background: linear-gradient(120deg, #111 0%, #999 55%, #ddd 100%);
  display: flex; align-items: center; justify-content: center;
  color: #111; font-size: 11px; letter-spacing: 0.2em;
}

/* ---------- 5d. art ---------- */

/* the hub: a field of tiles laid flat and viewed at an angle, running from the
   top-left corner down towards the bottom-right. The field is drawn three times,
   one copy over another at slightly different offsets and blurs, so the tiles
   overlap into a haze rather than reading as one crisp grid. */
.tilefield {
  position: absolute; inset: 0;
  perspective: 1500px;
  perspective-origin: 30% 25%;
}
.tilegrid {
  position: absolute; top: -8%; left: -8%;
  display: grid; grid-template-columns: repeat(11, 58px); gap: 9px;
  transform: rotateX(34deg) rotateZ(20deg);
  transform-origin: 25% 20%;
}
/* the two duplicate copies: offset, hazier, and drifting out of step */
.tilegrid.haze-1 {
  filter: blur(6px); opacity: 0.7;
  margin: 12px 0 0 -16px;
  animation: haze 17s ease-in-out infinite alternate;
  will-change: translate;          /* blur once, then just move the texture */
}
.tilegrid.haze-2 {
  filter: blur(16px); opacity: 0.5;
  margin: -18px 0 0 20px;
  animation: haze 23s ease-in-out infinite alternate-reverse;
  will-change: translate;
}
@keyframes haze {
  from { translate: 0 0; }
  to   { translate: 26px -18px; }
}
.tile {
  width: 58px; height: 46px;
  background: rgba(105, 0, 0, 0.6);
  border: 1px solid rgba(0, 229, 255, 0.45);
  box-shadow: inset 0 0 16px rgba(255, 0, 0, 0.45), 0 0 6px rgba(0, 229, 255, 0.18);
  /* JS blurs and fades individual tiles so the haze lands in random places */
}

/* blurred copies of one shape follow the pointer, each easing slower than the
   one ahead. JS drives the transforms. */
.cursor-orbit {
  position: absolute; left: 0; top: 0;
  width: 0; height: 0;
  will-change: transform;
}
.cursor {
  position: absolute;
  width: 150px; height: 150px;
  /* offset so the arrow's tip, not the middle of the box, sits on the pointer */
  margin: -12px 0 0 -30px;
  filter: drop-shadow(0 0 18px rgba(0, 229, 255, 0.9));
}
/* the hub replaces the pointer with the drawn one, so hide the real one */
body.hub-cursor, body.hub-cursor a, body.hub-cursor li { cursor: none; }

/* section labels, stepped down the diagonal of the tile field */
.art-links { position: relative; z-index: 3; height: 100vh; }
.art-links a {
  position: absolute;
  font-size: 15px; letter-spacing: 0.14em;
  color: var(--cyan);
  text-shadow: 0 0 12px rgba(0, 229, 255, 0.85);
}
.art-links a:hover { color: #fff; }
.art-links a:nth-of-type(1) { left: 13%; top: 44%; }
.art-links a:nth-of-type(2) { left: 23%; top: 54%; }
.art-links a:nth-of-type(3) { left: 33%; top: 63%; }

/* reading layout: index on the left, text columns on the right */
.reader { background: #000; display: grid; grid-template-columns: 220px 1fr; gap: 40px; border: 1px solid var(--cyan); padding: 26px; }
.reader ul { list-style: none; }
.reader ul li { margin-bottom: 14px; cursor: pointer; }
.reader ul li:hover { color: #fff; }
.reader .cols { columns: 2; column-gap: 30px; font-size: 11px; color: #9fb0b5; }
.reader .cols p { margin-bottom: 14px; }

/* image gallery: photographs standing in a row that recedes to a vanishing
   point on the right, so the nearest one is large and the rest taper away. */
.gallery {
  position: relative;
  height: 88vh;
  margin: 0 -32px;            /* full-bleed: cancel the .page padding */
  perspective: 1100px;
  perspective-origin: 74% 50%;
  overflow: hidden;
}
.gallery .row {
  position: absolute; left: -1%; top: 50%;
  display: flex; align-items: center; gap: 4px;
  transform: translateY(-50%) rotateY(34deg);
  transform-origin: left center;
  transform-style: preserve-3d;
}
.gallery .shot {
  width: 340px; height: 620px;    /* JS shrinks each panel from here */
  flex: none;
  transform: rotateY(var(--curl, 0deg));
  transform-origin: left center;
  border-left: 1px solid rgba(255, 255, 255, 0.25);
  display: flex; align-items: flex-end; justify-content: center;
  padding-bottom: 10px;
  color: rgba(255, 255, 255, 0.75); font-size: 10px; letter-spacing: 0.3em;
  cursor: pointer;
}
.gallery .shot:hover { outline: 1px solid var(--cyan); }

/* the red laser streak that runs out to the vanishing point */
.gallery .streak {
  position: absolute; right: -6%; top: 52%;
  width: 60%; height: 10px;
  background: linear-gradient(90deg, rgba(255,0,0,0) 0%, #ff1a1a 55%, #ff9a9a 100%);
  filter: blur(6px);
  transform: rotate(-4deg);
}

/* ---------- 5e. contact ---------- */
.pause {
  position: fixed; top: 24px; right: 32px; z-index: 5;
  background: none; border: none; cursor: pointer;
  color: #fff; font-size: 30px; letter-spacing: -4px; font-family: var(--mono);
  opacity: 0.85;
}
.terminal {
  width: min(560px, 92vw); margin: 20px auto 0;
  border: 1px solid #6c6c6c; border-radius: 6px;
  background: #1c1c1c; overflow: hidden;
  box-shadow: 0 12px 40px rgba(0,0,0,0.8);
}
.terminal .bar {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 8px; background: linear-gradient(#e6e6e6, #c9c9c9);
  color: #333; font-size: 11px;
}
.terminal .bar span { width: 11px; height: 11px; border-radius: 50%; }
.terminal .bar .r { background: #ff5f57; } .terminal .bar .y { background: #febc2e; }
.terminal .bar .g { background: #28c840; }
.terminal .bar em { flex: 1; text-align: center; font-style: normal; }
.terminal pre {
  min-height: 230px; padding: 8px 10px;
  color: #e8e8e8; font-family: var(--mono); font-size: 12px; white-space: pre-wrap;
}
.caret { animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }

/* ---------- 5f. adios / licenses ---------- */
.adios { min-height: 100vh; display: grid; place-content: center; gap: 10px; text-align: center; }
#noise { width: 260px; height: 190px; border-radius: 22px; background: #999; }
.licenses { min-height: 100vh; display: grid; place-content: center; text-align: center; gap: 20px; }
/* the toy monkey; credit is on the page */
.monkey { position: relative; width: 220px; margin: 0 auto; }
.monkey img {
  width: 100%; display: block;
  filter: grayscale(1) contrast(1.08) drop-shadow(0 0 14px rgba(255,255,255,0.18));
}
  45%       { opacity: 0.6;  transform: translate(-50%,-50%) scale(1.05); }
  70%, 100% { opacity: 0;    transform: translate(-50%,-50%) scale(1.35); }
}

.credit { font-size: 10px; color: #6d7f85; }
.credit a { color: #6d7f85; text-decoration: underline; }
