@font-face {
  font-family: "Michroma";
  src: url("fonts/michroma-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "DM Mono";
  src: url("fonts/dm-mono-light-latin.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: dark;
  --space: #03060d;
  --ink: #eef8ff;
  --holo: 127, 232, 255;
  --hud-inset: clamp(14px, 2.6vmin, 30px);
  --hud-arm: 20px; /* length of the HUD corner brackets' arms */
  /* COMMS panel in the corner: the same distance from both frame lines (vmin, not vw and vh
     separately), clear of the bracket arms, so it sits concentric with the HUD corner */
  --comms-off: calc(var(--hud-inset) + var(--hud-arm) + clamp(8px, 1.6vmin, 24px));
  --brand-top: 37%;
  /* fallback planet geometry, mirrors the WebGL composition */
  --fb-d: min(116vh, 80vw);
  --fb-top: 53vh;
}

@media (max-aspect-ratio: 1/1) {
  :root {
    /* portrait: wordmark, tagline and the COMMS panel stack from here down */
    --stage-top: clamp(72px, 19vh, 240px);
    --fb-d: 106vw;
    /* roughly where main.js puts the planet: under the stack, 7.5vh below it */
    --fb-top: max(48vh, calc(var(--stage-top) + 11.7vh + 13rem));
  }
}

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

html,
body {
  margin: 0;
  height: 100%;
}

html {
  background: var(--space);
}

body {
  overflow: hidden;
  color: var(--ink);
  background:
    radial-gradient(120% 70% at 50% 110%, #0c2742 0%, #071427 34%, rgba(3, 6, 13, 0) 68%),
    linear-gradient(#02040a, #050b18);
  background-color: var(--space);
  font-family: "DM Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
}

/* ---- sky and scene -------------------------------------------------------------- */

.sky {
  position: fixed;
  inset: 0;
  background: url("stars.svg") 0 0 / 900px 900px repeat;
  opacity: 0.85;
}

#scene {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 1.2s ease;
}

.gl-ready #scene {
  opacity: 1;
}

.no-gl #scene {
  display: none;
}

/* ---- fallback: shown without JS, without WebGL, or after a lost context ------------- */

.fallback {
  display: none;
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

html:not(.js) .fallback,
.no-gl .fallback {
  display: block;
}

.fb-planet {
  position: absolute;
  left: 50%;
  top: var(--fb-top);
  width: var(--fb-d);
  height: var(--fb-d);
  margin-left: calc(var(--fb-d) / -2);
  border-radius: 50%;
  background: radial-gradient(
    circle closest-side,
    #020712 0 66%,
    #061a30 88%,
    rgba(50, 140, 215, 0.55) 98.4%,
    rgba(185, 240, 255, 0.95) 99.5%,
    rgba(185, 240, 255, 0) 100%
  );
  box-shadow:
    0 0 36px rgba(80, 190, 255, 0.22),
    0 0 160px rgba(40, 120, 210, 0.2);
}

/* halftone dots over the upper cap, like the dotted globe */
.fb-planet::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(rgba(var(--holo), 0.7) 0.9px, transparent 1.4px) 0 0 / 11px 11px;
  -webkit-mask: radial-gradient(circle at 50% 18%, #000 0 22%, transparent 62%);
  mask: radial-gradient(circle at 50% 18%, #000 0 22%, transparent 62%);
  opacity: 0.45;
}

.fb-ring,
.fb-reticle {
  position: absolute;
  left: 50%;
  top: calc(var(--fb-top) + var(--fb-d) / 2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
}

.fb-ring-outer {
  width: calc(var(--fb-d) * 2.25);
  height: calc(var(--fb-d) * 0.62);
  border: 1px solid rgba(var(--holo), 0.2);
  transform: translate(-50%, -50%) rotate(-3deg);
}

.fb-ring-inner {
  width: calc(var(--fb-d) * 1.42);
  height: calc(var(--fb-d) * 0.5);
  border: 1px solid rgba(var(--holo), 0.14);
  transform: translate(-50%, -50%) rotate(8deg);
}

.fb-reticle {
  width: calc(var(--fb-d) * 1.07);
  height: calc(var(--fb-d) * 1.07);
  border: 1px solid rgba(var(--holo), 0.1);
}

/* ---- HUD frame ------------------------------------------------------------------- */

.hud {
  position: fixed;
  inset: var(--hud-inset);
  pointer-events: none;
}

.hud i {
  position: absolute;
  width: var(--hud-arm);
  height: var(--hud-arm);
  border: 0 solid rgba(var(--holo), 0.4);
}

.hud .tl { top: 0; left: 0; border-top-width: 1px; border-left-width: 1px; }
.hud .tr { top: 0; right: 0; border-top-width: 1px; border-right-width: 1px; }
.hud .bl { bottom: 0; left: 0; border-bottom-width: 1px; border-left-width: 1px; }
.hud .br { bottom: 0; right: 0; border-bottom-width: 1px; border-right-width: 1px; }

/* side rulers: a minor tick every 8px, a major one every 40px */
.hud::before,
.hud::after {
  content: "";
  position: absolute;
  top: calc(50% - 88px);
  width: 14px;
  height: 177px;
  background:
    repeating-linear-gradient(to bottom, rgba(var(--holo), 0.42) 0 1px, transparent 1px 40px) 0 0 / 14px 100% no-repeat,
    repeating-linear-gradient(to bottom, rgba(var(--holo), 0.24) 0 1px, transparent 1px 8px) 0 0 / 7px 100% no-repeat;
}

.hud::before {
  left: 0;
}

.hud::after {
  right: 0;
  transform: scaleX(-1);
}

/* ---- wordmark -------------------------------------------------------------------- */

.brand {
  position: fixed;
  left: 50%;
  top: var(--brand-top);
  transform: translate(-50%, -50%);
  width: max-content;
  max-width: calc(100vw - 32px);
  text-align: center;
}

/* soft dark lens behind the text, on top of the scene */
.brand::before {
  content: "";
  position: absolute;
  inset: -70% -22%;
  z-index: -1;
  background: radial-gradient(closest-side, rgba(3, 6, 13, 0.62), rgba(3, 6, 13, 0.3) 55%, rgba(3, 6, 13, 0));
  pointer-events: none;
}

.wordmark {
  margin: 0;
  padding-left: 0.3em; /* balances the trailing letter-spacing */
  font-family: "Michroma", "Eurostile", "Microgramma", "Arial Black", sans-serif;
  font-weight: 400;
  font-size: clamp(1.85rem, 7.4vw, 12rem);
  line-height: 1.1;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink);
  text-shadow:
    0 0 1px rgba(3, 6, 13, 0.6),
    0 2px 22px rgba(3, 6, 13, 0.9),
    0 0 44px rgba(var(--holo), 0.18);
}

.tagline {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.2em;
  margin: 1.9em 0 0;
  font-size: clamp(0.72rem, 0.5rem + 0.5vw, 1.9rem);
  letter-spacing: 0.46em;
  text-transform: uppercase;
  color: rgba(176, 228, 246, 0.9);
  text-shadow: 0 0 14px rgba(3, 6, 13, 0.95);
}

.tagline::before,
.tagline::after {
  content: "";
  width: clamp(22px, 4.5vw, 140px);
  height: 1px;
  background: linear-gradient(90deg, rgba(var(--holo), 0), rgba(var(--holo), 0.65));
}

.tagline::after {
  margin-left: -0.46em; /* cancels the trailing letter-spacing */
  background: linear-gradient(90deg, rgba(var(--holo), 0.65), rgba(var(--holo), 0));
}

/* ---- COMMS panel: the contact readout ------------------------------------------- */
/* A small terminal in the HUD's bottom-left corner; on portrait screens it sits under the
   tagline instead. DM Mono advances exactly 0.6em (1ch), so 1ch + the letter-spacing is one
   character cell. The lines are white-space: pre and never wrap; app.js types them. */

.comms {
  --ls: 0.06em;
  --cell: calc(1ch + var(--ls));
  --corner: rgba(var(--holo), 0.62);
  --k: 9px;
  position: fixed;
  left: var(--comms-off);
  bottom: var(--comms-off);
  width: max-content;
  max-width: calc(100vw - 32px);
  margin: 0;
  padding: 0.85em 1.4em 0.95em;
  font-size: clamp(0.72rem, 0.3rem + 0.5vw, 1.25rem);
  line-height: 1.75;
  letter-spacing: var(--ls);
  text-transform: uppercase;
  color: rgba(160, 204, 228, 0.76);
  /* corner brackets like the HUD frame, over a dark glass plate */
  background:
    linear-gradient(var(--corner), var(--corner)) left top / var(--k) 1px,
    linear-gradient(var(--corner), var(--corner)) left top / 1px var(--k),
    linear-gradient(var(--corner), var(--corner)) right top / var(--k) 1px,
    linear-gradient(var(--corner), var(--corner)) right top / 1px var(--k),
    linear-gradient(var(--corner), var(--corner)) left bottom / var(--k) 1px,
    linear-gradient(var(--corner), var(--corner)) left bottom / 1px var(--k),
    linear-gradient(var(--corner), var(--corner)) right bottom / var(--k) 1px,
    linear-gradient(var(--corner), var(--corner)) right bottom / 1px var(--k),
    linear-gradient(rgba(6, 17, 32, 0.8), rgba(3, 8, 17, 0.72));
  background-repeat: no-repeat;
  box-shadow:
    inset 0 0 0 1px rgba(var(--holo), 0.1),
    0 0 48px rgba(3, 6, 13, 0.55);
}

.comms p,
.comms dl,
.comms dd {
  margin: 0;
}

.comms .ln {
  white-space: pre;
}

.comms-top {
  margin-bottom: 0.45em;
  padding-bottom: 0.3em;
  border-bottom: 1px solid rgba(var(--holo), 0.16);
}

.comms-head {
  letter-spacing: 0.16em;
  color: rgba(var(--holo), 0.92);
}

.comms .dim {
  color: rgba(var(--holo), 0.42);
}

.comms .gt {
  color: rgba(var(--holo), 0.55);
}

.comms-link {
  color: rgba(160, 204, 228, 0.56);
}

/* ▮▮▮▮▯ drawn in CSS: the font subset has no block glyphs, a fallback font would break the grid */
.sig i {
  display: inline-block;
  width: 0.34em;
  height: 0.72em;
  margin-right: calc(var(--cell) - 0.34em);
  background: rgba(var(--holo), 0.8);
}

.sig .off {
  background: none;
  box-shadow: inset 0 0 0 1px rgba(var(--holo), 0.5);
}

.comms-kv dt {
  display: inline-block;
  vertical-align: top;
  width: calc(10 * var(--cell));
}

/* The value column wraps under itself instead of pushing past the frame: the spelled-out
   address ("hello [at] nexioty [dot] com", 28 cells after a 10-cell key) is wider than a
   320 px screen allows; the link itself (17 cells) never wraps. */
.comms-kv dd {
  display: inline-block;
  vertical-align: top;
  max-width: calc(100% - 10 * var(--cell));
  white-space: normal;
  color: #e2f5ff;
}

.comms .mail {
  text-transform: none;
}

/* without JS the <noscript> copy shows instead */
html:not(.js) .mail-fb {
  display: none;
}

.comms .mail a {
  color: #d4f6ff;
  text-decoration: underline;
  text-decoration-color: rgba(var(--holo), 0.4);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.32em;
  transition: color 0.2s, background-color 0.2s, box-shadow 0.2s, text-decoration-color 0.2s, text-shadow 0.2s;
}

.comms .mail a:hover,
.comms .mail a:focus-visible {
  color: #fff;
  background-color: rgba(var(--holo), 0.13);
  box-shadow: 0 0 0 0.2em rgba(var(--holo), 0.13);
  text-decoration-color: rgba(var(--holo), 0.95);
  text-shadow: 0 0 10px rgba(var(--holo), 0.6);
}

.comms .mail a:focus-visible {
  outline: 1px solid rgba(var(--holo), 0.95);
  outline-offset: 0.3em;
}

.comms .mail a:focus:not(:focus-visible) {
  outline: none;
}

.comms-prompt {
  display: flex;
  align-items: center;
}

/* block cursor, one character cell; blinks only once the readout has been typed */
.caret {
  display: inline-block;
  width: 0.6em;
  height: 1.12em;
  background: rgba(var(--holo), 0.88);
  box-shadow: 0 0 10px rgba(var(--holo), 0.45);
}

.comms.typing .caret {
  position: absolute;
  left: 0;
  top: 0;
}

.comms.live .caret {
  animation: caret-blink 1.1s steps(1, end) infinite;
}

@keyframes caret-blink {
  50% { opacity: 0; }
}

/* portrait: wordmark, tagline and the panel form one centred column above the planet
   (main.js puts the planet under the whole column) */
@media (max-aspect-ratio: 1/1) {
  .stage {
    position: fixed;
    top: var(--stage-top);
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .brand {
    position: relative;
    left: auto;
    top: auto;
    z-index: 0;
    transform: none;
  }

  .comms {
    position: relative;
    left: auto;
    bottom: auto;
    margin-top: clamp(22px, 4.2vh, 44px);
  }
}

/* Short landscape (a phone on its side: 280-420 px of height). The full panel is ~131 px
   tall at its font floor and would reach the tagline below ~350 px of height: the wordmark
   group moves up, and the panel drops the decorative link line and tightens to ~87 px.
   main.js adds .comms-compact (same rules) when some other window needs it too. */
@media (min-aspect-ratio: 1/1) and (max-height: 420px) {
  :root {
    --brand-top: 29%;
  }

  .comms {
    padding: 0.6em 1.1em 0.7em;
    line-height: 1.45;
  }

  .comms-top {
    margin-bottom: 0.25em;
    padding-bottom: 0.15em;
  }

  .comms-link {
    display: none;
  }
}

.comms.comms-compact {
  padding: 0.6em 1.1em 0.7em;
  line-height: 1.45;
}

.comms-compact .comms-top {
  margin-bottom: 0.25em;
  padding-bottom: 0.15em;
}

.comms-compact .comms-link {
  display: none;
}

/* ---- intro (only when JS runs; the page is complete without it) ------------------ */

.js .wordmark {
  animation: wordmark-in 2.1s cubic-bezier(0.16, 0.84, 0.24, 1) 0.4s both;
}

.js .tagline {
  animation: rise-in 1.4s cubic-bezier(0.2, 0.7, 0.2, 1) 1.35s both;
}

.js .hud,
.js .brand::before {
  animation: fade-in 1.6s ease 1.1s both;
}

/* the frame fades in at 2.0-2.6 s of the intro; app.js starts typing when this animation
   has finished (src/comms.js), so both run on the same clock */
.js .comms {
  animation: fade-in 0.6s ease 2s both;
}

/* no WebGL (or a lost context): the panel is there at once, printed, cursor steady */
.no-gl .comms,
.no-gl .comms .caret {
  animation: none;
}

/* Once revealed, by any path, the panel stays: app.js adds .shown, and a focused link is
   never transparent even mid-fade. Without the pin, dropping .no-gl after a restored
   context (or switching reduced motion off) would re-arm the fade: 2 s at opacity 0. */
.comms.shown,
.comms:focus-within {
  animation: none;
  opacity: 1;
}

@keyframes wordmark-in {
  from {
    opacity: 0;
    letter-spacing: 0.72em;
    padding-left: 0.72em;
    filter: blur(12px);
  }
  to {
    opacity: 1;
    letter-spacing: 0.3em;
    padding-left: 0.3em;
    filter: blur(0);
  }
}

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

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* landscape: size by the smaller of width and height so 4K and short screens both work */
@media (min-aspect-ratio: 1/1) {
  .wordmark {
    font-size: clamp(1.85rem, min(5.3vw, 8.5vh), 12rem);
  }
}

@media (max-width: 640px) {
  .hud::before,
  .hud::after {
    display: none;
  }
}

/* below ~520 px of height the left ruler (centre +-88 px) would run alongside the corner panel */
@media (min-aspect-ratio: 1/1) and (max-height: 540px) {
  .hud::before,
  .hud::after {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .js .wordmark,
  .js .tagline,
  .js .hud,
  .js .brand::before,
  .js .comms,
  .comms.live .caret {
    animation: none;
  }

  #scene {
    transition: none;
  }
}
