/* demon.at: the login page and the invitation page, a ritual terminal. */
@font-face{font-family:'JetBrains Mono';src:url('/kit/fonts/jetbrains-mono-regular.woff2') format('woff2');font-weight:400;font-display:swap}
@font-face{font-family:'JetBrains Mono';src:url('/kit/fonts/jetbrains-mono-bold.woff2') format('woff2');font-weight:700;font-display:swap}
:root{--bg:#08060A;--t1:#E8DED8;--dim:#6A5A5E;--red:#FF2D2D;--ember:#FF8A3D;--amb:#FFB300;
  --mono:'JetBrains Mono','IBM Plex Mono','SF Mono',Consolas,'Courier New',monospace}
*{box-sizing:border-box}
[hidden]{display:none!important}
/* Cut at the screen's edge: the dark yard behind the text reaches past it. */
html,body{margin:0;min-height:100%;background:var(--bg);color:var(--t1);overflow-x:clip}
/* Upper part of the screen like a terminal, on phones above the keyboard. */
body{display:flex;justify-content:center;align-items:flex-start;min-height:100vh;min-height:100dvh;
  padding:clamp(20px,8vh,100px) 24px 24px;font:16px/1.7 var(--mono);font-variant-ligatures:none}
button{font:inherit;color:inherit}
p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}
.p{color:var(--red)}.d{color:var(--dim)}.ok{color:var(--ember)}.e{color:var(--red)}.a{color:var(--amb)}.g{color:#7FD6A0}
.unseen{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);padding:0;border:0}
.link{padding:0;border:0;background:none;color:var(--dim);text-decoration:underline dotted;text-underline-offset:3px;cursor:pointer}
.link:hover,.link:focus-visible{color:var(--t1);outline:0}
.msg{min-height:1.7em}
body.passkey .pw{display:none}

@keyframes draw{to{stroke-dashoffset:0}}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes shake{20%,60%{transform:translateX(-7px)}40%,80%{transform:translateX(7px)}}
@keyframes blink{50%{opacity:0}}
@keyframes glitch{0%{text-shadow:-2px 0 #FF2D2D,2px 0 #6E8BFF;transform:translateX(1px)}25%{text-shadow:2px 0 #FF2D2D,-2px 0 #6E8BFF;transform:translateX(-1px) skewX(-6deg)}50%{text-shadow:-1px 0 #FF2D2D,1px 0 #6E8BFF;transform:none}75%,100%{text-shadow:none;transform:none}}
.glitch{animation:glitch .36s steps(1) both}

main{position:relative;width:min(640px,100%);isolation:isolate}
/* A dark yard behind the text, so the rain does not show through there. */
main::before{content:"";position:absolute;inset:-60px -90px;z-index:-1;background:radial-gradient(closest-side,var(--bg) 70%,transparent);pointer-events:none}
.rain{position:fixed;inset:0;overflow:hidden;pointer-events:none;color:#22151A;font-size:12px;line-height:1.5;white-space:pre}
.rain>div{animation:rain 140s linear infinite}
@keyframes rain{from{transform:translateY(-50%)}to{transform:translateY(0)}}
/* Power-on like an old tube: a bright line opens across the screen and fades. */
.tube{position:fixed;left:0;right:0;top:36%;height:2px;background:#FFE8E8;box-shadow:0 0 18px 4px rgb(255 45 45 / 55%);pointer-events:none;opacity:0}
.go .tube{animation:tube .5s ease-out both}
@keyframes tube{0%{transform:scaleX(0);opacity:1}35%{transform:scaleX(1);opacity:1}70%{transform:scaleY(50);opacity:.15}100%{transform:scaleY(110);opacity:0}}

/* The old page's red demon, flickering; a wrong word makes him glitch, his eyes glow white. */
.demon{margin:0 0 18px;color:var(--red);font:inherit;font-size:clamp(5.5px,1.55vw,12.5px);line-height:1.05;text-shadow:0 0 12px rgb(255 45 45 / 25%);animation:flicker 5s linear infinite;white-space:pre}
.demon .eyes{transition:color .15s,text-shadow .15s}
.angry .demon .eyes{color:#fff;text-shadow:0 0 6px #fff,0 0 16px var(--red),0 0 32px var(--red)}
.angry .demon{animation:glitch .36s steps(1) 2}
@keyframes flicker{0%,19.9%,22%,62.9%,64%,64.9%,70%,100%{opacity:1}20%,21.9%,63%,63.9%,65%,69.9%{opacity:.55}}

/* Start-up: the script hides the lines and shows one after another; without it they stand. */
.booting .boot:not(.on){opacity:0}
/* The host's place before the login: runes that keep changing. */
.scramble{display:inline-block;min-width:10ch;color:var(--dim)}
.scramble.revealed{color:var(--t1);animation:settle .6s ease-out}
@keyframes settle{from{color:var(--ember);text-shadow:0 0 8px var(--ember)}}

/* The prompt: the field itself is unseen, a dagger per character stands on top of it. */
form{display:flex;gap:1ch;margin:0}
label{color:var(--red);flex:none}
.field{position:relative;flex:1;min-width:0}
.field input{width:100%;padding:0;border:0;outline:0;background:transparent;color:transparent;font:inherit;caret-color:transparent}
.field input::selection{background:transparent}
.mask{position:absolute;inset:0;pointer-events:none;white-space:pre;overflow:hidden;color:var(--t1)}
.cursor{position:absolute;top:.3em;left:calc(var(--at,0) * 1ch);width:1ch;height:1.1em;background:var(--red);pointer-events:none;animation:blink 1s steps(1) infinite}
.field input:disabled~.cursor,.field input:not(:focus)~.cursor{animation:none;opacity:.35}
.caps{position:absolute;top:.3em;left:calc((var(--at,0) + 2) * 1ch);pointer-events:none;padding:0 .5ch;border:1px solid rgb(255 179 0 / 50%);border-radius:3px;color:var(--amb);font-size:.66em;line-height:1.6;animation:caps 1.6s ease-in-out infinite}
@keyframes caps{50%{opacity:.45}}

/* A wrong word: the screen flushes red for a moment. */
.flush{position:fixed;inset:0;background:var(--red);opacity:0;pointer-events:none;z-index:5}
.flash .flush{animation:flush .7s ease-out}
@keyframes flush{0%{opacity:0}20%{opacity:.13}100%{opacity:0}}

/* The passkey: an inverted pentagram in a double circle whose lines draw in, a heartbeat ring
   round it: a heavy double beat (lub-dub), slow; racing while the passkey is asked, glowing
   when in, flat and grey when refused. */
.pk{display:grid;justify-items:center;gap:14px;margin-top:6px}
.sigil-wrap{position:relative;display:grid;place-items:center;width:204px;height:204px}
.ring{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}
.ring path{fill:none;stroke-linecap:round;stroke-linejoin:round;transition:stroke .3s}
.ring-track{stroke:rgb(255 45 45 / 14%);stroke-width:1}
.ring-beat{stroke:var(--red);stroke-width:1.7;stroke-dasharray:.22 .78;filter:drop-shadow(0 0 3px var(--red));animation:ringbeat 3.2s linear infinite}
@keyframes ringbeat{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
.sigil-wrap:has(.scanning) .ring-beat{animation-duration:.7s}
.sigil-wrap:has(.matched) .ring-beat{stroke:var(--ember);stroke-dasharray:1 0;animation:none;filter:drop-shadow(0 0 5px var(--ember))}
.sigil-wrap.flat .ring-beat{stroke:#4A3A3E;stroke-dasharray:1 0;animation:none;filter:none}
.sigil-wrap.flat .ring-track{stroke:transparent}
.sigil-wrap.flat .sigil{color:#5A2A30;animation:none;box-shadow:none}
.sigil{position:relative;width:150px;height:150px;padding:0;border:0;border-radius:50%;background:radial-gradient(circle,rgb(255 45 45 / 10%),transparent 70%);color:var(--red);cursor:pointer;animation:breathe 3s ease-in-out infinite;-webkit-tap-highlight-color:transparent;touch-action:manipulation}
.sigil:focus-visible{outline:2px solid var(--red);outline-offset:6px}
.sigil:disabled{cursor:default}
.sigil svg{width:100%;height:100%;overflow:visible;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.sigil .dr{stroke-dasharray:1;stroke-dashoffset:1;filter:drop-shadow(0 0 4px currentColor)}
.go .sigil .dr{animation:draw 1s ease-out calc(var(--i,0) * 140ms + .3s) forwards}
.sigil.scanning{animation:none;box-shadow:0 0 34px 4px rgb(255 45 45 / 35%)}
.sigil.scanning svg{animation:spin 2.2s linear infinite}
.sigil.matched{color:var(--ember);animation:none;box-shadow:0 0 44px 8px rgb(255 138 61 / 35%)}
.sigil.refused{animation:shake .38s}
@keyframes breathe{50%{box-shadow:0 0 30px 2px rgb(255 45 45 / 22%)}}

/* The invitation: under the sigil the passkey's name, then the button that makes it. */
.named{display:flex;gap:1ch;width:min(260px,100%)}
.named input{flex:1;min-width:0;padding:0;border:0;border-bottom:1px dotted var(--dim);border-radius:0;outline:0;background:transparent;color:var(--t1);font:inherit;caret-color:var(--red)}
.named input:focus{border-bottom-color:var(--red)}
.seal{padding:4px 2ch;border:1px solid var(--red);border-radius:2px;background:rgb(255 45 45 / 8%);color:var(--t1);cursor:pointer;transition:box-shadow .2s,background .2s}
.seal:hover,.seal:focus-visible{outline:0;background:rgb(255 45 45 / 16%);box-shadow:0 0 18px rgb(255 45 45 / 35%)}
.seal:disabled,.invite .sigil:disabled:not(.matched){cursor:default;opacity:.6}
.invite .gate .pk{margin-top:6px}

/* In: the hell gate (gate.js) slams shut over the page; the next page opens it. Over everything,
   never in the way of a click. Its parts: the dark behind it, the two wings in perspective
   (each the stone, the seal's glow, the runes' glow and a shade), the dust and sparks. */
.hellgate{display:none;position:fixed;inset:0;z-index:60;overflow:hidden;pointer-events:none}
.hellgate.shut{display:block}
.hellgate>*,.hellgate .wing>*{position:absolute;inset:0;width:100%;height:100%}
.hellgate .dim,.hellgate .shade{background:#000;opacity:0}
.hellgate .stage{perspective-origin:50% 48%}
.hellgate .wing{position:absolute;top:0;height:100%;backface-visibility:hidden;will-change:transform}
.hellgate .wing.l{left:0;transform-origin:0 50%}
.hellgate .wing.r{right:0;transform-origin:100% 50%}

/* Wide screens: the demon over both columns, the lines left, the gate (prompt or sigil) right
   of a thin rule that fades out at both ends, all in the middle of the screen. The gate has
   one height for both, so nothing on the left moves when switching. Phones keep one column. */
/* Phones and narrow windows: one column, centred as a whole; inside it the lines stay
   left-aligned, so their ok column holds. */
@media(max-width:959.98px){
main{display:flex;flex-direction:column;align-items:center}
main>*{max-width:100%}
.text,.log{width:max-content}
/* The prompt in the middle too: the field is as wide as what is typed (the mask sizes it, the
   real input lies over it), so "wort:" and the cursor stand centred and grow from there. */
.gate{display:flex;flex-direction:column;align-items:center}
form{justify-content:center}
.field{flex:0 1 auto;min-width:1ch}
.field input{position:absolute;inset:0}
.mask{position:static;display:block;min-height:1.7em}
}
@media(min-width:960px){
body{align-items:center;padding-top:24px}
main{width:auto;display:grid;grid-template-areas:"demon demon" "text gate" "log gate";grid-template-columns:auto auto;grid-template-rows:auto auto 1fr;column-gap:56px}
.text{grid-area:text}
.spacer{display:none}
.gate{grid-area:gate;align-self:center;display:grid;align-content:center;min-height:260px;width:330px;padding-left:56px;border-left:1px solid;border-image:linear-gradient(transparent,#4A2A30,transparent) 1}
.pk{margin-top:0}
/* Width 0: a long answer never widens the column (and so never moves the gate); it wraps. */
.log{grid-area:log;width:0;min-width:calc(100% + 40px)}
.demon{grid-area:demon;justify-self:center;margin-bottom:34px;font-size:clamp(8px,.9vw,12px)}
}

/* Less motion: nothing moves, nothing flickers; every line still stands there. (.saved-tick
   keeps its own still way of going.) */
@media(prefers-reduced-motion:reduce){
*:not(.saved-tick),*::before,*::after{animation:none!important;transition:none!important}
.tube,.flush,.hellgate{display:none}
.sigil .dr{stroke-dashoffset:0}
.ring-beat{stroke-dasharray:1 0}
}

/* The display settings (classes on <html>, from demon.display.v1): no rain, no flicker and no
   glitch, and „weniger Bewegung“ as the system's reduced motion. .saved-tick still goes away
   by itself. */
html.no-rain .rain{display:none}
html.no-glitch .demon,html.no-glitch .glitch{animation:none}
html.still *:not(.saved-tick),html.still *::before,html.still *::after{animation:none!important;transition:none!important}
html.still :is(.tube,.flush,.hellgate){display:none}
html.still .sigil .dr{stroke-dashoffset:0}
html.still .ring-beat{stroke-dasharray:1 0}
html.still .saved-tick{animation:still-tick 1.8s steps(1,end) forwards!important}
@keyframes still-tick{0%,99%{opacity:1}100%{opacity:0}}
