*,*::before,*::after{ box-sizing:border-box }

html,body{
  margin:0;
  min-height:100%;
  background:#0b0610;
}

/* Scrollbar. color-scheme alone gets us a dark native bar as a floor;
   the rest tints it with the violet already in the background field.
   scrollbar-color covers Firefox and current Chrome, the -webkit- rules
   cover Safari and older Chromium. */
html{
  color-scheme:dark;
  scroll-behavior:smooth;
  scrollbar-width:thin;
  scrollbar-color:rgba(120,95,235,.45) #0b0610;
}
::-webkit-scrollbar{ width:11px; height:11px }
::-webkit-scrollbar-track{ background:#0b0610 }
::-webkit-scrollbar-thumb{
  background:rgba(120,95,235,.45);
  border-radius:6px;
  border:2px solid #0b0610;      /* inset, so the thumb reads as floating */
}
::-webkit-scrollbar-thumb:hover{ background:rgba(140,110,255,.72) }
::-webkit-scrollbar-corner{ background:#0b0610 }
body{ overflow-x:hidden }

#c{
  position:fixed; inset:0;
  width:100%; height:100%;
  display:block;
  z-index:0;
}

/* A soft radial pool sized to the text block. No hard edge anywhere,
   it just fades out before it reaches the sides. */
.intro::before{
  content:"";
  position:absolute;
  left:50%; top:50%;
  transform:translate(-50%,-50%);
  width:min(1150px, 150vw);
  height:230%;
  z-index:-1;
  pointer-events:none;
  background:radial-gradient(ellipse 50% 50% at 50% 50%,
    rgba(5,2,9,.60) 0%,
    rgba(5,2,9,.48) 38%,
    rgba(5,2,9,.26) 66%,
    rgba(5,2,9,.08) 85%,
    rgba(5,2,9,0)   100%);
}

.name, .tag, .links a{
  text-shadow:0 0 2px rgba(4,2,8,.9), 0 1px 2px rgba(4,2,8,.85);
}

main{
  position:relative;
  z-index:2;
  min-height:100svh;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:flex-start;
  gap:clamp(44px, 8vh, 88px);
  padding:clamp(50px, 11vh, 130px) 24px 0;
  font-family:"Martian Mono", ui-monospace, "SF Mono", Menlo, monospace;
  color:#fff;
}

.intro{ position:relative; text-align:center }

.name{
  margin:0;
  font-size:clamp(.62rem, 2.15vw, 1.85rem);
  font-weight:500;
  letter-spacing:-.015em;
  line-height:1.35;
  white-space:nowrap;
}

.bar{ padding:0 .12em }

.tag{
  margin:clamp(18px,3.2vh,30px) auto 0;
  max-width:46ch;
  font-size:clamp(.72rem, 1.5vw, .9rem);
  font-weight:300;
  line-height:1.85;
  letter-spacing:.01em;
  color:rgba(255,255,255,.82);
}

.links{
  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  gap:10px clamp(20px,3.4vw,38px);
  margin-top:clamp(30px,5vh,52px);
  font-size:clamp(.66rem, 1.3vw, .78rem);
  font-weight:400;
  letter-spacing:.04em;
}
.links a{
  position:relative;
  color:#fff;
  text-decoration:none;
  padding-bottom:4px;
  opacity:.88;
  transition:opacity .25s ease;
}
.links a::after{
  content:"";
  position:absolute; left:0; right:0; bottom:0;
  height:1px;
  background:#fff;
  transform:scaleX(0);
  transform-origin:center;
  transition:transform .3s cubic-bezier(.22,.68,.3,1);
}
.links a:hover{ opacity:1 }
.links a:hover::after{ transform:scaleX(1) }
.links a:focus-visible{ outline:2px solid #fff; outline-offset:5px }

/* ── Progress card ────────────────────────────────────────────
   A single panel showing the newest update and its notes, with a
   way through to everything older. Left aligned inside, because
   patch notes are a list you scan rather than a statement. */
.progress{
  width:min(1040px, 100%);
  text-align:left;
  background:rgba(5,2,9,.74);
  border:1px solid rgba(255,255,255,.14);
  border-radius:4px;
  padding:clamp(18px,2.6vw,26px) clamp(18px,2.8vw,30px);
  transition:border-color .25s ease, background .25s ease;
}
.progress:hover{
  border-color:rgba(255,255,255,.26);
  background:rgba(5,2,9,.8);
}

.prog__head{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:16px;
  margin-bottom:clamp(14px,2vh,20px);
  padding-bottom:12px;
  border-bottom:1px solid rgba(255,255,255,.12);
}

.eyebrow{
  font-size:.6rem;
  font-weight:500;
  letter-spacing:.22em;
  text-transform:uppercase;
  color:rgba(255,255,255,.62);
}

.all{
  font-size:.6rem;
  letter-spacing:.06em;
  color:#fff;
  opacity:.72;
  text-decoration:none;
  white-space:nowrap;
  transition:opacity .2s ease;
}
.all:hover{ opacity:1; text-decoration:underline; text-underline-offset:4px }

.up__t{
  margin:0;
  font-size:clamp(.78rem,1.7vw,.95rem);
  font-weight:500;
  letter-spacing:-.01em;
  line-height:1.4;
}
.up__d{
  margin:6px 0 0;
  font-size:.6rem;
  letter-spacing:.1em;
  color:rgba(255,255,255,.55);
}

.notes{
  margin:clamp(14px,2vh,18px) 0 0;
  padding:0;
  list-style:none;
  font-size:clamp(.62rem,1.35vw,.74rem);
  font-weight:300;
  line-height:1.9;
  color:rgba(255,255,255,.84);
}
.notes li{
  position:relative;
  padding-left:1.5em;
}
.notes li::before{
  content:"+";
  position:absolute;
  left:0;
  color:rgba(255,255,255,.42);
}

/* ── Projects ─────────────────────────────────────────────────
   One card per project. Same panel treatment as the progress
   card so the page reads as one surface, with the tech tags
   picking up the purple, indigo and crimson already in the
   background field. */
.projects{ width:min(1040px, 100%) }

.proj__grid{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));
  gap:clamp(14px, 2vw, 20px);
}

.card{
  display:flex;
  flex-direction:column;
  background:rgba(5,2,9,.74);
  border:1px solid rgba(255,255,255,.14);
  border-radius:4px;
  padding:clamp(16px,2.2vw,22px);
  transition:border-color .25s ease, background .25s ease;
}
.card:hover{
  border-color:rgba(255,255,255,.26);
  background:rgba(5,2,9,.8);
}

.card__head{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:12px;
}
.card__n{
  margin:0;
  font-size:.82rem;
  font-weight:500;
  letter-spacing:-.01em;
}
.badge{
  font-size:.52rem;
  font-weight:400;
  letter-spacing:.14em;
  text-transform:uppercase;
  white-space:nowrap;
  padding:.4em .7em;
  border-radius:3px;
  border:1px solid rgba(140,110,255,.45);
  color:rgba(190,170,255,.92);
}
.badge--wip{
  border-color:rgba(200,80,80,.45);
  color:rgba(240,150,145,.92);
}
.badge--closed{
  border-color:rgba(255,255,255,.22);
  color:rgba(255,255,255,.6);
}

.card__thumb{
  margin:clamp(12px,1.8vh,16px) 0 0;
  aspect-ratio:16/9;
  overflow:hidden;
  border:1px solid rgba(255,255,255,.1);
  border-radius:3px;
  background:
    repeating-linear-gradient(-16deg,
      rgba(255,255,255,.035) 0 2px, transparent 2px 9px),
    rgba(255,255,255,.02);
  display:grid;
  place-items:center;
  font-size:.55rem;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:rgba(255,255,255,.3);
}
/* The 16:9 box is fixed; the image is cropped to fill it rather than
   the box resizing to whatever the file happens to be. grid-area pins
   it to the single cell so place-items:center can't shrink it. */
.card__thumb img{
  grid-area:1 / 1;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:center;
  display:block;
}

.card__lede{
  margin:clamp(13px,2vh,17px) 0 0;
  font-size:.65rem;
  font-weight:400;
  line-height:1.7;
  color:rgba(255,255,255,.66);
}
.card__d{
  margin:10px 0 0;
  font-size:.63rem;
  font-weight:300;
  line-height:1.9;
  color:rgba(255,255,255,.84);
}

/* Tags and the link share one row, and the row is pushed to the bottom
   of the card so the feet line up across a row of uneven cards. */
.card__foot{
  display:flex;
  align-items:flex-end;
  justify-content:space-between;
  gap:14px;
  margin-top:auto;
  padding-top:clamp(14px,2vh,18px);
}

.tags{
  display:flex;
  flex-wrap:wrap;
  gap:6px;
  margin:0;
  min-width:0;
}
.tag-i{
  font-size:.52rem;
  letter-spacing:.06em;
  padding:.4em .6em;
  border-radius:3px;
  border:1px solid rgba(255,255,255,.16);
  color:rgba(255,255,255,.7);
}
.tag-i.p{ border-color:rgba(120,95,235,.45); color:rgba(185,170,255,.9) }
.tag-i.i{ border-color:rgba(90,100,210,.45); color:rgba(160,175,245,.9) }
.tag-i.r{ border-color:rgba(200,80,80,.45);  color:rgba(240,155,150,.9) }

.card__go{
  flex:0 0 auto;
  display:inline-grid;
  place-items:center;
  width:30px;
  height:30px;
  color:#fff;
  text-decoration:none;
  border:1px solid rgba(255,255,255,.24);
  border-radius:3px;
  transition:border-color .2s ease, background .2s ease, color .2s ease;
}
.card__go svg{
  width:15px;
  height:15px;
  fill:currentColor;
  display:block;
  opacity:.85;
  transition:opacity .2s ease;
}
.card__go:hover svg{ opacity:1 }
.card__go:focus-visible{ outline:2px solid #fff; outline-offset:3px }
.card__go:hover{
  border-color:rgba(255,255,255,.5);
  background:rgba(255,255,255,.06);
}

/* ── Header ───────────────────────────────────────────────────
   The mirror of the footer: a mass hanging from the top edge with
   the nav sitting in it. Retracts upward on scroll down. */
/* The mirror of the footer: a mass hanging from the top edge with the
   nav sitting in it. Stays put the whole way down the page; the bar is
   kept tight so it doesn't take much off the top. */
.top{
  position:fixed;
  top:0; left:0; right:0;
  z-index:4;
  background:#05020a;
  padding:3px 24px 3px;
  transition:background .3s ease;
}

/* Past the top of the page the bar and its edge drop away and only the
   links remain, floating over the field. They pick up the same tight
   halo the header text uses so they stay readable without a backdrop. */
.top.is-bare{ background:transparent }
.top.is-bare .ooze--top{ opacity:0 }
.top.is-bare .top__in a{
  text-shadow:0 0 2px rgba(4,2,8,.95), 0 0 5px rgba(4,2,8,.85), 0 1px 2px rgba(4,2,8,.9);
}

.top__in{
  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  gap:8px clamp(18px,3vw,36px);
  font-size:.55rem;
  letter-spacing:.14em;
  text-transform:uppercase;
}
.top__in a{
  position:relative;
  color:rgba(255,255,255,.8);
  text-decoration:none;
  padding-bottom:4px;
  transition:color .2s ease;
}
.top__in a::after{
  content:"";
  position:absolute; left:0; right:0; bottom:0;
  height:1px;
  background:currentColor;
  transform:scaleX(0);
  transition:transform .3s cubic-bezier(.22,.68,.3,1);
}
.top__in a:hover{ color:#fff }
.top__in a:hover::after{ transform:scaleX(1) }
.top__in a:focus-visible{ outline:2px solid #fff; outline-offset:5px }

.top__soon{
  color:rgba(255,255,255,.34) !important;
  cursor:default;
}
.top__soon::after{ display:none }

/* Its own shallow wave rather than the footer's mass scaled down.
   Squashing the tall tendrils just gave the same silhouette in
   miniature; this shape has no tendrils to begin with. */
.ooze--top{
  top:100%;
  bottom:auto;
  height:3px;
  transition:opacity .3s ease;
}
.ooze--top svg{ transform:scaleY(-1) }
/* No droplets up here — at this size they read as specks of dust. */
.ooze--top .drip{ display:none }

/* The fixed header would otherwise sit on top of an anchored section. */
#projects{ scroll-margin-top:40px }

/* ── Footer ───────────────────────────────────────────────────
   Full-bleed. A mass of black reaches up out of the bottom of the
   page and the contact line sits down in the solid part of it. The
   footer breaks out of main's centred column with the 50%/50vw
   trick, then the body below the curve is flat black. */
.foot{
  position:relative;
  width:100vw;
  margin-left:calc(50% - 50vw);
  margin-top:clamp(60px, 14vh, 150px);
  padding:0 24px clamp(26px, 5vh, 46px);
  background:#05020a;
}

.ooze{
  position:absolute;
  left:0; right:0;
  bottom:100%;
  height:clamp(110px, 17vh, 200px);
  pointer-events:none;
}
.ooze svg{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  display:block;
}
.ooze__front{ fill:#05020a }
.ooze__back{ fill:#05020a; opacity:.55 }
.ooze--top .ooze__back{ opacity:.3 }

/* Detached droplets above the tips, so it reads as something rising
   rather than a curve drawn along the edge. */
.drip{
  position:absolute;
  border-radius:50%;
  background:#05020a;
}
.drip--a{ left:12.7%; top:8%;  width:7px;  height:10px }
.drip--b{ left:57.6%; top:-4%; width:9px;  height:13px }
.drip--c{ left:79.8%; top:14%; width:5px;  height:7px }

/* The surface itself morphs (see the <animate> tags in the markup).
   The droplets bob separately so they drift out of step with it. */
@media (prefers-reduced-motion:no-preference){
  .drip{ animation:bob 7s ease-in-out infinite }
  .drip--b{ animation-duration:9s;  animation-delay:-2s }
  .drip--c{ animation-duration:11s; animation-delay:-4s }
}

@keyframes bob{
  0%,100%{ transform:translateY(0) }
  50%    { transform:translateY(-9px) }
}

.foot__in{
  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  align-items:center;
  gap:6px 12px;
  max-width:1040px;
  margin:0 auto;
  font-size:.58rem;
  line-height:1.7;
  letter-spacing:.04em;
  text-align:center;
  color:rgba(255,255,255,.62);
}

.foot__in a{
  color:rgba(255,255,255,.82);
  text-decoration:none;
  border-bottom:1px solid rgba(255,255,255,.22);
  padding-bottom:2px;
  transition:color .2s ease, border-color .2s ease;
}
.foot__in a:hover{
  color:#fff;
  border-color:rgba(255,255,255,.6);
}
.foot__in a:focus-visible{ outline:2px solid #fff; outline-offset:4px }

.sep{ color:rgba(120,95,235,.6) }

@media (max-width:700px){
  .name{
    white-space:normal;
    max-width:20ch;
    text-wrap:balance;
    font-size:clamp(1.05rem, 4.6vw, 1.6rem);
  }
}

@media (prefers-reduced-motion:reduce){
  .links a, .links a::after{ transition:none }
}
