/* ---------- fonts ----------

   Self-hosted. Subsetted to latin, latin-ext and greek; the unicode-range on
   each face means a page of English prose fetches three files and ignores the
   rest. STIX Two Text is a variable font, so one file covers 400-700 and gives
   a real bold rather than a synthesised one.

   Regenerate by re-running the download against the Google Fonts css2 API; the
   subsets and weights are the only thing to choose.                          */

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/ibm-plex-mono-400-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/ibm-plex-mono-400-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/ibm-plex-mono-500-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/ibm-plex-mono-500-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "STIX Two Text";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/static/fonts/stix-two-text-400-700-italic-greek.woff2) format("woff2");
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

@font-face {
  font-family: "STIX Two Text";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/static/fonts/stix-two-text-400-700-italic-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "STIX Two Text";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/static/fonts/stix-two-text-400-700-italic-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "STIX Two Text";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/static/fonts/stix-two-text-400-700-greek.woff2) format("woff2");
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

@font-face {
  font-family: "STIX Two Text";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/static/fonts/stix-two-text-400-700-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "STIX Two Text";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/static/fonts/stix-two-text-400-700-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

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

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

body {
  margin: 0
}

img {
  max-width: 100%;
  height: auto;
  display: block
}

a {
  color: inherit
}

table {
  border-collapse: collapse;
  width: 100%
}

figure {
  margin: 0
}

:root {
  --ground: #f6f5f1;
  --panel: #fffffe;
  --ink: #14140f;
  --ink-2: #26251d;
  --ink-3: #57544a;
  --ink-4: #86827a;
  --box: #ada693;
  --box-2: #d8d3c6;
  --rule: #e2ded2;
  --accent: #bf4a11;
  --shade: #eeebe2;
  --cast: #ded9ca;
}

/* The ground is the desk the sheets are lying on, so it is the thing that gets
   a texture; the sheets themselves stay clean. A 160px tile of desaturated
   fractal noise at 3.5%: fine enough to be felt rather than seen.

   The two numbers are coupled to --cast. Grain darkens the average ground, so
   a shadow chosen against the bare colour lands on a surface that has moved
   underneath it. At 10% the ground dropped far enough to swallow the shadow
   whole. Change the opacity and the cast wants rechecking.

   Baked into the SVG rather than laid over the page as an overlay: an element
   with `opacity` would need a stacking context, a fixed position and
   pointer-events juggling to sit under the content. A background image is
   already under the content, because that is what a background is.

   Left scrolling rather than fixed. A tiled random field cannot be perceived
   to move, and a fixed background repaints on every scroll frame.

   Parameters are readable in place -- baseFrequency is the feature size,
   numOctaves adds finer layers at halving amplitude, seed only rearranges. The
   only escaped character is the `#` of the filter reference. */

body {
  background: var(--ground) url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='g' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' seed='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/><feColorMatrix type='matrix' values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1'/></filter><rect width='160' height='160' filter='url(%23g)' opacity='0.035'/></svg>");
  color: var(--ink-2);
  font-family: "STIX Two Text", Georgia, serif;
  font-size: 18px;
  line-height: 1.62;
}

::selection {
  background: var(--accent);
  color: #fff
}

.box__h,
.box__b,
.main__h,
.kv,
.toc,
.idx,
.specimen__label,
.section-label,
.refs,
.notes,
.eqno,
.absbox__h,
.doc__meta,
figcaption,
.chip {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
}

pre,
code {
  font-family: "IBM Plex Mono", ui-monospace, monospace
}

.grid {
  max-width: 1180px;
  margin: 0 auto;
  padding: 16px;
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

/* The sheet is first in the source, so the columns are assigned rather than
   left to auto-placement. The rail stays one element, which is what lets it
   stick as a single unit: a sticky item can only travel inside its own grid
   area, so splitting it in two would give whichever half came first an area no
   taller than itself and nothing to travel through. */

.rail {
  grid-column: 1;
  grid-row: 1;
  position: sticky;
  top: 16px
}

.main {
  grid-column: 2;
  grid-row: 1
}

/* ---------- panels: header bars share a height and a baseline ----------

   The cast shadow is a hard offset block: zero blur, zero spread, one flat
   tone. That is what lifts a panel off the ground -- a blurred shadow reads as
   a UI card, this reads as a sheet sitting on a desk. It is `box-shadow` only
   because that is the cheapest way to paint an offset rectangle; nothing about
   it is the browser's default look.

   The offset is smaller than the 16px grid gutter, so it never reaches the
   next panel and never needs layout space.                                   */

.box,
.main {
  box-shadow: 3px 3px 0 var(--cast)
}

.box {
  border: 1px solid var(--box);
  background: var(--panel);
  margin-bottom: 16px
}

.box:last-child {
  margin-bottom: 0
}

.box__h,
.main__h {
  height: 30px;
  display: flex;
  align-items: center;
  padding: 0 12px;
  border-bottom: 1px solid var(--box);
  font-size: 11px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink);
}

.box__h {
  justify-content: center
}

.main__h {
  padding: 0 16px
}

.box__b {
  padding: 10px 12px;
  font-size: 12.5px;
  line-height: 1.55
}

.navlist {
  display: flex;
  flex-direction: column;
  gap: 2px
}

.navlist a {
  color: var(--ink-2);
  text-decoration: none;
  padding: 2px 0
}

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

.navlist a[aria-current] {
  color: var(--accent)
}

.navlist a[aria-current]::before {
  content: "> "
}

.kv {
  margin: 0
}

.kv div {
  display: flex;
  gap: 8px;
  padding: 2px 0;
  align-items: baseline
}

.kv dt {
  color: var(--ink-4);
  flex: 0 0 7ch
}

.kv dd {
  margin: 0;
  min-width: 0;
  color: var(--ink-2)
}

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

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

.toc {
  display: flex;
  flex-direction: column;
  gap: 2px;
  counter-reset: t
}

.toc a {
  color: var(--ink-2);
  text-decoration: none;
  padding: 1px 0;
  display: flex;
  gap: 7px
}

.toc a::before {
  content: counter(t) ".";
  counter-increment: t;
  color: var(--ink-4);
  flex: 0 0 2ch
}

.toc a:hover {
  color: var(--accent)
}

/* ---------- tag chips ----------

   The neutral is on .chip itself, not on a .chip--default nobody applies. A tag
   with no entry below is a tag someone has just invented, and it now lands in
   the palette's grey rather than taking `border: 1px solid` at currentColor and
   drawing itself a near-black box.

   The class comes from the tag verbatim, so an entry only matches a lowercase
   tag. `{% set tags = ["AI"] %}` renders grey, not blue.                     */

.chip {
  display: inline-block;
  font-size: 10.5px;
  line-height: 1.5;
  padding: 1px 5px;
  margin: 0 3px 3px 0;
  background: var(--shade);
  border: 1px solid #c9c3b4;
  white-space: nowrap;
  color: var(--ink);
}

.chip--cryptography {
  background: #fdf3d0;
  border-color: #d8c47e
}

.chip--ai {
  background: #e4edf7;
  border-color: #9fb8d4
}

/* Astronomy vacates the blue for AI; violet suits it better anyway. */
.chip--astronomy {
  background: #eae4f5;
  border-color: #b3a2cd
}

.chip--forecasting {
  background: #dbebe8;
  border-color: #8fbab2
}

.chip--reverse_engineering {
  background: #fae3e3;
  border-color: #d6a3a3
}

.chip--bluetooth {
  background: #e6f1e6;
  border-color: #a6c6a6
}

/* ---------- sheet ---------- */

.main {
  border: 1px solid var(--box);
  background: var(--panel)
}

.main__b {
  padding: 30px 34px 40px
}

.doc {
  margin: 0 0 22px
}

.doc h1 {
  font-size: 26px;
  font-weight: 600;
  line-height: 1.28;
  letter-spacing: -.005em;
  color: var(--ink);
  margin: 0 0 10px;
}

.doc__meta {
  font-size: 11.5px;
  letter-spacing: .08em;
  color: var(--ink-4);
  text-transform: uppercase
}

.absbox {
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  padding: 12px 0;
  margin: 0 0 30px;
}

.absbox__h {
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin-bottom: 5px;
}

.absbox p {
  margin: 0;
  font-size: 16.5px;
  line-height: 1.55;
  color: var(--ink-3)
}

/* ---------- running text ---------- */

.body {
  counter-reset: sec fig
}

.body p {
  margin: 0 0 1.05em;
  text-align: justify;
  hyphens: auto
}

.body>p:first-child {
  margin-top: 0
}

.body h2 {
  counter-increment: sec;
  font-family: "STIX Two Text", serif;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
  margin: 32px 0 10px;
}

.body h2::before {
  content: counter(sec) ".";
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 15px;
  color: var(--ink-4);
  margin-right: 11px;
  font-weight: 400;
}

.body ul {
  margin: 0 0 1.05em;
  padding: 0 0 0 1.5em;
  list-style: none;
  counter-reset: li
}

.body ul li {
  position: relative;
  margin-bottom: .22em
}

.body ul li::before {
  content: "(" counter(li, lower-alpha) ")";
  counter-increment: li;
  position: absolute;
  left: -2.1em;
  width: 1.9em;
  text-align: right;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 13px;
  color: var(--ink-4);
}

.body a,
.specimen a,
.refs a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--box);
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

.body a:hover,
.specimen a:hover,
.refs a:hover {
  color: var(--accent);
  text-decoration-color: var(--accent)
}

/* ---------- footnote markers ---------- */

.body a.ref {
  font-size: 11px;
  vertical-align: super;
  line-height: 0;
  padding: 0 1px;
  color: var(--accent);
  text-decoration: none;
}

.body a.ref:hover {
  text-decoration: underline
}

/* ---------- footnote markers ---------- */

/* Lettered, to stay distinct from the numbered [n] citations alongside them. */
.fnm {
  font-size: 11px;
  vertical-align: super;
  line-height: 0;
  padding: 0 1px;
  color: var(--accent);
  text-decoration: none;
}

.fnm:hover {
  text-decoration: underline
}

/* ---------- plates ---------- */

figure {
  margin: 26px 0
}

figure img {
  border: 1px solid var(--box-2);
  background: #fff;
  padding: 10px
}

figcaption {
  margin-top: 8px;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--ink-3)
}

figcaption::before {
  content: "FIG. " counter(fig) ".  ";
  counter-increment: fig;
  color: var(--ink)
}

/* ---------- equations ---------- */

.eq {
  margin: 20px 0;
  display: flex;
  align-items: center;
  gap: 20px
}

.eq__b {
  flex: 1;
  text-align: center
}

.eqno {
  color: var(--ink-4);
  font-size: 13px
}

/* ---------- references and notes ---------- */

.refs,
.notes {
  margin: 34px 0 0;
  font-size: 12px;
  line-height: 1.55
}

.refs__h,
.notes__h {
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-4);
  border-top: 1px solid var(--ink);
  padding-top: 8px;
  margin-bottom: 9px;
}

.refs ol,
.notes ol {
  margin: 0;
  padding: 0;
  list-style: none
}

.refs li,
.notes li {
  position: relative;
  padding-left: 3.2em;
  margin-bottom: 6px
}

.refs .n,
.notes .n {
  position: absolute;
  left: 0;
  color: var(--ink-4)
}

/* The marker is the way back to the text; a separate return arrow would be noise. */
.notes .n {
  text-decoration: none
}

.notes .n:hover {
  color: var(--accent)
}

.refs a {
  word-break: break-all
}

/* ---------- specimen ---------- */

.specimen {
  margin-top: 40px
}

.specimen__label {
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-4);
  border-top: 1px solid var(--ink);
  padding-top: 8px;
  margin-bottom: 16px;
}

.specimen p {
  margin: 0 0 1.05em
}

pre {
  background: var(--shade);
  border: 1px solid var(--box-2);
  padding: 11px 13px;
  overflow-x: auto;
  font-size: 12.5px;
  line-height: 1.58;
  margin: 0 0 1.2em;
}

pre code {
  background: none;
  padding: 0;
  font-size: 1em
}

code {
  font-size: .72em;
  background: var(--shade);
  padding: 1px 4px
}

blockquote {
  margin: 0 0 1.2em;
  padding-left: 16px;
  border-left: 2px solid var(--box);
  color: var(--ink-3)
}

blockquote p {
  margin: 0;
  text-align: left
}

.specimen table {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  margin: 0 0 1.2em
}

.specimen th,
.specimen td {
  text-align: left;
  padding: 5px 16px 5px 0;
  border-bottom: 1px solid var(--rule)
}

.specimen thead th {
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ---------- index ---------- */

.idx {
  width: 100%;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  margin: 0
}

.idx th {
  text-align: left;
  padding: 6px 16px 6px 0;
  vertical-align: baseline;
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}

.idx td {
  padding: 7px 16px 7px 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top
}

.idx tr:hover td {
  background: var(--shade)
}

.link {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--box);
  text-underline-offset: 2px
}

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

.idx .d {
  color: var(--ink-4);
  white-space: nowrap
}

.idx .b {
  display: block;
  font-family: "STIX Two Text", serif;
  font-size: 15px;
  color: var(--ink-3);
  margin-top: 2px;
  line-height: 1.45;
}

.section-label {
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin: 30px 0 8px;
}

.about {
  margin-bottom: 6px
}

.about p {
  margin: 0 0 1.05em
}

.about p:last-child {
  margin-bottom: 0
}

@media (max-width:900px) {
  /* One column, and the sheet moves into the middle of the rail's panels:
     wayfinding above it, metadata and links below. `display: contents` drops
     the rail's own box so its panels become grid items in their own right and
     can be sequenced individually. The source keeps the sheet first, so the
     document still opens with the article whatever the panels do around it.

     `grid-row: auto` undoes the desktop placement -- an explicitly placed item
     is not sequenced by `order`, so leaving it pinned to row 1 would strand the
     sheet at the top with every panel flowing beneath it.

     Sequenced rather than pinned to row numbers: the index has no Contents or
     Article panel, and fixed rows would leave two empty tracks there with their
     gaps still between them. */

  .grid {
    grid-template-columns: 1fr;
    padding: 12px;
    gap: 12px
  }

  .rail {
    display: contents
  }

  .box--nav       { order: 1 }
  .box--toc       { order: 2 }
  .main           { order: 3; grid-column: 1; grid-row: auto }
  .box--meta      { order: 4 }
  .box--elsewhere { order: 5 }

  /* The gap does the spacing now that the panels are not stacked in a rail. */
  .box {
    margin-bottom: 0
  }

  /* One column, so the sheet's header is no longer a bar sitting beside the
     rail's headers -- it is the only one on the row, and centring it matches
     the panels above it. */
  .main__h {
    justify-content: center
  }

  .main__b {
    padding: 22px 18px 28px
  }

  .body p {
    text-align: left
  }
}

/* ---------- print: the sheet becomes a page ----------

   Last, so it outranks the width rules above.                                */

@media print {
  /* `background` shorthand, so the grain image goes with the colour. A 10%
     noise field is an expensive thing to send to a printer. */
  body {
    background: #fff;
    font-size: 10.5pt
  }

  .grid {
    display: block;
    max-width: none;
    padding: 0
  }

  .rail {
    display: none
  }

  .main {
    border: 0
  }

  /* The shadow says "this is a sheet on a surface". On paper it already is. */
  .box,
  .main {
    box-shadow: none
  }

  .main__h {
    display: none
  }

  .main__b {
    padding: 0
  }

  .body a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 8.5pt;
    word-break: break-all
  }

  h1,
  h2 {
    break-after: avoid
  }

  figure,
  .eq {
    break-inside: avoid
  }
}


/* ---------- unwritten content ---------- */

.todo {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .06em;
  background: #ffe9b8;
  border: 1px solid #d8a93a;
  color: #6b4a00;
  padding: 1px 5px;
  display: inline-block;
}