/*!
 * PrivacySafe Foundation, Inc. | privacysafe.foundation
 * Copyright (C) 2026 PrivacySafe Foundation, Inc.
 * SPDX-License-Identifier: AGPL-3.0-or-later
 *
 * This stylesheet is source code, licensed GNU Affero General Public License
 * version 3 or any later version. The site content it renders is separately
 * licensed CC BY-SA 4.0. See the header of index.html.
 *
 * ---------------------------------------------------------------------------
 * FILE ORDER, AND THE RULE THAT KEEPS IT FREE OF !important
 *
 *   1  tokens
 *   2  element base, including the canonical type scale
 *   3  layout primitives  (.wrap, .section, .section-head)
 *   4  accessibility utilities
 *   5  components, each one contiguous
 *   6  link treatments
 *   7  breakpoints, one block per width, widest first
 *
 * The type scale is set on bare h1/h2/h3/h4 selectors in section 2. A bare
 * element selector has specificity 0,0,1, so any component rule with a single
 * class beats it without needing !important: .panel-title and .footer-panel h2
 * simply set their own size. Keep the scale near the top for that reason.
 *
 * Every responsive override belongs in section 7 and nowhere else. A media query
 * does not raise specificity, so a same-specificity base rule appearing later in
 * the file would beat it at every viewport.
 *
 * Palette sampled from the "Support our mission" donation graphic and the live
 * Zeffy membership page, then adjusted for WCAG 2.1 AA.
 *
 * Measured contrast (sRGB) against the four surface levels:
 *
 *   foreground        on #001235  #002753  #003566  #04294B   verdict
 *   --text  #FFFFFF       18.45    14.84    12.34    14.75    AA
 *   --mist  #D6EEF0       15.24    12.26    10.20    12.18    AA
 *   --body  #BBD6E8       12.21     9.82     8.17     9.76    AA
 *   --quiet #8FB3CC        8.34     6.70     5.58     6.66    AA
 *   --green #4FD1A5        9.66     7.77     6.46     7.72    AA
 *   --teal  #5CC8C6        9.26     7.44     6.19     7.40    AA
 *   --blue  #63B3E8        8.04     6.46     5.38     6.42    AA
 *   --orange#FF7A45        7.14     5.74     4.77     5.70    AA
 *
 * The raw brand values sampled from the artwork are kept as -graphic tokens
 * and used ONLY for fills, gradients and decorative strokes, because as text
 * they do not reach 4.5:1:
 *
 *   #20AA8A green 4.22 | #38A4A2 teal 4.12 | #F64418 orange 3.39
 *   #2077B6 blue 2.57, below 3.0, so not even valid for a border or focus
 *                        ring on the deep surface. Never use it for those.
 *
 * The primary button uses a dark label on green, which measures 9.66:1. White
 * on the same green would be 2.93:1, under the 4.5:1 minimum, so it is not used
 * anywhere in this file.
 */

/* ===========================================================================
 * 1. TOKENS
 * ======================================================================== */
:root {
  /* surfaces, darkest to lightest */
  --ink: #001235;
  --navy: #002753;
  --deep: #003566;
  --card: #04294B;
  --card-2: #063560;

  /* text */
  --text: #FFFFFF;
  --mist: #D6EEF0;
  --body: #BBD6E8;
  --quiet: #8FB3CC;

  /* accents, AA-safe as text */
  --green: #4FD1A5;
  --teal: #5CC8C6;
  --blue: #63B3E8;
  --orange: #FF7A45;

  /* raw sampled brand values: fills and strokes only, never text */
  --graphic-green: #20AA8A;
  --graphic-teal: #38A4A2;
  --graphic-blue: #2077B6;
  --graphic-orange: #F64418;
  --graphic-orange-2: #FF6428;

  --line: rgba(214, 238, 240, .14);
  --line-strong: rgba(214, 238, 240, .26);

  /* -------------------------------------------------------------------
   * TYPE SCALE: the whole site uses these nine tokens and nothing else.
   * Deliberately few, and deliberately large: body copy is 19px, not the
   * 16px privacysafe.app currently runs, because long-form prose about
   * privacy is easier to read big. Anything below --fs-body is reserved for
   * uppercase micro-labels and genuine secondary text.
   *
   * This is the canonical scale, kept in step with privacysafe.app so
   * headings match in weight and rhythm across properties.
   * ----------------------------------------------------------------- */
  --fs-label: 14px;                          /* UPPERCASE LABELS ONLY */
  --fs-small: 17px;                          /* captions, notes, nav */
  --fs-body: 19px;                           /* all paragraph copy */
  --fs-lead: clamp(21px, 2.2vw, 26px);       /* hero lead */
  --fs-h4: 20px;
  --fs-h3: 24px;
  --fs-h2: clamp(2rem, 4.4vw, 3.8rem);
  --fs-h1: clamp(2.25rem, 4.9vw, 4.1rem);
  --fs-panel: clamp(28px, 3vw, 38px);        /* footer panel headings */

  /* rhythm: one token for the gap under the sticky header, so the hero and
     the first section can never drift apart */
  --gap-header: 22px;

  --radius: 18px;
  --radius-lg: 28px;
  --radius-pill: 999px;
  --shadow: 0 18px 70px rgba(0, 6, 20, .42);
  --max: 1440px;

  color-scheme: dark;
  --font: "Inter", "Liberation Sans", "Helvetica Neue", Helvetica, Arial,
    "DejaVu Sans", Roboto, "Segoe UI", system-ui, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
  --mono: "JetBrains Mono", "DejaVu Sans Mono", "Liberation Mono", Menlo,
    Consolas, monospace;
}

/* ===========================================================================
 * 2. ELEMENT BASE
 * ======================================================================== */
* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* keeps anchored sections clear of the sticky header */
  scroll-padding-top: 96px;
}

/*
 * Column layout so a short page still puts its footer at the bottom of the
 * viewport and main takes whatever height is left over. This is what lets
 * .page-standalone centre its content in the viewport rather than guessing at
 * the height of the surrounding chrome.
 */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-body);
  color: var(--body);
  background:
    radial-gradient(circle at 14% 6%, rgba(32, 119, 182, .22), transparent 30rem),
    radial-gradient(circle at 86% 12%, rgba(56, 164, 162, .18), transparent 32rem),
    radial-gradient(circle at 50% 78%, rgba(32, 170, 138, .12), transparent 36rem),
    var(--ink);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--blue); text-decoration: none; }
button { font: inherit; }
img, video { max-width: 100%; }

body > main { flex: 1 0 auto; }
body > footer { flex: 0 0 auto; }

h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin-top: 0; }

h1, h2, h3, h4 {
  color: var(--text);
  font-weight: 700;
  font-kerning: normal;
}

/* The canonical scale. Bare element selectors on purpose, see the note at
   the top of this file. Components override by class alone. */
h1 { font-size: var(--fs-h1); line-height: 1.18; letter-spacing: -.02em; }
h2 { font-size: var(--fs-h2); line-height: 1.18; letter-spacing: -.012em; }
h3 { font-size: var(--fs-h3); line-height: 1.18; letter-spacing: 0; }
h4 { font-size: var(--fs-h4); line-height: 1.20; letter-spacing: -.01em; }

p { line-height: 1.62; }

/* ===========================================================================
 * 3. LAYOUT PRIMITIVES
 * ======================================================================== */
.wrap { width: min(var(--max), calc(100% - 48px)); margin: 0 auto; }

.section { padding: 34px 0; }

.band {
  background: linear-gradient(180deg,
    rgba(0, 39, 83, 0), rgba(0, 39, 83, .55) 18%, rgba(0, 39, 83, 0));
}

/*
 * A section intro capped at 104ch. Past roughly 100 characters a line is hard
 * to track back from, so the cap stays, just a generous one.
 */
.section-head { max-width: 104ch; margin-bottom: 22px; }
.section-head h2 { margin-bottom: 10px; }
.kicker { color: var(--body); margin-bottom: 0; }

/*
 * CENTRED SECTION HEADS
 *
 * Centring a block takes three declarations, not one:
 *
 *   max-width: none   lets the head span the full container
 *   margin-inline     centres each child block within it
 *   text-align        centres the lines inside those blocks
 *
 * The intro paragraph is then re-capped at 88ch on its own, so a centred line
 * does not run the full 1440px and become unreadable. The heading is left
 * uncapped so it centres against the true row width, matching the grid of
 * cards beneath it.
 */
.section-head.center { max-width: none; text-align: center; }
.section-head.center > * { margin-inline: auto; }
/*
 * The intro is uncapped so a centred head spans the same measure as the content
 * it introduces. 1.55 leading compensates: a wide measure is harder to track back
 * from at the end of a line, and leading is the lever that fixes it.
 */
.section-head.center .kicker { max-width: none; line-height: 1.55; }

/* ===========================================================================
 * 4. ACCESSIBILITY UTILITIES
 * ======================================================================== */

/* WCAG 2.4.1 Bypass Blocks */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 12px 18px;
  border-radius: 0 0 12px 0;
  background: var(--green);
  color: var(--ink);
  font-weight: 700;
}
.skip-link:focus { left: 0; }

/* WCAG 2.4.7 focus visible */
:focus-visible {
  outline: 3px solid var(--green);
  outline-offset: 3px;
  border-radius: 6px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/*
 * WCAG 2.3.3 Animation from Interactions. Every transition in this file is
 * declared on a single class, so `transition: none` on a universal selector
 * inside this query wins on order alone and no !important is needed.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition: none;
    animation: none;
  }
}

/* ===========================================================================
 * 5. COMPONENTS
 * ======================================================================== */

/* ---------- header ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  padding: 14px 0;
  backdrop-filter: blur(16px);
  background: linear-gradient(to bottom, rgba(0, 18, 53, .92), rgba(0, 18, 53, .66));
  border-bottom: 1px solid rgba(214, 238, 240, .07);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  min-height: 64px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 20px;
  background: rgba(0, 39, 83, .72);
  box-shadow: 0 8px 34px rgba(0, 6, 20, .3);
}

/*
 * The horizontal lock-up carries the whole name, so no text sibling is needed.
 * Sized as privacysafe.app sizes its wordmark, scaled for the longer word:
 * "PRIVACYSAFE FOUNDATION" is 4.64:1 against the app wordmark's 5.81:1, so it
 * needs a little less width for the same cap height.
 */
.brand { display: flex; align-items: center; text-decoration: none; min-width: 0; }
.brand-logo { display: block; width: clamp(196px, 17vw, 262px); height: auto; }

.navlinks { display: flex; gap: 4px; align-items: center; }
.navlinks a { padding: 9px 13px; font-size: var(--fs-small); }

.nav-actions { display: flex; align-items: center; gap: 10px; }

/* ---------- buttons ----------
 * One base, three modifiers, one interaction model.
 *
 * Every button is a pill with a 2px border and a 44px minimum height
 * (2.5.8 Target Size). The modifiers change colour only, never geometry, so a
 * row of mixed buttons always aligns.
 *
 *   .btn          outline, green border      secondary action
 *   .btn.primary  filled green, dark label   the main action in its context
 *   .btn.ghost    outline, neutral border    tertiary / "not this one"
 *   .btn.small    40px tall, tighter padding in-card actions
 *
 * Hover and focus-visible get identical treatment, so keyboard users see
 * exactly what mouse users see.
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 44px;
  padding: 0 22px;
  font-size: var(--fs-body);
  font-weight: 600;
  letter-spacing: -.005em;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  border: 2px solid var(--green);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text);
  transition: transform .16s ease, background .16s ease,
              box-shadow .16s ease, border-color .16s ease;
}
.btn:hover,
.btn:focus-visible {
  transform: translateY(-1px);
  background: rgba(79, 209, 165, .14);
  box-shadow: 0 10px 26px rgba(32, 170, 138, .18);
}
.btn:active { transform: translateY(0); box-shadow: none; }

/* 1.4.3: a dark label on green measures 9.66:1. White on the same green is
   2.93:1, which is under the minimum. */
.btn.primary { border-color: var(--green); background: var(--green); color: var(--ink); }
.btn.primary:hover,
.btn.primary:focus-visible {
  background: #6FDDB6;
  border-color: #6FDDB6;
  box-shadow: 0 12px 30px rgba(32, 170, 138, .28);
}

.btn.ghost { border-color: var(--line-strong); color: var(--body); }
.btn.ghost:hover,
.btn.ghost:focus-visible {
  border-color: var(--blue);
  background: rgba(214, 238, 240, .08);
  box-shadow: 0 10px 26px rgba(0, 6, 20, .28);
  color: var(--text);
}

.btn.small { min-height: 40px; padding: 0 16px; font-size: var(--fs-small); }
.btn .ico { width: 18px; height: 18px; }

/* ---------- icons: inline SVG ----------
 * Icons are inlined in the markup rather than fetched, so they render from a
 * file:// document, where an opaque origin blocks the fetch, and so they inherit
 * currentColor from the cascade. img/icon-*.svg holds the same geometry as
 * standalone files, for reference and reuse elsewhere.
 *
 * No background-color here: on an inline <svg> that paints a solid block behind
 * the glyph. Colour comes from currentColor in the paths, which is what lets
 * .card-icon, .badge and .social-link:hover control it.
 */
.ico {
  display: block;
  flex: 0 0 auto;
  /* guard against a stray width/height attribute on the root svg */
  max-width: 100%;
  max-height: 100%;
}

/* ---------- hero ----------
 * --gap-header is the single source of truth for the clearance between the
 * sticky header and the first line of content.
 */
.hero { padding: var(--gap-header) 0 34px; }
.hero-copy { max-width: 104ch; }   /* same cap as .section-head */

/*
 * CENTRED HERO COPY
 *
 * Centring a block takes three things. text-align centres the lines inside the
 * block, margin-inline centres the block itself, and the button row needs
 * justify-content because text-align does not reach flex items. The lead is
 * capped separately so a centred measure does not run the full container width.
 */
.hero-copy.center { max-width: none; text-align: center; }
.hero-copy.center > * { margin-inline: auto; }
.hero-copy.center .lead { max-width: 62ch; }
.hero-copy.center .hero-actions { justify-content: center; }

.hero h1 {
  margin: 22px 0 16px;
  /*
   * No white-space: nowrap. It would hold the line together at default sizes but
   * overflow horizontally under WCAG 1.4.4 text-only scaling to 200%, where the
   * clamp's rem floor keeps the type large while the viewport does not grow.
   * text-wrap: balance handles the narrow case instead.
   */
  text-wrap: balance;
}

.accent { color: var(--orange); }
.accent-green { color: var(--green); }

.lead {
  color: var(--mist);
  font-size: var(--fs-lead);
  line-height: 1.52;
  max-width: 60ch;
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 28px 0 26px; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 14px;
  border: 1px solid rgba(79, 209, 165, .34);
  border-radius: var(--radius-pill);
  background: rgba(32, 170, 138, .13);
  color: var(--green);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--graphic-green);
  box-shadow: 0 0 0 5px rgba(32, 170, 138, .16);
}

/* ---------- hero media panels ----------
 * Two equal panels: the Support our mission board and the PrivacySafe Bot
 * demo. Equal columns so neither reads as subordinate to the other.
 *
 * align-items: stretch (the default) rather than start, so the two panels are
 * the same height and their media boxes line up top and bottom, the point of
 * the square frame below. The captions differ in length, so the shorter panel
 * gains trailing space rather than a shorter box.
 */
.hero-duo {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  margin-top: 26px;
}

.panel {
  display: flex;
  flex-direction: column;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(160deg, rgba(32, 119, 182, .16), rgba(4, 41, 75, .9));
  box-shadow: var(--shadow);
}

/* opts out of the canonical h2 size by class alone */
.panel-title {
  margin: 2px 2px 14px;
  font-size: var(--fs-h3);
  line-height: 1.2;
  letter-spacing: -.01em;
  color: var(--text);
}

/*
 * The two panels sit side by side, so their media must occupy identical boxes
 * or the row looks broken. Source dimensions are 800x800 for the board and
 * 800x800 declared on the video element, and a 1/1 frame is imposed on both so
 * the boxes match regardless of what the sources turn out to be.
 *
 * The board uses cover and is already square, so nothing is cropped. The video
 * uses contain: cropping a UI demo would cut into the interface being
 * demonstrated, so it letterboxes against the panel colour instead.
 */
.panel img,
.panel video {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  height: auto;
  border-radius: 14px;
  background: var(--ink);
  border: 1px solid rgba(214, 238, 240, .12);
}
.panel img { object-fit: cover; }
.panel video { object-fit: contain; }

/*
 * The fallback content inside the video element: a still of the first frame and a
 * download link, shown only when the element itself is unsupported. It has to be
 * styled here because it inherits nothing from .panel video.
 */
.panel video img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  height: auto;
  object-fit: cover;
  border-radius: 14px;
}
.panel video p {
  margin: 10px 2px 0;
  color: var(--quiet);
  font-size: var(--fs-small);
  line-height: 1.5;
}

/*
 * The board links to the donation page, so the <a> is the block-level box and
 * the image fills it. Without display: block the anchor stays inline and its
 * line-height adds a few stray pixels under the image, which would break the
 * square alignment with the video in the panel beside it.
 */
.panel-media { display: block; border-radius: 14px; }
.panel-media img { margin: 0; }

.panel .panel-actions { margin: 16px 2px 2px; }

.panel .media-caption:last-child { margin-bottom: 2px; }

.media-caption {
  margin: 12px 2px 0;
  color: var(--quiet);
  font-size: var(--fs-small);
  line-height: 1.5;
}
.media-caption strong { color: var(--green); }

/* ---------- ways to give ----------
 * Four across on a wide screen, folding to two then one. auto-fit collapses
 * the empty tracks, so the four cards always divide the full row.
 */
.methods {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 12px;
}

.method {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(4, 41, 75, .68);
  transition: border-color .16s ease, background .16s ease;
}
.method:hover { border-color: rgba(79, 209, 165, .5); background: rgba(6, 53, 96, .8); }

.method-head { display: flex; align-items: center; gap: 11px; }
.method-head h3 { margin: 0; }
.method-icon {
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(79, 209, 165, .3);
  border-radius: 12px;
  background: rgba(32, 170, 138, .12);
  color: var(--green);
}
.method-icon .ico { width: 21px; height: 21px; }

.method p { margin: 0; color: var(--body); }
.method .note { color: var(--quiet); font-size: var(--fs-small); }
.method .actions { margin-top: auto; padding-top: 4px; }
.method dl { margin: 0; }
.method dt {
  margin-top: 10px;
  color: var(--mist);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.method dd { margin: 3px 0 0; color: var(--body); }
.method address { font-style: normal; color: var(--body); line-height: 1.55; }

.tax-note {
  margin: 18px 0 0;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--graphic-green);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: rgba(0, 39, 83, .6);
  color: var(--body);
  line-height: 1.6;
}
.tax-note p { margin: 0 0 10px; }
.tax-note p:last-child { margin-bottom: 0; }
.tax-note strong { color: var(--text); }
.tax-note .note { color: var(--quiet); font-size: var(--fs-small); }
.tax-note .ein { font-family: var(--mono); color: var(--mist); }

/* ---------- project cards ----------
 * Structure borrowed from the profdiggity site directory: the whole card is one
 * link, a fixed-ratio thumbnail sits above a padded body, and the image is held
 * back slightly so text over it stays comfortable, brightening on hover.
 */
.site-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  align-items: stretch;
}

.site-card {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background:
    linear-gradient(180deg, rgba(32, 119, 182, .10), rgba(214, 238, 240, .015)),
    rgba(4, 41, 75, .7);
  transition: transform .18s ease, border-color .18s ease, background .18s ease;
}
.site-card:hover,
.site-card:focus-within {
  transform: translateY(-3px);
  border-color: rgba(79, 209, 165, .4);
  background:
    linear-gradient(180deg, rgba(32, 170, 138, .09), rgba(214, 238, 240, .018)),
    rgba(6, 53, 96, .78);
}

/* one link per card, so the card has a single accessible name */
.site-link {
  display: flex;
  flex-direction: column;
  height: 100%;
  color: inherit;
  text-decoration: none;
}

/*
 * A fixed height rather than aspect-ratio. The sources are 800px wide at ratios
 * from 1.12 to 1.37; a ratio box would make the thumbnail roughly 400px tall in
 * a two-column grid, which swamps the card. A flat 210px keeps the screenshot
 * a recognisable strip and the text the substance, and does not depend on
 * aspect-ratio being honoured. The absolute positioning means the image cannot
 * push the box open even if object-fit is ignored.
 */
.site-thumb {
  position: relative;
  display: block;
  height: 210px;
  overflow: hidden;
  background: var(--ink);
  border-bottom: 1px solid var(--line);
}
.site-thumb img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  /* top-anchored: the identifying part of a UI screenshot is its header */
  object-fit: cover;
  object-position: top center;
  filter: saturate(.9) brightness(.82);
  transition: transform .25s ease, filter .25s ease;
}
.site-thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 48%, rgba(0, 12, 32, .34));
  pointer-events: none;
}
.site-card:hover .site-thumb img,
.site-card:focus-within .site-thumb img {
  transform: scale(1.02);
  filter: saturate(1) brightness(.96);
}

.site-body { display: flex; flex-direction: column; flex: 1; padding: 20px 20px 22px; }
.site-body h3 { margin: 0 0 8px; color: var(--text); }

.site-url {
  display: block;
  margin-bottom: 12px;
  font-family: var(--mono);
  font-size: var(--fs-small);
  line-height: 1.35;
  color: var(--teal);
  overflow-wrap: anywhere;
}

.site-description { display: block; color: var(--body); line-height: 1.55; }

.site-kicker {
  display: block;
  margin-top: auto;
  padding-top: 18px;
  color: var(--text);
  font-weight: 700;
}
.site-kicker::after { content: " \2197"; color: var(--green); }

/* ---------- media rows ----------
 * The photographs were running the full height of the row and dwarfing the
 * copy beside them. The image column is narrower and the picture capped in
 * height, so the two sides carry comparable weight.
 */
.media-row {
  display: grid;
  grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
  gap: 34px;
  align-items: center;
  padding: 14px 0;
}
.media-row figure { margin: 0; }
.media-row img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 300px;
  object-fit: cover;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.media-row figcaption {
  margin-top: 10px;
  color: var(--quiet);
  font-size: var(--fs-small);
  line-height: 1.5;
}
.media-row h2 { margin-bottom: 12px; }
.media-row p { color: var(--body); }

/* ---------- standards strip ---------- */
.standards-strip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
  margin-top: 18px;
}
.standard-logo {
  min-height: 92px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(4, 41, 75, .7);
}
/*
 * Mixed aspect ratios: the compliance wordmarks are 500x86 (5.81:1), the
 * 3NWeb mark is 96x59 (1.63:1). Cap height, let width follow, so nothing is
 * enlarged past its native size and every logo lands on one optical weight.
 */
.standard-logo img {
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: 34px;
  object-fit: contain;
}
.standard-logo--tall img { max-height: 48px; }
.standard-logo:hover,
.standard-logo:focus-visible {
  border-color: rgba(79, 209, 165, .5);
  background: rgba(32, 170, 138, .08);
}

/* ---------- program cards ---------- */
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }

.card {
  padding: 24px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: rgba(4, 41, 75, .7);
  box-shadow: 0 14px 42px rgba(0, 6, 20, .24);
}
.card h3 { margin-bottom: 9px; }
/*
 * Paragraphs inside a card were flush against each other at margin: 0, so a card
 * carrying three or four of them read as one undifferentiated block. Spacing
 * between them, none after the last, so the rule above the action row keeps its
 * measured gap.
 */
.card p { margin: 0 0 14px; color: var(--body); }
.card p:last-of-type { margin-bottom: 0; }

/* ---------- pull-out ----------
 * For the one sentence in a card that a reader is scanning for. Set apart with a
 * green rule and a slightly lifted background rather than by making the type
 * bigger: the surrounding paragraphs are already 19px, and raising this further
 * would start competing with the heading.
 */
.pullout {
  margin: 18px 0;
  padding: 16px 20px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--green);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: rgba(32, 170, 138, .07);
  line-height: 1.6;
}
.pullout-lead { color: var(--green); }

/*
 * The identity handles, in the mono face. They are addresses to be read
 * character by character and typed, which is the same reason the cryptocurrency
 * addresses use it, and the tint separates them from the ordinary bold around
 * them without adding another colour.
 */
.handle {
  font-family: var(--mono);
  font-size: .93em;
  font-weight: 600;
  color: var(--mist);
  background: rgba(0, 18, 53, .5);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1px 7px;
  white-space: nowrap;
}

.card-icon {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  margin-bottom: 18px;
  border: 1px solid rgba(79, 209, 165, .3);
  border-radius: 14px;
  background: rgba(32, 170, 138, .12);
  color: var(--green);
}
.card-icon .ico { width: 24px; height: 24px; }

/* ---------- people ----------
 * The portraits are 570x447 RGBA cutouts: the subject already sits on its own
 * shaped gradient with transparent corners and a flat bottom edge. So there is
 * no card here at all: no border, background, or padding. Boxing a cutout
 * just draws a rectangle around artwork made to float, and the two competing
 * shapes read as a mistake.
 */
.people {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /*
   * A tighter column gap than the section default, to give the columns
   * themselves the room instead. The two tracks are what has to hold the
   * contact rows, and the gap was taking 72px that the longest handle needed.
   */
  gap: 28px clamp(20px, 3.5vw, 48px);
  margin: 4px 0 34px;
  justify-items: center;
}

.person {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  /*
   * 520px, not 400px. The contact grid inside is a fixed label column of roughly
   * 90px plus a 14px gap, so a 400px card left about 296px for the value, and
   * the longest handle on the page, @missdiorsheree@privacysafe.social, does not
   * fit in that: it wrapped mid-word onto a second line. 520px leaves around
   * 415px, which clears it with room to spare.
   *
   * The portraits are 570px wide at source, so they are still being scaled down
   * rather than up at this size.
   */
  max-width: 520px;
}

.person img {
  display: block;
  width: 100%;
  height: auto;
  /* lifts the cutout off a dark background without drawing an edge */
  filter: drop-shadow(0 18px 34px rgba(0, 6, 20, .55));
}

/* short gradient rule in place of a card border */
.person h3 { position: relative; margin: 20px 0 0; padding-top: 18px; }
.person h3::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 54px;
  height: 2px;
  transform: translateX(-50%);
  border-radius: 2px;
  background: linear-gradient(90deg, var(--graphic-blue), var(--graphic-green));
}

.person .role { display: block; margin-top: 7px; color: var(--body); line-height: 1.55; }
.person .role strong {
  display: block;
  margin-bottom: 4px;
  color: var(--green);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
}

/* per-person contact details */
.person-contact {
  margin: 20px 0 0;
  width: 100%;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 4px 14px;
  text-align: left;
}
.person-contact dt {
  color: var(--quiet);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  padding-top: 3px;
  white-space: nowrap;
}
.person-contact dd { margin: 0; color: var(--body); min-width: 0; overflow-wrap: anywhere; }

/* fingerprints are read character by character, so they get a mono face and
   tighter leading than body copy */
.fpr {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--fs-small);
  line-height: 1.45;
  letter-spacing: .02em;
  color: var(--mist);
  background: rgba(0, 18, 53, .55);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 8px;
}

/* ---------- full-width stacked info rows ---------- */
.stack { display: grid; gap: 16px; }

/* the two divergent calls to action in the "who sells it" row */
.split-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
/* ---------- partner wordmark ----------
 * The Ivy Cyber wordmark inset in the top-right corner of the card that explains
 * who sells the software. This is separate from the "Buy services from Ivy Cyber"
 * button in the action row below; both point at the shop.
 *
 * Floated and placed after the heading, so it sits level with the first paragraph
 * and the copy wraps around it, rather than pushing the text down or crowding the
 * h3.
 *
 * Sized to the space it occupies: 328px wide, which at the source ratio of
 * 2300:512 is 73px tall. Padding insets it from the copy on the left and from the
 * paragraph below, with nothing on the card's own edge side so it reads as
 * belonging to that corner.
 *
 * BOTH DIMENSIONS ARE SET, HERE AND ON THE ELEMENT, DELIBERATELY. The supplied SVG
 * had no viewBox, so it carried no intrinsic aspect ratio and width: auto had
 * nothing to resolve against: the image ignored its height and stretched to fill
 * the card. A viewBox has been added to the file, and the box is stated in both
 * places, so no single missing piece can blow the layout out again. If this mark is
 * replaced, confirm the new file has a viewBox before trusting auto on either axis.
 */
.has-partner { overflow: hidden; }   /* contain the float */
.partner-mark {
  display: block;
  float: right;
  padding: 2px 0 16px 30px;
  transition: opacity .16s ease, transform .16s ease;
}
.partner-mark img { display: block; width: 328px; height: 73px; max-width: 100%; }
.partner-mark:hover,
.partner-mark:focus-visible { transform: translateY(-1px); opacity: .85; }


/* ---------- contact channels ----------
 * A <dl> laid out directly as a grid puts every dt and dd in its own cell, so the
 * pairs scatter across columns. Each pair is wrapped in a div and the grid tracks
 * those wrappers instead.
 *
 * Each channel is a tile with its icon above the label, so the icon identifies
 * the channel before the text is read. The tiles run horizontally rather than
 * stacking, which keeps the block short inside a full-width card.
 *
 * Three of the four tiles are links across their whole area, giving a target far
 * larger than the 24px icon and clearing 2.5.8 comfortably. The postal address is
 * not actionable, so .is-static keeps it inert rather than looking clickable.
 */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
  gap: 12px;
  margin: 18px 0 0;
  align-items: stretch;
}
.contact-item { min-width: 0; }

.contact-tile {
  display: flex;
  flex-direction: column;
  gap: 4px;
  height: 100%;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(0, 18, 53, .42);
  color: inherit;
  text-decoration: none;
  transition: border-color .16s ease, background .16s ease, transform .16s ease;
}
a.contact-tile:hover,
a.contact-tile:focus-visible {
  transform: translateY(-2px);
  border-color: rgba(79, 209, 165, .45);
  background: rgba(6, 53, 96, .6);
}

.contact-icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  margin-bottom: 8px;
  border: 1px solid rgba(79, 209, 165, .3);
  border-radius: 11px;
  background: rgba(32, 170, 138, .12);
  color: var(--green);
}
.contact-icon .ico { width: 19px; height: 19px; }

.contact-tile dt {
  margin: 0;
  color: var(--quiet);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.contact-tile dd {
  margin: 0;
  color: var(--mist);
  font-size: var(--fs-small);
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.contact-tile address { font-style: normal; line-height: 1.5; }

/* the link tiles carry their own affordance, so no prose underline */
a.contact-tile dd { color: var(--text); }

/* ---------- secondary block inside a full-width card ----------
 * The newsletter sits inside "Get in touch" as a footnote: separated by a rule,
 * given its own heading, with its buttons on a row beneath, so the eye reads the
 * contact details first and the invitation second.
 */
.card-footnote {
  /*
   * display: block, not grid. A float has no effect on a grid item: as a grid
   * container this laid the avatar and the heading out as separate rows, so the
   * mark sat above the text instead of being inset into it. Block formatting is
   * what lets the copy wrap around the avatar.
   */
  display: block;
  margin-top: 24px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}
.card-footnote::after { content: ""; display: block; clear: both; }

/*
 * The newsletter avatar is inset and floated rather than given a grid column of
 * its own. A fixed column reserves its width down the whole block, so every line
 * of the paragraph wraps short against empty space beside the heading; floated,
 * only the first two lines sit beside it and the rest run full width.
 */
.newsletter-avatar {
  display: block;
  float: left;
  width: 56px;
  height: 56px;
  margin: 2px 16px 8px 0;
  border-radius: 14px;
  border: 1px solid var(--line);
}
.card-footnote::after { content: ""; display: block; clear: both; }
.card-footnote h4 { margin: 0 0 8px; color: var(--mist); }
.card-footnote p { margin: 0 0 8px; }
.card-footnote p:last-child { margin-bottom: 0; }
.card-footnote .newsletter-note { color: var(--quiet); font-size: var(--fs-small); }

.card-footnote-actions { display: flex; flex-wrap: wrap; gap: 10px; align-content: center; margin: 16px 0 12px; }

/* ---------- license table ----------
 * Used on weblabels.html. The markup is fixed by the LibreJS specification
 * (table#jslicense-labels1, three cells per row), so all of the presentation
 * has to happen here rather than by restructuring the table.
 *
 * On narrow screens it stays a table rather than collapsing into stacked blocks:
 * three short cells fit, and a card layout would lose the column headings that
 * tell you which link is the license and which is the source.
 */
.jslicense {
  width: 100%;
  margin: 0 0 22px;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: rgba(4, 41, 75, .7);
  font-size: var(--fs-small);
  text-align: left;
}
.jslicense caption {
  padding: 0 0 10px;
  color: var(--quiet);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  text-align: left;
}
.jslicense th {
  padding: 13px 16px;
  border-bottom: 1px solid var(--line-strong);
  background: rgba(0, 18, 53, .5);
  color: var(--mist);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.jslicense td {
  padding: 15px 16px;
  border-bottom: 1px solid var(--line);
  color: var(--body);
  font-family: var(--mono);
  overflow-wrap: anywhere;
}
.jslicense tr:last-child td { border-bottom: 0; }
.jslicense a {
  color: var(--blue);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.jslicense a:hover,
.jslicense a:focus-visible { color: var(--mist); text-decoration-style: solid; }

/* inline code in prose, used on weblabels.html */
code:not(.fpr):not(.crypto-addr) {
  font-family: var(--mono);
  font-size: .92em;
  color: var(--mist);
  background: rgba(0, 18, 53, .55);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1px 6px;
}

/* ---------- notices ----------
 * A callout that has to be read before acting, used for the irreversibility
 * warning on crypto.html. Amber-orange rather than the site green, because green
 * reads as confirmation and this is the opposite. --orange clears 4.5:1 on every
 * surface; --graphic-orange does not, so it is only the border.
 */
.notice {
  margin: 0 0 22px;
  padding: 20px 22px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--graphic-orange-2);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: rgba(0, 18, 53, .5);
}
.notice h2 { font-size: var(--fs-h3); line-height: 1.2; margin: 0 0 10px; color: var(--orange); }
.notice p { margin: 0 0 10px; color: var(--body); }
.notice p:last-child { margin-bottom: 0; }
.notice .note { color: var(--quiet); font-size: var(--fs-small); }
.notice .note strong { color: var(--mist); }

/*
 * On a centred page the notice heading sits with everything else, but the
 * paragraphs stay left-aligned: centred multi-line prose is harder to read, and
 * this is the block a donor has to read before acting.
 */
.notice-center {
  border-left: 0;
  border-top: 4px solid var(--graphic-orange-2);
  border-radius: 0 0 var(--radius) var(--radius);
}
.notice-center h2 { text-align: center; }

/* ---------- cryptocurrency addresses ----------
 * Ten assets in two rows of five. At five across the QR sits above the address
 * rather than beside it, and the address column is narrow, so the mono size drops
 * to 12px and the QR to 148px. A 148px render of a 176px source is a downscale,
 * which a camera handles.
 *
 * Never scale a QR up. These spend part of their error correction budget on the
 * logo overlay, and interpolation eats the rest.
 *
 * The address is never truncated. A partial address is useless, and people check
 * the last characters against their wallet before sending.
 */
.coin-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 22px;
}
.coin {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(4, 41, 75, .7);
  text-align: center;
}
.coin-qr {
  display: block;
  width: 148px;
  height: 148px;
  max-width: 100%;
  padding: 7px;
  margin-bottom: 12px;
  border-radius: 10px;
  /* white plate: a QR needs a light quiet zone, and these sources have
     transparent corners against a dark page */
  background: #FFFFFF;
}
.coin-line { margin: 0; width: 100%; min-width: 0; }
.coin-line strong {
  display: block;
  margin-bottom: 7px;
  color: var(--text);
  font-size: var(--fs-small);
  line-height: 1.3;
}
.crypto-addr {
  display: block;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(0, 18, 53, .6);
  color: var(--mist);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.55;
  letter-spacing: .01em;
  text-align: left;
  overflow-wrap: anywhere;
  user-select: all;
}
/* address plus its copy button, side by side inside the card */
.coin-addr {
  display: flex;
  align-items: stretch;
  gap: 6px;
  width: 100%;
  min-width: 0;
}
.coin-addr .crypto-addr { flex: 1; min-width: 0; }

/*
 * The copy button. 40px square, which is under the 44px in 2.5.8 but the
 * exception applies: the same address is selectable text alongside it, so the
 * function is available another way in the same block.
 *
 * Both icons are in the button and CSS swaps them, so confirming a copy costs no
 * DOM work and the page behaves identically with the script blocked, minus the
 * button doing anything.
 */
.copy-btn {
  flex: 0 0 40px;
  width: 40px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(0, 18, 53, .6);
  color: var(--quiet);
  cursor: pointer;
  transition: color .16s ease, border-color .16s ease, background .16s ease;
}
.copy-btn:hover,
.copy-btn:focus-visible { color: var(--green); border-color: rgba(79, 209, 165, .45); }
.copy-btn .ico { width: 18px; height: 18px; }
.copy-btn .ico-copied { display: none; }
.copy-btn.is-copied { color: var(--green); border-color: rgba(79, 209, 165, .6); }
.copy-btn.is-copied .ico-copy { display: none; }
.copy-btn.is-copied .ico-copied { display: block; }

.page-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin: 0; }

/* ---------- standalone pages ----------
 * donate-thanks.html and donate-error.html: the header and footer of every other
 * page, with a single centred block between them.
 *
 * The heading is capped in ch rather than left to the h1 clamp. At the clamp's
 * upper end a nine-word heading broke onto four lines in a 62ch column, which
 * read as a stack of fragments; 22ch on the heading and a wider column for the
 * body keeps it to two.
 */
.page-standalone {
  display: grid;
  align-content: center;
  justify-items: center;
  width: min(72ch, calc(100% - 40px));
  margin: 0 auto;
  padding: 30px 0 40px;
  text-align: center;
}
.page-standalone h1 { max-width: 22ch; margin: 0 0 14px; text-wrap: balance; }
.page-standalone .lead { max-width: 52ch; margin: 0 auto; }
.page-standalone .hero-actions { justify-content: center; margin: 26px 0 0; }
/*
 * The countdown line on the return pages, and the control that stops it. The
 * button is styled as a link rather than a pill: it sits inside a sentence, and a
 * second pill next to the real buttons above would read as another destination.
 */
.redirect-note {
  margin: 22px auto 0;
  max-width: 56ch;
  color: var(--quiet);
  font-size: var(--fs-small);
  line-height: 1.55;
}
.btn-inline {
  padding: 0;
  border: 0;
  background: none;
  color: var(--blue);
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
}
.btn-inline:hover,
.btn-inline:focus-visible { color: var(--mist); text-decoration-style: solid; }

.standalone-note {
  margin: 24px auto 0;
  max-width: 56ch;
  color: var(--quiet);
  font-size: var(--fs-small);
  line-height: 1.55;
}

/* the artwork links home, so it needs a focus target and a little lift */
.mark-link {
  display: block;
  border-radius: 12px;
  transition: transform .16s ease;
}
.mark-link:hover,
.mark-link:focus-visible { transform: translateY(-2px); }

/* ---------- status marks ----------
 * The pixel-art computers on donate-thanks.html and donate-error.html.
 *
 * image-rendering: pixelated keeps the pixel grid crisp when the GIF is scaled.
 * Without it the browser smooths a 58px logical grid into mush at any size other
 * than an exact multiple. Both sources are 10x, so a 10x display is nearest to
 * native, but pixelated makes every size behave.
 *
 * Sized in ch rather than px so the artwork tracks the type around it.
 */
.mark {
  display: block;
  width: clamp(220px, 40vw, 340px);
  height: auto;
  margin: 0 auto 28px;
  image-rendering: pixelated;
}

/* ---------- footer ---------- */
footer { padding: 34px 0 42px; color: var(--body); }

.footer-panels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  margin-bottom: 22px;
}
.footer-panel {
  padding: 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(150deg, rgba(32, 170, 138, .09), transparent 46%), rgba(4, 41, 75, .7);
}
/* opts out of the canonical h2 size by specificity alone */
.footer-panel h2 { font-size: var(--fs-panel); line-height: 1.12; margin-bottom: 12px; }
.footer-panel p { color: var(--body); }
.footer-logo { display: block; width: min(310px, 100%); height: auto; margin-bottom: 18px; }

.social-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.social-link {
  width: 46px;
  height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid rgba(79, 209, 165, .55);
  border-radius: var(--radius-pill);
  color: var(--text);
  transition: transform .16s ease, background .16s ease, border-color .16s ease;
}
.social-link:hover,
.social-link:focus-visible {
  transform: translateY(-1px);
  background: rgba(32, 170, 138, .16);
  border-color: var(--green);
}
.social-link .ico { width: 20px; height: 20px; }

/*
 * Centred single column. .fine keeps its 78ch cap: centred text needs the cap
 * more than left-aligned text, not less, because neither edge is fixed and
 * uncapped lines running the full container make the next line hard to find.
 */
.footer-inner {
  border-top: 1px solid var(--line);
  padding-top: 22px;
  display: grid;
  justify-items: center;
  gap: 16px;
  text-align: center;
}
/*
 * No measure cap: the copyright runs the full container width. It is fine print
 * that is scanned rather than read line by line, and a 78ch cap left an obvious
 * gutter either side of it against a 1440px container.
 */
/*
 * Left aligned and running the full container width. Centred, it gave every line
 * a different start position, which is hard to scan for a specific clause, and
 * the last line ended as a short stub in the middle.
 *
 * justify-self overrides the justify-items: center on .footer-inner. Without it
 * the paragraph shrinks to its content box and stays centred, and text-align has
 * nothing wider to align against. The links row above keeps its centring.
 */
.fine {
  width: 100%;
  justify-self: stretch;
  line-height: 1.62;
  margin: 0;
  text-align: left;
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 10px;
  align-content: start;
}
.footer-links a { padding: 8px 10px; }

/* ===========================================================================
 * 6. LINKS: two kinds, and only two.
 *
 * 1. TEXT LINKS sit inside running prose. WCAG 1.4.1 (Use of Colour) needs more
 *    than colour to mark them, because although --blue clears 4.5:1 against every
 *    surface, it is under 3:1 against the body text beside it. So: dotted
 *    underline at rest, solid and brighter on hover and focus.
 *
 * 2. CHROME LINKS are nav items, footer links, social buttons and whole-card
 *    links. Position and shape already make them obviously interactive, so they
 *    carry no underline and signal with background and colour instead.
 *
 * The container list is wrapped in :where() to hold specificity at zero, so a
 * component can override it with a plain class.
 * ======================================================================== */
:where(p, li, dd, dt, address, figcaption, blockquote,
       .kicker, .fine, .tax-note, .lead, .method dl, .card-footnote,
       .media-caption, .person .role) a:not(.btn) {
  color: var(--blue);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color .16s ease, text-decoration-color .16s ease;
}
:where(p, li, dd, dt, address, figcaption, blockquote,
       .kicker, .fine, .tax-note, .lead, .method dl, .card-footnote,
       .media-caption, .person .role) a:not(.btn):hover,
:where(p, li, dd, dt, address, figcaption, blockquote,
       .kicker, .fine, .tax-note, .lead, .method dl, .card-footnote,
       .media-caption, .person .role) a:not(.btn):focus-visible {
  color: var(--mist);
  text-decoration-style: solid;
}

.navlinks a,
.footer-links a {
  color: var(--body);
  border-radius: 11px;
  text-decoration: none;
  transition: background .16s ease, color .16s ease;
}
.navlinks a:hover,
.navlinks a:focus-visible,
.footer-links a:hover,
.footer-links a:focus-visible {
  color: var(--text);
  background: rgba(214, 238, 240, .09);
}

/* ===========================================================================
 * 7. BREAKPOINTS
 *
 * One block per width, widest first, and every responsive declaration in the
 * file lives here. Nothing below this line except the motion query.
 * ======================================================================== */

@media (max-width: 1060px) {
  .coin-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /*
   * From here down the two person columns are too narrow to hold a label beside
   * its value without the longest handle wrapping mid-word, so the label moves
   * above the value and takes the full card width.
   */
  .person-contact { grid-template-columns: 1fr; gap: 2px; }
  .person-contact dt { margin-top: 10px; }
  .navlinks { display: none; }
  .media-row,
  .footer-panels { grid-template-columns: 1fr; }
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .standards-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .site-thumb { height: 190px; }
}

@media (max-width: 900px) {
  .coin-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .standards-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
  .coin-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .partner-mark { float: none; padding: 0 0 18px; }
  .partner-mark img { width: 100%; height: auto; max-width: 328px; }
  .wrap { width: min(100% - 28px, var(--max)); }
  .nav { border-radius: 18px; }
  /* One image containing both words, so FOUNDATION cannot be dropped here. */
  .brand-logo { width: min(200px, 54vw); }
  .nav-actions .btn.ghost { display: none; }   /* keep the Donate CTA visible */
  .hero { padding-top: 14px; }
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn { width: 100%; }
  .section { padding: 26px 0; }
  .hero-duo,
  .methods,
  .site-grid,
  .grid-3,
  .people { grid-template-columns: 1fr; }
  .site-thumb { height: 180px; }
  .person { max-width: 400px; }
}

@media (max-width: 520px) {
  .coin-grid { grid-template-columns: 1fr; }
  .coin-qr { width: 176px; height: 176px; }
  .standards-strip { grid-template-columns: 1fr; }
  .page-actions .btn { width: 100%; }
  .split-actions .btn { width: 100%; }
  .card-footnote-actions { width: 100%; }
  .card-footnote-actions .btn { width: 100%; }
}

/* Motion last, so it cannot be reintroduced by a later transition. */
@media (prefers-reduced-motion: reduce) {
  .btn:hover, .btn:focus-visible, .btn:active,
  .social-link:hover, .social-link:focus-visible,
  .site-card:hover, .site-card:focus-within,
  .site-card:hover .site-thumb img,
  .site-card:focus-within .site-thumb img { transform: none; }
}
