/*
  porch, by denyfirst
  -------------------
  A dark, quiet surface with one red accent, and the same page in light for
  whoever prefers it. The colour scheme follows the system until the reader
  picks one with the switch in the header; theme.js remembers that choice and
  nothing else.

  What could not be measured is set in the same weight as what could, because a
  reader who is not told what was skipped will read silence as a clean result.
  Every colour text is set in is checked for contrast against both surfaces in
  both schemes (contrast_test.go), so a token below is a promise, not a taste.
*/

:root {
  --paper:       #f7f7f8;
  --paper-sunk:  #ececf0;
  --ink:         #16171b;
  --ink-soft:    #464953;
  /* The faint ink carries the coverage line, the words "not measured", and
     everything else a reader has to see in order not to mistake silence for a
     clean result. It is held to the same threshold as the body text. */
  --ink-faint:   #5c606a;
  --rule:        #d9d9df;

  /* Links. The accent, deepened for a light page. */
  --link:        #a41d2a;

  /* The brand red of the wordmark. */
  --brand:       #c42b3a;

  /* A button's face. Its label is white in both schemes. */
  --accent:       #b3202e;
  --accent-hover: #8f1a25;

  --strong:      #1f6a44;
  --weak:        #845900;
  --insecure:    #b3202e;

  --sans: "Segoe UI Variable Text", "Segoe UI", ui-sans-serif, system-ui,
          -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", "Roboto Mono", Menlo,
          Consolas, monospace;

  --edge: rgba(22, 23, 27, 0.20);
  --radius: 6px;

  --measure: 38rem;
  --page: 78rem;

  color-scheme: light;
}

/* The design's own scheme: the system's choice, unless the switch said light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:      #101114;
    --paper-sunk: #18191d;
    --ink:        #f0f0f2;
    --ink-soft:   #b4b5be;
    --ink-faint:  #999ba6;
    --rule:       #2c2d33;
    --link:       #f28b94;
    --brand:      #ed4552;
    --accent:       #c72f3d;
    --accent-hover: #d23443;
    --strong:     #6fbf92;
    --weak:       #d9a23c;
    --insecure:   #ff7c86;
    --edge:       rgba(240, 240, 242, 0.18);
    color-scheme: dark;
  }
}

/* And the switch, when it said dark on a light system. */
:root[data-theme="dark"] {
  --paper:      #101114;
  --paper-sunk: #18191d;
  --ink:        #f0f0f2;
  --ink-soft:   #b4b5be;
  --ink-faint:  #999ba6;
  --rule:       #2c2d33;
  --link:       #f28b94;
  --brand:      #ed4552;
  --accent:       #c72f3d;
  --accent-hover: #d23443;
  --strong:     #6fbf92;
  --weak:       #d9a23c;
  --insecure:   #ff7c86;
  --edge:       rgba(240, 240, 242, 0.18);
  color-scheme: dark;
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover { text-decoration-thickness: 2px; }

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

/* ── Masthead ─────────────────────────────────────────────────────────── */

.masthead {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
  max-width: var(--page);
  margin: 0 auto;
  min-height: 100px;
  padding: 1.2rem 1.5rem;
  border-bottom: 1px solid var(--rule);
}

/*
  The mark is a link home, and a link home is a target: min-height holds the
  24 pixels a pointer needs on the day the type shrinks, and the inline-block
  is what lets a min-height apply at all.
*/
.wordmark {
  display: inline-block;
  min-height: 24px;
  font-family: var(--sans);
  font-size: 1.95rem;
  font-weight: 750;
  letter-spacing: -0.055em;
  line-height: 1.1;
  color: var(--ink);
  text-decoration: none;
}

/* deny, then first. The brand's red is the first half. */
.wordmark-deny { color: var(--brand); }

.masthead-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.6rem;
  margin-left: auto;
  font-size: 0.92rem;
}

.masthead-nav a {
  color: var(--ink-soft);
  text-decoration: none;
}

.masthead-nav a:hover { color: var(--ink); }

/*
  The colour scheme switch. Drawn as text rather than an icon so it says what
  it does, and only present where theme.js runs to make it work.
*/
.theme-toggle {
  margin-left: auto;
  min-height: 32px;
  padding: 0.3rem 0.75rem;
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
}

.masthead-nav + .theme-toggle { margin-left: 0; }

.theme-toggle:hover { color: var(--ink); border-color: var(--ink-faint); }

/*
  The maker, named in the footer on an installation somebody runs, in the brand
  colour. Red is also the colour of an insecure verdict; in running text in the
  footer it does not read as one.
*/
.colophon-brand {
  color: var(--ink);
  font-weight: 700;
  letter-spacing: -0.03em;
}

/* ── The ask ──────────────────────────────────────────────────────────── */

main {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 1.5rem;
}

.ask { padding: 3.6rem 0 2.4rem; }

h1 {
  margin: 0 0 1.1rem;
  font-size: clamp(2.2rem, 5.5vw, 3.6rem);
  line-height: 1.08;
  letter-spacing: -0.035em;
  font-weight: 600;
}

.lede {
  max-width: var(--measure);
  margin: 0 0 2.2rem;
  font-size: 1.06rem;
  color: var(--ink-soft);
}

.ask-form { margin: 0 0 1.1rem; }

.field-label {
  display: block;
  font-size: 0.74rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  margin-bottom: 0.5rem;
}

.field-row {
  display: flex;
  gap: 0.6rem;
  max-width: var(--measure);
}

.field {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--sans);
  font-size: 1rem;
  padding: 0.75rem 0.9rem;
  color: var(--ink);
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.field:focus { border-color: var(--ink-faint); }

.field::placeholder { color: var(--ink-faint); }

/* The one action a page asks for, in the accent. White on the accent holds
   the text threshold in both schemes. */
.submit {
  flex: 0 0 auto;
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.75rem 1.4rem;
  color: #ffffff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  cursor: pointer;
}

.submit:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }

/*
  A button that is working says so in words and in colour, never by fading.
  An opacity fades the label and its ground together below legibility, on the
  only thing a page shows for the seconds a scan takes; a softened ink says the
  same thing and keeps the word readable.
*/
.submit:disabled {
  cursor: progress;
  color: var(--paper);
  background: var(--ink-soft);
  border-color: var(--ink-soft);
}

.field-help,
.noscript {
  max-width: var(--measure);
  font-size: 0.82rem;
  color: var(--ink-faint);
}

.field-help { margin: 0.6rem 0 0; }

.noscript {
  margin: 1.4rem 0 0;
  padding: 0.9rem 1rem;
  background: var(--paper-sunk);
  border-left: 3px solid var(--ink-faint);
}

/* ── Result ───────────────────────────────────────────────────────────── */

.result {
  padding: 0 0 4rem;
  animation: settle 200ms ease-out;
}

@keyframes settle {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .result { animation: none; }
}

.summary {
  padding: 1.5rem 0;
  border-top: 2px solid var(--ink);
  border-bottom: 1px solid var(--rule);
}

/*
  The verdict and the thing it is a verdict on, on one line.

  The two sentences below the row are deliberately outside it. They were in
  it until 2026-09-01, inside the left column, and a dl is a block: the column
  took the whole width, and the stamp wrapped to a line of its own beneath
  four lines of coverage — underneath the sentence that explains it. Whatever
  is added here has to grow beside the stamp, not through it, which is why
  this row holds nothing that wraps to more than a few words.
*/
.summary-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
}

.summary-target {
  font-family: var(--mono);
  font-size: 1.28rem;
  font-weight: 600;
  margin: 0;
  overflow-wrap: anywhere;
}

.summary-meta {
  margin: 0.35rem 0 0;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--ink-faint);
}

/*
  The signature of the page. A verdict is not a badge or a pill; it is a mark
  pressed onto a record, so it gets hard edges, weight, and letterspacing.
*/
.stamp {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.86rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  padding: 0.5rem 0.9rem 0.5rem 1.08rem;
  border: 2.5px solid currentColor;
}

.stamp-strong   { color: var(--strong); }
.stamp-weak     { color: var(--weak); }
.stamp-insecure { color: var(--insecure); }
.stamp-ungraded { color: var(--ink-faint); }

.section-title {
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--ink-faint);
  margin: 2.6rem 0 0.9rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--rule);
}

/* ── Findings ─────────────────────────────────────────────────────────── */

.finding {
  padding: 0 0 0 1.05rem;
  margin: 0 0 1.9rem;
  border-left: 3px solid var(--rule);
}

.finding-strong   { border-left-color: var(--strong); }
.finding-weak     { border-left-color: var(--weak); }
.finding-insecure { border-left-color: var(--insecure); }

.finding-head {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  flex-wrap: wrap;
}

.finding-title {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 620;
  letter-spacing: -0.012em;
}

.finding-rule {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--ink-faint);
}

.finding-body {
  max-width: var(--measure);
  margin: 0.45rem 0 0.7rem;
  color: var(--ink-soft);
  font-size: 0.94rem;
}

/*
  The citations are the argument. They are set as a strip rather than a
  footnote list so that they read as part of the finding, not as an appendix
  somebody can skip.
*/
.sources {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.9rem;
  font-size: 0.78rem;
}

.sources a { color: var(--link); }

/* ── Tables ───────────────────────────────────────────────────────────── */

.rows {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
}

.rows th {
  text-align: left;
  font-weight: 500;
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.68rem;
  padding: 0 0.8rem 0.5rem 0;
  border-bottom: 1px solid var(--rule);
}

.rows td {
  padding: 0.42rem 0.8rem 0.42rem 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

/*
  Only the identifier column breaks mid-word. Suite names are long and contain
  no spaces, so on a narrow screen they need it; the short labels beside them
  do not, and are unreadable when it happens — "strong" split across two lines
  reads as "stron g".
*/
.rows td.identifier {
  overflow-wrap: anywhere;
  word-break: break-all;
}

.rows td:last-child, .rows th:last-child { padding-right: 0; }

/*
  Every cipher table has the same columns, so they are given the same columns.

  One table per protocol version, each sizing itself to its own contents:
  measured on a live report of kapitalbank.az, "Key exchange" began 92 pixels
  further right under TLS 1.2 than under TLS 1.3, and "Cipher" 68 pixels
  further left. Nothing was wrong with any row. A table exists so that a
  reader can run an eye down a column, and down two of these it could not.

  The widths sit on the <col> elements rather than on the header cells,
  because a fixed layout takes its geometry from the columns first and only
  then from the first row. That distinction matters here: the header is set
  smaller than the body, so a width written in ch on a <th> would be measured
  in the wrong font. A <col> inherits the table's own monospace, which is the
  font the values are set in.

  ch is the unit because every value in this table is fixed-width text of a
  known maximum length. The longest cipher suite name the standard library
  can print is TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, forty-five
  characters; the longest key exchange is "ephemeral (TLS 1.3)"; the longest
  cipher is "ChaCha20-Poly1305"; the longest grade is "insecure". Each column
  is given room for its own worst case and no more.
*/
.suites {
  table-layout: fixed;
  /* The four columns at their worst case, plus the three gutters between
     them. Written in ch so that it shrinks with the table on a narrow screen
     rather than reserving desktop widths on a phone. */
  min-width: calc(89ch + 2.4rem);
}

.suites .col-grade  { width: calc(8ch + 0.8rem); }
.suites .col-kex    { width: calc(19ch + 0.8rem); }
.suites .col-cipher { width: 17ch; }

/* Declared rather than left out. The suite column takes whatever the other
   three do not, and saying so is what the other three are measured against. */
.suites .col-suite  { width: auto; }

/*
  A suite name is an identifier, and an identifier is never broken.

  The column beside it used to break mid-word on a narrow screen, so
  TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 arrived as three fragments and a
  reader copying it by hand could produce a name that is not a cipher suite.
  The name is the finding here. It is kept whole and the container is allowed
  to scroll instead.
*/
.suites td.identifier {
  white-space: nowrap;
  overflow-wrap: normal;
  word-break: normal;
}

/*
  The container scrolls, and says so.

  A table wider than the screen that gives no sign of it hides its right-hand
  columns completely: a reader on a phone would see Grade and Suite and never
  learn that the mode of encryption was recorded at all. The two shadows are
  painted on the box and the two paper-coloured covers travel with the
  content, so each shadow is hidden exactly while that edge is at the end of
  its travel and shows the moment there is something past it.
*/
.table-scroll {
  overflow-x: auto;
  background:
    linear-gradient(to right, var(--paper), rgba(0, 0, 0, 0)) left / 1.4rem 100% no-repeat local,
    linear-gradient(to left,  var(--paper), rgba(0, 0, 0, 0)) right / 1.4rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, var(--edge), rgba(0, 0, 0, 0)) left / 0.5rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, var(--edge), rgba(0, 0, 0, 0)) right / 0.5rem 100% no-repeat scroll;
}

/*
  Paper does not scroll.

  On a screen a table too wide for its column is scrolled to. On a sheet of
  paper the part past the edge is simply not there, and a printed report that
  drops the mode of encryption is worse than one that breaks a suite name
  across two lines. So on paper the container stops scrolling, the table
  gives up its minimum, and the identifier is allowed to wrap after all —
  the last resort, and only where there is no other way to show it.
*/
@media print {
  .table-scroll { overflow-x: visible; background: none; }
  .suites { min-width: 0; }

  /* Shares of the sheet rather than counts of characters, because the sheet
     is what there is. Both tables are given the same shares, so the columns
     still line up on paper; the suite column takes the remaining 42% and is
     the only one allowed to wrap. */
  .suites .col-grade  { width: 12%; }
  .suites .col-kex    { width: 24%; }
  .suites .col-cipher { width: 22%; }

  /* Last resort, and only where a value cannot fit its column on its own
     line. break-word leaves everything that does fit exactly as it is. */
  .suites td { overflow-wrap: break-word; }
  .suites td.identifier { white-space: normal; overflow-wrap: anywhere; }
}

.mark-strong   { color: var(--strong); }
.mark-weak     { color: var(--weak); }
.mark-insecure { color: var(--insecure); }
.mark-faint    { color: var(--ink-faint); }

.group-label {
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--ink-faint);
  margin: 1.5rem 0 0.5rem;
}

/*
  Why a row says what it says.

  A version that was not measured carries the probe's own sentence underneath
  the word, so "not measured" is never left to be guessed at. Small and faint
  because it is a qualification of the cell above it rather than a second
  reading, and it must not compete with the grade column beside it.
*/
.rows td .row-note {
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--ink-faint);
  margin: 0.22rem 0 0;
  max-width: 24rem;
}

/*
  A list that stopped early, said where the list is rather than only in the
  folded notes.

  Coloured as a warning rather than faint. The suites missing from a truncated
  enumeration are the weak ones, so this is the sentence that stops the table
  above it from being read as the whole answer, and a grey line under a
  heading is read as a caption.
*/
.group-note {
  font-size: 0.76rem;
  line-height: 1.45;
  color: var(--weak);
  margin: -0.25rem 0 0.6rem;
  max-width: 42rem;
}

/* What a section is, under its title. Faint, because it explains and does not warn. */
.section-note {
  font-size: 0.76rem;
  line-height: 1.45;
  color: var(--ink-faint);
  margin: -0.25rem 0 0.6rem;
  max-width: 42rem;
}

/* ── Certificate ──────────────────────────────────────────────────────── */

.pairs {
  display: grid;
  grid-template-columns: minmax(6.5rem, max-content) 1fr;
  gap: 0.42rem 1.2rem;
  font-family: var(--mono);
  font-size: 0.82rem;
}

.pairs dt {
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.68rem;
  padding-top: 0.12rem;
}

.pairs dd {
  margin: 0;
  overflow-wrap: anywhere;
}

/* ── Notes and errors ─────────────────────────────────────────────────── */

.notes {
  max-width: var(--measure);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.notes li {
  padding-left: 1rem;
  margin-bottom: 0.6rem;
  border-left: 1px solid var(--rule);
}

/*
  The limits of a scan, folded.

  They are never hidden — the count is in the summary line, so a reader sees
  that there are three of them before deciding whether to read three of them.
  Silence would be the failure; a heading that has to be opened is not
  silence.

  The marker is the browser's own triangle, kept rather than replaced. It is
  the one affordance every reader already knows, and a redrawn one would be a
  smaller triangle that means the same thing.
*/
.notes-section {
  margin: 2.6rem 0 0;
  padding-top: 0.5rem;
  border-top: 1px solid var(--rule);
}

/*
  Three sections, one block.

  They are siblings and each carries the rule above it, so the first is
  separated from the report and the rest are separated from each other by the
  same line at a smaller distance. Without this the three read as three
  unrelated boxes, which is the layout this replaced.
*/
.notes-section + .notes-section { margin-top: 0.9rem; }

/*
  Two display declarations, and the order is the point.

  A summary draws its triangle because the user agent gives it
  display: list-item. Setting display: flex replaces that outer display and
  the triangle goes with it — which is how the comment above came to promise
  a marker the stylesheet had already removed.

  The two-value syntax keeps both: list-item for the marker, flex for the
  layout. A browser that does not understand it ignores the second line and
  keeps the first, which is exactly the behaviour this file had before.

  The marker becomes the first flex item when it is drawn, so the count is
  pushed right with margin-left rather than with justify-content. That places
  it correctly whether there are two items or three, so the fallback and the
  modern path lay out the same.
*/
.notes-head {
  display: flex;
  display: list-item flex;
  align-items: baseline;
  gap: 0.55rem;
  cursor: pointer;
  padding: 0.35rem 0;
  list-style-position: inside;
}

.notes-head::-webkit-details-marker { color: var(--ink-faint); }
.notes-head::marker { color: var(--ink-faint); }

.notes-title,
.notes-count {
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--ink-faint);
}

.notes-count { flex: 0 0 auto; margin-left: auto; }

.notes-section:hover .notes-title,
.notes-section[open] .notes-title { color: var(--ink-soft); }

.notes-section .notes { margin-top: 0.7rem; }

.failure {
  max-width: var(--measure);
  margin: 2rem 0 0;
  padding: 1rem 1.1rem;
  background: var(--paper-sunk);
  border-left: 3px solid var(--insecure);
}

.failure p { margin: 0; }
.failure p + p { margin-top: 0.5rem; font-size: 0.85rem; color: var(--ink-faint); }

.working {
  font-family: var(--mono);
  font-size: 0.84rem;
  color: var(--ink-faint);
  padding: 2rem 0;
}

/* ── Colophon ─────────────────────────────────────────────────────────── */

.colophon {
  max-width: var(--page);
  margin: 0 auto;
  padding: 2rem 1.5rem;
  border-top: 1px solid var(--rule);
  font-size: 0.82rem;
  color: var(--ink-faint);
}

/*
  The measure is for the colophon's prose, and the row of links is not prose.

  This was `.colophon p`, and the link row is written as a paragraph, so it
  inherited a 34rem measure — the width running text can be read at. The five
  links come to 677 pixels. They broke onto a second line inside a footer 884
  pixels wide, with 207 to spare, because a constraint on reading sentences
  had been applied to a row of links.

  Excluded here rather than overridden below: `.colophon p` beats a bare
  `.colophon-links` on specificity whatever the order, so an override there
  would have had to be written stronger than it looks, and the next person to
  read it would not know why.
*/
.colophon p:not(.colophon-links) { max-width: var(--measure); }
.colophon p { margin: 0 0 0.9rem; }
/*
  The counter is set in the same monospace as the data above it, because it is
  data: the only record this service keeps, shown rather than described.
*/
.tally {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--ink-soft);
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 1.1rem !important;
}

/* They still wrap on a phone, where wrapping is the right answer. */
.colophon-links {
  display: flex;
  gap: 1.3rem;
  flex-wrap: wrap;
}

@media (max-width: 34rem) {
  .field-row { flex-direction: column; }
  .submit { width: 100%; }
  .pairs { grid-template-columns: 1fr; gap: 0.1rem 0; }
  .pairs dd { margin-bottom: 0.6rem; }
    /* Narrower columns leave the identifiers more room to sit on one line. */
  .rows { font-size: 0.74rem; }
  .rows th, .rows td { padding-right: 0.5rem; }
}

/* ── Prose pages ──────────────────────────────────────────────────────── */

.prose {
  padding: 3.2rem 0 3rem;
  max-width: var(--measure);
}

.prose h1 {
  margin-bottom: 1.1rem;
}

.prose h2 {
  margin: 3rem 0 0.8rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
  font-size: 1.12rem;
  font-weight: 640;
  letter-spacing: -0.014em;
}

.prose h3 {
  margin: 1.9rem 0 0.6rem;
  font-size: 0.96rem;
  font-weight: 620;
  color: var(--ink);
}

.prose p {
  margin: 0 0 1rem;
  color: var(--ink-soft);
}

.prose .lede {
  color: var(--ink-soft);
  font-size: 1.02rem;
  margin-bottom: 2rem;
}

.prose a { color: var(--link); }

.prose code {
  font-family: var(--mono);
  font-size: 0.88em;
  color: var(--ink);
}

/*
  One page now answers what used to be three, so it needs a way in. The jump
  links matter most for the reader who arrived from a scanning notice and
  wants one section, not the whole document.
*/
.jump {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin: 0 0 2.6rem;
  padding: 0.9rem 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.78rem;
}

/* Anchored headings should not land under the top of the window. */
.prose h2[id] { scroll-margin-top: 1.5rem; }

.plain {
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
  color: var(--ink-soft);
}

.plain li {
  padding-left: 1rem;
  margin-bottom: 0.55rem;
  border-left: 1px solid var(--rule);
}

.prose .colophon-links {
  margin-top: 2.2rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
}
/* The link that hands a reader the report they are looking at. Quiet, because
   it is an offer rather than the point of the page. */
.download {
  display: inline-block;
  margin-top: 0.6rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--ink-faint);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.download:hover,
.download:focus-visible {
  color: var(--ink);
}

/*
  The pointer that replaced a section.

  Set in the same small uppercase as the section headings so it reads as part
  of that block rather than as a stray sentence after it, and left open — a
  link folded inside a summary is one nobody follows.
*/
.notes-method {
  margin: 1.1rem 0 0;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
  font-size: 0.85rem;
  color: var(--ink-faint);
}

.notes-method-link { color: var(--ink-soft); }

/*
  The two sentences under the verdict.

  Both belong to the summary rather than to a block of their own: one says
  what a verdict means and the other says how much of the picture it rests on,
  and a reader meets them in the two seconds they spend on the stamp.

  Quiet by design. What replaced them here was a nine-line block that restated
  the tables below it, and the lesson of that is not to shout louder.
*/
.summary-pairs { margin: 1rem 0 0; }

.summary-worst,
.summary-coverage {
  max-width: var(--measure);
  margin: 0.7rem 0 0;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--ink-faint);
}

.summary-worst { color: var(--ink-soft); }

/* ── The console ─────────────────────────────────────────────────────────

   The surface a self-hosted installation puts at "/".

   Deliberately not the look of the pages at /tls and /web. Those are written
   for somebody who arrived from a log line and is deciding whether to worry,
   so they lead with a sentence and give the argument room. This is written for
   the person who installed it, who decided already: the field is the first
   thing, the type is smaller, and the space between things is tighter, because
   what they want is density and an answer rather than persuasion.

   Same tokens throughout. A second palette would be a second thing to keep in
   step, and the colours already carry meaning a reader has learned. */

.console {
  max-width: 54rem;
  margin: 0 auto;
}

/* A heading for assistive technology, and nothing on the screen: the header
   already names the tool. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.console-form { margin: 1.6rem 0 2rem; }

/* The check list. A row each, so the rule set that will grade a report is
   readable beside the box that runs it — the string a reader needs in order to
   know whether two reports are comparable at all. */
.checks {
  border: 0;
  padding: 0;
  margin: 1.1rem 0 0;
}

.check {
  display: grid;
  grid-template-columns: auto minmax(5.5rem, auto) 1fr auto;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.42rem 0;
  border-bottom: 1px solid var(--rule);
  cursor: pointer;
}

.check:last-child { border-bottom: 0; }

.check-name {
  font-family: var(--mono);
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--ink);
}

.check-says {
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.check-policy {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--ink-faint);
  white-space: nowrap;
}

.console-results { margin-top: 2rem; }

/* One check's block. The heading carries the verdict, so a reader scrolling
   three reports can see where they stand without opening any of them. */
.run {
  border-top: 2px solid var(--ink);
  padding-top: 0.9rem;
  margin-bottom: 2.6rem;
}

.run-head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 0.3rem 1rem;
  margin-bottom: 1.1rem;
}

.run-name {
  font-family: var(--mono);
  font-size: 0.96rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin: 0;
  color: var(--ink);
}

.run-says {
  grid-column: 1;
  margin: 0;
  font-size: 0.84rem;
  color: var(--ink-soft);
}

/* Empty once the report below carries the verdict: one result, said once. */
.run-state:empty { display: none; }

.run-state {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  margin: 0;
  font-family: var(--mono);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-faint);
}

.run-body { min-height: 1.4rem; }

/* A plain two-column table, used where a report has rows rather than a chain:
   what a zone publishes, and what this installation is. */
.grid {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.grid th {
  text-align: left;
  font-family: var(--mono);
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  padding: 0.4rem 1.2rem 0.4rem 0;
  white-space: nowrap;
  vertical-align: baseline;
  width: 1%;
}

.grid td {
  padding: 0.4rem 0;
  color: var(--ink);
  vertical-align: baseline;
}

.console-state {
  margin-top: 3rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
}

.state-grid {
  display: grid;
  grid-template-columns: minmax(6rem, auto) 1fr;
  gap: 0.55rem 1.4rem;
  margin: 0.9rem 0 0;
  font-size: 0.88rem;
}

.state-grid dt {
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}

.state-grid dd {
  margin: 0;
  color: var(--ink-soft);
}

.state-policy {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--ink);
  margin-right: 0.7rem;
}

/*
  The proof dialog. The record is the thing to copy, so it is set in the mono
  face and allowed to break anywhere: a token is one long word, and a dialog
  that scrolls sideways on a phone hides half of it.
*/
.proof {
  width: min(36rem, calc(100vw - 2rem));
  max-height: calc(100vh - 2rem);
  overflow: auto;
  padding: 1.4rem 1.5rem;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid var(--ink);
  border-radius: 0;
}

.proof::backdrop { background: rgba(0, 0, 0, 0.55); }

.proof-lede {
  margin: 0 0 1.1rem;
  font-size: 0.92rem;
  color: var(--ink-soft);
}

.proof-level {
  font-family: var(--mono);
  font-size: 0.95rem;
  padding: 0.45rem 0.6rem;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid var(--ink);
  border-radius: 0;
  max-width: 100%;
}

.proof-hint {
  margin: 0.4rem 0 0.9rem;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.proof-record {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.5rem 1rem;
  margin: 0.9rem 0 0;
  padding: 0.9rem 1rem;
  background: var(--paper-sunk);
}

.proof-record dt {
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--ink-faint);
  padding-top: 0.2rem;
}

.proof-record dd {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.proof-record code {
  font-size: 0.86rem;
  overflow-wrap: anywhere;
  word-break: break-all;
}

.proof-copy,
.proof-cancel {
  font-family: var(--sans);
  font-size: 0.8rem;
  min-height: 24px;
  padding: 0.2rem 0.6rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 0;
  cursor: pointer;
}

.proof-copy:hover,
.proof-cancel:hover { border-color: var(--ink); }

.proof-status {
  min-height: 1.4em;
  margin: 1rem 0 0.8rem;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.proof-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
}

.proof-cancel { padding: 0.6rem 1rem; font-size: 0.9rem; }

@media (max-width: 34rem) {
  .check {
    grid-template-columns: auto 1fr;
    row-gap: 0.15rem;
  }
  .check-says,
  .check-policy { grid-column: 2; }

  .state-grid { grid-template-columns: 1fr; gap: 0.15rem 0; }
  .state-grid dd { margin-bottom: 0.7rem; }
}

/* ── The front page and the Porch page ──────────────────────────────────

   The demonstration's own pages: the denyfirst front page, and Porch shown
   running. Built from the same tokens as everything else, so the report a
   visitor gets here is the report they would get from their own copy. */

.eyebrow {
  margin: 0 0 0.9rem;
  font-size: 0.74rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
}

.hero {
  padding: 4.5rem 0 1rem;
}

.hero h1 {
  font-size: clamp(2.8rem, 7vw, 5.4rem);
  line-height: 1.04;
  letter-spacing: -0.045em;
  margin-bottom: 2rem;
}

.hero-soft { color: var(--ink-faint); }

.hero-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem 2.5rem;
}

.hero-bottom p {
  max-width: var(--measure);
  margin: 0;
  font-size: 1.1rem;
  color: var(--ink-soft);
}

.button {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.7rem 1.3rem;
  font-size: 0.95rem;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
}

.button-primary {
  color: #ffffff;
  background: var(--accent);
  border-color: var(--accent);
}

.button-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.button-secondary {
  color: var(--ink);
  background: var(--paper-sunk);
  border-color: var(--rule);
}

.button-secondary:hover { border-color: var(--ink-faint); }

.text-link {
  display: inline-block;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.3rem 0;
}

.band {
  padding: 3.6rem 0;
  border-bottom: 1px solid var(--rule);
}

.band[id] { scroll-margin-top: 1rem; }

.band-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem 2.5rem;
  margin-bottom: 2.2rem;
}

.band-title {
  margin: 0;
  font-size: clamp(1.8rem, 3.6vw, 2.5rem);
  line-height: 1.15;
  letter-spacing: -0.03em;
  font-weight: 600;
}

.band-note {
  max-width: 22rem;
  margin: 0;
  font-size: 0.98rem;
  color: var(--ink-soft);
}

.band-footnote {
  margin: 1.2rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-faint);
}

.band-split {
  display: grid;
  grid-template-columns: minmax(10rem, 1fr) 2fr;
  gap: 1.5rem 2.5rem;
}

.band-prose {
  max-width: var(--measure);
  margin: 1.4rem 0;
  font-size: 1.02rem;
  color: var(--ink-soft);
}

.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 1.5rem 1.5rem 1.2rem;
  color: var(--ink);
  text-decoration: none;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: calc(var(--radius) + 2px);
}

.card p {
  margin: 0.6rem 0 1.4rem;
  font-size: 0.95rem;
  color: var(--ink-soft);
}

.card-live { border-color: var(--accent); }

a.card:hover { border-color: var(--link); }

.card-name {
  margin: 1.4rem 0 0;
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.card-foot {
  margin-top: auto;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule);
  font-size: 0.74rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
}

.badge {
  align-self: flex-start;
  padding: 0.2rem 0.55rem;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  border: 1px solid var(--rule);
  border-radius: 4px;
}

.badge-live {
  color: var(--link);
  border-color: var(--accent);
}

.columns {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem 2.5rem;
}

.columns article {
  padding-top: 1.2rem;
  border-top: 1px solid var(--rule);
}

.columns h3 {
  margin: 0.8rem 0 0.6rem;
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.columns p { margin: 0; color: var(--ink-soft); font-size: 0.96rem; }

.columns .index {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--link);
}

.porch-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 2.5rem 3.5rem;
  align-items: center;
  padding: 3.6rem 0;
  border-bottom: 1px solid var(--rule);
}

.porch-hero h1 {
  font-size: clamp(2.4rem, 5vw, 3.8rem);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 1.2rem;
}

.chips span {
  padding: 0.3rem 0.65rem;
  font-size: 0.82rem;
  color: var(--ink-soft);
  border: 1px solid var(--rule);
  border-radius: 4px;
}

.check-panel {
  padding: 1.6rem;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: calc(var(--radius) + 3px);
}

.check-panel-title {
  margin: 0 0 1.4rem;
  font-size: 1.7rem;
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 600;
}

.check-panel .field { width: 100%; background: var(--paper); }

.check-panel .check { grid-template-columns: auto minmax(4.5rem, auto) 1fr; }

.check-panel .check-policy { display: none; }

.submit-wide {
  width: 100%;
  margin-top: 1.2rem;
}

.porch-report {
  padding: 2.4rem 0 1rem;
  scroll-margin-top: 1rem;
}

.porch-report-head { margin-bottom: 1.2rem; }

.porch-report-title {
  margin: 0;
  font-size: 1.9rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}

/* One tab per check. Each carries the check's own state, so the verdict of
   one is never read as the verdict of the others. */
.tabs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.8rem;
  margin: 0 0 1.2rem;
}

.tab {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.35rem 1rem;
  align-items: baseline;
  padding: 1rem 1.1rem;
  font-family: var(--sans);
  text-align: left;
  color: var(--ink);
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
}

.tab[aria-selected="true"] {
  border-color: var(--accent);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.tab-name {
  font-size: 1.05rem;
  font-weight: 600;
}

.tab-state {
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
}

/* The verdict colours, which the grey above would otherwise override. */
.tab-state.stamp-strong   { color: var(--strong); }
.tab-state.stamp-weak     { color: var(--weak); }
.tab-state.stamp-insecure { color: var(--insecure); }

.tab-says {
  grid-column: 1 / -1;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.tab-panel {
  padding: 1.4rem 1.5rem;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.tab-panel .summary { border-top: 0; padding-top: 0; }

.cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.2rem 2rem;
  margin: 3rem 0;
  padding: 1.8rem;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: calc(var(--radius) + 2px);
}

.cta p { margin: 0; color: var(--ink-soft); }

.cta-title {
  margin: 0 0 0.4rem;
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

@media (max-width: 52rem) {
  .cards,
  .columns,
  .porch-hero,
  .band-split { grid-template-columns: 1fr; }
}

@media (max-width: 34rem) {
  .masthead-nav { order: 3; width: 100%; margin-left: 0; }
  .masthead-nav + .theme-toggle { margin-left: auto; }
  .hero { padding-top: 2.8rem; }
  .check-panel { padding: 1.2rem; }
  .tab-panel { padding: 1rem; }
}

/* ── Finishing ───────────────────────────────────────────────────────────

   The small things the design carries: an arrow on a link that goes
   somewhere, a red dot before a heading's label, a short red rule under the
   hero, a symbol on a card. Every arrow is drawn by CSS rather than written in
   the markup, with empty alternative text, so a screen reader announces the
   link and not "north east arrow", and a script that rewrites a button's label
   cannot lose it. */

.arrow-ne::after,
.arrow-se::after,
.arrow-e::after,
.arrow-down::after {
  display: inline-block;
  margin-left: 0.55em;
  transition: transform 160ms ease-out;
}

.arrow-ne::after   { content: "↗"; content: "↗" / ""; }
.arrow-se::after   { content: "↘"; content: "↘" / ""; }
.arrow-e::after    { content: "→"; content: "→" / ""; }
.arrow-down::after { content: "↓"; content: "↓" / ""; }

a:hover.arrow-ne::after,
.card-link:hover .arrow-ne::after,
button:hover.arrow-ne::after { transform: translate(2px, -2px); }
a:hover.arrow-se::after      { transform: translate(2px, 2px); }
a:hover.arrow-e::after       { transform: translateX(3px); }
a:hover.arrow-down::after    { transform: translateY(2px); }

.card-name.arrow-ne {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}

.card-name.arrow-ne::after {
  font-size: 1.1rem;
  font-weight: 400;
  color: var(--ink-faint);
}

.eyebrow-dot::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin: 0 0.6rem 0.12em 0;
  border-radius: 50%;
  background: var(--brand);
}

.hero-rule {
  position: relative;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 3rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
}

.hero-rule::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 4rem;
  height: 2px;
  background: var(--brand);
}

.card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.card-symbol {
  font-size: 1.9rem;
  line-height: 1;
  color: var(--brand);
}

.card-link {
  transition: transform 180ms ease-out, border-color 180ms ease-out;
}

.card-link:hover { transform: translateY(-3px); }

.cards-two { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.check-panel-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 0 0 0.9rem;
}

.check-panel-top .eyebrow { margin: 0; }

.live {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.74rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--link);
}

.live::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brand);
}

.docs-hero {
  padding: 3.8rem 0 2rem;
}

.docs-hero .lede { margin-bottom: 0; }

/* The footer: who made it on one side, four ways on at the other. */
.colophon-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem 2rem;
}

.colophon-row .colophon-links { margin: 0; }


@media (prefers-reduced-motion: reduce) {
  .card-link,
  .arrow-ne::after,
  .arrow-se::after,
  .arrow-e::after,
  .arrow-down::after { transition: none; }
  .card-link:hover { transform: none; }
}

@media (max-width: 52rem) {
  .cards-two { grid-template-columns: 1fr; }
}

/* Quiet links in the footer, as the design has them; they brighten on hover
   and keep an underline only then. */
.colophon-links a {
  color: var(--ink-soft);
  text-decoration: none;
}

.colophon-links a:hover {
  color: var(--ink);
  text-decoration: underline;
}

/* The last section needs no rule of its own: the footer draws one. */
main > .band:last-child { border-bottom: 0; }


/* The Products menu. Opens on hover and on keyboard focus, and needs no
   script; the list sits under its button with a small bridge so the pointer
   can travel to it. */
.nav-menu { position: relative; }

.nav-menu-button::after {
  content: "▾";
  content: "▾" / "";
  margin-left: 0.35em;
  font-size: 0.8em;
}

.nav-menu-list {
  display: none;
  position: absolute;
  top: 100%;
  left: -1rem;
  z-index: 10;
  min-width: 16rem;
  margin: 0;
  padding: 0.5rem;
  list-style: none;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.25);
}

.nav-menu-list::before {
  content: "";
  position: absolute;
  top: -0.8rem;
  left: 0;
  right: 0;
  height: 0.8rem;
}

.nav-menu:hover .nav-menu-list,
.nav-menu:focus-within .nav-menu-list { display: block; margin-top: 0.6rem; }

.nav-menu-list a,
.nav-menu-soon {
  display: grid;
  gap: 0.1rem;
  padding: 0.55rem 0.7rem;
  border-radius: calc(var(--radius) - 2px);
  text-decoration: none;
}

.nav-menu-list a:hover,
.nav-menu-list a:focus-visible { background: var(--paper); }

.nav-menu-name {
  font-weight: 600;
  color: var(--ink);
}

.nav-menu-note {
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.nav-menu-soon .nav-menu-name { color: var(--ink-soft); }

@media (hover: none) {
  .nav-menu:hover .nav-menu-list { display: none; }
}

/* The footer's left side: the mark, as the header draws it and a size down,
   and one line beside it. Both sentences sit on that line, so the footer is a
   single row wherever there is room for one. */
.colophon-by {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.9rem;
}

.colophon .colophon-by p { margin: 0; }

.colophon-mark .colophon-brand {
  font-size: 1.35rem;
  font-weight: 750;
  letter-spacing: -0.05em;
}

/*
  Three numbered steps, one row each: what the step is for on the left, the
  commands on the right, wide enough to read without scrolling.
*/
.steps {
  display: grid;
  gap: 0.8rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.steps li {
  counter-increment: step;
  display: grid;
  grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
  grid-template-rows: auto auto 1fr;
  column-gap: 2rem;
  align-items: start;
  padding: 1.4rem;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: calc(var(--radius) + 2px);
}

.steps li::before {
  content: "0" counter(step);
  grid-column: 1;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--link);
}

.steps h3 {
  grid-column: 1;
  margin: 0.4rem 0 0.35rem;
  font-size: 1.2rem;
  font-weight: 600;
}

.steps p {
  grid-column: 1;
  margin: 0;
  font-size: 0.93rem;
  color: var(--ink-soft);
}

.steps pre {
  grid-column: 2;
  grid-row: 1 / 4;
  align-self: center;
  margin: 0;
  padding: 0.9rem 1rem;
  overflow-x: auto;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.steps pre code {
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.75;
  color: var(--ink);
  white-space: pre;
}

.steps p code {
  font-family: var(--mono);
  font-size: 0.88em;
  color: var(--ink);
}

@media (max-width: 52rem) {
  .steps li { grid-template-columns: minmax(0, 1fr); }
  .steps pre { grid-column: 1; grid-row: auto; margin-top: 1rem; }
}

/* A domain the page shows rather than offers: the field's box, no control. */
.field-fixed {
  margin: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.field-fixed .field-note {
  font-weight: 400;
  color: var(--ink-soft);
}

/* A glossary inside prose: the term, then what it means, indented under it. */
.prose .terms {
  margin: 1.2rem 0;
}

.prose .terms dt {
  margin-top: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}

.prose .terms dd {
  margin: 0.2rem 0 0 1.1rem;
  color: var(--ink-soft);
}

/* Four cards: two rows of two until there is room for one row. */
.cards-four { grid-template-columns: repeat(2, minmax(0, 1fr)); }

@media (min-width: 72rem) {
  .cards-four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 52rem) {
  .cards-four { grid-template-columns: 1fr; }
}

/* Three tabs always; one that was not chosen is there to say so, and is not
   a control. */
.tabs { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.tab.tab-off {
  cursor: default;
  background: transparent;
  border-style: dashed;
}

.tab.tab-off .tab-name,
.tab.tab-off .tab-says { color: var(--ink-faint); }

@media (max-width: 40rem) {
  .tabs { grid-template-columns: 1fr; }
}

/*
  The footer's row sits in the middle of its band: the same space above and
  below, the mark and the links on one centre line, and no paragraph margin
  under the last line. It sat 29px from the rule and 48px from the bottom.
*/
.colophon-row { align-items: center; }
.colophon .colophon-row p { margin: 0; }
.colophon .tally { margin: 1.2rem 0 0; }

/*
  The lens on the front page.

  A layer under the opening's text, masked to a circle around the pointer: a
  barely-there wash of the brand colour, a grid of points, and a few words,
  all far quieter than anything a reader is meant to read. Closed, the circle
  has no radius, so nothing is drawn; hero.js opens it while a pointer is
  over the opening. The radius is a registered property so that it can ease
  open and shut; a browser without @property opens it at once.

  A second mask fades the whole layer out towards every edge of the opening,
  so the circle dissolves as it nears one instead of being cut by it. The
  first version was bright enough to catch the eye and stopped at the edge in
  a hard line.
*/
@property --lens-r {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

.hero {
  position: relative;
  isolation: isolate;
}

.hero-lens {
  --lens-x: 70%;
  --lens-y: 40%;
  position: absolute;
  inset: 0 0 3rem;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background-image:
    radial-gradient(circle 16rem at var(--lens-x) var(--lens-y),
      color-mix(in srgb, var(--brand) 5%, transparent), transparent 75%),
    radial-gradient(circle, color-mix(in srgb, var(--ink-faint) 22%, transparent) 1px, transparent 1.6px);
  background-size: 100% 100%, 24px 24px;
  -webkit-mask-image:
    radial-gradient(circle var(--lens-r) at var(--lens-x) var(--lens-y), #000 0%, rgb(0 0 0 / 0.4) 45%, transparent 100%),
    linear-gradient(to right, transparent, #000 18%, #000 82%, transparent),
    linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
  -webkit-mask-composite: source-in;
  mask-image:
    radial-gradient(circle var(--lens-r) at var(--lens-x) var(--lens-y), #000 0%, rgb(0 0 0 / 0.4) 45%, transparent 100%),
    linear-gradient(to right, transparent, #000 18%, #000 82%, transparent),
    linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
  mask-composite: intersect;
  transition: --lens-r 0.6s ease;
}

.hero-lit .hero-lens { --lens-r: 15rem; }

.hero-lens span {
  position: absolute;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  color: color-mix(in srgb, var(--ink-faint) 45%, transparent);
  white-space: nowrap;
}

.hero-lens .lens-big {
  font-family: var(--sans);
  font-size: clamp(2rem, 4vw, 3.4rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  color: color-mix(in srgb, var(--ink-faint) 28%, transparent);
}

.lens-1 { top: 20%; left: 60%; }
.lens-2 { top: 62%; left: 70%; }
.lens-3 { top: 30%; left: 82%; }
.lens-4 { top: 40%; left: 58%; }

@media (prefers-reduced-motion: reduce) {
  .hero-lens { transition: none; }
}

/*
  On a phone the footer's row wraps, and left-aligned it left the links in a
  corner with the width beside them empty. There it is one centred column:
  the mark and its line, then the links.
*/
@media (max-width: 40rem) {
  .colophon-row {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }
  .colophon-by,
  .colophon-row .colophon-links { justify-content: center; }
}

/* ── The workspace ─────────────────────────────────────────────────────
   An installation somebody runs: a rail of what can be done here beside the
   page. The demonstration keeps the site's masthead; none of this applies
   there, because its body carries no .workspace class. */

.workspace {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  min-height: 100vh;
}

.rail {
  position: sticky;
  top: 0;
  align-self: start;
  display: flex;
  flex-direction: column;
  height: 100vh;
  padding: 1.6rem 1rem;
  background: var(--paper-sunk);
  border-right: 1px solid var(--rule);
}

.rail-identity {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  margin: 0 0.6rem 2.2rem;
}

.rail-identity .wordmark { font-size: 1.7rem; }

.rail-tag {
  padding: 0.2rem 0.4rem;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--brand);
  border: 1px solid color-mix(in srgb, var(--brand) 40%, var(--rule));
  border-radius: 3px;
  white-space: nowrap;
}

.rail-caption {
  margin: 0 0.75rem 0.6rem;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.rail-nav {
  display: grid;
  gap: 0.25rem;
  margin-bottom: 1.8rem;
}

.rail-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 42px;
  padding: 0.55rem 0.75rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
}

.rail-item:hover { color: var(--ink); background: var(--paper); }

.rail-item[aria-current="page"] {
  color: var(--ink);
  background: color-mix(in srgb, var(--brand) 10%, var(--paper));
  border-color: color-mix(in srgb, var(--brand) 35%, var(--rule));
}

.rail-glyph {
  width: 1.1rem;
  text-align: center;
  color: var(--ink-faint);
}

.rail-item[aria-current="page"] .rail-glyph { color: var(--brand); }

.rail-foot {
  margin-top: auto;
  padding: 1.2rem 0.75rem 0;
  border-top: 1px solid var(--rule);
}

.rail-foot .theme-toggle { margin: 0; }

.workspace-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.topbar {
  display: flex;
  align-items: center;
  min-height: 3.6rem;
  padding: 0 2rem;
  border-bottom: 1px solid var(--rule);
}

.topbar-path {
  margin: 0;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

.topbar-path strong { font-weight: 500; color: var(--ink); }
.topbar-slash { margin: 0 0.35rem; }

.workspace main {
  width: 100%;
  max-width: 72rem;
  margin: 0;
  padding: 2rem 2rem 3rem;
  flex: 1;
}

.workspace .colophon {
  max-width: none;
  margin: 0;
  padding: 1rem 2rem;
}

.work-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
  margin-bottom: 1.6rem;
}

.work-heading h1 {
  margin: 0 0 0.4rem;
  font-size: 1.75rem;
  letter-spacing: -0.02em;
}

.work-heading p {
  margin: 0;
  color: var(--ink-soft);
}

.work-help {
  margin: 1.2rem 0 0;
  max-width: var(--measure);
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.panel {
  padding: 1.4rem 1.5rem;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: calc(var(--radius) + 2px);
}

.panel p { margin: 0 0 0.8rem; }
.panel p:last-child { margin-bottom: 0; }

.work-code {
  margin: 0;
  padding: 0.8rem 1rem;
  overflow-x: auto;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: 0.82rem;
}

/* New check: one card, two steps. */
.composer {
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: calc(var(--radius) + 2px);
  overflow: hidden;
}

.composer-part {
  margin: 0;
  padding: 1.4rem 1.5rem;
  border: 0;
}

.composer-part + .composer-part { border-top: 1px solid var(--rule); }

.composer-step {
  display: block;
  margin-bottom: 0.7rem;
  padding: 0;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.composer-field {
  width: 100%;
  max-width: 28rem;
  font-size: 1.1rem;
  background: var(--paper);
}

.composer-scope {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  margin: 0.9rem 0 0;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.scope-dot {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--weak);
}

.scope-dot-on { background: var(--strong); }

.check-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.8rem;
}

.composer .check {
  margin: 0;
  background: var(--paper);
}

.composer-run {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.1rem;
}

.composer-run p {
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.report-empty {
  margin-top: 1.5rem;
  padding: 2rem;
  text-align: center;
  color: var(--ink-soft);
  border: 1px dashed var(--rule);
  border-radius: calc(var(--radius) + 2px);
}

.report-empty p { margin: 0; font-size: 0.9rem; }

.report-empty-title {
  margin-bottom: 0.4rem;
  font-weight: 600;
  color: var(--ink);
}

.report-empty .report-empty-title { margin-bottom: 0.4rem; }

.console-results:not([hidden]) + .report-empty { display: none; }

/* Domains. */
.domain-form .field-row { max-width: 36rem; }
.domain-record { margin-top: 1rem; }
.domain-state { font-weight: 600; }

.workspace .state-grid { margin: 0; }

@media (max-width: 60rem) {
  .check-grid { grid-template-columns: 1fr; }
}

/* A phone: the rail becomes a bar of four across the top. */
@media (max-width: 46rem) {
  .workspace { display: block; }
  .rail {
    position: static;
    height: auto;
    padding: 1rem 1rem 0.5rem;
    border-right: 0;
    border-bottom: 1px solid var(--rule);
  }
  .rail-identity { margin: 0 0 0.9rem; }
  .rail-caption { display: none; }
  .rail-nav {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin-bottom: 0.5rem;
  }
  .rail-nav + .rail-caption + .rail-nav { grid-template-columns: 1fr; }
  .rail-item {
    flex-direction: column;
    justify-content: center;
    gap: 0.25rem;
    min-height: 3.4rem;
    padding: 0.4rem 0.2rem;
    font-size: 0.72rem;
    text-align: center;
  }
  .rail-foot { margin: 0; padding: 0.6rem 0 0.4rem; border: 0; }
  .topbar { padding: 0 1rem; min-height: 2.8rem; }
  .workspace main { padding: 1.2rem 1rem 2rem; }
  .work-heading { flex-direction: column; gap: 0.8rem; }
  .composer-part { padding: 1.1rem 1rem; }
  .composer-run { flex-direction: column; align-items: stretch; }
  .workspace .colophon { padding: 1rem; }
}

/* The checks in the composer are cards: the box and the name on one line,
   what the check reads under them, its rule set at the foot. */
.composer .checks { margin: 0; }

.composer-part > legend.composer-step {
  float: left;
  width: 100%;
}

.composer-part > legend.composer-step + * { clear: both; }

.check-grid .check {
  grid-template-columns: auto 1fr;
  grid-template-rows: auto 1fr auto;
  align-items: start;
  gap: 0.35rem 0.6rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.check-grid .check:last-child { border-bottom: 1px solid var(--rule); }

.check-grid .check input { margin: 0.2rem 0 0; }

.check-grid .check-name {
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 600;
}

.check-grid .check-says,
.check-grid .check-policy { grid-column: 2; }

.check-grid .check-says { font-size: 0.88rem; line-height: 1.5; }

.check-grid .check:has(input:checked) {
  border-color: color-mix(in srgb, var(--brand) 45%, var(--rule));
}

/* A phone: four across and nothing else. The documents are in the footer,
   the colour switch sits beside the name, and a button repeating a rail item
   is left out. */
@media (max-width: 46rem) {
  .rail { position: relative; }
  .rail-identity { padding-right: 4.5rem; }
  .rail-nav + .rail-caption + .rail-nav { display: none; }
  .rail-foot {
    position: absolute;
    top: 1rem;
    right: 1rem;
    padding: 0;
  }
  .work-heading > .button { display: none; }
}

/* Signing in: the one page with no rail, a card in the middle. */
.workspace-signin { display: block; }

.workspace-signin main {
  max-width: 31rem;
  margin: 0 auto;
  padding-top: 12vh;
}

.signin-mark {
  margin: 0 0 1.4rem;
  text-align: center;
}

.signin-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.4rem;
}

.signin-head h1 { margin: 0; }

.signin-first {
  margin-top: 1.4rem;
  font-size: 0.88rem;
  color: var(--ink-soft);
}

.signin-first p { margin: 0 0 0.6rem; }
.signin-first .work-code { margin-bottom: 0.6rem; font-size: 0.78rem; }

.signin-mark .wordmark { font-size: 2rem; }

.signin-form,
.password-form {
  display: grid;
  gap: 0.6rem;
}

.signin-form h1 { font-size: 1.4rem; }
.signin-form .field,
.password-form .field { width: 100%; background: var(--paper); }
.signin-form .submit { margin-top: 0.4rem; }

.signin-status {
  min-height: 1.3em;
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-soft);
}

.password-form {
  max-width: 30rem;
  margin-top: 1.2rem;
}

.password-form .section-title { margin: 0 0 0.4rem; }
.password-form .submit { justify-self: start; margin-top: 0.4rem; }

.rail-foot {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.topbar-signout {
  margin-left: auto;
  padding: 0.35rem 0.7rem;
  font: inherit;
  font-size: 0.82rem;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
}

.topbar-signout:hover { color: var(--ink); border-color: var(--ink-faint); }

/* A heading between the parts of one workspace page. */
.work-section {
  margin: 2rem 0 0.8rem;
  font-size: 1.05rem;
  font-weight: 600;
}

.work-heading + .work-section { margin-top: 0; }

.password-form { margin-top: 0; }

/* History: the list, and a report opened under it. */
.history-table { width: 100%; }
.history-table th { text-align: left; font-weight: 600; color: var(--ink-faint); font-size: 0.78rem; }
.history-table td { font-family: var(--sans); white-space: nowrap; vertical-align: middle; }
.history-target { font-weight: 600; white-space: normal; overflow-wrap: normal; }
.history-actions { text-align: right; white-space: nowrap; }
.history-status:empty, .history-status[hidden] { display: none; }
.history-empty { margin: 0; color: var(--ink-soft); }
.history-note { margin: 0 0 0.8rem; color: var(--ink-soft); }
.history-note[hidden] { display: none; }

.history-open,
.history-delete {
  margin-left: 0.4rem;
  padding: 0.3rem 0.7rem;
  font: inherit;
  font-size: 0.82rem;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
}

.history-open:hover { color: var(--ink); border-color: var(--ink-faint); }
.history-delete:hover { color: var(--insecure); border-color: var(--insecure); }
#history-report { margin-top: 1.5rem; }

/* The sign-in page's footer sits at the foot of the window, centred under the
   card like everything else on the page. It floated in the middle, flush
   left, because the page had no rail to stretch it. */
.workspace-signin .workspace-main { min-height: 100vh; }
.workspace-signin .colophon-row { justify-content: center; }

/* On a phone the command wraps rather than scrolling out of sight. */
@media (max-width: 46rem) {
  .signin-first .work-code code { white-space: pre-wrap; overflow-wrap: anywhere; }
}

/* A shell command, coloured the way a terminal would: the program, its
   flags, a quoted string and the pipe each told apart. Colour only; the
   text is the command exactly, so what is copied is what runs. */
.shell .sh-cmd  { color: var(--ink); font-weight: 700; }
.shell .sh-flag { color: var(--link); }
.shell .sh-str  { color: var(--strong); }
.shell .sh-op   { color: var(--ink-faint); }
.shell code     { color: var(--ink-soft); }
