/* obelisk-lite — HTML output.
 *
 * Typst emits the markup, and one inline `style` carrying the theme, the
 * configuration and the design geometry. Everything below is either one of those
 * properties or the reading size derived from them; this file holds no number the
 * package does not own.
 *
 * Print output is Typst's job; there is deliberately no @media print block. */

.obelisk {
  /* The reading size: a viewport ratio, floored for legibility and ceilinged so
   * the column stops growing on very wide screens. `--ob-size` is the print size,
   * which still nudges the screen type. */
  --ob-reading-size: clamp(1rem, calc(0.15 * var(--ob-size, 11pt) + 1.2vw), 1.55rem);

  /* Typst's `par(spacing:)`: the gap it leaves between two blocks, which is
   * `1.5 * (step + ascender) - ascender`. */
  --ob-block-gap: calc(1.5 * var(--ob-step) - 0.5 * var(--ob-ascender));

  /* The same gap for blocks that are not lines of text. A line box carries the
   * ascender, so text gives back half of it; a theorem environment is a box, so
   * it takes the spacing whole — which is what leaves the stroke bars a gap the
   * width the paged output gives them. */
  --ob-env-gap: calc(1.5 * var(--ob-step) + 0.5 * var(--ob-ascender));

  /* Screen-only: without page breaks a section needs a break of its own, over
   * and above the room its emblem hangs into. */
  --ob-chapter-gap: calc(3 * var(--ob-env-gap));

  /* Screen-only tuning: the gutter a marker has to clear is a print measure that
   * does not grow with the screen type. */
  --ob-marker-scale: 0.7;

  /* Half the distance between the viewport edge and the text column; zero once
   * the viewport is narrower than the column. */
  --ob-gutter: max(0px, calc((100% - var(--ob-paper-w)) / 2));

  /* The page. The theme colour covers the whole viewport, and the text column is
   * centered in it, at most `--ob-paper-w` wide, with the margin column living
   * in its left padding. The top padding carries the first baseline's grid step,
   * as the paged top margin does. */
  position: relative;
  isolation: isolate;
  overflow: hidden;
  box-sizing: border-box;
  min-height: 100vh;
  padding: calc(var(--ob-margin-t) + var(--ob-step))
    calc(var(--ob-gutter) + var(--ob-margin-s)) var(--ob-margin-f)
    calc(var(--ob-gutter) + var(--ob-margin-e));
  background: var(--ob-page);
  color: var(--ob-text);
  /* The package's body face, then its design relatives: XCharter is a Charter, so
   * Charter and Charis SIL are the nearest substitutes, and Charis SIL is the one a
   * reader is most likely to have besides it. */
  font-family: var(--ob-font-body), Charter, "Charis SIL", "Bitstream Charter", Georgia,
    serif;
  font-size: var(--ob-reading-size);
  line-height: var(--ob-pitch);
  text-align: justify;
}

/* Mathematics. A browser has a math face of its own and uses it unless one is named,
 * which reads as a second voice in the text around it. The design names one — a font
 * with an OpenType math table, so fences and radicals stretch — and the faces a reader
 * is most likely to have follow it: STIX Two Math, then Latin Modern Math, then the
 * browser's own `math`, which is what a machine with none of them is left with. */
.obelisk math {
  font-family: var(--ob-font-math), "STIX Two Math", "Latin Modern Math", math, serif;
}

/* The margin rule. Painted under the content, and the paged version's eraser
 * patch behind the page number has nothing to do here. */
.obelisk::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0 auto 0 calc(var(--ob-gutter) + var(--ob-margin-e) - var(--ob-half-gutter));
  width: var(--ob-rule);
  background: var(--ob-line);
}

/* Prose. A paragraph takes the flow gap from whatever precedes it — another
 * paragraph, a theorem's bar, a proof. A heading brings its own gap, and nothing
 * needs one at the top. */
.obelisk p {
  margin: var(--ob-block-gap) 0 0;
}

.obelisk .h + p,
.obelisk p:first-child {
  margin-top: 0;
}

/* Section headings: the emblem, the kicker, the title.
 *
 * The emblem is a 7cm square whose top-left corner sits `--ob-emblem-overhang`
 * outside the text column, hanging into the page's top margin the way the
 * divider page hangs it off the paper corner. */
.obelisk .h[data-level="1"] {
  margin: calc(var(--ob-margin-t) + var(--ob-step) + var(--ob-emblem-overhang) +
      var(--ob-chapter-gap)) 0 calc(var(--ob-lead) * var(--ob-step));
  padding-top: calc(4 * var(--ob-lead) * var(--ob-step));
  font-family: var(--ob-font-sans), "HK Grotesk", system-ui, Roboto, "Helvetica Neue",
    Arial, sans-serif;
  font-weight: 400;
}

.obelisk .section {
  position: relative;
  display: block;
  text-align: right;
  font-size: calc(var(--ob-title-scale) * 1em);
  /* The divider page sets its title with `set par(leading: lead * step)`, so a line
   * of it advances by that leading plus an ascent at `title-scale` times the body
   * size. A unitless line-height would do the opposite — multiply the whole advance
   * by the type size, which is where the loose lines came from. */
  line-height: calc(var(--ob-lead) * var(--ob-step) + var(--ob-title-scale) * var(--ob-ascender));
}

/* The first section hangs its emblem into the paper's own top margin, exactly as
 * the divider page does. Later sections reserve the space with their margin. */
.obelisk > .h[data-level="1"]:first-child {
  margin-top: 0;
}

.obelisk .section__kicker {
  display: block;
  font-weight: 300;
}

.obelisk .section__name {
  display: block;
  font-weight: 800;
}

/* The outline's title keeps the section's type treatment but has no emblem to
 * reserve space for. */
.obelisk .h.outline-title {
  margin-block: calc(2 * var(--ob-step));
  padding-top: 0;
}

.obelisk .section__emblem {
  position: absolute;
  left: calc(-1 * (var(--ob-margin-e) + var(--ob-emblem-overhang)));
  top: calc(
    -1 * (var(--ob-margin-t) + var(--ob-step) + var(--ob-emblem-overhang) + 4 *
      var(--ob-lead) * var(--ob-step))
  );
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: var(--ob-emblem);
  height: var(--ob-emblem);
  border: var(--ob-emblem-border) solid var(--ob-line);
  background: var(--ob-page);
  font-size: var(--ob-emblem-font);
  line-height: 1;
}

/* The numeral is stroked and then filled in the page colour, so the fill covers
 * the stroke's inner half and the glyph reads as an outline. `paint-order` is
 * what makes that work: painting the stroke over the fill instead doubles its
 * weight and swaps thin parts of the glyph, such as the flag of a `1`. */
.obelisk .section__num {
  color: var(--ob-page);
  -webkit-text-stroke: var(--ob-emblem-stroke) var(--ob-line);
  paint-order: stroke fill;
}

/* Subheadings: a marker centered on the margin rule, then the numbered text. */
.obelisk .h {
  text-align: left;
  /* Hanken Grotesk's free relative, then whatever sans the reader's system has. */
  font-family: var(--ob-font-sans), "HK Grotesk", system-ui, Roboto, "Helvetica Neue",
    Arial, sans-serif;
  font-weight: 700;
  line-height: var(--ob-pitch);
  /* The tag is Typst's — a level-1 heading opens as an `h2` — and a browser's own
   * heading sizes are not the design's: `h2` is `1.5em`, so the section title came
   * out half again as large as `--ob-title-scale` asks for. Every level is the
   * reading size unless its own rule scales it. */
  font-size: 1em;
}

.obelisk .h[data-level="2"] {
  margin: calc(var(--ob-h2-gap-above) * var(--ob-step)) 0
    calc(var(--ob-h2-gap-below) * var(--ob-step));
  font-size: calc(var(--ob-h2-size) * 1em);
  /* Typst's `set par(leading: step)` at this level's size: one step of leading plus
   * an ascent measured at the type it is set in. `--ob-pitch` is the same sum for
   * the body — using it here would scale the leading with the type as well. */
  line-height: calc(var(--ob-step) + var(--ob-h2-size) * var(--ob-ascender));
}

.obelisk .h[data-level="3"] {
  margin: calc(var(--ob-h3-gap-above) * var(--ob-step)) 0
    calc(var(--ob-h3-gap-below) * var(--ob-step));
  font-size: calc(var(--ob-h3-size) * 1em);
  line-height: calc(var(--ob-step) + var(--ob-h3-size) * var(--ob-ascender));
}

/* Lists indent their items' boxes, and a float is placed against the box it sits in:
 * margin furniture inside a list item lands that much inside the text column. The
 * indent is the engine's own default, written out because the pull has to name it;
 * nothing else about how a list looks is decided yet. */
.obelisk ol,
.obelisk ul {
  --ob-list-indent: 40px;
  padding-inline-start: var(--ob-list-indent);
}

/* Margin furniture. Everything that lives left of the body shares one geometry:
 * a float as wide as the sidenote column, pulled out by its own width plus the
 * gutter, so its right edge lands on the margin rule — and by the indent of any
 * list it sits in, since that is where its own box starts. */
.obelisk .sidenote,
.obelisk .thm__label,
.obelisk .marker {
  box-sizing: border-box;
  float: left;
  width: var(--ob-side-w);
  margin-left: calc(
    -1 * (var(--ob-side-w) + var(--ob-half-gutter) + var(--ob-list-indent, 0px))
  );
}

.obelisk .marker {
  margin-top: var(--ob-dy);
  text-align: right;
  /* A heading marker is a glyph, so the math face suits it; `math` is the reader's
   * own, for when the package's is not installed. */
  font-family: var(--ob-font-math), "STIX Two Math", "Latin Modern Math", math, serif;
  font-weight: 400;
}

/* `place-node` shifts the glyph by half its own width to center it on the rule.
 * The marker is scaled down from the heading size: the gutter it has to fit in
 * does not grow with the type the way the print gutter does. */
.obelisk .marker__sym {
  display: inline-block;
  font-size: calc(var(--ob-marker-scale) * 1em);
  transform: translateX(50%);
}

.obelisk .sidenote {
  /* The first baseline has to land on the baseline of the line the note is
   * anchored to. A float's top sits on the line box's top, so the note is pushed
   * down by the difference between the two first-baseline offsets: the body's is
   * `(step - descent) / 2 + ascent`, the note's is
   * `(step * small-scale - small-size * descent) / 2 + small-size * ascent`, and
   * the difference is what is written here. */
  margin-top: calc(
    var(--ob-step) * (1 - var(--ob-small-scale)) / 2 - var(--ob-descent) *
      (1 - var(--ob-small-size)) / 2 + var(--ob-ascender) *
      (1 - var(--ob-small-size)) + var(--ob-dy)
  );
  padding: 0 var(--ob-half-gutter);
  font-size: calc(var(--ob-small-size) * 1em);
  line-height: calc(var(--ob-ascender) * var(--ob-small-size) + var(--ob-step) *
      var(--ob-small-scale));
  text-align: right;
}

/* Theorems: a bar in the gutter and a label in the margin. The bar is a painted
 * child of the environment, so it spans the label as well as the body — which is
 * what many pages show — and overshoots the block by half a grid step on each
 * side. It sits on the margin rule rather than beside it, half of it crossing to
 * the left, and the text stays on the body edge. */
.obelisk .thm {
  position: relative;
  /* Both sides, so the gap to whatever follows is settled by margin collapsing
   * rather than by guessing which neighbour it is. */
  margin: var(--ob-env-gap) 0;
}

/* The bar is centered on the margin rule. The rule is stroked on its own centre,
 * so the bar's centre has to be moved by half the rule as well as half itself. */
.obelisk .thm::before {
  content: "";
  position: absolute;
  inset: calc(var(--ob-step) / -2) auto calc(var(--ob-step) / -2)
    calc(-1 * (var(--ob-half-gutter) + (var(--ob-bar) - var(--ob-rule)) / 2));
  width: var(--ob-bar);
  background-image: var(--ob-paint);
}

.obelisk .thm__label {
  padding: 0 var(--ob-half-gutter);
  text-align: right;
  font-family: var(--ob-font-sans), "HK Grotesk", system-ui, Roboto, "Helvetica Neue",
    Arial, sans-serif;
  color: var(--ob-label);
}

.obelisk .thm__title {
  display: block;
  font-weight: 400;
}

/* Proofs. The head floats so the text runs on from it, as the paged version sets
 * the text after its `Proof.` run-in; only the first line is shortened, since
 * the float is exactly one line tall. The block itself is a flow root so that
 * head is contained — a folded proof is one `Proof.` line and nothing else. */
.obelisk .proof {
  position: relative;
  display: flow-root;
  margin: var(--ob-block-gap) 0;
}

/* The head is a control, so it is a button reset to read as text, and the gap to
 * the text is a word space. It floats so the text runs on from it, and it is
 * pulled out over the margin rule by the gutter it holds the toggle in — so the
 * word itself still starts on the body edge, as the paged output sets it.
 *
 * `position` and `z-index` are load-bearing. The body's box wraps around the
 * float but still covers it, so without them painting and hit-testing land on the
 * body, and the control cannot be clicked once there is text to cover it. */
.obelisk .proof__head {
  position: relative;
  z-index: 1;
  float: left;
  box-sizing: border-box;
  margin-left: calc(-1 * var(--ob-half-gutter));
  margin-right: 0.25em;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0 0 0 var(--ob-half-gutter);
}

/* Out of flow rather than floated to the last line: the body is not a block
 * formatting context (it must not be one, or the text would sit beside the head
 * instead of wrapping around it), so a float here would escape the body and hold
 * a folded proof open by the height of a line that is no longer there. Pinned to
 * the block's end corner, it lands on the last line either way, and leaves with
 * the body when the body is folded. */
.obelisk .proof__body::after {
  content: "\25A1";
  position: absolute;
  inset-inline-end: 0;
  inset-block-end: 0;
}

/* References and links.
 *
 * A reference in running prose is the text it sits in — the page's colour lives in
 * the margin, where the labels and bars are, not in a sentence — so it is marked by
 * a rule rather than by a colour: a solid underline, half again as thick as the
 * page's hairlines so it reads as a mark rather than as a mis-set rule, in the
 * colour of those hairlines, darkening when the reader reaches for it. It stays
 * visible rather than appearing on hover: a reference is meant to be found by
 * reading, which is the one thing a hover state cannot do.
 *
 * Paper has no notion of a link that leaves, but a screen does, so those carry the
 * arrow the reader expects — set in the math face, the same way the margin markers
 * are, because it is the face guaranteed to have one. */
.obelisk a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-color: var(--ob-line);
  text-decoration-thickness: calc(1.5 * var(--ob-rule));
  text-underline-offset: 0.18em;
}

.obelisk a:hover,
.obelisk a:focus-visible {
  text-decoration-color: var(--ob-strong);
}

.obelisk a[href^="http"]::after,
.obelisk a[href^="mailto:"]::after {
  content: "\2197";
  font-family: var(--ob-font-math), "STIX Two Math", "Latin Modern Math", math, serif;
  font-size: 0.7em;
  margin-left: 0.15em;
  vertical-align: 0.25em;
  color: var(--ob-muted);
}

/* Outline. The paged template makes each entry a link with a leader rule, a grid
 * step above level 1 and an indent for every deeper level; the same treatment
 * applies here, minus the page numbers, which mean nothing without pages. */
.obelisk nav[role="doc-toc"] ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

.obelisk nav[role="doc-toc"] li {
  margin: 0;
}

/* Deeper levels indent, as `h(1.5em * (level - 2))` does. */
.obelisk nav[role="doc-toc"] ol ol {
  padding-left: 1.5em;
}

.obelisk nav[role="doc-toc"] > ol > li {
  margin-top: var(--ob-step);
  font-weight: 700;
}

.obelisk nav[role="doc-toc"] a {
  display: flex;
  align-items: baseline;
  gap: 0.5em;
  color: inherit;
  text-decoration: none;
}

/* The leader `outline.entry(fill:)` runs between the title and the page number. */
.obelisk nav[role="doc-toc"] a::after {
  content: "";
  flex: 1;
  border-bottom: max(1px, var(--ob-outline-rule)) solid var(--ob-line);
  transform: translateY(-0.25em);
}

.obelisk nav[role="doc-toc"] a:hover {
  text-decoration: underline;
}

/* The catalogue: the same treatment, with the collections as its level 1 and the
 * notes in each of them indented under it. Two things have to be said that an
 * outline has no use for — the group's line is not a link, so it is not bold by
 * inheritance but by being the level above (and the entries below it take their
 * weight back), and an entry carries a second link, the PDF. */
.obelisk nav[role="doc-toc"].catalogue .catalogue__group ol {
  font-weight: 400;
}

.obelisk nav[role="doc-toc"].catalogue ol ol > li {
  display: flex;
  align-items: baseline;
  gap: 0.6em;
}

/* The title takes the room the leader stretches into; the download sits at the end
 * of the line the leader has crossed. */
.obelisk .catalogue__note {
  flex: 1;
  min-width: 0;
}

.obelisk .catalogue__pdf {
  flex: none;
  font-family: var(--ob-font-sans), system-ui, sans-serif;
  font-size: calc(var(--ob-small-size) * 1em);
  letter-spacing: 0.04em;
  color: var(--ob-muted);
  text-decoration: none;
}

/* The leader is the title's, and a download has nothing to lead to. */
.obelisk nav[role="doc-toc"] .catalogue__pdf::after {
  content: none;
}

.obelisk .catalogue__pdf:hover,
.obelisk .catalogue__pdf:focus-visible {
  color: var(--ob-strong);
}

/* The theme colour comes from the wrapper, which spans the viewport; the page
 * itself only needs its own margin cleared. */
body {
  margin: 0;
}

/* Below the column's width the margin column cannot exist, so the notes, labels
 * and markers fold back into the flow and the page goes fluid. */
@media (max-width: 54rem) {
  .obelisk {
    --ob-gutter: 0px;
    padding: 1.5rem;
  }

  .obelisk::before {
    display: none;
  }

  .obelisk .sidenote,
  .obelisk .thm__label,
  .obelisk .marker {
    float: none;
    width: auto;
    margin: 0;
    padding: 0;
    text-align: left;
  }

  .obelisk .marker__sym {
    transform: none;
  }

  .obelisk .thm__label {
    margin-bottom: var(--ob-step);
  }

  /* No room for an emblem on a phone, so a section opens as a plain heading with
   * a chapter break and nothing more. */
  .obelisk .h[data-level="1"] {
    margin-block: var(--ob-chapter-gap) 0;
    padding-top: 0;
  }

  .obelisk .section {
    font-size: calc(var(--ob-title-scale) * 0.6em);
    text-align: left;
  }

  .obelisk .section__emblem {
    display: none;
  }
}
