@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-400.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-700.woff2") format("woff2");
}

:root {
  color-scheme: dark;

  /* Neutral ramp (primitives) */
  --gray-950: #0d1117;
  --gray-800: #30363d;
  --gray-500: #8b949e;
  --gray-100: #e6edf3;
  --green-400: #7ee787;

  /* Semantic colors */
  --color-bg: var(--gray-950);
  --color-text: var(--gray-100);
  --color-text-muted: var(--gray-500);
  --color-border: var(--gray-800);
  --color-accent: var(--green-400);

  /* Type: 16px base, ratio 1.333 */
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace;
  --text-base: 1rem;
  --text-lg: 1.333rem;
  --text-4xl: clamp(2.8rem, 12vw, 4.21rem);
  --text-link: var(--text-lg);

  /* Spacing: 4px base */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;

  --hit-target: 2.75rem;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --row-stagger: 60ms;
  --page-out: 120ms;
}

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

html {
  background: var(--color-bg);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  align-content: center;
  padding: var(--space-8) max(var(--space-6), env(safe-area-inset-right)) var(--space-8) max(var(--space-6), env(safe-area-inset-left));
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-mono);
  font-size: var(--text-base);
  font-weight: 400;
  line-height: 1.6;
  caret-color: var(--color-accent);
}

::selection {
  background: var(--color-accent);
  color: var(--color-bg);
}

/* Shrink to the widest line so the visible block, not an empty column, is centered. */
main {
  width: fit-content;
  max-width: min(40rem, 100%);
  margin-inline: auto;
}

.prompt {
  margin: 0;
  color: var(--color-text-muted);
}

.prompt::before {
  content: "$ ";
  color: var(--color-accent);
}

h1 {
  margin: var(--space-2) 0 var(--space-12);
  font-size: var(--text-4xl);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.04em;
  overflow-wrap: anywhere;
}

.links {
  list-style: none;
  margin: var(--space-2) 0 var(--space-8);
  padding: 0;
}

.link {
  display: grid;
  grid-template-columns: 2ch 11ch 1fr;
  align-items: baseline;
  align-content: center;
  min-height: var(--hit-target);
  color: var(--color-text);
  text-decoration: none;
  border-radius: 0;
}

.link::before {
  content: ">";
  color: var(--color-accent);
  opacity: 0;
  transform: translateX(-0.5ch);
  transition: opacity 120ms var(--ease-out), transform 120ms var(--ease-out);
}

.link-label {
  color: var(--color-text-muted);
}

.link-target {
  font-size: var(--text-link);
  text-decoration: underline;
  text-decoration-color: var(--color-text-muted);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.3em;
  text-underline-position: from-font;
  overflow-wrap: anywhere;
  transition: color 120ms var(--ease-out), text-decoration-color 120ms var(--ease-out);
}

.link:hover::before,
.link:focus-visible::before {
  opacity: 1;
  transform: none;
}

.link:hover .link-target,
.link:focus-visible .link-target {
  color: var(--color-accent);
  text-decoration-color: var(--color-accent);
}

.link:active .link-target {
  color: var(--color-text);
}

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* router.js focuses the new page's heading for screen readers; it is not a control. */
h1[tabindex="-1"]:focus {
  outline: none;
}

/* router.js: a page is taking longer than usual to load. */
.is-loading,
.is-loading * {
  cursor: progress;
}

/* "buy me a coffee" on the home page: the one bordered control on the page. Indented
   2ch so it lines up with the link labels above. */
.coffee-button {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit-target);
  margin: 0 0 var(--space-8) 2ch;
  padding: 0 2ch;
  border: 1px solid var(--color-accent);
  color: var(--color-accent);
  text-decoration: none;
  transition: background-color 120ms var(--ease-out), color 120ms var(--ease-out);
}

.coffee-button::before {
  content: "> ";
  white-space: pre;
}

.coffee-button:hover,
.coffee-button:focus-visible {
  background: var(--color-accent);
  color: var(--color-bg);
}

/* Coffee page. */
.lead {
  max-width: 36ch;
  margin: calc(-1 * var(--space-6)) 0 var(--space-12);
  color: var(--color-text-muted);
  text-wrap: pretty;
}

.wallets {
  list-style: none;
  margin: var(--space-4) 0 var(--space-8);
  padding: 0;
  display: grid;
  gap: var(--space-6);
}

.wallet {
  display: grid;
  grid-template-columns: 2ch 11ch minmax(0, 1fr);
  align-items: baseline;
}

.wallet > .link-label {
  grid-column: 2;
}

.wallet-body {
  display: grid;
  justify-items: start;
  gap: var(--space-1);
  min-width: 0;
}

.wallet-address {
  max-width: 100%;
  font: inherit;
  font-size: var(--text-base); /* a 42-char 0x address fits on one line on desktop */
  overflow-wrap: anywhere;
  word-break: break-all; /* a break opportunity between any two characters, so balance can work */
  text-wrap: balance; /* when it wraps, split evenly instead of leaving one orphan character */
  user-select: all;
}

.wallet-note {
  color: var(--color-text-muted);
}

.wallet-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: start;
  gap: 0 var(--space-4);
}

.wallet-action {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit-target);
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-text-muted);
  font: inherit;
  cursor: pointer;
  transition: color 120ms var(--ease-out);
}

/* The copy label changes (copy / copied / ctrl+c); a fixed width keeps [qr] from moving. */
.wallet-copy {
  min-width: 8ch;
}

.wallet-action::before {
  content: "[";
}

.wallet-action::after {
  content: "]";
}

.wallet-action:hover,
.wallet-action:focus-visible,
.wallet-action.is-done,
.wallet-action[aria-expanded="true"] {
  color: var(--color-accent);
}

.wallet-qr {
  display: block;
  width: 11rem;
  margin: 0 0 var(--space-2);
  padding: 0;
  border: 0;
  background: none;
  line-height: 0;
  cursor: zoom-in;
}

.wallet-qr[hidden] {
  display: none;
}

.wallet-qr img {
  display: block;
  width: 100%;
  height: auto;
}

/* Enlarged QR (coffee.js): a native modal <dialog>, so focus is trapped and Esc closes it.
   The image is sized to fit the viewport with room left for the hint line. */
.qr-zoom {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: var(--space-4);
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  cursor: zoom-out;
}

.qr-zoom[open] {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--space-4);
  animation: zoom-in 180ms var(--ease-out) both;
}

.qr-zoom::backdrop {
  background: rgb(13 17 23 / 0.94);
}

.qr-zoom img {
  display: block;
  width: min(88vw, calc(100dvh - 8rem));
  height: auto;
}

.qr-zoom p {
  margin: 0;
}

@keyframes zoom-in {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
}

.copy-status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.prompt-link {
  color: var(--color-text-muted);
  text-decoration: underline;
  text-decoration-color: var(--color-border);
  text-underline-offset: 0.3em;
  transition: color 120ms var(--ease-out), text-decoration-color 120ms var(--ease-out);
}

.prompt-link:hover,
.prompt-link:focus-visible {
  color: var(--color-accent);
  text-decoration-color: var(--color-accent);
}

/* Spans exactly the ink of the "$" glyph in JetBrains Mono (yMin -140, yMax 870 per 1000 units). */
.cursor {
  display: inline-block;
  width: 0.6em;
  height: 1.01em;
  vertical-align: -0.14em;
  background: var(--color-accent);
  animation: blink 1.06s steps(1, end) infinite; /* 530 ms on, 530 ms off, the classic terminal rate */
}

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

/* Interactive prompt (terminal.js). The fake cursor stays in flow so the field keeps
   its height; the real input is overlaid on top, with its native caret hidden so the
   fake cursor (moved to the caret by terminal.js) is the only one and blinks in sync
   with the tab title (blink.js). */
.shell {
  position: relative;
  margin: 0;
}

.shell-line {
  display: flex;
  gap: 1ch;
}

.shell-dollar {
  color: var(--color-accent);
}

.shell-field {
  position: relative;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  background: linear-gradient(var(--color-accent), var(--color-accent)) 0 100% / 0 1px no-repeat;
  transition: background-size 160ms var(--ease-out);
}

.shell-field:focus-within {
  background-size: 100% 1px;
}

.shell-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 1ch 0 0; /* room for the block cursor after the last character */
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--color-text);
  font: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  caret-color: transparent;
  outline: none;
}

.shell-input:not(:focus):not(:placeholder-shown) ~ .cursor {
  visibility: hidden;
}

/* Output floats below the prompt, so it never moves the centered card. */
.shell-output {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  max-height: 8rem;
  margin-top: var(--space-2);
  overflow-y: auto;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
  scrollbar-width: thin;
  scrollbar-color: var(--color-border) transparent;
}

.shell-echo {
  color: var(--color-text-muted);
}

.shell-output a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--color-text-muted);
  text-underline-offset: 0.3em;
  transition: color 120ms var(--ease-out), text-decoration-color 120ms var(--ease-out);
}

.shell-output a:hover,
.shell-output a:focus-visible {
  color: var(--color-accent);
  text-decoration-color: var(--color-accent);
}

/* One authored moment: the rows settle in one after another, on both pages. */
.links li,
.wallet {
  animation: settle 360ms var(--ease-out) both;
  animation-delay: calc(var(--row, 0) * var(--row-stagger));
}

:is(.links li, .wallet):nth-child(2) { --row: 1; }
:is(.links li, .wallet):nth-child(3) { --row: 2; }
:is(.links li, .wallet):nth-child(4) { --row: 3; }
:is(.links li, .wallet):nth-child(5) { --row: 4; }
:is(.links li, .wallet):nth-child(6) { --row: 5; }
:is(.links li, .wallet):nth-child(n + 7) { --row: 6; }

@keyframes settle {
  from {
    opacity: 0;
    transform: translateY(0.5rem);
  }
}

/* Moving between pages: the old page fades out, then the new one fades in, instead of a
   hard repaint. router.js runs the same ::view-transition animation for its in-page
   swaps; @view-transition covers ordinary navigations (no JS, or a failed fetch).
   Browsers without support simply navigate as before. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

::view-transition-old(root) {
  animation: page-out var(--page-out) ease-in both;
}

/* Starts when the old page is gone, so the two never show through each other. */
::view-transition-new(root) {
  animation: page-in 180ms var(--ease-out) var(--page-out) both;
}

@keyframes page-out {
  to {
    opacity: 0;
  }
}

@keyframes page-in {
  from {
    opacity: 0;
  }
}

@media (max-width: 30rem) {
  :root {
    --text-link: var(--text-base);
  }

  .link {
    grid-template-columns: 2ch 1fr;
    padding-block: var(--space-2);
  }

  .link::before {
    grid-row: 2;
  }

  .link-label {
    grid-row: 1;
    grid-column: 2;
    line-height: 1.4;
  }

  .link-target {
    grid-column: 2;
  }

  .wallet {
    grid-template-columns: 2ch minmax(0, 1fr);
    gap: var(--space-1) 0;
  }

  .wallet > .link-label,
  .wallet-body {
    grid-column: 2;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cursor,
  .links li,
  .wallet {
    animation: none;
  }

  .link::before,
  .link-target,
  .shell-field,
  .shell-output a,
  .coffee-button,
  .wallet-action,
  .prompt-link {
    transition: none;
  }

  .qr-zoom[open] {
    animation: none;
  }
}
