/* Layout and typography. No colour decisions live here -- every value below
   comes from assets/tokens.css, which notes/theme.py generates. If you find a hex
   value in this file, it is a bug: move it to theme.py.

   The same discipline as analysis/interactive/couplings_pretty/style.css, for the
   same reason: one place to change how the thing looks. */

/* ---- fonts ------------------------------------------------------------- */
/* Latin Modern Sans, the face the paper figures use. Served from assets/fonts,
   not a cdn, so the page holds its typography offline and on first paint. */
@font-face {
  font-family: 'LM Sans';
  src: url('fonts/lmsans10-regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'LM Sans';
  src: url('fonts/lmsans10-bold.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

* { box-sizing: border-box; }

html {
  /* Anchors land under the fixed nothing, but headings want air above them
     when linked to directly. */
  scroll-padding-top: 2rem;
}

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-ink);
  font-family: var(--font);
  font-size: var(--size);
  line-height: var(--line);
  -webkit-font-smoothing: antialiased;
}

.skip {
  position: absolute;
  left: -100vw;
}
.skip:focus {
  left: 0.5rem;
  top: 0.5rem;
  z-index: 10;
  background: var(--c-sheet);
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--c-accent);
}

/* ---- shell ------------------------------------------------------------- */
.shell {
  display: flex;
  align-items: flex-start;
  gap: 0;
}

.sidebar {
  position: sticky;
  top: 0;
  flex: 0 0 var(--sidebar);
  width: var(--sidebar);
  height: 100vh;
  overflow-y: auto;
  padding: 2.4rem 1.4rem 2rem 1.8rem;
  font-size: var(--size-small);
  border-right: 1px solid var(--c-hairline);
}

main {
  flex: 1 1 auto;
  min-width: 0;          /* lets the flex child shrink below its content width */
  display: flex;
  justify-content: center;
  padding: 0 var(--gutter);
}

/* The reading column is pure white against an off-white surround, so it reads as
   a sheet. It is white and not tinted because figure svgs are drawn on white and
   any tint would show as a seam around every picture.

   The sheet is wide enough for the measure *plus* the margin asides float into,
   which is why the width is a sum rather than a round number. Reserving the space
   is what keeps asides inside the sheet: an earlier version floated them out with
   a negative margin, which put them past the right border and gave the whole
   document horizontal overflow. */
.sheet {
  background: var(--c-sheet);
  width: 100%;
  max-width: calc(var(--column) + var(--margin-note) + 1.5rem + 6rem);
  min-height: 100vh;
  padding: 4rem 3rem 5rem;
  border-left: 1px solid var(--c-hairline);
  border-right: 1px solid var(--c-hairline);
}

/* Prose sits at the measure. The margin column to its right stays empty unless
   an aside claims it. */
.sheet > * { max-width: var(--column); }

/* Figures and code earn the full width, including the margin
   column -- so they clear any aside rather than sitting beside one. Text wraps
   around a float; a wide block would collide with it. */
.sheet > figure,
.sheet > .slot,
.sheet > .codeblock,
.sheet > .algorithm,
.sheet > .palette,
.sheet > table,
.sheet > pre {
  max-width: none;
  clear: right;
}

/* ---- sidebar nav ------------------------------------------------------- */
/* Back to the project page, above the brand. Small and faint, so it reads as a
   breadcrumb and not as the first entry in the contents. */
.home {
  display: inline-block;
  margin-bottom: 1.2rem;
  font-size: var(--size-tiny);
  color: var(--c-ink-faint);
  text-decoration: none;
}
.home:hover { color: var(--c-ink); }

.brand {
  display: block;
  text-decoration: none;
  color: var(--c-ink);
  margin-bottom: 2rem;
}
.brand-title {
  display: block;
  font-weight: 700;
  line-height: var(--line-tight);
}
.brand-tagline {
  display: block;
  margin-top: 0.35rem;
  color: var(--c-ink-muted);
  font-size: var(--size-tiny);
}

.navtree, .navtree ol {
  list-style: none;
  margin: 0;
  padding: 0;
}
.navpart > span {
  display: block;
  margin: 1.5rem 0 0.5rem;
  font-size: var(--size-tiny);
  letter-spacing: 0.06em;
  text-transform: lowercase;
  color: var(--c-ink-faint);
}
.navpart > ol { border-left: 1px solid var(--c-hairline); }
.navitem > a {
  display: block;
  padding: 0.28rem 0 0.28rem 0.75rem;
  margin-left: -1px;
  color: var(--c-ink-muted);
  text-decoration: none;
  border-left: 2px solid transparent;
}
.navitem > a:hover { color: var(--c-ink); }
/* Set by site.js from the scroll position, not by the build. Scoped with `>` so
   marking a parent does not also light up every child under it. */
.navitem.here > a {
  color: var(--c-accent);
  border-left-color: var(--c-accent);
}
.navitem ol .navitem > a {
  padding-left: 1.6rem;
  font-size: var(--size-tiny);
}

.sidefoot {
  margin-top: 2.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--c-hairline);
  font-size: var(--size-tiny);
  color: var(--c-ink-faint);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* ---- prose ------------------------------------------------------------- */
.pagehead { margin-bottom: 2.5rem; }
.eyebrow {
  margin: 0 0 0.5rem;
  font-size: var(--size-tiny);
  letter-spacing: 0.06em;
  color: var(--c-ink-faint);
}
h1, h2, h3, h4 {
  line-height: var(--line-tight);
  font-weight: 700;
  margin: 2.6rem 0 0.8rem;
}
.pagehead h1 { margin: 0; font-size: 1.85rem; }
.pagehead.front h1 { font-size: 2.1rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.08rem; }
h4 { font-size: 1rem; color: var(--c-ink-muted); }

/* The whole site is one page, so a section boundary is the only thing telling a
   reader they have finished something. Ordinary heading spacing is not enough at
   this length, hence the extra air and the rule. */
.sheet > h2 {
  margin-top: 5rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--c-hairline);
  clear: both;                 /* never start a section beside a floated aside */
}
/* Deliberately does NOT clear. An aside floats into the margin, and the whole
   point is that the prose keeps running beside it. Clearing an h3 past a tall
   aside leaves a hole the height of the aside in the middle of the reading
   column, which reads as a mistake. h2 clears because a new top-level section
   really should not start level with the previous one's marginalia. */
.sheet > h3 { margin-top: 3.2rem; }

.lede {
  margin: 0.8rem 0 0;
  color: var(--c-ink-muted);
  font-size: 1.06rem;
}
/* A section's blurb, sitting between its heading and its first paragraph. Under
   the page title the lede is the last thing in the header and needs no gap
   below it; here it is not. */
.sheet > h2 + .lede,
.sheet > h3 + .lede { margin: 0.5rem 0 var(--block); }

p, ul, ol, dl { margin: 0 0 var(--block); }
li { margin-bottom: 0.3rem; }

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

strong { font-weight: 700; }
hr {
  border: 0;
  border-top: 1px solid var(--c-hairline);
  margin: 2.5rem 0;
}
blockquote {
  margin: 0 0 var(--block);
  padding-left: 1.1rem;
  border-left: 2px solid var(--c-hairline-strong);
  color: var(--c-ink-muted);
}

code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--c-wash);
  padding: 0.08em 0.3em;
  border-radius: var(--radius);
}
a.coderef { text-decoration: none; border-bottom: 1px solid var(--c-hairline-strong); }
a.coderef code { background: var(--c-accent-soft); color: var(--c-accent); }
a.coderef:hover code { background: var(--c-accent); color: var(--c-sheet); }

/* ---- tables ------------------------------------------------------------ */
table {
  border-collapse: collapse;
  width: 100%;
  margin: 0 0 var(--block);
  font-size: var(--size-small);
}
th, td {
  text-align: left;
  padding: 0.45rem 0.7rem 0.45rem 0;
  border-bottom: 1px solid var(--c-hairline);
  vertical-align: top;
}
th {
  border-bottom: 1px solid var(--c-hairline-strong);
  font-weight: 700;
  font-size: var(--size-tiny);
  letter-spacing: 0.03em;
  color: var(--c-ink-muted);
}

/* ---- figures, and the frames that stand in for them -------------------- */
figure { margin: 2.2rem 0; }
figcaption {
  margin-top: 0.7rem;
  font-size: var(--size-small);
  line-height: 1.45;
  color: var(--c-ink-muted);
}
/* A drawn figure sits at the measure, so its left and right edges line up with the
   prose around it and the type inside it comes out at prose size. An instrument is
   the same: it is read as part of the argument, not as an exhibit beside it, and a
   viewer that ran past the measure into the margin column would be the only thing
   on the page that did. Code and tables still take the measure plus the margin.

   Not cleared, unlike the wide blocks above: a measure-width figure and an aside in
   the margin column do not overlap, so clearing would only open a gap. */
.sheet > figure.figure,
.sheet > figure.instrument {
  max-width: var(--column);
  clear: none;
}
/* A slot takes the same width as the thing it stands for, because a placeholder
   wider than the picture or viewer that will land there misreports the space there
   is to fill. */
.sheet > figure.slot-figure,
.sheet > figure.slot-interactive { max-width: var(--column); }
.figure img {
  display: block;
  width: 100%;
  height: auto;
}

/* A slot is a picture that has not been drawn yet. It carries its caption,
   because the caption is the claim the picture has to make, and having the claim
   on the page is most of the value of writing the page before the art. */
.slot-frame {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  align-items: center;
  justify-content: center;
  min-height: 9rem;
  padding: 1.5rem;
  border: 1px dashed var(--c-hairline-strong);
  border-radius: var(--radius);
  background: var(--c-wash);
  text-align: center;
}
.slot-kind {
  font-size: var(--size-tiny);
  letter-spacing: 0.08em;
  color: var(--c-ink-faint);
}
.slot-name { font-weight: 700; color: var(--c-ink-muted); }
.slot-hint {
  font-family: var(--font-mono);
  font-size: var(--size-tiny);
  color: var(--c-ink-faint);
}

.broken-block, code.broken {
  color: var(--c-flag);
  border: 1px solid var(--c-flag);
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius);
}

/* ---- code blocks ------------------------------------------------------- */
.codeblock { margin: 2rem 0; }
.codeblock > p {
  margin: 0 0 0.6rem;
  font-size: var(--size-small);
  color: var(--c-ink-muted);
}
.codeblock .hl {
  margin: 0;
  padding: 0.9rem 1.1rem;
  overflow-x: auto;
  background: var(--c-wash);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius);
}
/* The frame belongs to .hl, which is the element with the padding. Resetting the
   pre explicitly matters because the fenced-block rule further down also matches
   this pre by descent, and two nested frames is what that looked like. */
.codeblock pre {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-mono);
  font-size: var(--size-code);
  line-height: 1.5;
}
.codeblock code { background: none; padding: 0; }
/* Real line numbers, matching the file, so the caption's line range is checkable
   against what is on screen. */
.codeblock .linenos {
  color: var(--c-ink-faint);
  padding-right: 1rem;
  user-select: none;
}
.codeblock figcaption {
  font-family: var(--font-mono);
  font-size: var(--size-tiny);
}
.codeblock figcaption a { text-decoration: none; color: var(--c-ink-faint); }
.codeblock figcaption a:hover { color: var(--c-accent); }

/* Fenced blocks written directly in the markdown, as opposed to extracted. */
.sheet pre {
  background: var(--c-wash);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius);
  padding: 0.9rem 1.1rem;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--size-code);
  line-height: 1.5;
  margin: 0 0 var(--block);
}
.sheet pre code { background: none; padding: 0; }

/* ---- pseudocode float -------------------------------------------------- */
/* Rules above and below and nothing at the sides, which is what the algorithm
   floats in the paper do. */
.algorithm {
  border-top: 1.5px solid var(--c-ink);
  border-bottom: 1.5px solid var(--c-ink);
  padding: 0.8rem 0 1rem;
  margin: 2.2rem 0;
}
.algorithm > figcaption {
  order: -1;
  margin: 0 0 0.7rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--c-hairline);
  color: var(--c-ink);
  font-weight: 700;
  font-size: var(--size-small);
}
.algorithm { display: flex; flex-direction: column; }
.algorithm ol {
  margin: 0;
  padding-left: 2.2rem;
  font-size: var(--size-small);
  line-height: var(--line-tight);
  list-style: none;
  counter-reset: step;
}
.algorithm li {
  counter-increment: step;
  position: relative;
  margin: 0.28rem 0;
}
.algorithm li::before {
  content: counter(step);
  position: absolute;
  left: -2.2rem;
  width: 1.6rem;
  text-align: right;
  color: var(--c-ink-faint);
  font-size: var(--size-tiny);
}
.algorithm code { background: none; padding: 0; font-size: 0.92em; }

/* ---- asides ------------------------------------------------------------ */
/* On a wide screen an aside floats into the margin the sheet reserves for it.
   Below that width it becomes an indented block, which is the same content in
   the same reading order -- the float is decoration, not structure. */
.aside {
  font-size: var(--size-small);
  line-height: 1.5;
  color: var(--c-ink-muted);
  border-left: 2px solid var(--c-accent);
  padding-left: 1rem;
  margin: 1.6rem 0;
}
.aside p:last-child { margin-bottom: 0; }
.aside-label {
  font-size: var(--size-tiny);
  letter-spacing: 0.05em;
  color: var(--c-accent);
  margin-bottom: 0.3rem !important;
}

/* Wide enough for the sheet to have opened up its margin column. The aside then
   floats into space that already exists, so nothing overflows and the block-level
   fallback above is what narrow screens keep.

   The number is the sheet at full width plus the sidebar and the gutters -- if the
   measure changes in theme.py, this moves with it, or the float starts eating the
   measure instead of the margin. */
@media (min-width: 1280px) {
  .sheet > .aside {
    float: right;
    clear: right;
    width: var(--margin-note);
    max-width: var(--margin-note);
    margin: 0.3rem 0 1.4rem 1.5rem;
    border-left: 0;
    border-top: 1px solid var(--c-hairline-strong);
    padding: 0.6rem 0 0;
  }
}

/* ---- key ---------------------------------------------------------------- */
/* The counterpart to an aside. An aside is what you may skip, so it goes to the
   margin and greys out; this is what you must not, so it stays in the measure,
   keeps full-strength ink, and is the only block drawn in the accent. Reserved
   for the handful of claims the whole page is built on -- one of these per
   section is a lot, and a page where everything is boxed emphasises nothing. */
.key {
  margin: 2rem 0;
  padding: 1rem 1.2rem;
  border: 1px solid var(--c-accent);
  border-left-width: 3px;
  border-radius: var(--radius);
  background: var(--c-accent-soft);
}
.key > p:last-child { margin-bottom: 0; }
.key-label {
  font-size: var(--size-tiny);
  letter-spacing: 0.05em;
  font-weight: 700;
  color: var(--c-accent);
  margin-bottom: 0.4rem !important;
}

/* ---- details ----------------------------------------------------------- */
.detail {
  margin: 1.8rem 0;
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius);
  padding: 0 1.1rem;
  background: var(--c-wash);
}
.detail > summary {
  cursor: pointer;
  padding: 0.7rem 0;
  font-size: var(--size-small);
  color: var(--c-accent);
  font-weight: 700;
}
.detail[open] > summary { border-bottom: 1px solid var(--c-hairline); }
.detail > div { padding: 0.9rem 0 0.3rem; font-size: var(--size-small); }

/* ---- palette key ------------------------------------------------------- */
.palette .legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  font-size: var(--size-tiny);
}
.swatch { display: inline-flex; align-items: center; gap: 0.4rem; }
.swatch i {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 2px;
  flex: 0 0 auto;
}

/* ---- instruments ------------------------------------------------------- */
/* An embedded viewer, at the measure like a figure. A heavy one keeps its iframe
   out of the markup -- a payload is the largest thing on any page it appears on,
   and a reader scrolling past should not pay for it -- and the load button says
   what it costs before they do. Under config.AUTOLOAD_BYTES the frame is just
   there, so the stage holds an iframe from the start and no button at all. */
.instrument {
  margin: 2.4rem 0;
  border: 1px solid var(--i-hairline);
  border-radius: var(--radius);
  background: var(--i-sheet);
  overflow: hidden;
}
.instrument-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--i-hairline);
  background: var(--i-bg);
}
.instrument-title {
  font-weight: 700;
  font-size: var(--size-small);
  color: var(--i-ink);
}
.instrument-blurb { font-size: var(--size-tiny); color: var(--i-ink-muted); }

.instrument-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--i-bg);
}
.instrument-stage .poster {
  display: block;
  width: 100%;
  height: auto;
}
/* Without a poster the card is a blank panel, so it is deliberately shorter than
   the loaded viewer -- an empty box the size of the real thing reads as something
   that failed to load rather than something waiting to be asked for. */
.instrument-stage .poster.empty { min-height: 13rem; width: 100%; }
/* The height is set to what the viewer's own drawing wants at the measure. A
   viewer scales its svg to fit, so a box taller than the drawing does not make it
   bigger, it just opens a band of empty background above and below it. */
.instrument-stage iframe {
  display: block;
  width: 100%;
  height: 29rem;
  border: 0;
}

.instrument .load {
  position: absolute;
  display: inline-flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.55rem 1.1rem;
  font-family: var(--font);
  font-size: var(--size-small);
  cursor: pointer;
  color: var(--i-ink);
  background: var(--i-sheet);
  border: 1px solid var(--c-accent);
  border-radius: var(--radius);
}
.instrument .load:hover { background: var(--c-accent); color: var(--c-sheet); }
.instrument .load .size { font-size: var(--size-tiny); opacity: 0.7; }

.instrument-foot {
  padding: 0.5rem 1rem;
  border-top: 1px solid var(--i-hairline);
  background: var(--i-bg);
  font-size: var(--size-tiny);
}
.instrument-foot a { color: var(--i-ink-muted); text-decoration: none; }
.instrument-foot a:hover { color: var(--c-accent); }
.instrument > figcaption { padding: 0 1rem 0.9rem; margin-top: 0.7rem; }

/* ---- summary and contents ---------------------------------------------- */
/* The standfirst. Set a notch larger than the body and in muted ink, so it reads
   as the thing before the writing rather than the first paragraph of it. */
.summary {
  margin: 2.5rem 0 0;
  font-size: 1.03rem;
  color: var(--c-ink-muted);
}
.summary > p:last-child { margin-bottom: 0; }

/* The same outline as the sidebar, boxed and compact. It is not redundant: the
   sidebar drops below the writing on a narrow screen, and a reader arriving at
   the top should see the whole shape before committing to the scroll.
   Deliberately small -- it is a map, and a map you have to scroll is not doing
   its job. Blurbs live in the prose, not here. */
.contents {
  margin: 2.5rem 0 1rem;
  padding: 1rem 1.3rem 1.2rem;
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius);
  background: var(--c-wash);
  font-size: var(--size-small);
}
.contents-head {
  margin: 0 0 0.2rem;
  font-size: var(--size-tiny);
  letter-spacing: 0.07em;
  color: var(--c-ink-faint);
}
.contents-part {
  font-size: var(--size-tiny);
  letter-spacing: 0.06em;
  color: var(--c-ink-faint);
  margin: 0.9rem 0 0.25rem;
}
.contents-list, .contents-sub { list-style: none; padding: 0; margin: 0; }
.contents-list > li { margin: 0.1rem 0; }
.contents-list a {
  text-decoration: none;
  line-height: 1.45;
  display: inline-block;
  padding: 0.08rem 0;
}
.contents-list a:hover { text-decoration: underline; }
/* Indented past the parent's text, with a rule to carry the eye down the group. */
.contents-sub {
  margin-left: 0.2rem;
  padding-left: 0.8rem;
  border-left: 1px solid var(--c-hairline-strong);
}
.contents-sub a { color: var(--c-ink-muted); font-size: var(--size-tiny); }

/* ---- narrow ----------------------------------------------------------- */
@media (max-width: 860px) {
  /* align-items: flex-start is what the two-column layout wants, but stacked it
     makes each child shrink-to-fit, and a code block's max-content is wider than
     a phone. The result is horizontal overflow of the whole document rather than
     a code block that scrolls on its own, which is what .hl already does. */
  .shell { flex-direction: column; align-items: stretch; }
  main { width: 100%; }
  .sidebar {
    position: static;
    width: 100%;
    flex: none;
    height: auto;
    border-right: 0;
    border-bottom: 1px solid var(--c-hairline);
    padding: 1.6rem 1.4rem;
  }
  main { padding: 0; }
  .sheet { padding: 2.2rem 1.3rem 3.5rem; border: 0; }
  .sheet > *, .sheet > figure { max-width: 100%; }
  /* The sidebar is already sitting at the top of the document here, so the
     contents block would be the same outline twice in a row. */
  .contents { display: none; }
  .sheet > h2 { margin-top: 3.2rem; }
}
