/*
 * The documentation pages, structure only. The fourth sheet in the load
 * order `fonts.css`, `tokens.css`, `base.css`, then this, the same order
 * `index.html` uses, with `site.css` in this slot. Two structures over one
 * set of elements, which is the whole reason ADR-0047 §1 chose a stylesheet
 * over a component library: a React component cannot cross into this
 * repository and a stylesheet can.
 *
 * Every value here comes from `tokens.css` and every bare element is
 * already dressed by `base.css`, both of them copies of the console's (see
 * `tools/vendored.json`). What is left is what a documentation page needs
 * and the element layer deliberately withholds: a section sidebar, a page
 * table of contents, a readable measure, code blocks, and the block rhythm
 * and heading scale that `base.css` leaves to each surface because a
 * console panel and a prose column do not want the same ones.
 *
 * Two rules bind it, as they bind `site.css`.
 *
 * NOTHING NAMES A COLOUR. Every colour in the system is defined in exactly
 * two blocks and never inside a media query (ADR-0047 §2); a colour minted
 * here (or worse, minted here behind `prefers-color-scheme`) would be
 * stranded in the unresolved state and would fork the palette besides. So
 * there is no `:root` colour block below and no colour inside the one media
 * query there is.
 *
 * NOTHING IS FETCHED FROM ANOTHER ORIGIN (ADR-0019, ADR-0045 §5).
 * `tools/check-external-assets.mjs` fails the build if it ever is.
 */

/* ---- The page ----
   The same blueprint linework as the front door (`site.css`, and
   docs/branding/identity.md: datum lines and hairlines, calipers,
   Blueprints as a domain object), drawn by the same two rule tokens at the
   same spacing. The prose sits on a surface above it, so on a documentation
   page the grid reads in the gutters, which is where a drawing's grid
   reads anyway. */

:root {
  --grid-minor: var(--space-5);
  --grid-major: calc(var(--space-5) * 5);

  /* The one measurement the sticky column offsets have to agree with. The
     top bar is a line of --text-m plus its padding, and the sidebar and the
     table of contents both hang from underneath it. */
  --topbar-height: 3.25rem;
}

/* Every heading on the page is a link target (the table of contents is made
   of them) and the top bar is sticky, so without this a jump to `#grades`
   puts `Grades` underneath the bar. The offset is the bar plus a line of
   air. */
html {
  scroll-padding-top: calc(var(--topbar-height) + var(--space-4));
}

body {
  /* `background-image` and not the `background` shorthand: `base.css` has
     already set the ground to --colour-bg and the shorthand would drop it. */
  background-image:
    linear-gradient(var(--colour-rule) 1px, transparent 1px),
    linear-gradient(90deg, var(--colour-rule) 1px, transparent 1px),
    linear-gradient(var(--colour-rule-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--colour-rule-soft) 1px, transparent 1px);
  background-size:
    var(--grid-major) var(--grid-major),
    var(--grid-major) var(--grid-major),
    var(--grid-minor) var(--grid-minor),
    var(--grid-minor) var(--grid-minor);
  /* The grid belongs to the page, not to the scroll: a fixed attachment
     keeps the drawing still while the prose moves over it. */
  background-attachment: fixed;
}

/* The first thing in the tab order and the last thing most readers see. Off
   screen rather than `display: none`, because a hidden element cannot be
   focused and a skip link that cannot be focused is decoration. */
.skip {
  position: absolute;
  top: 0;
  left: -100vw;
  z-index: 10;
  padding: var(--space-2) var(--space-4);
  background: var(--colour-surface-raised);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
}

.skip:focus {
  left: var(--space-2);
  top: var(--space-2);
}

/* ---- The top bar ----
   --colour-chrome is the token minted for exactly this: the band that is
   not the ground and not a surface. Opaque, so the blueprint grid stops at
   its edge rather than showing through a translucent bar. */

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--topbar-height);
  padding: var(--space-2) clamp(var(--space-4), 4vw, var(--space-6));
  background: var(--colour-chrome);
  border-bottom: 1px solid var(--colour-rule);
}

/* The same mark as the front door, in the same markup, at the size a bar
   can hold. Wordmark-first: the mark supports the word and does not replace
   it (identity.md). `site.css` sets the display size of this same class for
   the one page that is nothing but the mark; the two sheets never load
   together. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-m);
  font-weight: var(--weight-bold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
}

/* The pack's clear space rule is two reading bands on every side, and its
   minimum is 12px tall; `--text-m` clears both. */
.wordmark-mark {
  flex: none;
  /* Taller than the caps beside it, because the mark is measured from its
     datum and the wordmark is measured from its baseline. At `--text-m` this
     is 18px, comfortably over the pack's 12px floor for a bare mark. */
  height: 1.3em;
  width: auto;
}

.wordmark-lead {
  color: var(--brand);
}

/* The word goes left and everything else goes right together, which
   `space-between` alone stops doing the moment the bar holds three things
   rather than two. */
.topnav {
  display: flex;
  gap: var(--space-5);
  margin-inline-start: auto;
  font-size: var(--text-s);
}

/* Three drawn marks on one plate, which is the shape a bar can hold at any
   width the word could not. It is a radio group because there are three
   states and following the machine is one of them, and it is `hidden` in the
   markup and unhidden by `theme.js`, because a control that cannot act is
   worse than no control and the page resolves to a complete theme from the
   bare `:root` block without it.

   `fieldset` and `legend` bring a border, a padding and a float with them.
   All of it goes: what the elements are wanted for is the grouping and the
   name they give it. */
.theme-control {
  display: flex;
  gap: 2px;
  margin: 0;
  padding: 2px;
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-1);
}

/* Off the screen, not out of the document: it is the group's name. */
.theme-control legend,
.theme-option > span {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.theme-option {
  position: relative;
  display: flex;
  cursor: pointer;
}

.theme-option input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* A plate, not a pill (identity.md): the smallest radius in the scale, and
   the mark drawn in the same hairline stroke as every other mark on the
   site. */
.theme-mark {
  box-sizing: content-box;
  width: 16px;
  height: 16px;
  padding: 4px;
  border-radius: var(--radius-1);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--colour-text-faint);
}

.theme-option:hover .theme-mark {
  color: var(--colour-text-muted);
}

/* The chosen one is filled and comes up to full strength, which is how every
   other settled state on these surfaces reads. */
.theme-option input:checked + .theme-mark {
  background: var(--colour-fill);
  color: var(--colour-chrome);
}

.theme-option input:focus-visible + .theme-mark {
  outline: var(--focus-width) solid var(--focus-colour);
  outline-offset: var(--focus-offset);
}

/* The gaps close on the narrowest bars. This one does not wrap at any width
   a phone has, because it is sticky and a second line of chrome is a second
   line a reader never gets back; it is allowed to as a last resort, because
   a control run off the edge of the screen is worse than a bar one line
   taller. */
@media (max-width: 34rem) {
  .topbar {
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    padding-inline: var(--space-3);
  }

  .topnav {
    gap: var(--space-3);
  }

  /* On the line it wraps onto it goes to the same edge it sits at when it
     does not wrap, so the bar reads as one group either way. */
  .theme-control {
    margin-inline-start: auto;
  }
}

/* Interactive text is told apart by its underline and its full-strength ink
   (ADR-0047 §4). In a bar of two or three items the underline is noise, so
   the bar spends the other half of that pair: the links sit at muted
   strength and the current one comes up to full, under a datum line. */
.topnav a {
  color: var(--colour-text-muted);
  text-decoration: none;
  padding-bottom: var(--space-1);
  border-bottom: 1px solid transparent;
}

.topnav a:hover {
  color: var(--colour-text);
}

.topnav a[aria-current='page'] {
  color: var(--colour-text);
  border-bottom-color: var(--colour-fill);
}

/* ---- Three columns ----
   Navigation, page, and where you are in the page. The middle column takes
   `minmax(0, 1fr)` because a wide code block in a grid track will otherwise
   push the track wider than the viewport instead of scrolling inside it. */

.shell {
  display: grid;
  grid-template-columns: 15.5rem minmax(0, 1fr) 14rem;
  gap: clamp(var(--space-5), 3vw, var(--space-6));
  align-items: start;
  max-width: 82rem;
  margin: 0 auto;
  padding: var(--space-6) clamp(var(--space-4), 4vw, var(--space-6))
    calc(var(--space-6) * 2);
}

.sidebar,
.toc {
  position: sticky;
  top: calc(var(--topbar-height) + var(--space-3));
  max-height: calc(100vh - var(--topbar-height) - var(--space-6));
  overflow-y: auto;
  font-size: var(--text-s);
}

/* ---- The sidebar ----
   Derived from `nav.yaml` by `scripts/lib/nav.mjs`. Nothing about its order
   is decided in this sheet. */

.sidebar .navgroup {
  margin: var(--space-5) 0 var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--colour-text-muted);
}

.sidebar .navgroup:first-child {
  margin-top: 0;
}

.sidebar .navgroup a {
  color: inherit;
  text-decoration: none;
}

.sidebar .navgroup a:hover {
  color: var(--colour-text);
}

/* A datum line down the section, and each entry hanging off it. The list's
   rule and the entry's marker are the same line at two strengths, which is
   why the marker is pulled back over it by a pixel. */
.sidebar ul {
  margin: 0;
  padding: 0;
  list-style: none;
  border-left: 1px solid var(--colour-rule);
}

.sidebar li a {
  display: block;
  margin-left: -1px;
  padding: var(--space-1) 0 var(--space-1) var(--space-3);
  border-left: 2px solid transparent;
  text-decoration: none;
  color: var(--colour-text-muted);
}

.sidebar li a:hover {
  color: var(--colour-text);
}

/* Where you are, marked three ways and not one: the ink comes to full
   strength, the weight goes up, and the datum line thickens. Hue is never
   load-bearing (ADR-0047 §5) and there is no accent hue to spend here in
   any case. */
.sidebar a[aria-current='page'] {
  color: var(--colour-text);
  font-weight: var(--weight-semibold);
  border-left-color: var(--colour-fill);
}

/* ---- The table of contents ---- */

.toc h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--colour-text-muted);
}

.toc ul {
  margin: 0;
  padding: 0;
  list-style: none;
  border-left: 1px solid var(--colour-rule);
}

.toc li a {
  display: block;
  padding: var(--space-1) 0 var(--space-1) var(--space-3);
  color: var(--colour-text-muted);
  text-decoration: none;
}

.toc li a:hover {
  color: var(--colour-text);
}

.toc .toc-h3 a {
  padding-left: var(--space-5);
  font-size: var(--text-xs);
}


/* ---- The page ---- */

.page {
  min-width: 0;
}

.prose {
  padding: clamp(var(--space-5), 4vw, calc(var(--space-6) + var(--space-5)));
  background: var(--colour-surface);
  border: 1px solid var(--colour-rule-soft);
  border-radius: var(--radius-2);
}

/* Block rhythm. `base.css` withholds it on purpose ("a console panel and a
   marketing column do not want the same rhythm"), so the prose column sets
   its own, out of the space scale. */

.prose > :first-child {
  margin-top: 0;
}

.prose > :last-child {
  margin-bottom: 0;
}

.prose p,
.prose ul,
.prose ol,
.prose dl {
  margin: var(--space-4) 0;
}

/* The measure. Around 70 characters at --text-base in Atkinson, which is
   where a line stops needing the eye to hunt for the next one. It is set on
   the text blocks rather than on the column so that a table, a code block
   and a card grid can still use the full width. Those are read by
   scanning, not by line. */
.prose p,
.prose li,
.prose dd {
  max-width: 38rem;
}

.prose li {
  margin: var(--space-1) 0;
}

.prose ul,
.prose ol {
  padding-left: var(--space-5);
}

/* Headings. The type scale stops at 22px because it was cut for interface
   text, and a page title is not interface text; the same departure, and the
   same reasoning, as the wordmark in `site.css`. Adding a display step to
   `tokens.css` would be a change to the shared token layer and belongs
   upstream, not here. h2 down are scale steps exactly. */

.prose h1 {
  margin: 0 0 var(--space-5);
  font-size: clamp(var(--text-xl), 4.2vw, 2.25rem);
  letter-spacing: -0.01em;
}

/* A datum line above every section, which is the identity's own device and
   does the work a size jump would otherwise have to do. */
.prose h2 {
  margin: calc(var(--space-6) + var(--space-2)) 0 var(--space-3);
  padding-top: var(--space-5);
  border-top: 1px solid var(--colour-rule-soft);
  font-size: var(--text-xl);
}

.prose h3 {
  margin: var(--space-6) 0 var(--space-2);
  font-size: var(--text-l);
}

.prose h4 {
  margin: var(--space-5) 0 var(--space-2);
  font-size: var(--text-base);
  color: var(--colour-text-muted);
}

/* Atkinson draws long descenders, and `base.css` says a surface that wants
   its rule further from them sets the offset itself. This one does. */
.prose a {
  text-underline-offset: 0.2em;
}

.prose blockquote {
  margin: var(--space-5) 0;
  padding-left: var(--space-4);
  border-left: 2px solid var(--colour-rule);
  color: var(--colour-text-muted);
}

.prose hr {
  margin: var(--space-6) 0;
  border: 0;
  border-top: 1px solid var(--colour-rule-soft);
}

/* Description lists. `docs/index.md` is written term-then-definition, so
   this is prose furniture rather than an edge case. The term is told from
   the definition by weight, and the definition hangs off a datum line. */
.prose dt {
  margin-top: var(--space-4);
  font-weight: var(--weight-semibold);
}

.prose dd {
  margin: var(--space-1) 0 0;
  padding-left: var(--space-4);
  border-left: 1px solid var(--colour-rule-soft);
}

/* ---- Tables ----
   `base.css` has already collapsed the borders, set the cell padding, and
   put a rule under the header rather than a fill, because the identity is datum
   lines and hairlines. What is left is the wrapper that lets a wide table
   scroll inside the column instead of widening it, and the rules between
   rows. */

.table-scroll {
  margin: var(--space-5) 0;
  overflow-x: auto;
  border: 1px solid var(--colour-rule-soft);
  border-radius: var(--radius-2);
}

.prose table {
  width: 100%;
  font-size: var(--text-s);
}

.prose td {
  vertical-align: top;
  border-bottom: 1px solid var(--colour-rule-soft);
}

.prose tbody tr:last-child td {
  border-bottom: 0;
}

/* ---- Code ----
   `base.css` has already put `code` and `pre` in the mono face. A code
   block is recessed rather than raised: it drops to --colour-bg, the
   page's own ground, which reads as a well on both themes where
   --colour-surface-raised would be all but invisible on the light one. */

.prose :not(pre) > code {
  padding: 0.1em 0.34em;
  font-size: 0.88em;
  background: var(--colour-bg);
  border: 1px solid var(--colour-rule-soft);
  border-radius: var(--radius-1);
}

.prose pre.code {
  margin: var(--space-5) 0;
  padding: var(--space-4);
  overflow-x: auto;
  font-size: var(--text-s);
  line-height: var(--leading-normal);
  background: var(--colour-bg);
  border: 1px solid var(--colour-rule-soft);
  border-radius: var(--radius-2);
}

.prose pre.code code {
  padding: 0;
  font-size: inherit;
  background: none;
  border: 0;
}

/* ---- Highlighting ----
   Applied at build time by highlight.js, so the browser downloads nothing
   to read a listing.

   It is drawn in weight and ink strength, not in hue, and that is the
   token layer's decision rather than a taste. There is no syntax palette
   in `tokens.css`; the only hues it carries are the three severities,
   which it says in as many words that "nothing else uses", and the four
   signal lanes, which do not survive colour-vision deficiency and are
   never allowed to appear without their lane name (ADR-0047 §5). Minting a
   dozen token colours here to paint keywords would break the rule this
   sheet exists under, and borrowing the severity trio would spend three
   colours that mean something on three things that mean nothing.

   Three strengths of the one ink, then, plus weight and italic: comments
   recede, declarations come forward, values sit between. That is the same
   distinction a palette makes and it survives being printed, being
   photocopied, and being read by someone who sees no hue at all. */

.hljs-comment,
.hljs-quote {
  color: var(--colour-text-faint);
  font-style: italic;
}

.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-doctag,
.hljs-section,
.hljs-formula {
  font-weight: var(--weight-semibold);
}

.hljs-title,
.hljs-name,
.hljs-attr,
.hljs-attribute,
.hljs-selector-id,
.hljs-selector-class,
.hljs-type,
.hljs-built_in,
.hljs-meta {
  font-weight: var(--weight-medium);
}

.hljs-string,
.hljs-regexp,
.hljs-number,
.hljs-symbol,
.hljs-bullet,
.hljs-variable,
.hljs-template-variable {
  color: var(--colour-text-muted);
}

/* An addition and a deletion already carry a `+` or a `-` in the listing,
   which is the mark that does the work; severity colour is reserved. */
.hljs-addition,
.hljs-deletion {
  color: var(--colour-text-muted);
}

.hljs-emphasis {
  font-style: italic;
}

.hljs-strong {
  font-weight: var(--weight-bold);
}

/* ---- Section index cards ----
   The list a section gets when it has no `index.md` of its own. Plates, not
   pills, and no fill: a card is a rule around a title and a sentence. */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-4);
  margin: var(--space-5) 0;
  padding: 0;
  list-style: none;
}

.cards li {
  margin: 0;
  max-width: none;
}

.cards a {
  display: block;
  height: 100%;
  padding: var(--space-4);
  border: 1px solid var(--colour-rule);
  border-radius: var(--radius-2);
  text-decoration: none;
  transition: border-color var(--motion-fast) var(--motion-ease);
}

.cards a:hover {
  border-color: var(--colour-fill);
}

.cards strong {
  display: block;
  margin-bottom: var(--space-1);
  font-weight: var(--weight-semibold);
}

.cards span {
  color: var(--colour-text-muted);
  font-size: var(--text-s);
}

/* ---- The foot ----
   Where to edit the page, and the colophon the front door's foot carries in
   the same words. The theme control is in the bar on both surfaces. */

.pagefoot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--colour-rule-soft);
  font-size: var(--text-xs);
  color: var(--colour-text-muted);
}

.pagefoot p {
  margin: 0;
}

.colophon {
  letter-spacing: 0.04em;
}

/* ---- Narrow ----
   One column, and the page first in it.

   The table of contents goes: it duplicates headings that are now a thumb's
   scroll away. The sidebar stays, unsticks, and moves *below* the page,
   which is the one place this sheet departs from source order. The
   navigation is derived from `nav.yaml` and `nav.yaml` currently publishes
   thirty-seven pages: above the article that is roughly a screen and a half
   of links between a reader and the thing they followed a link to get to.
   Below it, it is a section index at the foot of the page, which is where a
   reader who has finished reading looks for one anyway.

   Source order is kept for everyone the visual order does not reach: the
   sidebar is still the second thing in the document, so the skip link, the
   tab order and a screen reader all find it before the article.

   No colour is set here, or anywhere else behind a media query
   (ADR-0047 §2). */

@media (max-width: 62rem) {
  .shell {
    grid-template-columns: minmax(0, 1fr);
  }

  .toc {
    display: none;
  }

  .page {
    order: 1;
  }

  .sidebar {
    order: 2;
    position: static;
    max-height: none;
    padding-top: var(--space-4);
    border-top: 1px solid var(--colour-rule-soft);
  }
}
