/* ---------------------------------------------------------------- fonts --
   Vendored from node_modules into /fonts by build.ts. Nothing is fetched
   from a third party: a page whose purpose is displaying a fingerprint must
   not be alterable by anyone else, and reading one must not disclose the
   visitor to a CDN.                                                       */

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

/* --------------------------------------------------------------- tokens */

:root {
  --sans: "Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Slashed zero, at every monospace site without exception. The whole job
     on this site is telling characters apart in a long string. */
  --mono-features: "zero" 1;

  --bg: #f6f7f8;
  --surface: #ffffff;
  --sunken: #edeff2;
  --line: #dde1e6;
  --line-strong: #b6bdc5;

  --ink: #14181c;
  --ink-2: #565e66;
  --ink-3: #6e767e;

  --accent: #1b4b8f;
  --accent-hover: #14396e;
  --accent-ink: #ffffff;
  --accent-soft: #e9eff8;
  --accent-line: #c2d3ea;

  --danger: #9c1d14;
  --danger-soft: #fbebe9;
  --danger-line: #f0c9c4;

  --r-card: 12px;
  --r-ctl: 8px;
  --r-chip: 6px;
  --r-sm: 4px;

  --page-max: 60rem;
  --gutter: 24px;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1215;
    --surface: #171b1f;
    --sunken: #1c2126;
    --line: #2a3037;
    --line-strong: #3c444d;

    --ink: #e8ebee;
    --ink-2: #a0a8b0;
    --ink-3: #8a939b;

    --accent: #86adef;
    --accent-hover: #a6c4f5;
    --accent-ink: #0b1220;
    --accent-soft: #172538;
    --accent-line: #2c4262;

    --danger: #f0a099;
    --danger-soft: #2b1512;
    --danger-line: #4a2420;

    color-scheme: dark;
  }
}

/* ---------------------------------------------------------------- reset */

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

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

img,
svg {
  display: block;
  max-width: 100%;
}

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

a:hover {
  color: var(--accent-hover);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

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

/* --------------------------------------------------------------- layout */

.wrap {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

main {
  flex: 1;
  padding-block: 40px 72px;
}

.section + .section {
  margin-top: 44px;
}

.section-head {
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 14px;
}

/* ------------------------------------------------------------- masthead */

.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.masthead-inner {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 60px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
}

.brand:hover {
  color: var(--ink);
}

.brand svg {
  width: 20px;
  height: 20px;
  color: var(--ink-2);
}

.brand:hover svg {
  color: var(--accent);
}

.masthead nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 20px;
}

.masthead nav a {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
}

.masthead nav a:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* ----------------------------------------------------------- page intro */

.page-title {
  font-size: clamp(1.75rem, 1.4rem + 1.6vw, 2.35rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.lede {
  margin-top: 12px;
  max-width: 62ch;
  color: var(--ink-2);
  font-size: 1.0625rem;
}

.back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  margin-bottom: 18px;
}

.back:hover {
  text-decoration: underline;
}

.back svg {
  width: 16px;
  height: 16px;
}

/* -------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 16px;
  border-radius: var(--r-ctl);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 120ms var(--ease), border-color 120ms var(--ease),
    color 120ms var(--ease);
}

.btn svg {
  width: 17px;
  height: 17px;
  flex: none;
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.btn-primary:hover {
  background: var(--accent-hover);
  color: var(--accent-ink);
}

/* A press is acknowledged on the control itself, not only by the toast that
   follows it — the toast is a scripted enhancement and may never arrive. */
.btn:active {
  transform: translateY(1px);
}

.btn-primary:active {
  background: var(--accent-hover);
}

.btn-secondary {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-strong);
}

.btn-secondary:hover {
  background: var(--sunken);
  color: var(--ink);
  border-color: var(--ink-3);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: var(--r-ctl);
  color: var(--ink-3);
  transition: background-color 120ms var(--ease), color 120ms var(--ease);
}

.icon-btn svg {
  width: 17px;
  height: 17px;
}

.icon-btn:hover {
  background: var(--sunken);
  color: var(--accent);
}

.icon-btn:active {
  background: var(--accent-soft);
  color: var(--accent);
}

/* A 64px row whose only affordance is a 34px icon reads as inert until the
   cursor happens to find it. The row answers the pointer instead. */
.row:hover .icon-btn {
  background: var(--sunken);
  color: var(--accent);
}

/* ----------------------------------------------------------------- tags */

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* Chips sit on the sunken key header, so they take the surface colour rather
   than the sunken one — a chip the same colour as its ground is not a chip. */
.tag {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 9px;
  border-radius: var(--r-chip);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink-2);
  font-size: 0.8125rem;
  font-weight: 500;
  white-space: nowrap;
}

.tag-danger {
  background: var(--danger-soft);
  border-color: var(--danger-line);
  color: var(--danger);
}

/* ---------------------------------------------------------- fingerprint --
   The one element on the site allowed to be loud. Two fixed lines of five
   groups that never reflow: a visitor comparing forty characters against a
   phone loses their place when the grouping changes with the viewport, so
   the grouping is identical at every width.

   Alternating groups carry a faint tint. It is a place-keeping aid for the
   eye moving between two devices, nothing more — it encodes nothing, and
   the characters themselves are the only thing to be compared.             */

.fp-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}

/* Sized against the block it sits in, not the viewport. Between 737px and
   ~800px the desktop layout puts the QR back beside the fingerprint while the
   page is still narrow, and a viewport-based size overflowed there — silently,
   because the card clips. Container units measure the space the value actually
   has. The horizontal auto-overflow is the backstop: if any width ever defeats
   the sizing, the value scrolls and stays readable rather than losing
   characters. Nothing may clip a fingerprint out of reach. */
.fp {
  font-family: var(--mono);
  font-feature-settings: var(--mono-features);
  font-weight: 500;
  font-size: clamp(1.15rem, 6.2cqi, 2rem);
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-left: -0.34em;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.fp-line {
  display: flex;
  flex-wrap: nowrap;
  gap: 2px;
  width: max-content;
}

.fp-line span {
  padding: 2px 0.34em;
  border-radius: var(--r-sm);
}

.fp-line span:nth-child(odd) {
  background: var(--sunken);
}

/* ------------------------------------------------------------------- QR --
   The white plate is the one hardcoded colour on the site and stays white in
   dark mode: a scanner needs a quiet zone whatever the visitor's theme.     */

.qr-figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  flex: none;
}

.qr-plate {
  background: #fff;
  padding: 10px;
  border-radius: var(--r-ctl);
  border: 1px solid var(--line);
}

.qr-plate img {
  width: 132px;
  height: 132px;
}

.qr-figure figcaption {
  font-size: 0.75rem;
  color: var(--ink-3);
  text-align: center;
  max-width: 16ch;
  line-height: 1.35;
}

/* -------------------------------------------------------------- the key --
   The handover block. Everything a correspondent needs in order to leave
   with the right key sits inside it, on the landing page, no click first.  */

.key {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}

.key + .key {
  margin-top: 20px;
}

.key-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 16px 24px;
  border-bottom: 1px solid var(--line);
  background: var(--sunken);
}

.key-name {
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.key-email {
  font-family: var(--mono);
  font-feature-settings: var(--mono-features);
  font-size: 0.875rem;
  color: var(--ink-2);
  text-decoration: none;
  overflow-wrap: anywhere;
}

.key-email:hover {
  color: var(--accent);
  text-decoration: underline;
}

.key-head .tags {
  margin-left: auto;
  justify-content: flex-end;
}

.key-body {
  display: flex;
  align-items: flex-start;
  gap: 32px;
  padding: 24px;
}

.key-main {
  flex: 1;
  min-width: 0;
  container-type: inline-size;
}

.key-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 22px;
}

.detail-link {
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.detail-link:hover {
  text-decoration: underline;
}

.detail-link svg {
  width: 15px;
  height: 15px;
}

/* -------------------------------------------------------------- command */

.command {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-ctl);
  padding: 7px 7px 7px 14px;
  max-width: max-content;
}

.command code {
  font-family: var(--mono);
  font-feature-settings: var(--mono-features);
  font-size: 0.875rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* ------------------------------------------------------------- advisory --
   The one place on the site that raises its voice, and it does so to warn,
   never to sell. Scarcity is what keeps it legible as a warning.           */

.advisory {
  display: flex;
  gap: 14px;
  padding: 18px 20px;
  border-radius: var(--r-card);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  color: var(--ink);
}

.advisory svg {
  width: 20px;
  height: 20px;
  flex: none;
  margin-top: 2px;
  color: var(--accent);
}

.advisory p {
  max-width: 66ch;
}

.advisory strong {
  font-weight: 600;
}

/* --------------------------------------------------------------- record --
   A key page's factual rows. The label sits above the value rather than
   beside it, so a 95-character lookup URL never squeezes its own label.    */

.record {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}

.row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 16px 13px 20px;
  min-height: 64px;
}

.row + .row {
  border-top: 1px solid var(--line);
}

.row-text {
  flex: 1;
  min-width: 0;
}

.row-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 3px;
}

.row-value {
  display: block;
  font-family: var(--mono);
  font-feature-settings: var(--mono-features);
  font-size: 0.9375rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.row-value.plain {
  font-family: var(--sans);
}

/* An expiry that has already passed is the one fact on a key page that has to
   raise its voice, so it does not read as one more grey row. */
.row-value.danger {
  color: var(--danger);
  font-weight: 500;
}

.note {
  margin-top: 12px;
  max-width: 66ch;
  font-size: 0.875rem;
  color: var(--ink-2);
}

.mono {
  font-family: var(--mono);
  font-feature-settings: var(--mono-features);
  font-size: 0.9em;
}

/* ----------------------------------------------------------- disclosure --
   Native <details>: the structural detail stays present and openable with
   scripting off, but demoted so it never competes with the fingerprint.    */

.disclosure {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}

.disclosure + .disclosure {
  margin-top: 10px;
}

.disclosure summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 15px 20px;
  font-size: 0.9375rem;
  font-weight: 500;
  cursor: pointer;
  list-style: none;
  transition: background-color 120ms var(--ease);
}

.disclosure summary::-webkit-details-marker {
  display: none;
}

.disclosure summary:hover {
  background: var(--sunken);
}

.disclosure summary .count {
  color: var(--ink-3);
  font-weight: 400;
}

.disclosure summary .chevron {
  margin-left: auto;
  display: flex;
  color: var(--ink-3);
  transition: transform 200ms var(--ease);
}

.disclosure summary .chevron svg {
  width: 18px;
  height: 18px;
}

.disclosure[open] summary {
  border-bottom: 1px solid var(--line);
}

.disclosure[open] summary .chevron {
  transform: rotate(180deg);
}

.disclosure-body {
  padding: 18px 20px;
}

/* -------------------------------------------------------------- packets */

.packets {
  list-style: none;
  padding: 0;
  margin: 0;
}

.packets li {
  padding: 10px 0;
}

.packets li:first-child {
  padding-top: 0;
}

.packets li:last-child {
  padding-bottom: 0;
}

.packets li + li {
  border-top: 1px solid var(--line);
}

.packet-name {
  display: block;
  font-family: var(--mono);
  font-feature-settings: var(--mono-features);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ink);
}

.packet-detail {
  display: block;
  margin-top: 2px;
  font-size: 0.8125rem;
  color: var(--ink-2);
}

/* -------------------------------------------------------------- armored */

.armored {
  font-family: var(--mono);
  font-feature-settings: var(--mono-features);
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--ink-2);
  background: var(--sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-ctl);
  padding: 16px;
  overflow-x: auto;
  white-space: pre;
}

/* ---------------------------------------------------------------- toast --
   The site's one authored motion moment, and the only element that leaves
   the page plane. Everything else transitions or does not move at all.     */

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 18px;
  border-radius: var(--r-ctl);
  background: var(--ink);
  color: var(--bg);
  font-size: 0.875rem;
  font-weight: 500;
  box-shadow: 0 6px 24px -6px rgb(0 0 0 / 0.35), 0 2px 6px -2px rgb(0 0 0 / 0.2);
  opacity: 0;
  transform: translate(-50%, 14px);
  pointer-events: none;
  transition: opacity 260ms var(--ease), transform 260ms var(--ease);
}

.toast svg {
  width: 17px;
  height: 17px;
  flex: none;
}

.toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* --------------------------------------------------------------- footer */

.site-footer {
  border-top: 1px solid var(--line);
  padding-block: 24px 32px;
}

.site-footer .inner {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  align-items: baseline;
  font-size: 0.875rem;
  /* --ink-3 clears 4.5:1 on --surface but only reaches 4.3:1 on the --bg the
     footer sits on, so the footer takes the stronger of the two greys. */
  color: var(--ink-2);
}

.site-footer .inner p:last-child {
  margin-left: auto;
}

/* ------------------------------------------------------------------ 404 */

.notfound {
  max-width: 60ch;
}

/* ----------------------------------------------------- no-js and motion --
   A scripted enhancement is removed when scripting is off, never shipped
   inert. Every value the copy controls duplicate is selectable text on the
   page regardless, so a dead button would be the only broken thing in view. */

.no-js [data-copy] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ----------------------------------------------------------- responsive --
   One breakpoint. Below it the key block stacks and the QR moves under the
   fingerprint. The fingerprint's grouping does not change, because that is
   the thing being compared, and the phone is where comparing happens.      */

@media (max-width: 46rem) {
  :root {
    --gutter: 16px;
  }

  main {
    padding-block: 28px 56px;
  }

  .key-head {
    flex-direction: column;
    gap: 10px;
    padding: 16px;
  }

  .key-head .tags {
    margin-left: 0;
    justify-content: flex-start;
  }

  .key-body {
    flex-direction: column;
    gap: 22px;
    padding: 18px 16px;
  }

  /* Stacked, the cross axis is horizontal, so .key-main would shrink to fit —
     and because it is a size-containment context its contents no longer feed
     that width, which collapses it to zero. It must be told to stretch. */
  .key-main {
    align-self: stretch;
  }

  .qr-figure {
    flex-direction: row;
    align-items: center;
    gap: 14px;
    align-self: flex-start;
  }

  .qr-figure figcaption {
    text-align: left;
    max-width: 20ch;
  }

  .key-foot .btn {
    flex: 1 1 auto;
  }

  .detail-link {
    flex-basis: 100%;
  }

  /* On a phone the QR is the least useful thing on the block — the visitor is
     already holding the second device — so it shrinks and yields the space. */
  .qr-plate img {
    width: 104px;
    height: 104px;
  }

  .command {
    max-width: none;
  }

  .row {
    padding: 12px 12px 12px 16px;
  }

  .toast {
    left: 16px;
    right: 16px;
    bottom: 16px;
    transform: translate(0, 14px);
  }

  .toast.show {
    transform: translate(0, 0);
  }

  .site-footer .inner p:last-child {
    margin-left: 0;
  }
}

/* ------------------------------------------------------------- printing --
   Printing this page is a legitimate second channel for comparing a
   fingerprint, so the printed sheet is designed rather than left to chance. */

@media print {
  :root {
    --bg: #fff;
    --surface: #fff;
    --sunken: #f2f2f2;
    --line: #bbb;
    --ink: #000;
    --ink-2: #333;
    --ink-3: #555;
  }

  .masthead,
  .toast,
  .icon-btn,
  .detail-link,
  .command,
  .key-foot {
    display: none !important;
  }

  .key,
  .record,
  .disclosure,
  .advisory {
    break-inside: avoid;
  }

  a {
    color: #000;
  }
}
