/* ===========================================================================
   RelayKey marketing site — the whole design system, in one hand-written file.
   There is no build step and no framework. Edit this file directly.

   The type system speaks in two voices on purpose. RelayKey's world is a shell
   command pasted onto a machine, so every structural element — headings,
   eyebrows, labels, data — is set in MONOSPACE. Prose is set in the reader's
   system UI face, because paragraphs are for reading, not for atmosphere.

   Colour comes from brand/README.md (violet #8B5CF6 → indigo #4338CA). The
   violet here is darkened to #5B21B6 so it passes AA as link text on paper;
   the brand values stay in the SVG artwork where contrast is not a factor.
   Amber is reserved for honest caveats and is ALWAYS paired with the word
   "Caveat" or a label, never used as the only signal.
   =========================================================================== */

:root {
  color-scheme: light dark;

  /* palette — light ("paper") */
  --paper:      #f5f4fa;   /* cool paper, faintly violet. not cream. */
  --surface:    #ffffff;
  --surface-2:  #edebf6;
  --ink:        #17162a;   /* near-black indigo */
  --muted:      #504c6b;   /* 7.4:1 on --paper */
  --rule:       #dedbee;
  --rule-firm:  #c3bede;   /* decorative card edges only */
  /* Interactive boundaries — input and button borders — need 3:1 against the
     adjacent background (WCAG 1.4.11), which --rule-firm does not reach. Keep
     these two separate so a purely visual tweak to card edges cannot quietly
     break a form control's perceivability. 4.9:1 on paper, 5.4:1 on white. */
  --field-rule: #6b6785;
  --violet:     #5b21b6;   /* 8.0:1 on --paper */
  --violet-dim: #ede9fb;
  --amber:      #8a4b06;   /* 5.6:1 on --paper */
  --amber-dim:  #fdf3e4;
  --amber-rule: #c98a3c;

  /* type */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, "Cascadia Mono",
          "Segoe UI Mono", "Roboto Mono", "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* rhythm */
  --measure: 38rem;   /* prose column */
  --wide:    68rem;   /* diagrams, tables, the header */
  --gap:     clamp(1rem, 3vw, 1.75rem);
  --section: clamp(3.25rem, 8vw, 5.5rem);
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:      #121121;
    --surface:    #1a1930;
    --surface-2:  #232141;
    --ink:        #edebf7;
    --muted:      #a9a4c6;   /* 8.1:1 on --paper */
    --rule:       #2d2b48;
    --rule-firm:  #423e6b;
    --field-rule: #8a85b5;   /* 5.0:1 on --surface, 5.4:1 on --paper */
    --violet:     #bda4ff;   /* 8.3:1 on --paper */
    --violet-dim: #221f3d;
    --amber:      #f0b45f;
    --amber-dim:  #251c10;
    --amber-rule: #8a6023;
  }
}

/* --- reset, kept to what is actually needed ------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  /* No scroll-behavior: smooth. Both clients deep-link to /help/turn#twilio and
     friends, and an animated fragment jump is exactly the kind of decoration
     that makes landing on the right heading less reliable. Instant is correct. */
  overflow-x: hidden;   /* nothing may push the page sideways, at any width */
  /* Fragment targets clear the section rule they sit under. */
  scroll-padding-top: 1.5rem;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 1rem/1.65 var(--sans);
  overflow-wrap: break-word;
}

img, svg { max-width: 100%; height: auto; }

/* --- links and focus ------------------------------------------------------ */

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

:focus-visible {
  outline: 3px solid var(--violet);
  outline-offset: 2px;
  border-radius: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface);
  color: var(--ink);
  padding: .6rem 1rem;
  z-index: 10;
  border: 1px solid var(--rule-firm);
}
.skip:focus { left: .5rem; top: .5rem; }

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

.shell { width: 100%; max-width: var(--wide); margin-inline: auto; padding-inline: clamp(1rem, 4vw, 2rem); }
.prose { max-width: var(--measure); }

section { padding-block: var(--section); border-top: 1px solid var(--rule); }
section:first-of-type { border-top: 0; }

/* --- typography ---------------------------------------------------------- */

h1, h2, h3, .eyebrow, .label, code, pre, kbd, th, .num, .stat {
  font-family: var(--mono);
}

h1 {
  margin: 0 0 1rem;
  font-size: clamp(1.9rem, 5.4vw, 2.9rem);
  line-height: 1.1;
  letter-spacing: -0.035em;
  font-weight: 600;
  text-wrap: balance;
}
h1 .thin { font-weight: 400; color: var(--muted); display: block; }

h2 {
  margin: 0 0 1.5rem;
  font-size: clamp(1.35rem, 3.4vw, 1.9rem);
  line-height: 1.15;
  letter-spacing: -0.025em;
  font-weight: 600;
  text-wrap: balance;
}

h3 {
  margin: 2rem 0 .5rem;
  font-size: 1.02rem;
  letter-spacing: -0.01em;
  font-weight: 600;
}

p { margin: 0 0 1.1rem; }
p:last-child { margin-bottom: 0; }

.lede {
  font-family: var(--sans);
  font-size: clamp(1.05rem, 2.4vw, 1.25rem);
  line-height: 1.55;
  color: var(--muted);
  max-width: 34rem;
}

.eyebrow {
  display: block;
  margin: 0 0 1.25rem;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.eyebrow .on { color: var(--violet); }

/* The live strip on /how/firefox: latency, and which route this page came by.
   Above the eyebrow because it answers "is this working" before the page
   explains what "this" is — and given a whole line of its own, at reading size
   rather than caption size, because it is the one thing on the page that is
   about THIS moment. Tabular numerals so a number that updates every second
   does not shuffle the words after it. */
.connstrip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem 1.5rem;
  margin: 0 0 1.5rem;
  padding: .7rem 1rem;
  border: 1px solid var(--rule);
  border-radius: 10px;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.connstrip b { font-weight: 600; }
/* The route not taken: dimmed, not removed. The pair is the point — the
   alternative is what makes "RelayKey Tunnel" mean anything. */
.connstrip .off { opacity: .45; }
.connstrip .on { color: var(--violet); font-weight: 600; }
.connstrip .ico { vertical-align: -.2em; }
/* The [hidden] rule has to be repeated here, as it is twice elsewhere in this
   file: `display: flex` on the parent beats the UA's `[hidden] { display: none }`
   for a child that sets it. A ping that could not be measured leaves no gap. */
.connstrip [hidden] { display: none; }

/* Visible to a screen reader, nowhere else: the colour says "this one" to a
   reader who can see it, and this says it to one who cannot. */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.tools { list-style: none; padding: 0; }
.tools li { margin: 0 0 .85rem; }
.tools li > a { font-weight: 600; }
.tools li > span {
  display: block;
  font-size: .92rem;
  color: var(--muted);
}

.label {
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}

small, .fine { font-size: .875rem; color: var(--muted); }

ul, ol { margin: 0 0 1.1rem; padding-left: 1.3rem; }
li { margin: .35rem 0; }
li::marker { color: var(--rule-firm); }

dl.fields { margin: 1.1rem 0; }
dl.fields dt { font-family: var(--mono); font-weight: 600; margin-top: .7rem; font-size: .95rem; }
dl.fields dd { margin: .1rem 0 0 1.1rem; color: var(--muted); }

code {
  background: var(--surface-2);
  padding: .1em .35em;
  border-radius: 4px;
  font-size: .875em;
}

pre {
  margin: 1.1rem 0;
  padding: 1rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow-x: auto;
  font-size: .82rem;
  line-height: 1.55;
}
pre code { background: none; padding: 0; font-size: 1em; }

/* --- header -------------------------------------------------------------- */

.masthead {
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}
.masthead > .shell {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem 1.25rem;
  padding-block: .9rem;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-family: var(--mono);
  font-weight: 600;
  font-size: 1.02rem;
  letter-spacing: -0.02em;
  color: inherit;
  text-decoration: none;
  margin-right: auto;
}
.brand img { width: 28px; height: 28px; border-radius: 7px; }
.brand .k { color: var(--violet); }

.masthead nav { display: flex; flex-wrap: wrap; gap: .35rem 1.15rem; align-items: center; }
.masthead nav a {
  font-family: var(--mono);
  font-size: .82rem;
  letter-spacing: -0.01em;
  color: var(--muted);
  text-decoration: none;
}
.masthead nav a:hover, .masthead nav a[aria-current="page"] { color: var(--ink); text-decoration: underline; }
.masthead nav a[aria-current="page"] { font-weight: 600; }

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

.actions { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 2rem; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-family: var(--mono);
  font-size: .86rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  padding: .68rem 1.1rem;
  border-radius: 8px;
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
}
.btn-primary { background: var(--violet); color: var(--paper); }
.btn-primary:hover { filter: brightness(1.12); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--field-rule); }
.btn-ghost:hover { background: var(--surface); }
.btn[disabled] { opacity: .55; cursor: not-allowed; }

/* --- the hero terminal --------------------------------------------------- */

.hero { padding-block: clamp(2.75rem, 7vw, 5rem) var(--section); }

/* At narrow widths the hero is one column, headline first. From 64rem the
   terminal sits beside the copy, because the command IS the argument — it
   should be readable without scrolling, not filed below the fold. */
.hero-grid { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: start; }
@media (min-width: 64rem) {
  .hero-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); align-items: center; }
  .hero-copy .lede { max-width: 30rem; }
}

.terminal {
  margin-top: 0;
  border: 1px solid var(--rule-firm);
  border-radius: 12px;
  background: var(--surface);
  overflow: hidden;
  max-width: 44rem;
}
.terminal-bar {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: .5rem .9rem;
  border-bottom: 1px solid var(--rule);
  background: var(--surface-2);
}
.terminal-bar .label:last-child { white-space: nowrap; }
.terminal-body { padding: 1rem .9rem 1.1rem; font-family: var(--mono); font-size: .82rem; line-height: 1.7; }
.terminal-body .line { display: block; overflow-x: auto; white-space: pre; padding-bottom: .1rem; }
.terminal-body .prompt { color: var(--violet); user-select: none; }
.terminal-body .out { color: var(--muted); white-space: normal; }
.terminal-note {
  padding: .8rem .9rem;
  border-top: 1px dashed var(--rule-firm);
  font-size: .82rem;
  color: var(--muted);
}
.sas {
  font-family: var(--mono);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: .22em;
  color: var(--ink);
}

/* --- the wire, annotated (signature element) ----------------------------- */

.wire {
  display: grid;
  gap: 0;
  margin: 2rem 0 0;
  grid-template-columns: 1fr;
}
.wire-node, .wire-hop { padding: 1.15rem 1.15rem; }
.wire-node {
  background: var(--surface);
  border: 1px solid var(--rule-firm);
  border-radius: 10px;
}
.wire-node h3 { margin: .35rem 0 .5rem; font-size: 1rem; }
.wire-node p { font-size: .9rem; margin-bottom: .7rem; }
.wire-node .sees {
  margin: 0;
  padding-top: .7rem;
  border-top: 1px solid var(--rule);
  font-size: .84rem;
  color: var(--muted);
}
.wire-node .sees b { color: var(--ink); font-family: var(--mono); font-size: .78rem; letter-spacing: .06em; text-transform: uppercase; }
.wire-node.is-relay { border-color: var(--amber-rule); background: var(--amber-dim); }

.wire-hop {
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
  padding-block: .9rem;
  position: relative;
}
/* the connector: a vertical rule on mobile, horizontal from 62rem up */
.wire-hop::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  margin-left: -1px;
  background: repeating-linear-gradient(to bottom, var(--rule-firm) 0 6px, transparent 6px 11px);
}
.wire-hop span {
  position: relative;
  background: var(--paper);
  padding: .2rem .5rem;
  display: inline-block;
}

@media (min-width: 62rem) {
  /* the relay column gets more room: it carries the observation that matters */
  .wire { grid-template-columns: 1fr auto 1.3fr auto 1fr; align-items: stretch; }
  .wire-hop { display: flex; align-items: center; justify-content: center; width: 8.5rem; padding: 0 .2rem; }
  .wire-hop span { white-space: nowrap; letter-spacing: .06em; }
  .wire-hop::before { left: 0; right: 0; top: 50%; bottom: auto; width: auto; height: 2px; margin: 0;
    background: repeating-linear-gradient(to right, var(--rule-firm) 0 6px, transparent 6px 11px); }
  .wire-hop span { max-width: 100%; }
}

/* --- cards / feature grid ------------------------------------------------ */

.grid {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15.5rem), 1fr));
  margin-top: 2rem;
}
/* Card counts, declared.
   `auto-fit` picks the column count from the available width and knows NOTHING about
   how many cards there are, which produced both of the page's card problems: a
   four-card row landed 3+1 with one card orphaned on its own line, and a two-card row
   was given three columns so its cards came out 498px against the other section's
   323px — two card sizes on one page.
   A count belongs to the content, so the content states it. */
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
/* Four halves to 2x2 rather than stepping through 3+1. */
@media (max-width: 64rem) {
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 34rem) {
  .grid-4, .grid-2 { grid-template-columns: 1fr; }
}
.card {
  padding: 1.2rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
}
.card h3 { margin: .5rem 0 .45rem; }
.card p { font-size: .92rem; margin-bottom: 0; color: var(--muted); }
.card p strong { color: var(--ink); font-weight: 600; }

/* --- numbered steps (a real sequence, hence numbers) -------------------- */

.steps { list-style: none; padding: 0; margin: 2rem 0 0; counter-reset: step; }
.steps > li {
  display: grid;
  /* minmax(0,1fr), not 1fr: a 1fr track's automatic minimum is max-content, so
     the <pre> in step 2 would widen the whole page at 360px. */
  grid-template-columns: 2.4rem minmax(0, 1fr);
  gap: .4rem 1rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}
.steps > li::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--violet);
  padding-top: .18rem;
}
/* EVERY child belongs in column 2. Without this, only the first child lands
   there: the second and subsequent items auto-place into row 2 of column ONE —
   the 2.4rem number track — and wrap at three characters a line. The layout
   therefore silently required each <li> to have exactly one child, which is not
   a constraint a stylesheet can enforce or a writer can be expected to know. */
.steps > li > * { grid-column: 2; }
/* And bare text directly inside an <li> becomes an anonymous grid item, which no
   selector can reach. Keep step content in real elements — never loose text. */
.steps h3 { margin: 0 0 .3rem; }
.steps p { font-size: .93rem; margin: 0; color: var(--muted); }
.steps pre { margin: .8rem 0 0; }

/* --- tables -------------------------------------------------------------- */

.scroller { overflow-x: auto; margin: 2rem 0 0; -webkit-overflow-scrolling: touch; }
table { width: 100%; min-width: 34rem; border-collapse: collapse; font-size: .92rem; }
caption { text-align: left; color: var(--muted); font-size: .875rem; padding-bottom: .7rem; }
th, td { text-align: left; padding: .65rem .7rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
/* Column headers are labels, so they get the small-caps label treatment. Row
   headers are content ("macOS Apple Silicon") and must stay in sentence case. */
thead th {
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom-color: var(--rule-firm);
}
tbody th { font-size: .9rem; font-weight: 600; color: var(--ink); }
tbody tr:hover { background: var(--surface); }
td .arch { font-family: var(--mono); font-size: .8rem; color: var(--muted); display: block; }
/* The annual total under a monthly rate, in a table cell. Same treatment as
   .arch: a secondary line, --muted, no new colour pair. */
td .annual { font-family: var(--mono); font-size: .8rem; color: var(--muted); display: block; }

/* --- notes: neutral, and caveat (amber + the word "Caveat") -------------- */

.note, .caveat {
  padding: .95rem 1.1rem;
  margin: 1.5rem 0;
  border: 1px solid var(--rule);
  border-left-width: 3px;
  border-radius: 0 8px 8px 0;
  background: var(--surface);
  font-size: .93rem;
}
.caveat { background: var(--amber-dim); border-color: var(--amber-rule); }
.note > :first-child, .caveat > :first-child { margin-top: 0; }
.note > :last-child, .caveat > :last-child { margin-bottom: 0; }
.caveat .label { color: var(--amber); }

/* honest list: each item names a limit. no colour-only signalling — every
   entry is prefixed by the literal word "No" in the copy. */
.limits { list-style: none; padding: 0; margin: 2rem 0 0; }
.limits li {
  padding: .9rem 0;
  border-top: 1px solid var(--rule);
  font-size: .95rem;
  margin: 0;
}
.limits li b { font-family: var(--mono); font-size: .92rem; letter-spacing: -0.01em; }

/* --- form (purchase) ----------------------------------------------------- */

form { margin-top: 2rem; max-width: 34rem; }
.field { margin-bottom: 1.4rem; }
.field > label {
  display: block;
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  margin-bottom: .4rem;
}
.field .hint { display: block; font-size: .86rem; color: var(--muted); margin-bottom: .5rem; }
.field input, .field textarea, .field select {
  width: 100%;
  font: 400 .92rem/1.5 var(--mono);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--field-rule);
  border-radius: 8px;
  padding: .65rem .75rem;
}
.field input:focus-visible, .field textarea:focus-visible { border-color: var(--violet); }
.field .err {
  display: block;
  margin-top: .4rem;
  font-size: .86rem;
  color: var(--amber);
  font-weight: 600;
}
.field .err:empty { display: none; }
.field input[aria-invalid="true"] { border-color: var(--amber-rule); border-width: 2px; }

.form-status {
  margin-top: 1.5rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--rule-firm);
  border-radius: 8px;
  background: var(--surface);
  font-size: .93rem;
}
.form-status:empty { display: none; }
.form-status dl { margin: .8rem 0 0; }
.form-status dt { font-family: var(--mono); font-size: .7rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.form-status dd { margin: 0 0 .7rem; font-family: var(--mono); font-size: .88rem; overflow-wrap: anywhere; }

/* --- tier cards and prices ---------------------------------------------- */

.price {
  display: block;
  font-family: var(--mono);
  font-size: clamp(1.6rem, 4.5vw, 2.1rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--ink);
  margin: .3rem 0 .1rem;
}
/* the qualifier that rides along with a headline figure ("then $5", "per year") */
.price .per {
  display: block;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: .35rem;
}
.tier {
  padding: 1.3rem 1.35rem;
  border: 1px solid var(--rule-firm);
  border-radius: 10px;
  background: var(--surface);
}
.tier h3 { margin: 0; font-size: 1.05rem; }
.tier ul { margin: 1rem 0 0; padding-left: 1.15rem; font-size: .9rem; color: var(--muted); }
.tier .machines { font-family: var(--mono); font-size: .78rem; letter-spacing: .06em; color: var(--muted); }

/* --- the price calculator (purchase) -------------------------------------
   Progressive enhancement: assets/purchase.js removes the `hidden` attribute.
   The [hidden] rule has to be here because the display below would otherwise
   beat the UA stylesheet and show an inert control to a visitor with no
   JavaScript. */

.calc {
  display: grid;
  gap: var(--gap);
  align-items: start;
  margin-top: 2rem;
  padding: 1.25rem 1.35rem;
  border: 1px solid var(--rule-firm);
  border-radius: 10px;
  background: var(--surface);
  max-width: var(--wide);
}
.calc[hidden] { display: none; }
/* Two blocks, controls and result. From 48rem they sit side by side; the
   controls are one grid item (.calc-controls) so adding a control cannot break
   the placement of the result column. */
@media (min-width: 48rem) {
  .calc { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); }
}
.calc-controls { display: grid; gap: 1.1rem; align-content: start; min-width: 0; }

.calc .field { margin-bottom: 0; }

/* Slider and number field drive the same value, so they share a row. The number
   field is fixed-width and the slider takes the rest; minmax(0,…) keeps the
   range input from widening the page at 360px. */
.rangerow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 5.5rem;
  gap: .75rem;
  align-items: center;
}
.rangerow input[type="range"] {
  width: 100%;
  min-width: 0;
  accent-color: var(--violet);
  margin: 0;
}
.rangerow input[type="number"] { width: 100%; text-align: right; }
.calc fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.calc legend {
  padding: 0;
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  margin-bottom: .4rem;
}
.calc .opt { display: flex; gap: .55rem; align-items: baseline; margin: 0 0 .5rem; font-size: .92rem; }
.calc .opt:last-child { margin-bottom: 0; }
.calc-controls > .opt { margin: 0; }
.calc-controls > .opt input { accent-color: var(--violet); margin: 0; flex: none; }
.calc-controls > .btn { justify-self: start; }
.calc .opt input { accent-color: var(--violet); margin: 0; flex: none; }
.calc .opt label { cursor: pointer; }

.calc output {
  display: block;
  padding-top: .25rem;
  border-top: 1px solid var(--rule);
}
@media (min-width: 48rem) {
  .calc output { padding: 0 0 0 var(--gap); border-top: 0; border-left: 1px solid var(--rule); }
}
.calc .total {
  font-family: var(--mono);
  font-size: clamp(1.7rem, 5vw, 2.3rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  margin: .6rem 0 .2rem;
}
@media (min-width: 48rem) { .calc .total { margin-top: 0; } }
.calc .total .per {
  display: block;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: .3rem;
}
.calc .breakdown {
  list-style: none;
  padding: 0;
  margin: .9rem 0;
  font-family: var(--mono);
  font-size: .82rem;
}
.calc .breakdown li {
  margin: 0;
  padding: .4rem 0;
  border-top: 1px solid var(--rule);
  color: var(--muted);
}
/* The per-year figure that rides under a monthly price. --muted on --surface,
   the same pair every other secondary line on this card already uses. */
.calc .annual { font-size: .875rem; color: var(--muted); margin: 0 0 .9rem; }

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

footer.site {
  border-top: 1px solid var(--rule);
  padding-block: 2.5rem 3.5rem;
  font-size: .875rem;
  color: var(--muted);
}
footer.site .cols {
  display: grid;
  gap: 1.5rem var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
}
footer.site h2 { font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin: 0 0 .6rem; font-weight: 600; }
footer.site ul { list-style: none; padding: 0; margin: 0; }
footer.site li { margin: .3rem 0; }
footer.site .colophon { margin-top: 2.25rem; padding-top: 1.25rem; border-top: 1px solid var(--rule); }

/* --- reference-document rhythm (the relay guide) ------------------------
   A manual is read top to bottom looking for one answer, so its sections sit
   closer together than the marketing pages' do. */
main.doc section { padding-block: clamp(2rem, 4vw, 3rem); }
main.doc .hero { padding-block: clamp(2.25rem, 5vw, 3.5rem) clamp(2rem, 4vw, 3rem); }
main.doc h2 { margin-bottom: 1.1rem; }

/* --- the download page: OS tabs and role cards ---------------------------
   Progressive enhancement, same shape as .calc above: the strip carries the
   `hidden` attribute in the markup and assets/download.js removes it, so the
   [hidden] rule has to live here or `display: flex` below would beat the UA
   stylesheet and show inert buttons to a visitor with no JavaScript.

   No new colour pairs. The selected tab is --violet / --paper, which is exactly
   .btn-primary; an unselected tab is --muted on --surface, the pair every
   secondary line on the site already uses. */

.ostabs { margin-top: 2rem; }
.ostabs[hidden] { display: none; }
.ostabs .label { display: block; margin-bottom: .5rem; }

/* Big picker tiles, not a segmented control. Choosing your OS is the first and
   most consequential thing on the download page, so it gets the size to match:
   a large mark, the name under it, and a whole tile as the hit target.

   No container border or background any more — the tiles ARE the control, so a
   box around them would only add a boundary the eye has to cross. */
.tabstrip {
  display: grid;
  /* auto-fit + minmax, not three fixed columns: at 360px the tiles fall to one
     per row on their own, rather than squashing to a width that clips the label. */
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: .75rem;
}
.tabstrip [role="tab"] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .8rem;
  min-height: 8.5rem;
  padding: 1.5rem 1rem;
  font-family: var(--mono);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  /* --field-rule, not --rule-firm: these are controls, so WCAG 1.4.11 wants 3:1
     against the page, and --surface-2 is only 1.06:1 against --paper — the
     border carries the whole of the tile's perceivability. */
  border: 1px solid var(--field-rule);
  border-radius: 12px;
  background: var(--surface-2);
  color: var(--muted);
  cursor: pointer;
}
.tabstrip [role="tab"]:hover { background: var(--surface); color: var(--ink); }
/* Selection is never colour-only: the selected tile inverts to a filled
   background AND the note underneath names the platform in words. */
.tabstrip [role="tab"][aria-selected="true"] {
  background: var(--violet);
  border-color: var(--violet);
  color: var(--paper);
}
.ostabs .fine { margin: .7rem 0 0; }
.ostabs .fine:empty { display: none; }

.ospanel { margin-top: 2.25rem; }
.ospanel[hidden] { display: none; }
.ospanel > h2 {
  margin-bottom: 0;
  padding-bottom: .8rem;
  border-bottom: 1px solid var(--rule);
}

/* Client and agent, side by side from 52rem. minmax(0,…) so a <pre> inside a
   card cannot widen the page at 360px. */
.dlpair {
  display: grid;
  gap: var(--gap);
  margin-top: 1.5rem;
  grid-template-columns: 1fr;
}
@media (min-width: 52rem) {
  /* No align-items: the default stretch gives the two cards equal height, which
     matters because one of them can carry a caveat box the other does not. */
  .dlpair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.dl {
  min-width: 0;
  padding: 1.2rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--rule-firm);
  border-radius: 10px;
}
.dl h3 { margin: .5rem 0 .5rem; }
.dl > p { font-size: .92rem; color: var(--muted); }
.dl > p strong { color: var(--ink); font-weight: 600; }
.dl .actions { margin-top: 1.1rem; }
.dl .fine { margin-top: .6rem; }
.dl pre { margin: .6rem 0 .4rem; }
.dl .caveat { margin-bottom: 0; }

/* --- a command you are meant to run, with a copy button ------------------
   Used where the install IS a line of shell rather than a file (the Linux
   agent). The box is the primary action on that card, so it is sized and
   spaced like the button it replaced rather than like the inline <code> in
   the list below it.

   The command must stay selectable and readable with no JavaScript — it is
   the whole instruction. So `overflow-x: auto` rather than truncation with an
   ellipsis: a command cut short is a command someone runs half of.

   The button is `hidden` in the markup and unhidden by download.js, the same
   rule the tab strip follows and for the same reason: a copy button that
   cannot copy is worse than no copy button. Its width is reserved in the grid
   either way, so unhiding it does not reflow the line. */
.cmd {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: .5rem;
  margin-top: 1.1rem;
  padding: .55rem .5rem .55rem .8rem;
  background: var(--surface-2);
  border: 1px solid var(--rule-firm);
  border-radius: 8px;
}
.cmd code {
  background: none;
  padding: 0;
  font-size: .84rem;
  line-height: 1.5;
  /* WRAPS. It does not scroll, and it does not truncate.
     This card is half a grid column wide, so the line does not fit — and the
     first version of this rule used `nowrap` with `overflow-x: auto`, which
     rendered as `… | sudo` with the `sh` off the edge. That reads as a complete
     command and is not one. Scrolling hides the ending behind a gesture nobody
     knows to make; an ellipsis hides it behind nothing at all.
     `pre-wrap` breaks at the spaces already in the command, so no token is split
     — the longest one is the URL and it fits. */
  white-space: pre-wrap;
}
.cmd-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.cmd-copy:hover { color: var(--ink); background: var(--surface); border-color: var(--rule); }
.cmd-copy:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }
/* Confirmation sits on its own row so appearing cannot shift the command. */
.cmd-said {
  grid-column: 1 / -1;
  font-size: .8rem;
  color: var(--muted);
}
.cmd-said:empty { display: none; }

/* The secondary ways to get the same thing: the other architecture, the
   browser client, a package manager. Deliberately a plain list under the one
   primary button, never a second button — one binary per click was the ask. */
.dl .alt {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
  border-top: 1px solid var(--rule);
  font-size: .88rem;
  color: var(--muted);
}
.dl .alt li { margin: 0; padding: .6rem 0 0; }

/* --- table of contents (help page) -------------------------------------- */

nav.toc { margin: 0 0 2.5rem; padding: 1rem 1.15rem; border: 1px solid var(--rule); border-radius: 10px; background: var(--surface); }
nav.toc h2 { font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin: 0 0 .6rem; }
nav.toc ol { list-style: none; padding: 0; margin: 0; counter-reset: toc; }
nav.toc li { margin: .2rem 0; font-family: var(--mono); font-size: .86rem; }

/* ---------------------------------------------------------------------------
   details/summary — used on /help/turn to keep the two harder relay options
   one click away instead of on the page. A visitor who wants the quick path
   should not have to scroll past a coturn config file to reach the answers.

   Styled as a card so a closed <summary> reads as a thing you can open. The
   marker is left as the browser's own: a custom triangle would have to be
   re-solved for both themes and both directions, and buys nothing here.
   --------------------------------------------------------------------------- */
details {
  border: 1px solid var(--field-rule);
  border-radius: 10px;
  background: var(--surface);
  padding: 0 1rem;
  margin: 0 0 0.75rem;
}
details > summary {
  cursor: pointer;
  padding: 0.85rem 0;
  font-weight: 600;
  color: var(--ink);
}
/* Keyboard users need to see focus, and the default outline is clipped by the
   rounded corner without this. */
details > summary:focus-visible {
  outline: 2px solid var(--violet);
  outline-offset: 3px;
  border-radius: 4px;
}
details[open] > summary {
  border-bottom: 1px solid var(--rule);
  margin-bottom: 0.5rem;
}
details > *:last-child { margin-bottom: 1rem; }

/* The OS marks in the tab strip. currentColor so each one inherits the tab's
   own state — muted when unselected, --paper on violet when selected — which
   means the icon can never disagree with the label beside it.

   flex-shrink: 0 keeps the mark its full size when three tabs share a 360px
   row; without it the icons squash before the text wraps, and a squashed Apple
   logo is less recognisable than no logo. They are aria-hidden, so this is
   purely visual and nothing is lost if it is not rendered. */
.osmark { width: 34px; height: 34px; flex: 0 0 34px; }

/* The role mark at the top of a download card. Stroked rather than filled so it
   sits at the same visual weight as the label beneath it — a solid shape here
   would outrank the card's heading, which is the thing to read first.

   --muted, not --violet: violet on this site means "you can act on this", and
   the button below is the action. An icon competing for that meaning would make
   the card look like it has two.

   display: block + margin so it stacks above the label rather than sharing its
   line, which is what makes it read as the card's mark rather than a bullet.

   44px, up from 30. stroke-width is in viewBox units, so the outline thickens
   with the mark instead of thinning to a hairline — which is what scaling by
   transform would have done. */
.rolemark {
  display: block;
  width: 44px;
  height: 44px;
  margin: 0 0 1rem;
  color: var(--muted);
}

/* ---------------------------------------------------------------------------
   Screenshots in a guide. width/height are on the <img> itself so the box is
   reserved before the file arrives — a step list that reflows as images load
   makes people lose their place mid-instruction.
   --------------------------------------------------------------------------- */
figure {
  margin: .9rem 0 1.2rem;
  max-width: 100%;
}
figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--rule-firm);
  border-radius: 8px;
}
figcaption {
  margin-top: .5rem;
  font-size: .85rem;
  color: var(--muted);
}
