/* One dark room, one screen, deliberately single-theme: the page is about
 * something you cannot quite make out, and a light version of that is a
 * different idea, not the same idea inverted.
 *
 * The neutral is picked rather than inherited -- #080c0c carries a green-blue
 * bias toward the accent, so the ground and the one hue on the page belong to
 * each other. The type is warm bone against it; that tension is the only
 * contrast the design spends. */
:root {
  --void: #080c0c;
  --bone: #d9d6cd;
  --dim: #59635f;
  --pine: #5cc5ac;

  --sans:
    "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono:
    "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    monospace;

  --pad: clamp(1.5rem, 4.5vw, 3.5rem);
  /* Every vertical gap comes from here, so the whole page compresses together
     on a short screen instead of one stack overflowing while another has room
     to spare. */
  --gap: clamp(0.9rem, 3vh, 2.1rem);
}

/* Self-hosted for the same reason the thing behind this page is: a site about
 * not leaking your life should not announce each view to a font CDN. */
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/plex-sans.woff2") format("woff2");
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/plex-mono.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

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

/* One screen, and in practice it does not scroll: everything sizes off `svh`
 * and the shared `--gap` so it fits, and no scrollbar appears while it does.
 *
 * It took three tries to make that true rather than merely intended. First
 * `overflow: hidden` turned overflow into content that could never be reached.
 * Removing it was not enough: `main` had a fixed `height`, so a block taller
 * than the viewport overflowed at both ends and the half above the top edge was
 * unreachable by scrolling. Nor was fixing that: `html, body { height: 100% }`
 * pinned the document to the viewport, so `main` growing produced no scrollable
 * area either. All three had to go. At a large browser font the page now simply
 * gets taller -- WCAG 1.4.4, not a matter of taste. */
html,
body {
  /* `min-height`, not `height`. A fixed 100% pins the document to the viewport,
     so `main` growing past it produced no scrollable area at all -- the page
     stayed exactly one screen tall and the overflow was simply unreachable,
     which is the bug `overflow: auto` was supposed to have fixed. */
  min-height: 100%;
  overscroll-behavior: none;
}

body {
  margin: 0;
  background: var(--void);
  color: var(--bone);
  font-family: var(--mono);
  -webkit-font-smoothing: antialiased;
}

#tape,
.veil {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

#tape {
  display: block;
  width: 100%;
  height: 100%;
}

/* Closes the corners, so the wall reads as something without edges rather than
 * as a rectangle that ends. Legibility is not this layer's job -- a scrim broad
 * enough to guarantee it also swallows the reveal it sits on top of. That job
 * belongs to `.block::before`, which travels with the type. */
.veil {
  background: radial-gradient(
    125% 95% at 50% 45%,
    transparent 36%,
    rgba(8, 12, 12, 0.66) 100%
  );
}

/* `min-height`, not `height`. With a fixed height the 1fr track cannot grow, so
   a `.block` taller than the viewport -- which is what a large browser font
   produces -- overflows it at BOTH ends, and `align-self: center` puts half of
   that above the top edge where no amount of scrolling reaches it. The wordmark
   and headline simply vanish. Letting the element grow keeps every overflow
   below the fold, where the page can actually grow to reach it. */
main {
  position: relative;
  z-index: 1;
  min-height: 100svh;
  max-width: 74rem;
  margin: 0 auto;
  padding: var(--pad);
  display: grid;
  grid-template-rows: auto 1fr;
  align-items: start;
}

/* The exchanges that say what this is are planted in the canvas, not here --
 * see SCRIPTS in tape.js. Anything legible in the foreground competes with the
 * wall and wins, which leaves two conversations on screen and one of them
 * shouting.
 *
 * The halo is anchored to the content rather than to the viewport: the reveal
 * wanders, so anywhere it can go it may end up behind type, bone on bone.
 * `main` is a stacking context, so z-index:-1 sits under the words and still
 * over the canvas. */
.block {
  align-self: center;
  position: relative;
  display: grid;
  gap: var(--gap);
  justify-items: start;
}
.block::before {
  content: "";
  position: absolute;
  inset: calc(var(--gap) * -1.6) -7rem;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    58% 56% at 30% 50%,
    rgba(8, 12, 12, 0.88) 0%,
    rgba(8, 12, 12, 0.6) 46%,
    transparent 100%
  );
}

/* ---- the mark ------------------------------------------------------------ */

.wordmark {
  margin: 0;
  font-size: 0.82rem;
  letter-spacing: 0.42em;
  color: var(--bone);
  display: flex;
  align-items: center;
  gap: 0.2em;
  opacity: 0;
  animation: surface 1.1s 0.15s ease-out forwards;
}

/* The block from the favicon, kept as a cursor. Slow enough to read as "still
   running" rather than as an animation. */
.caret {
  width: 0.42em;
  height: 0.42em;
  background: var(--pine);
  display: inline-block;
  animation: blink 3.4s steps(1) infinite;
}
@keyframes blink {
  0%,
  58% {
    opacity: 1;
  }
  59%,
  100% {
    opacity: 0.14;
  }
}

/* ---- the words ------------------------------------------------------------ */

/* The one sentence on the page. It does not say "text it" because the wall
   behind it is a conversation -- the interface is shown, so the words only have
   to carry the promise. Sized off whichever axis is tighter, so a short window
   shrinks it rather than clipping it. */
h1 {
  font-family: var(--sans);
  /* 200, not 300: the sentence has to sit at the same weight as the wall behind
     it, or it stops being part of the same picture. */
  font-weight: 200;
  font-size: clamp(1.75rem, min(5.6vw, 7vh), 3.7rem);
  line-height: 1.14;
  letter-spacing: -0.028em;
  margin: 0;
  max-width: 14ch;
  text-wrap: balance;
  opacity: 0;
  animation: surface 1.5s 0.45s ease-out forwards;
}

/* ---- the ask -------------------------------------------------------------- */

.join {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0;
  opacity: 0;
  animation: surface 1.5s 0.75s ease-out forwards;
}

/* A hairline surface rather than a solid slab, which was the loudest thing on a
   page whose whole idea is being barely audible. */
.joinbtn {
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
  font: inherit;
  font-size: 0.85rem;
  text-decoration: none;
  border-radius: 7px;
  padding: 0.72rem 1.3rem;
  border: 1px solid;
  background: rgba(217, 214, 205, 0.035);
  cursor: pointer;
  transition:
    background-color 0.2s ease,
    border-color 0.2s ease,
    color 0.2s ease;
  letter-spacing: 0.12em;
  color: var(--pine);
  border-color: rgba(92, 197, 172, 0.42);
  background: rgba(92, 197, 172, 0.08);
}
.joinbtn:hover,
.joinbtn:focus-visible {
  border-color: rgba(92, 197, 172, 0.75);
  background: rgba(92, 197, 172, 0.14);
}

/* ---- shared --------------------------------------------------------------- */

:where(a):focus-visible {
  outline: 1px solid var(--pine);
  outline-offset: 0.4rem;
}

@keyframes surface {
  from {
    opacity: 0;
    transform: translateY(0.6rem);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* The load is a sequence of things becoming legible, so with motion off the
   honest fallback is for them to simply be legible. */
@media (prefers-reduced-motion: reduce) {
  .wordmark,
  h1,
  .join {
    opacity: 1;
    animation: none;
  }
  .caret {
    animation: none;
  }
}

@media (max-width: 34rem) {
  .block::before {
    inset: calc(var(--gap) * -1.4) -2.5rem;
  }
  /* Both buttons full width and stacked, rather than two half-width ones that
     each wrap their label onto a second line. */
  .joinbtn {
    width: 100%;
    justify-content: center;
  }
  .join {
    width: 100%;
  }
}
