@font-face {
  font-family: "Martian Mono";
  src: url("fonts/MartianMono-Regular.woff2?v=bb677c9c") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Martian Mono";
  src: url("fonts/MartianMono-SemiBold.woff2?v=e6d4bb7a") format("woff2");
  font-weight: 600;
  font-display: swap;
}

/* light-dark() picks by color-scheme, which follows the system unless the theme switch pins it. */
:root {
  color-scheme: light dark;
  --bg: light-dark(#fbfbf8, #121313);
  --fg: light-dark(#1d1e1c, #efefec);
  --strong: light-dark(#111210, #f5f5f2);
  --mute: light-dark(#6a6c68, #9a9b97);
  --faint: light-dark(#c4c6c1, #3d3e3c);
  --accent: light-dark(#5e842b, #90c05b);
  --home: light-dark(#9dd163, #81af4b);
  --fill: var(--home);
  --ink: #111210;
  --eye: light-dark(#fbfbf8, #e4e4e1);
  --tongue: light-dark(#e0567f, #f28bad);
}

/* Each subpage has a colour of its own for its slabs, by the class on its body. Green stays with the
   front page and the way home. */
.ssh {
  --fill: light-dark(#edb345, #c8942c);
}

.gpg {
  --fill: light-dark(#ffa383, #e2805e);
}

.signal {
  --fill: light-dark(#76c8ff, #44a8e6);
}

.discord {
  --fill: light-dark(#c3afff, #a48de6);
}

.contact {
  --fill: light-dark(#ff98ca, #da7baa);
}

.not-found {
  --fill: light-dark(#c4c6c1, #6a6c68);
}

:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

/* Short slashes on a 24px grid: a line across each tile, which the two layers over it hide outside a
   7px square in the middle. */
body {
  margin: 0;
  color: var(--fg);
  background:
    linear-gradient(90deg, var(--bg) 9px, transparent 9px 16px, var(--bg) 16px) 0 0 / 24px 24px,
    linear-gradient(var(--bg) 9px, transparent 9px 16px, var(--bg) 16px) 0 0 / 24px 24px,
    linear-gradient(135deg, transparent calc(50% - 1px), var(--faint) 50%, transparent calc(50% + 1px))
      0.5px 0.5px / 24px 24px var(--bg);
  font: 1.0625rem/1.65 "Martian Mono", ui-monospace, monospace;
}

::selection {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
}

main {
  box-sizing: border-box;
  max-width: calc(52ch + 40px);
  margin: 0 auto;
  padding: clamp(48px, 14vh, 128px) 20px 64px;
  display: grid;
  gap: 3.4em;
}

h1,
p,
ul {
  margin: 0;
}

/* 44px is the smallest touch target that iOS recommends. */
.theme {
  position: absolute;
  top: 6px;
  right: 8px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--mute);
  cursor: pointer;
}

@media (hover: hover) {
  .theme:hover {
    color: var(--fg);
  }
}

.theme svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}

/* The icon shows the theme in use, whether the switch or the system chose it. */
.theme .moon,
:root[data-theme="dark"] .theme .sun {
  display: none;
}

:root[data-theme="dark"] .theme .moon {
  display: block;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme .sun {
    display: none;
  }

  :root:not([data-theme="light"]) .theme .moon {
    display: block;
  }
}

/* A bordered slab under the box, --lift down and right: the shadow 1px smaller than the one under
   it leaves that one as the slab's border. */
.card,
.qr,
:is(a:not(h2 a), .copy)::before {
  --lift: 6px;
  --slab: var(--fill);
  --slab-shadow: var(--lift) var(--lift) 0 -1px var(--slab), var(--lift) var(--lift) 0 0 var(--fg);
}

h1 {
  padding: 0.12em 0.45em;
  border: 1px solid var(--fg);
  background: var(--bg);
  font-size: 2.4em;
  line-height: 1.1;
  font-weight: 600;
  color: var(--strong);
}

/* The invitation's title on 30.alve.no stacks the same two slabs the other way round, green on
   top. */
h1.filled {
  background: var(--fill);
  color: var(--ink);
}

/* The title is a key too, 6px deep. frog.js has the frog look over while it is pressed. The header
   draws the slab, since a shadow on the key, offset to hold still, still shifts by part of a pixel. */
header {
  --lift: 6px;
  position: relative;
  isolation: isolate;
  justify-self: start;

  &::before {
    content: "";
    position: absolute;
    inset: var(--lift) calc(0px - var(--lift)) calc(0px - var(--lift)) var(--lift);
    z-index: -1;
    border: 1px solid var(--fg);
    background: var(--fill);
  }

  &:has(.filled)::before {
    background: var(--bg);
  }
}

/* top and left, not translate, keep the key's edges on whole pixels. */
h1 {
  position: relative;
  top: 0;
  left: 0;
  cursor: pointer;
  transition: top 70ms, left 70ms;

  @media (hover: hover) {
    &:hover {
      top: -1px;
      left: -1px;
    }
  }

  &:active {
    top: var(--lift);
    left: var(--lift);
  }
}

/* A subpage's sign stands where the front page has its title, and as tall: 2.4em at 1.1 line height,
   0.12em of padding twice and the border. Then the card stays put between the pages. */
.sign {
  box-sizing: border-box;
  display: grid;
  place-items: center;
  height: calc(2.4em * 1.34 + 2px);
  aspect-ratio: 1;
  border: 1px solid var(--fg);
  background: var(--bg);
  fill: var(--strong);
}

.card {
  /* Where the frog sits, which the trail stays clear of: its right edge, and its 28 columns of 2px
     pixels. */
  --frog-right: 3ch;
  --frog-width: 56px;
  position: relative;
  padding: 2.2em 2ch 1.8em;
  border: 1px solid var(--fg);
  background: var(--bg);
  box-shadow: var(--slab-shadow);
}

/* A row of arrows centred on the card's top border, on whole pixels so that the outlines stay
   sharp. The max-width stops it 0.5ch short of the frog, after the 1ch to its left and its 1ch of
   padding. */
.card h2 {
  --tip: calc(round(1em, 1px) / 2 + 6px);
  position: absolute;
  top: round(-6.5px - 0.5em, 1px);
  left: round(1ch, 1px);
  display: flex;
  max-width: calc(100% - var(--frog-right) - var(--frog-width) - 2.5ch);
  margin: 0;
  padding: 0 round(0.5ch, 1px);
  background: var(--bg);
  font: inherit;
  font-weight: 600;
  line-height: round(1em, 1px);
}

/* Each arrow is the outline colour, with its fill in a ::before 1px inside. The tip is 45
   degrees, as deep as half the arrow's height, and a 45 degree edge 1px further in is 1.414px
   further across. */
.card h2 > :not(.sep) {
  /* The box grows by --d to the right and below, for the slab under the arrow. --in and --mid are
     the arrow's far edges and its middle, inside that room. */
  --d: 3px;
  --in: calc(100% - var(--d));
  --mid: calc(50% - var(--d) / 2);
  /* --notch is 1 on an arrow with a notch in its back, and 0 on one with a straight back. --back
     is how far the line 1px inside the back sits across from it: 1px, or 1.414px in the notch. */
  --notch: 0;
  --back: calc(1px + var(--notch) * 0.41421px);
  --part: var(--bg);
  --slab: var(--fill);
  position: relative;
  flex: 0 1 auto;
  min-width: 1ch;
  margin: 0 calc(0px - var(--d)) calc(0px - var(--d)) 0;
  padding: 6px calc(var(--tip) + 0.6ch + var(--d)) calc(6px + var(--d)) 1ch;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  background: var(--fg);
  color: var(--accent);
  text-decoration: none;
  /* The arrow, and the slab under it: the same arrow, --d down and right. */
  clip-path: polygon(0 0, calc(var(--in) - var(--tip)) 0, 100% calc(var(--mid) + var(--d)), calc(100% - var(--tip)) 100%, var(--d) 100%, calc(var(--d) * (1 + var(--notch))) var(--in), 0 var(--in), calc(var(--notch) * var(--tip)) var(--mid));
  transition: translate 70ms;
}

.card h2 > :not(.sep)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--part);
  clip-path: polygon(calc(var(--notch) * 1px + var(--back)) 1px, calc(var(--in) - var(--tip) - 0.41421px) 1px, calc(var(--in) - 1.41421px) var(--mid), calc(var(--in) - var(--tip) - 0.41421px) calc(var(--in) - 1px), calc(var(--notch) * 1px + var(--back)) calc(var(--in) - 1px), calc(var(--notch) * var(--tip) + var(--back)) var(--mid));
}

/* The slab's fill, 1px inside its outline, where the arrow leaves it uncovered. The slab's upper
   diagonal meets the arrow's lower one 0.707px left of and below the arrow's tip. */
.card h2 > :not(.sep)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--slab);
  clip-path: polygon(calc(var(--d) + var(--notch) * 1px + var(--back)) calc(100% - 1px), calc(var(--d) * (1 + var(--notch)) + var(--back)) var(--in), calc(var(--in) - var(--tip)) var(--in), calc(var(--in) - 0.70711px) calc(var(--mid) + 0.70711px), calc(100% - 1.41421px) calc(var(--mid) + var(--d)), calc(100% - var(--tip) - 0.41421px) calc(100% - 1px));
}

/* The notch fits round the tip before it, 5px away. With far the larger shrink factor, the page
   names after alve.no give up the room when the trail runs out of it. */
.card h2 .sep + * {
  --notch: 1;
  flex-shrink: 1000;
  margin-left: calc(5px - var(--tip));
  padding-left: calc(var(--tip) + 0.8ch);
}

.card h2 [aria-current] {
  color: var(--strong);
}

/* Green as the way home, in the slab under it, as the page's own colour is under the current page's
   arrow. */
.card h2 a[href="/"]:not([aria-current]) {
  --slab: var(--home);
  color: var(--strong);
}

.card h2 .sep {
  display: none;
}

/* Each arrow rests on a 3px slab, since a deeper one would run into the next arrow's notch, 5px from
   the tip. Hover lifts the arrow 1px off the slab, which stays put, and a press puts it down on it. */
@media (hover: hover) {
  .card h2 a:is(:hover, :focus-visible) {
    --d: 4px;
    translate: -1px -1px;
  }
}

.card h2 a:active {
  --d: 0px;
  translate: 3px 3px;
}

.card h2 a:active::after {
  content: none;
}

/* The clip-path would cut off the outline that other links get. */
.card h2 a:focus-visible {
  outline: 0;
  text-decoration: underline 2px;
}

/* bottom is measured from inside the 1px border. Each pixel is 2px (the SVG's width and
   height), and the bottom two rows, the fingers, hang 4px over the border. */
.frog {
  position: absolute;
  bottom: calc(100% - 3px);
  right: var(--frog-right);
  color: var(--accent);
}

/* frog.js draws the idles on a canvas with 4 spare columns right of the frog. Only the frog's own
   pixels take the click that makes it croak. */
.frog.idle {
  right: calc(var(--frog-right) - 8px);
  pointer-events: none;

  & path {
    pointer-events: visiblePainted;
    cursor: pointer;
  }
}

.frog .fill {
  fill: var(--bg);
}

/* Light in both themes, so that the black pupils show on the dark site too. */
.frog .eyes {
  fill: var(--eye);
}

.frog .pupils {
  fill: var(--ink);
}

/* --strong, not the pupils' --ink, which would vanish on the dark page. */
.frog .speech {
  fill: var(--strong);
}

.frog .tongue {
  fill: var(--tongue);
}

.frog .prop {
  fill: var(--fill);
}

.frog .line,
.frog .lids {
  fill: currentColor;
}

.frog .pupils.blink {
  animation: frog-open 5s step-end infinite;
}

.frog .lids {
  opacity: 0;
  animation: frog-shut 5s step-end infinite;
}

@keyframes frog-open {
  96% {
    opacity: 0;
  }
  99% {
    opacity: 1;
  }
}

@keyframes frog-shut {
  96% {
    opacity: 1;
  }
  99% {
    opacity: 0;
  }
}

.links {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 1.2em;
}

.card > :not(h2) {
  letter-spacing: 0.04em;
}

.links li {
  display: grid;
  grid-template-columns: 12ch minmax(0, 1fr);
  column-gap: 1ch;
  overflow-wrap: anywhere;
}

.links .k {
  display: flex;
  gap: 1ch;
  white-space: nowrap;
  color: var(--mute);
}

.links .k::after {
  content: "································";
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--faint);
}

/* Below 600px the work address stops fitting beside its label, so the label goes above it. With a
   16px gutter it fits on one line at 17px from 423px, and at 14px from 359px. Changing font size at
   430px leaves 7px of slack for browsers that set the text wider. */
@media (max-width: 599px) {
  main {
    padding-inline: 16px;
  }

  .links li {
    grid-template-columns: minmax(0, 1fr);
  }

  .links .k::after {
    content: none;
  }
}

@media (max-width: 429px) {
  body {
    font-size: 0.875rem;
  }
}

.links .v {
  display: flex;
  flex-wrap: wrap;
  gap: 0 2ch;
}

/* Flex, so that an address that wraps lines up under its own first letter, not under the icon. */
.links a,
.fields a,
.copy {
  display: inline-flex;
  gap: 0.75ch;
}

/* The margin centres the icon on the lowercase letters of the first line, whole pixels only so that
   the icon's pixels stay sharp: 4px at 14px, 6px at 17px. */
.links svg,
.fields svg {
  flex: none;
  margin-top: round(0.895em - 9px, 1px);
  fill: var(--accent);
}

.fields {
  margin: 0;
  overflow-wrap: anywhere;
}

.fields dt {
  color: var(--mute);
}

.fields dd {
  margin: 0;
}

.fields dd + dt {
  margin-top: 1em;
}

.scan {
  display: flex;
  flex-wrap: wrap;
  align-items: start;
  gap: 2em 3ch;
}

/* Dark on light in both themes, since not every scanner reads a code the other way round. The
   max-width keeps the border on a phone narrower than the code. */
.qr {
  --lift: 4px;
  max-width: calc(100% - 2px);
  height: auto;
  border: 1px solid var(--fg);
  background: var(--eye);
  fill: var(--ink);
  box-shadow: var(--slab-shadow);
}

/* A GPG fingerprint wraps between its two halves, the way gpg prints it. */
.halves > span {
  white-space: nowrap;
}

a,
.copy {
  color: var(--fg);
  text-decoration: underline 1px var(--accent);
  text-underline-offset: 0.25em;
}

/* Phones draw a box over a tapped key, which would hide it pressing down. */
a,
.copy,
h1 {
  -webkit-tap-highlight-color: transparent;
}

/* The ::before is the key: 4px clear, inside its border, of the ascenders 0.882em above the
   baseline and the descenders 0.235em below, with the baseline 0.5lh + 0.412em down the line. The
   ::after holds the click area still while the key moves, so that a press near its edge lands. */
:is(a:not(h2 a), .copy) {
  position: relative;
  isolation: isolate;
  transition: translate 70ms;

  &::before,
  &::after {
    content: "";
    position: absolute;
    inset: round(0.5lh - 0.47em - 5px, 1px) -8px round(0.5lh - 0.647em - 5px, 1px);
    z-index: -1;
    transition: translate 70ms, box-shadow 70ms;
  }

  /* Without hover, as on phones, a focused key keeps the outline below. The border only comes with
     the key, since forced colours would show a transparent one at rest. */
  @media (hover: hover) {
    &:is(:hover, :focus-visible) {
      translate: -1px -1px;
      outline: 0;
      color: var(--strong);
      text-decoration-color: transparent;
    }

    &:is(:hover, :focus-visible)::before {
      --lift: 4px;
      border: 1px solid var(--fg);
      background: var(--bg);
      box-shadow: var(--slab-shadow);
    }

    &:is(:hover, :focus-visible)::after {
      translate: 1px 1px;
    }
  }

  &:active {
    translate: 3px 3px;
    color: var(--strong);
    text-decoration-color: transparent;
  }

  &:active::before {
    border: 1px solid var(--fg);
    background: var(--bg);
    box-shadow: none;
  }

  &:active::after {
    translate: -3px -3px;
  }
}

/* The insets need a box as tall as the line, which an inline link is not. */
a:not(h2 a) {
  display: inline-block;
}

.copy {
  padding: 0;
  border: 0;
  background: none;
  /* font leaves out letter-spacing, which buttons reset. */
  font: inherit;
  letter-spacing: inherit;
  text-align: left;
  cursor: pointer;
}

/* For screen readers only. On screen the check mark in a copy button's icon says what the note says,
   and the sign and the trail name a subpage. */
.note,
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The hint is the copy icon on a copy button and the external link icon on a link off the site. It
   is drawn 9 by 9, so it overflows the smaller icons that it stands in for. */
.links svg {
  overflow: visible;
}

.hint,
.copy .done,
.copy.copied path:not(.done),
:is(a, .copy:not(.copied)):focus-visible:has(.hint) path:not(.hint) {
  display: none;
}

.copy.copied .done,
:is(a, .copy:not(.copied)):focus-visible .hint {
  display: inline;
}

/* On phones a tap leaves :hover stuck, and the copy icon would stay after the check. */
@media (hover: hover) {
  :is(a, .copy:not(.copied)):hover:has(.hint) path:not(.hint) {
    display: none;
  }

  :is(a, .copy:not(.copied)):hover .hint {
    display: inline;
  }
}

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