/* Meld Mail · Coming soon: Quiet glass (Freddie's pick for meldmail.net, 4 Oct 2026).
   One overhead light, the pale glass mark settling in, the name rising into place, then a single graphite
   glass control. After about 2.5 s nothing moves.
   Meld v5 monochrome: black #0A0A0B, graphite #23282D, near white #F7F8FA, soft white #F4F4F5.
   Source: waitlist/quiet.css. scripts/build-waitlist.mjs copies it to site/assets/quiet.css; edit the
   source, never the copy. site/_headers allows styles from this site only, so there is no inline style. */
@font-face {
  font-family: 'Geist';
  src: url('/fonts/Geist-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('/fonts/GeistMono-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --black: #0a0a0b;
  --graphite: #23282d;
  --near-white: #f7f8fa;
  --soft-white: #f4f4f5;

  --bg: var(--black);
  --ink: var(--soft-white);
  --ink-2: #b4b4ba;        /* the line under the name: 9.3:1 on the page */
  --ink-3: #8d8d93;        /* "mail", the consent line: 5.8:1 on the page */
  --hair: rgba(244, 244, 245, 0.22);
  --placeholder: #97979d;  /* 5.4:1 on the glass control, focus included */

  --sans: 'Geist', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'Geist Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.22, 1, 0.36, 1);
  color-scheme: dark;
}

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

html, body {
  margin: 0;
  min-height: 100%;
  background: var(--bg);
  color: var(--ink);
}

body {
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

.sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- the room ----------------------------------------------------------------
   One soft overhead light, like a product lit from above at night, and a fine static
   grain so the light never bands. Nothing else in the background. */
.sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* The light falls from above onto the mark and dies out by the name, so the product
     sits in the light and the form sits in the dark. Long, slow tail: no visible edge. */
  background:
    radial-gradient(74% 62% at 50% -10%, rgba(247, 248, 250, 0.08) 0%, rgba(247, 248, 250, 0.036) 40%, rgba(247, 248, 250, 0.012) 64%, rgba(247, 248, 250, 0) 86%);
}
.grain {
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0.028;
  /* A file on this site, not an inline image: site/_headers allows images from this site only. */
  background-image: url('/assets/grain.svg');
  background-size: 180px 180px;
}

/* ---- the stack ------------------------------------------------------------- */
.stage {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 56px 16px calc(56px + 5vh);
  text-align: center;
}

.mark {
  position: relative;
  /* The mark is the product: about 156 px on a laptop, 114 px on a phone. */
  width: clamp(112px, 4vw + 98px, 160px);
  aspect-ratio: 286 / 272;
}
.mark > svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
/* One pass of light across the glass as it settles. The band is clipped to the mark's own
   shape (the same spec file, used as a mask: assets/mark-sheen.svg, written by
   scripts/build-waitlist.mjs), so the mark itself is never redrawn. */
.sheen {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  -webkit-mask: url('/assets/mark-sheen.svg') center / 100% 100% no-repeat;
  mask: url('/assets/mark-sheen.svg') center / 100% 100% no-repeat;
}
.sheen::before {
  content: '';
  position: absolute;
  top: -10%;
  bottom: -10%;
  /* The band only travels across the mark: its light starts just off the left edge and
     ends just off the right one, so the whole pass is spent on the glass. */
  left: -56%;
  width: 80%;
  /* clear well before the band's own edges, so the light has no hard side */
  background: linear-gradient(104deg, rgba(255, 255, 255, 0) 28%, rgba(255, 255, 255, 0.16) 42%, rgba(255, 255, 255, 0.36) 50%, rgba(255, 255, 255, 0.16) 58%, rgba(255, 255, 255, 0) 72%);
  opacity: 0;
}

.word {
  margin: clamp(26px, 4.2vh, 40px) 0 0;
  font-weight: 640;
  font-size: clamp(46px, 2.6vw + 34px, 74px);
  line-height: 1;
  letter-spacing: -0.046em;
  text-transform: lowercase;
  white-space: nowrap;
  color: var(--near-white);
}
.word .w2 { color: var(--ink-3); }

/* COMING SOON keeps its two hairlines from the live page, a little finer. */
.soon {
  margin: clamp(18px, 2.8vh, 26px) 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 13px;
  line-height: 1;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.soon-t { display: block; margin-right: -0.3em; /* tracked caps, optically centred */ }
.rule {
  display: block;
  width: 34px;
  height: 1px;
  background: var(--hair);
}
.r1 { transform-origin: 100% 50%; }
.r2 { transform-origin: 0 50%; }

.line {
  margin: clamp(22px, 3.4vh, 30px) 0 0;
  font-size: clamp(17px, 0.3vw + 15px, 20px);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: -0.012em;
  color: var(--ink-2);
}

/* ---- the waitlist: one glass control ---------------------------------------------
   Field and button live inside a single graphite glass capsule. The rim is brighter at the
   top, like the mark's own glass. The confirmation takes the same cell, so nothing moves. */
.join {
  width: min(460px, 100%);
  margin: clamp(34px, 5.6vh, 52px) auto 0;
  text-align: center;
}
.no-js .join { display: none; }

.join-slot {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  border-radius: 18px;
  background: linear-gradient(180deg, rgba(35, 40, 45, 0.78) 0%, rgba(35, 40, 45, 0.52) 100%);
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5), 0 22px 44px -22px rgba(0, 0, 0, 0.9);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  backdrop-filter: blur(18px) saturate(140%);
  transition: background-color 0.25s ease;
}
.join-slot > .join-row,
.join-slot > .join-done { grid-area: 1 / 1; }

/* The rim, drawn twice: a quiet one at rest and a brighter one that fades in on focus.
   Only opacity changes, so the compositor does the work. */
.join-slot::before,
.join-slot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
.join-slot::before {
  background: linear-gradient(180deg, rgba(247, 248, 250, 0.26) 0%, rgba(247, 248, 250, 0.09) 38%, rgba(247, 248, 250, 0.07) 70%, rgba(247, 248, 250, 0.12) 100%);
  transition: opacity 0.25s ease;
}
.join-slot::after {
  background: linear-gradient(180deg, rgba(247, 248, 250, 0.62) 0%, rgba(247, 248, 250, 0.34) 45%, rgba(247, 248, 250, 0.28) 100%);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.join:not(.is-done) .join-slot:hover::after { opacity: 0.38; }
.join:not(.is-done) .join-slot:has(.join-input:focus-visible)::after,
.join:not(.is-done) .join-slot:has(.join-input:focus)::after { opacity: 1; }
/* In focus the glass lifts a shade, as if the light found it. */
.join:not(.is-done) .join-slot:has(.join-input:focus) {
  background-color: rgba(247, 248, 250, 0.028);
}
.join-slot:has(.join-input[aria-invalid="true"])::after { opacity: 0.85; }

.join-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px;
  transition: opacity 0.3s ease, transform 0.45s var(--ease-out), visibility 0s linear 0s;
}
.join-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 46px;
  margin: 0;
  padding: 0 10px 0 16px;
  border: 0;
  border-radius: 12px;
  outline: none;
  background: transparent;
  color: var(--near-white);
  caret-color: var(--near-white);
  font: 400 16px/1.2 var(--sans);
  letter-spacing: -0.011em;
  -webkit-appearance: none;
  appearance: none;
}
.join-input::placeholder { color: var(--placeholder); opacity: 1; }
.join-input:-webkit-autofill,
.join-input:-webkit-autofill:hover,
.join-input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--near-white);
  caret-color: var(--near-white);
  transition: background-color 600000s 0s;
}

/* Concentric corners: 18 px capsule, 6 px inset, 12 px button. */
.join-btn {
  flex: none;
  display: grid;
  height: 46px;
  margin: 0;
  padding: 0 20px;
  border: 0;
  border-radius: 12px;
  background: linear-gradient(180deg, #ffffff 0%, var(--soft-white) 55%, #e8e8eb 100%);
  box-shadow: inset 0 -1px 0 rgba(10, 10, 11, 0.14), 0 1px 2px rgba(0, 0, 0, 0.35);
  color: var(--black);
  font: 560 15px/1 var(--sans);
  letter-spacing: -0.012em;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: filter 0.2s ease, transform 0.16s var(--ease-out);
}
.join-btn > span {
  grid-area: 1 / 1;
  align-self: center;
  transition: opacity 0.2s ease;
}
.join-btn-busy { opacity: 0; }
.join-btn:hover { filter: brightness(1.04); }
.join-btn:active { transform: scale(0.97); }
/* Keyboard focus: a crisp dark ring inside the white button, so it never crowds the
   capsule's rim 6 px away. The outline survives forced-colours mode. */
.join-btn:focus-visible {
  outline: 1.5px solid var(--black);
  outline-offset: -4.5px;
}
.is-sending .join-btn { cursor: progress; filter: brightness(0.9); }
.is-sending .join-btn:active { transform: none; }
.is-sending .join-btn-idle { opacity: 0; }
.is-sending .join-btn-busy { opacity: 1; }
.is-sending .join-input { color: var(--ink-3); }

.join-done {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  margin: 0;
  padding: 0 16px;
  font-size: 16px;
  font-weight: 520;
  letter-spacing: -0.011em;
  color: var(--near-white);
  outline: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.5s ease 0.14s, transform 0.7s var(--ease-out) 0.14s, visibility 0s linear 0.64s;
}
.join-tick {
  flex: none;
  width: 20px;
  height: 20px;
  fill: none;
  stroke: var(--near-white);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.join-tick circle { stroke: var(--hair); }
.join-tick path { stroke-dasharray: 12; stroke-dashoffset: 12; }

.is-done .join-row {
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.3s ease, transform 0.45s var(--ease-out), visibility 0s linear 0.3s;
}
.is-done .join-done {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.5s ease 0.14s, transform 0.7s var(--ease-out) 0.14s, visibility 0s linear 0s;
}
.is-done .join-tick path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.55s var(--ease-soft) 0.42s;
}

/* A wrong address: the capsule gives one small, quick shake, as a sign-in box does. */
.join-slot.shake { animation: shake 0.42s var(--ease-soft); }
@keyframes shake {
  0% { transform: translateX(0); }
  18% { transform: translateX(-5px); }
  40% { transform: translateX(4px); }
  62% { transform: translateX(-2.5px); }
  82% { transform: translateX(1px); }
  100% { transform: translateX(0); }
}

/* The honeypot: off screen, out of the tab order, hidden from assistive tech. */
.join-hp {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Note and message share one cell too; a message covers the note. */
.join-foot {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin-top: 16px;
  padding: 0 8px;
}
.join-foot > * { grid-area: 1 / 1; }
.join-note,
.join-msg {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  letter-spacing: -0.003em;
  text-wrap: balance;
}
.join-note {
  color: var(--ink-3);
  transition: opacity 0.25s ease, visibility 0s linear 0s;
}
.join-note a {
  margin-left: 0.2em;
  color: var(--ink-2);
  text-decoration: underline;
  text-decoration-color: var(--hair);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  border-radius: 3px;
  transition: text-decoration-color 0.2s ease, color 0.2s ease;
}
.join-note a:hover { color: var(--near-white); text-decoration-color: var(--near-white); }
.join-note a:focus-visible { outline: 1.5px solid var(--near-white); outline-offset: 2px; }
.join-msg {
  color: var(--near-white);
  font-weight: 500;
}
/* An empty message stays in the accessibility tree, so screen readers already know the status region
   when join.js puts words in it (visibility: hidden here took it out of the tree). It only lets clicks
   through to the Privacy link underneath. (Verifier, 4 Oct 2026.) */
.join-msg:empty { pointer-events: none; }
.has-msg .join-note {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0s linear 0.2s;
}
/* While sending, the message only speaks to screen readers. */
.is-sending .join-msg {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- the intro ---------------------------------------------------------------------
   About 2.5 s, then stillness. Opacity and transform only, plus a short blur on the
   mark alone (one element, once). Until assets/quiet.js says go, everything waits
   in the dark; with reduced motion, or without JavaScript, it is all simply there. */
.js:not(.still) .sky,
.js:not(.still) .mark,
.js:not(.still) .word,
.js:not(.still) .rule,
.js:not(.still) .soon-t,
.js:not(.still) .line { opacity: 0; }
.js:not(.still) .join { opacity: 0; visibility: hidden; }

/* Order: the light comes up, the mark comes into focus and the light crosses it once,
   then the words arrive in reading order and the form last. Only the mark blurs. */
.go .sky    { animation: fade 2.4s ease-out 0s both; }
.go .mark   { animation: settle 1.8s var(--ease-out) 0.1s both; }
.go .sheen::before { animation: sheen 1.5s cubic-bezier(0.45, 0, 0.25, 1) 0.75s both; }
.go .word   { animation: word-in 1.2s var(--ease-out) 0.6s both; }
.go .rule   { animation: rule 1.1s var(--ease-out) 0.95s both; }
.go .soon-t { animation: rise 1.0s var(--ease-out) 1.0s both; }
.go .line   { animation: rise 1.1s var(--ease-out) 1.15s both; }
.go .join   { animation: join-in 1.1s var(--ease-out) 1.35s both; }

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

/* The mark comes up out of soft focus and settles a few pixels into place. */
@keyframes settle {
  0%   { opacity: 0; transform: translateY(14px) scale(0.94); filter: blur(10px); }
  45%  { opacity: 1; filter: blur(0); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}

@keyframes sheen {
  0%   { opacity: 0; transform: translateX(0); }
  14%  { opacity: 1; }
  86%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(165%); }
}

/* The name simply rises into place: sharp type, no blur, so it reads as print, not effect. */
@keyframes word-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@keyframes rule {
  from { opacity: 0; transform: scaleX(0); }
  to   { opacity: 1; transform: none; }
}

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

@keyframes join-in {
  from { opacity: 0; visibility: hidden; transform: translateY(10px); }
  to   { opacity: 1; visibility: visible; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .sky, .mark, .word, .rule, .soon-t, .line { opacity: 1 !important; }
  .join { opacity: 1 !important; visibility: visible !important; }
  .sheen { display: none; }
}

/* ---- sizes ------------------------------------------------------------------------- */
@media (max-width: 520px) {
  .stage { padding: 40px 16px calc(40px + 4vh); }
  .soon { gap: 12px; font-size: 11px; letter-spacing: 0.32em; }
  .soon-t { margin-right: -0.32em; }
  .rule { width: 24px; }
  .line { margin-top: 20px; }
  .join { margin-top: 32px; }
  .join-btn { padding: 0 16px; }
}

/* Very narrow phones: the field sits above the button, still in one capsule. */
@media (max-width: 359px) {
  .join-row { flex-direction: column; align-items: stretch; }
  .join-input { height: 48px; text-align: center; padding: 0 12px; }
  .join-btn { height: 46px; justify-content: center; }
}

/* Short landscape screens: tighten the stack so the form stays on screen. */
@media (max-height: 560px) {
  .stage { padding-top: 28px; padding-bottom: 28px; }
  .mark { width: 72px; }
  .word { margin-top: 18px; font-size: 40px; }
  .soon { margin-top: 14px; }
  .line { margin-top: 12px; }
  .join { margin-top: 22px; }
}

@media (forced-colors: active) {
  .join-slot { border: 1px solid CanvasText; }
  .sheen, .grain, .sky { display: none; }
  /* Forced colours drop the gradient rim that shows focus on the field, so it gets a plain ring. */
  .join-input:focus { outline: 2px solid Highlight; outline-offset: -2px; }
}
