/* ================= PW PORTAL — the house transition =================
   The ouroboros portal-unlock, recovered from the pre-tower landing
   (git 6470028^) and extracted into a standalone pair so any page can
   wear it: load /pw-portal.css + /pw-portal.js and the intro, the exit
   wipe and the room-change pulse all come along. D's ruling 2026-08-14:
   this is a HOUSE transition style, not a one-off intro — "be liberal
   with the logo animation."

   Intro (~4.2s): seal stamps in → teal ring draws around it like a lock
   dial → the dial "clicks" open with a flash → you fly through the ring
   as the overlay dissolves and the page rises beneath. Pure CSS
   timeline: even if JS dies the overlay still ends hidden (pwp-out
   fills forwards).

   Theming: every colour funnels through --pwp-* below, which read the
   optional pack-facing hooks (--pw-portal-*) first, then the page's own
   tokens, then the Desert Signal defaults. A theme pack re-skins the
   transition by setting --pw-portal-accent & co. — the base image is a
   PARAMETER too (window.pwPortalConfig.image), never re-welded to one
   PNG. Sits above the hubnav launcher (2147483200). */

#portal{
  --pwp-accent:var(--pw-portal-accent, var(--accent-deep, #54c8c1));
  --pwp-soft:var(--pw-portal-soft, var(--accent-soft, #123430));
  --pwp-bg:var(--pw-portal-bg, var(--bg, #0c1316));
  --pwp-faint:var(--pw-portal-faint, var(--faint, #758785));
  --pwp-mono:var(--pw-portal-mono, var(--mono, var(--font-mono, ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace)));
  display:none;
}
html.portal-on #portal{
  display:flex; position:fixed; inset:0; z-index:2147483400;
  flex-direction:column; align-items:center; justify-content:center; gap:30px;
  cursor:pointer; -webkit-tap-highlight-color:transparent;
  background:
    radial-gradient(760px 560px at 50% 44%, color-mix(in srgb,var(--pwp-soft) 80%,transparent), transparent 62%),
    var(--pwp-bg);
  animation:pwp-out .7s cubic-bezier(.6,0,.85,.6) 4.15s forwards;
}
@keyframes pwp-out{to{opacity:0; visibility:hidden}}

.portal-stage{
  position:relative; width:min(66vw,300px); aspect-ratio:1;
  animation:pwp-fly .85s cubic-bezier(.72,-0.05,.9,.4) 4.05s forwards;
}
/* fly THROUGH the ring — scale past the viewport while dissolving */
@keyframes pwp-fly{to{transform:scale(8); opacity:0}}

.portal-seal{
  position:absolute; inset:8.5%; width:83%; height:83%;
  animation:
    pwp-stamp .75s cubic-bezier(.2,.85,.3,1.15) .05s both,
    pwp-click .38s cubic-bezier(.4,0,.4,1) 3.58s;
}
@keyframes pwp-stamp{
  from{opacity:0; transform:scale(1.4) rotate(-11deg); filter:blur(7px)}
  to{opacity:1; transform:none; filter:none}
}
/* the lock dial turning over — small counter-rotation, then it gives */
@keyframes pwp-click{
  0%{transform:rotate(0)} 32%{transform:rotate(-7deg)}
  68%{transform:rotate(3.5deg)} 100%{transform:rotate(0)}
}

.portal-ring{position:absolute; inset:0; width:100%; height:100%; transform:rotate(-90deg); overflow:visible}
.portal-ring .draw{
  fill:none; stroke:var(--pwp-accent); stroke-width:2.6; stroke-linecap:round;
  stroke-dasharray:603.2; stroke-dashoffset:603.2;
  filter:drop-shadow(0 0 9px color-mix(in srgb,var(--pwp-accent) 65%,transparent));
  animation:pwp-ring-draw 2.4s cubic-bezier(.55,.1,.3,1) .35s forwards;
}
@keyframes pwp-ring-draw{to{stroke-dashoffset:0}}
.portal-ring .dial{
  fill:none; stroke:var(--pwp-accent); stroke-width:1.3; opacity:0;
  stroke-dasharray:2 11;
  transform-box:view-box; transform-origin:50% 50%;
  animation:pwp-dial-in .6s ease .9s forwards, pwp-dial-spin 9s linear .9s infinite;
}
@keyframes pwp-dial-in{to{opacity:.45}}
@keyframes pwp-dial-spin{to{transform:rotate(360deg)}}

.portal-flash{
  position:absolute; inset:-42%; border-radius:50%; opacity:0; pointer-events:none;
  background:radial-gradient(circle, color-mix(in srgb,var(--pwp-accent) 60%,#fff) 0%, color-mix(in srgb,var(--pwp-accent) 55%,transparent) 34%, transparent 62%);
  animation:pwp-flash .55s cubic-bezier(.2,.6,.4,1) 3.85s;
}
@keyframes pwp-flash{18%{opacity:.95}100%{opacity:0}}

.portal-tag{
  margin:0; font-family:var(--pwp-mono); font-size:11px; letter-spacing:.3em;
  text-transform:uppercase; color:var(--pwp-accent); opacity:0;
  animation:pwp-tag-in .55s ease .95s forwards;
}
.portal-hint{
  margin:-18px 0 0; font-family:var(--pwp-mono); font-size:9.5px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--pwp-faint); opacity:0;
  animation:pwp-tag-in .6s ease 1.5s forwards;
}
@keyframes pwp-tag-in{to{opacity:1}}

/* Hold the page back until the portal opens, then rise as usual. */
html.portal-on .shell{animation:pwp-rise .6s cubic-bezier(.2,.8,.2,1) 4.25s both}
@keyframes pwp-rise{from{opacity:0; transform:translateY(12px)} to{opacity:1; transform:none}}

/* ---- PORTAL EXIT — the intro in reverse, compressed to ~0.9s so leaving
   never feels slow: the page sinks away, the seal + completed ring rush up
   around you, flash as you step through, then the destination loads. The
   overlay is injected by pw-portal.js; `html #portal.exit` outweighs the
   intro's `html.portal-on #portal` rules. */
.shell.sink{animation:pwp-sink .45s cubic-bezier(.55,0,.8,.4) both}
@keyframes pwp-sink{to{opacity:0; transform:translateY(14px)}}
html #portal.exit{
  display:flex; position:fixed; inset:0; z-index:2147483400;
  flex-direction:column; align-items:center; justify-content:center; gap:30px;
  cursor:pointer; -webkit-tap-highlight-color:transparent;
  background:
    radial-gradient(760px 560px at 50% 44%, color-mix(in srgb,var(--pwp-soft) 80%,transparent), transparent 62%),
    var(--pwp-bg);
  animation:pwp-exit-in .22s ease both;
}
@keyframes pwp-exit-in{from{opacity:0}to{opacity:1}}
html #portal.exit .portal-stage{animation:pwp-exit-rush .78s cubic-bezier(.18,.75,.25,1) both}
@keyframes pwp-exit-rush{from{transform:scale(5.5); opacity:0} 35%{opacity:1} to{transform:scale(1); opacity:1}}
#portal.exit .portal-seal{animation:none}
#portal.exit .portal-ring .draw{stroke-dashoffset:0; animation:none}
#portal.exit .portal-ring .dial{opacity:.45; animation:pwp-dial-spin 9s linear infinite}
#portal.exit .portal-flash{animation:pwp-flash .45s cubic-bezier(.2,.6,.4,1) .5s}
#portal.exit .portal-tag{animation:pwp-tag-in .4s ease .22s forwards}

/* ---- PULSE — the drill-down beat. A same-page room change (hash nav)
   gets a compressed echo of the exit: the completed ring blinks up around
   the content for ~.6s, no background, no pointer capture. Enough to make
   moving between rooms feel like stepping through the same portal without
   ever getting in the way of the click. */
html #portal.pulse{
  display:flex; position:fixed; inset:0; z-index:2147483400;
  flex-direction:column; align-items:center; justify-content:center;
  pointer-events:none; background:none;
  animation:pwp-out .3s ease .45s forwards;
}
html #portal.pulse .portal-stage{animation:pwp-pulse-rush .6s cubic-bezier(.18,.75,.25,1) both}
@keyframes pwp-pulse-rush{from{transform:scale(3.4); opacity:0} 30%{opacity:.9} to{transform:scale(.9); opacity:0}}
#portal.pulse .portal-seal{animation:none; opacity:.9}
#portal.pulse .portal-ring .draw{stroke-dashoffset:0; animation:none}
#portal.pulse .portal-ring .dial{opacity:.45; animation:pwp-dial-spin 9s linear infinite}
#portal.pulse .portal-flash{animation:pwp-flash .5s cubic-bezier(.2,.6,.4,1) .12s}

/* Reduced motion never sees any of it — belt to the JS gate's braces. */
@media (prefers-reduced-motion:reduce){
  #portal, html.portal-on #portal, html #portal.exit, html #portal.pulse{display:none !important}
  html.portal-on .shell{animation:none}
  .shell.sink{animation:none}
}
