/* ============================================================
   The portal — the entry to mattmckelvy.com.
   A living harmonic figure on midnight, resolving into the
   white site. Fully isolated: delete this file, js/portal.js
   and the two includes in index.html and the site is untouched.
   ============================================================ */

.portal{
  position:fixed;inset:0;z-index:200;
  overflow:hidden;
  background-color:#05070F;
  background-image:
    radial-gradient(115% 85% at 14% 6%,  rgba(0,164,214,.20), rgba(0,0,0,0) 58%),
    radial-gradient(105% 85% at 86% 94%, rgba(104,74,236,.20), rgba(0,0,0,0) 58%),
    radial-gradient(90% 90% at 50% 44%,  rgba(18,42,96,.55),  rgba(0,0,0,0) 72%);
  opacity:1;
  transition:opacity .34s ease;
  -webkit-font-smoothing:antialiased;
}
.portal *{box-sizing:border-box}

/* the figure */
.portal-canvas{
  position:absolute;inset:0;
  width:100%;height:100%;
  display:block;
}

/* the handoff: light blooms out of the figure and becomes the page */
.portal-wash{
  position:absolute;
  left:50%;top:var(--portal-cy,38.5%);
  width:170vmax;height:170vmax;
  margin-left:-85vmax;margin-top:-85vmax;
  border-radius:50%;
  background:radial-gradient(closest-side,
    #FFFFFF 0%, #FFFFFF 52%, rgba(255,255,255,.92) 68%,
    rgba(255,255,255,.55) 82%, rgba(255,255,255,0) 100%);
  opacity:0;
  transform:scale(.045);
  pointer-events:none;
  /* the light comes up almost at once, then travels outward — so it
     reads as the figure emitting, not the screen fading */
  transition:transform .86s cubic-bezier(.24,.58,.30,1) .12s,
             opacity .20s ease .12s;
}
.portal.is-entering .portal-wash{opacity:1;transform:scale(1.55)}

/* the copy */
.portal-copy{
  position:absolute;left:0;right:0;
  top:70%;
  display:flex;flex-direction:column;align-items:center;
  padding:0 22px;
  text-align:center;
  transition:opacity .28s ease,transform .5s cubic-bezier(.25,.1,.25,1);
}
.portal.is-entering .portal-copy{opacity:0;transform:translateY(-8px)}

.portal-name{
  margin:0;
  font-size:clamp(22px,2.4vw,30px);
  font-weight:600;
  letter-spacing:-.014em;
  line-height:1.1;
  color:#F5F5F7;
  opacity:0;
  animation:portal-rise 1.1s cubic-bezier(.25,.1,.25,1) .35s forwards;
}
.portal-role{
  margin:10px 0 0;
  font-size:clamp(14px,1.2vw,16px);
  letter-spacing:-.006em;
  color:rgba(245,245,247,.52);
  opacity:0;
  animation:portal-rise 1.1s cubic-bezier(.25,.1,.25,1) .5s forwards;
}

.portal-enter{
  margin-top:clamp(24px,3.2vh,38px);
  position:relative;
  display:inline-flex;align-items:center;gap:9px;
  font-family:inherit;
  font-size:16px;letter-spacing:-.012em;font-weight:400;
  color:#F5F5F7;
  background:rgba(245,245,247,.04);
  border:1px solid rgba(245,245,247,.28);
  border-radius:980px;
  padding:13px 28px;min-height:46px;
  cursor:pointer;
  opacity:0;
  animation:portal-rise 1.1s cubic-bezier(.25,.1,.25,1) .68s forwards;
  transition:background .35s ease,border-color .35s ease,transform .5s cubic-bezier(.25,.1,.25,1);
}
.portal-enter:hover{
  background:rgba(245,245,247,.10);
  border-color:rgba(245,245,247,.62);
}
.portal-enter:active{transform:scale(.985)}
.portal-enter:focus-visible{outline:2px solid rgba(122,190,255,.9);outline-offset:4px}
.portal-enter svg{
  width:13px;height:13px;flex:none;
  transition:transform .5s cubic-bezier(.25,.1,.25,1);
}
.portal-enter:hover svg{transform:translateX(3px)}

@keyframes portal-rise{
  from{opacity:0;transform:translateY(12px)}
  to{opacity:1;transform:none}
}

/* the figure is the button too — an invisible hit area over it */
.portal-hit{
  position:absolute;left:50%;transform:translate(-50%,-50%);
  background:none;border:0;padding:0;cursor:pointer;
  border-radius:50%;
}
.portal-hit:focus-visible{outline:2px solid rgba(122,190,255,.75);outline-offset:8px}

@media (max-width:700px){
  .portal-copy{top:62%}
  .portal-enter{font-size:15px;padding:12px 24px}
}

@media (prefers-reduced-motion:reduce){
  .portal-name,.portal-role,.portal-enter{animation-duration:.01ms;animation-delay:0s;opacity:1}
  .portal,.portal-wash,.portal-copy,.portal-enter,.portal-enter svg{transition-duration:.01ms}
}

@media print{ .portal{display:none!important} }
