:root {
  --void: #000;
  --ash: #4c5054;      /* log noise, quiet chrome */
  --concrete: #8d9296; /* secondary text */
  --bone: #e8e2d8;     /* primary text, the colour of bone */
  --blood: #b3121b;    /* the owner's colour: wounds, drips, hovers */
  --mono: 'Martian Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --display: 'Grenze Gotisch', 'UnifrakturMaguntia', serif;
  --gutter: clamp(16px, 4vw, 48px);
  --ease: cubic-bezier(.7, 0, .2, 1);
  --expo: cubic-bezier(.16, 1, .3, 1);   /* fast start, long soft landing */
  color-scheme: dark;
}

* { box-sizing: border-box; }
/* One screen, never scrolls. */
html, body { margin: 0; background: var(--void); height: 100%; overflow: hidden; overscroll-behavior: none; }
body {
  height: 100dvh;
  color: var(--bone);
  font: 400 13px/1.7 var(--mono);
  font-variation-settings: 'wdth' 87.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
[data-cursor='crosshair'] body, [data-cursor='crosshair'] a, [data-cursor='crosshair'] button { cursor: crosshair; }
[data-cursor='normal'] a, [data-cursor='normal'] button { cursor: pointer; }

/* The blood cursor: a dot right on the pointer and a ring that trails it. */
.has-cursor body, .has-cursor a, .has-cursor button, .has-cursor input { cursor: none; }
.cur-dot, .cur-ring {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 20;
  pointer-events: none;
  border-radius: 50%;
  opacity: 0;
  transition: opacity .3s;
}
.cur-on .cur-dot, .cur-on .cur-ring { opacity: 1; }
.cur-dot {
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  background: var(--blood);
  box-shadow: 0 0 8px var(--blood);
}
.cur-ring {
  width: 30px;
  height: 30px;
  margin: -15px 0 0 -15px;
  border: 1px solid rgb(232 226 216 / .35);
  transition: opacity .25s, scale .35s var(--expo);
}
/* Over a link the ring gets out of the way; the underline does the talking. */
.cur-hover .cur-ring { opacity: 0; scale: .4; }
.cur-down .cur-ring { scale: .8; }
.is-diving ~ .cur-dot, .is-diving ~ .cur-ring { opacity: 0; }
::selection { background: var(--blood); color: var(--bone); }
/* Rapid clicks would select the name; nothing on the page is meant to be selected. */
.main, .entry, .corner { -webkit-user-select: none; user-select: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
:focus-visible { outline: 1px solid var(--blood); outline-offset: 4px; }
[hidden] { display: none !important; }

/* Everything that switches off together when the connection closes. */
.screen { height: 100dvh; transform-origin: 50% 50%; }
/* A faint scanline texture over everything, so text and building read as one screen. */
.screen::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, rgb(255 255 255 / .022) 0 1px, transparent 1px 3px);
}
.screen.is-off { animation: crt-off .62s var(--ease) forwards; }
@keyframes crt-off {
  0%   { transform: scale(1, 1); filter: brightness(1); }
  45%  { transform: scale(1, .004); filter: brightness(3); }
  100% { transform: scale(0, .004); filter: brightness(6); }
}

#field {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* ---------- Intro: a counter in the eye of the dust ---------- */

.entry {
  position: fixed;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
}
.intro {
  display: grid;
  justify-items: center;
  translate: 0 -4vh;
}
.count {
  grid-area: 1 / 1;
  margin: 0;
  font-size: 12px;
  letter-spacing: .4em;
  padding-left: .4em;
  color: var(--concrete);
  font-variant-numeric: tabular-nums;
  transition: opacity .5s var(--expo), filter .6s var(--expo), translate .8s var(--expo);
}
.count.is-done { opacity: 0; filter: blur(6px); translate: 0 -.9em; }
.handle {
  grid-area: 1 / 1;
  margin: 0;
  font: 600 clamp(36px, 4.6vw, 60px)/1 var(--display);
  letter-spacing: .01em;
  color: var(--bone);
  text-shadow: 0 0 26px color-mix(in srgb, var(--blood) 45%, transparent);
  min-height: 1em;
}
.mori {
  position: fixed;
  left: 0;
  right: 0;
  bottom: max(18px, env(safe-area-inset-bottom));
  margin: 0;
  text-align: center;
  font-size: 10px;
  letter-spacing: .5em;
  padding-left: .5em;
  color: var(--ash);
  animation: fade-in 2s 1s both;
}
.entry.is-gone .mori { opacity: 0; transition: opacity .4s; }

/* The shell in the corner while it loads: real-looking commands for what's
   actually being loaded. */
.sh {
  position: fixed;
  left: var(--gutter);
  bottom: var(--gutter);
  margin: 0;
  max-width: min(64ch, calc(100vw - 2 * var(--gutter)));
  font: inherit;
  font-size: 11px;
  line-height: 1.65;
  color: var(--ash);
  white-space: pre-wrap;
  word-break: break-all;
  transition: opacity .6s var(--expo), translate .8s var(--expo), filter .6s var(--expo);
}
.sh .p { color: var(--blood); }
.sh .c { color: var(--concrete); }
.sh .o { color: var(--ash); }
.sh .cur { display: inline-block; width: .6em; height: 1.1em; margin-bottom: -.2em; background: var(--concrete); animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.entry.is-gone .sh { opacity: 0; translate: 0 -10px; filter: blur(6px); }
/* Phones: a short strip showing only the latest lines. */
@media (max-width: 640px) {
  .sh {
    font-size: 9px;
    bottom: 42px;
    max-height: calc(1.6em * 4);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    opacity: .8;
  }
  .sh .sh-in { display: block; }
}

.gate {
  grid-area: 2 / 1;
  display: grid;
  justify-items: center;
  gap: 4px;
  margin-top: 26px;
}
.gate .enter { animation: gate-in 1.1s var(--expo) both, beckon 2.6s 1.1s ease-in-out infinite; }
.gate-note { margin: 0; color: var(--ash); font-size: 11px; animation: fade-in 1s .35s both; }
@keyframes gate-in {
  from { opacity: 0; translate: 0 12px; filter: blur(8px); }
  to   { opacity: 1; translate: 0 0; filter: blur(0); }
}
@keyframes fade-in { from { opacity: 0; } }

/* The enter button: a clear box that fills in on hover or tap, with a slow
   red pulse so it reads as the thing to press. */
.enter {
  font-size: 13px;
  letter-spacing: .3em;
  padding: 15px 40px 15px calc(40px + .3em);
  min-width: 180px;
  color: var(--bone);
  border: 1px solid rgb(232 226 216 / .55);
  position: relative;
  isolation: isolate;
  animation: beckon 2.6s ease-in-out infinite;
  transition: color .3s, border-color .3s, scale .15s;
}
.enter::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--bone);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .45s var(--expo);
}
.enter:hover, .enter:active { color: var(--void); border-color: var(--bone); }
.enter:hover::before, .enter:active::before { transform: scaleX(1); }
.enter:active { scale: .97; }
.enter span { position: relative; }
.enter:focus-visible { outline: 1px solid var(--blood); outline-offset: 5px; }
@keyframes beckon {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50% { box-shadow: 0 0 22px 2px color-mix(in srgb, var(--blood) 45%, transparent); }
}
.entry.is-gone .gate .enter { animation: gate-out .6s cubic-bezier(.7, 0, .84, 0) forwards; }
.entry.is-gone .gate-note { animation: none; opacity: 0; transition: opacity .3s; }
@keyframes gate-out {
  to { opacity: 0; translate: 0 -14px; filter: blur(8px); }
}

/* ---------- Text that rises into place (see rise() in main.js) ---------- */

/* Each word is its own clip window; letters slide up into it. The padding
   leaves room for ascenders, descenders and the glow; .is-done removes the
   clip once everything has landed. */
.rise .w {
  display: inline-block;
  overflow: hidden;
  padding: .12em .06em .26em;
  margin: -.12em -.06em -.26em;
  vertical-align: top;
}
.rise .ch {
  display: inline-block;
  translate: 0 115%;
  opacity: 0;
  filter: blur(10px);
  transition: translate 1.1s var(--expo), opacity .8s var(--expo), filter 1.1s var(--expo);
  transition-delay: calc(var(--d, 0ms) + var(--i) * var(--stagger, 34ms));
}
.rise.is-in .ch { translate: 0 0; opacity: 1; filter: blur(0); }
.rise.is-done .w { overflow: visible; }
.rise.is-out .ch {
  translate: 0 -115%;
  opacity: 0;
  filter: blur(10px);
  transition-duration: .65s;
  transition-timing-function: cubic-bezier(.7, 0, .84, 0);
  transition-delay: calc(var(--i) * 22ms);
}

.fade-up {
  opacity: 0;
  translate: 0 .7em;
  filter: blur(8px);
  transition: opacity .9s var(--expo), translate 1.1s var(--expo), filter 1.1s var(--expo);
  transition-delay: var(--d, 0ms);
}
.fade-up.is-in { opacity: 1; translate: 0 0; filter: blur(0); }

/* ---------- Main: one centred column, vile-style ---------- */

.main {
  position: relative;
  z-index: 1;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 var(--gutter) clamp(40px, 8vh, 88px);
}
.main.is-pre { visibility: hidden; }
.spacer {
  /* The building takes whatever height the text leaves over. */
  flex: 1 1 auto;
  min-height: 90px;
  max-height: 600px;
  width: 1px;
  margin-top: 3vh;
}
.semi {
  margin: clamp(6px, 2vh, 22px) 0 .2em;
  color: var(--concrete);
  font-size: clamp(11px, 1.5vh, 12px);
  letter-spacing: .08em;
  min-height: 1.7em;
}
.name {
  margin: 0;
  font: 600 clamp(34px, min(6vw, 9vh), 84px)/.95 var(--display);
  letter-spacing: .005em;
  color: var(--bone);
  min-height: .9em;
  text-shadow: 0 0 30px color-mix(in srgb, var(--blood) 40%, transparent);
}
.bio {
  margin: .7em 0 0;
  font-size: clamp(12px, 1.7vh, 13px);
  color: var(--concrete);
  min-height: 1.7em;
}

.links {
  list-style: none;
  margin: clamp(10px, 3.5vh, 36px) 0 0;
  padding: 0;
  display: grid;
  justify-items: center;
  gap: 0;
}
.links a, .links button {
  display: inline-block;
  font-size: clamp(13px, 2vh, 16px);
  font-variation-settings: 'wdth' 100;
  line-height: 1.6;
  color: var(--bone);
  text-decoration: none;
  padding: 0 10px;
  position: relative;
  white-space: pre;
  /* Rising in from below the line, one after another (--d set per link). */
  translate: 0 110%;
  opacity: 0;
  transition: color .3s, text-shadow .3s, translate 1s var(--expo) var(--d, 0ms), opacity .7s var(--expo) var(--d, 0ms);
}
.links.is-in a, .links.is-in button { translate: 0 0; opacity: 1; }
.links li { overflow: hidden; padding: 0 14px; margin: 0 -14px; }
.links.is-done li { overflow: visible; }

/* The word and its handle share one cell; hover rolls one out and the other in. */
.roll { display: inline-grid; overflow: hidden; vertical-align: top; padding: .25em .5em; margin: -.25em -.5em; }
.roll > span { grid-area: 1 / 1; transition: translate .6s var(--expo), opacity .6s var(--expo); }
.roll .b { translate: 0 100%; opacity: 0; }
.links :hover > .roll .a, .links :focus-visible > .roll .a, .links .is-alt > .roll .a { translate: 0 -100%; opacity: 0; }
.links :hover > .roll .b, .links :focus-visible > .roll .b, .links .is-alt > .roll .b { translate: 0 0; opacity: 1; }
.links a::after, .links button::after {
  content: '';
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: .28em;
  height: 1px;
  background: var(--blood);
  transform: scaleX(0);
  transition: transform .45s var(--ease);
}
.links a:hover, .links button:hover, .links a:focus-visible, .links button:focus-visible {
  color: #fff;
  text-shadow: 0 0 2px var(--blood), 0 0 9px var(--blood);
}
.links a:hover::after, .links button:hover::after,
.links a:focus-visible::after, .links button:focus-visible::after { transform: scaleX(1); }
.links :focus-visible { outline: none; }

/* Every few seconds the name tears: two copies, sliced and shoved sideways. */
.name { position: relative; }
.name.is-glitch::before, .name.is-glitch::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  background: var(--void);
  pointer-events: none;
}
.name.is-glitch::before { clip-path: inset(18% 0 52% 0); color: var(--blood); animation: tear-a .2s steps(2) both; }
.name.is-glitch::after { clip-path: inset(58% 0 12% 0); color: var(--bone); animation: tear-b .2s steps(2) both; }

/* The liquid: drawn over the page, never in the way of a click. */
.blood {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  pointer-events: none;
}

/* Clicking a link: the words fall away fast while the camera flies into
   the figure (drawn in field.js). */
.main, .corner, .side, .blood { transition: opacity .22s ease-out, filter .22s ease-out; }
.is-diving .main, .is-diving .corner, .is-diving .side, .is-diving .blood { opacity: 0; filter: blur(6px); }
.is-diving ~ .cur-dot, .is-diving ~ .cur-ring { opacity: 0; }
}

/* ---------- Corner ---------- */

.corner {
  position: fixed;
  z-index: 3;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 16px var(--gutter) max(16px, env(safe-area-inset-bottom));
  font-size: 11px;
  color: var(--ash);
  pointer-events: none;
  animation: fade-in 1.4s both;
}
.corner > * { pointer-events: auto; }
.corner p { margin: 0; }
.state::before { content: ' // '; }
.who b { font-weight: 400; color: var(--concrete); }
.ctl { display: flex; align-items: center; gap: 16px; }
.ctl button { color: var(--ash); transition: color .25s; }
.ctl button:hover, .ctl button[aria-pressed='true'] { color: var(--bone); }
.meter { display: flex; align-items: flex-end; gap: 2px; height: 10px; }
.meter span {
  width: 2px;
  height: 100%;
  background: var(--blood);
  transform-origin: bottom;
  transform: scaleY(.1);
}
.ecg { width: 96px; height: 20px; display: block; }

/* ---------- Hidden console: appears when you start typing ---------- */

.console {
  position: fixed;
  z-index: 4;
  left: 50%;
  bottom: clamp(48px, 8vh, 80px);
  width: min(560px, calc(100% - 2 * var(--gutter)));
  translate: -50% 12px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s, translate .5s var(--ease);
  text-align: left;
}
/* The page steps back while the console is open. */
.main { transition: opacity .5s, filter .5s; }
body.is-typing .main { opacity: .14; filter: blur(2px); }
.console.is-open { opacity: 1; translate: -50% 0; pointer-events: auto; }
.out { color: var(--concrete); white-space: pre-wrap; word-break: break-word; font-size: 12px; }
.out p { margin: 0; transition: opacity .8s; }
.out p.is-old { opacity: 0; }
.out .echo { color: var(--ash); }
.out .em { color: var(--bone); }
.prompt {
  display: flex;
  align-items: baseline;
  gap: 1ch;
  padding: 10px 0 0;
  border-top: 1px solid var(--ash);
  margin-top: 8px;
}
.ps1 { color: var(--ash); white-space: nowrap; font-size: 12px; }
.ps1::after { content: ' $'; }
.prompt input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 12px;
  color: var(--bone);
  background: none;
  border: 0;
  padding: 0;
  caret-color: var(--blood);
  outline: none;
}

.closed {
  position: fixed;
  inset: 0;
  z-index: 4;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 18px;
  color: var(--ash);
  font-size: 12px;
  animation: fade-in 1.2s .5s both;
}
.closed p { margin: 0; }

@media (max-width: 640px) {
  .spacer { margin-top: 2vh; }
  .meter { display: none; }
}

/* ---------- Two columns on computers: everything left, the right side free ---------- */

.side { display: none; }
@media (min-width: 900px) {
  :root { --col: 50vw; --col-left: 0vw; }
  .main { width: var(--col); margin-left: var(--col-left); }
  .console { left: calc(var(--col-left) + var(--col) / 2); }
  /* The right half. Small and medium show the whole thing, centred; large
     fills the half edge to edge with its left edge fading into the page. */
  .side {
    position: fixed;
    z-index: 0;
    top: 0;
    bottom: 0;
    left: 50vw;
    right: 0;
    display: grid;
    place-items: center;
    pointer-events: none;
    opacity: 0;
    filter: blur(10px);
    transition: opacity 1.6s var(--expo), filter 1.6s var(--expo);
  }
  .side.is-in { opacity: 1; filter: blur(0); }
  .side > img, .side > video { display: block; object-fit: contain; }
  .side.size-small > img, .side.size-small > video { max-width: 26vw; max-height: 44vh; }
  .side.size-medium > img, .side.size-medium > video { max-width: 38vw; max-height: 66vh; }
  .side.size-large {
    left: 46vw;
    display: block;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 22%);
    mask-image: linear-gradient(to right, transparent, #000 22%);
  }
  .side.size-large > img, .side.size-large > video { width: 100%; height: 100%; object-fit: cover; }
}

/* Phones: the dashboard's picture or video takes the figure's box. */
/* The box only gets the space the text leaves over (basis 0), and the media
   is laid over it, so its own size can never stretch the page. */
.spacer.has-media {
  flex: 1 1 0;
  width: 100%;
  min-height: 0;
  position: relative;
  overflow: hidden;
}
.spacer.has-media > .side-media {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  filter: blur(10px);
  transition: opacity 1.6s var(--expo), filter 1.6s var(--expo);
}
.spacer.has-media.is-in > .side-media { opacity: 1; filter: blur(0); }

/* Very short screens (a phone on its side): tighter list, room for the corner. */
@media (max-height: 480px) {
  .main { padding-bottom: 50px; }
  .semi { display: none; }
  .links a, .links button { line-height: 1.3; }
}
@media (max-height: 380px) {
  .spacer { min-height: 56px; }
  .name { font-size: 30px; }
  .links { margin-top: 6px; }
  .links a, .links button { font-size: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; animation-delay: 0s !important; }
}
