/* Commutative diagrams for /changing-the-frame/
   ─────────────────────────────────────────────────────────────────
   Loaded by this page alone, via `styles:` in its frontmatter — the same
   arrangement /styleguide/ uses for its specimen CSS. Nothing here is true of
   the publication generally, so none of it belongs in base.css.

   The diagrams are inline <svg>, not <img>, and that is the whole reason this
   file can be short. An <img> is an isolated document: it inherits no custom
   property and no font from the page, so each of the five would have had to
   carry the ink colour copied out as a literal and its own @font-face — five
   more places for the ramp to drift, and five font payloads. Inline, they
   inherit both, and the only thing to declare is which stop of the ramp each
   part of a diagram takes.

   Geometry is not here. Box widths are computed from PT Mono's exact 0.6em
   advance, so the font size *is* geometry and is written into the markup as a
   presentation attribute alongside the coordinates it determined. A font size
   in this file would be a second copy of a number the markup already depends
   on. Same reason the natural width and height are attributes: the diagram
   renders at the size it was drawn for and only ever shrinks.

   **The markup also carries fill, stroke and font-family, and those are not
   redundant with the rules below — do not delete them.** The RSS feed takes
   the compiled body verbatim and loads no stylesheet at all, and SVG's own
   defaults there are `fill: black; stroke: none` on every shape: each entity
   box would be a solid black slab with its text invisible inside it, and every
   arrow, which is a stroked path with no fill, would disappear entirely. The
   attributes give the feed `currentColor` and a generic mono, so a diagram
   reads in a reader app and follows its text colour into dark mode. A
   presentation attribute loses to *any* CSS rule, so on the page the ramp
   below still wins — verified in both directions rather than assumed. It costs
   about 5KB across the five, which is the whole price of the feed being
   legible.

   Every mark PT lacks is drawn rather than set — the check mark, the centred
   ellipsis, the second arrowhead on a surjection. PT has none of U+2713,
   U+22EF or U+21A0, checked in the cmaps of all seven faces, and a character
   the face lacks is the silent fallback CLAUDE.md § *The fonts are
   self-hosted* warns about. Drawn, they also cost the subsetting pass
   nothing: the only text in these diagrams is ASCII identifiers, every one
   of which appears in the prose as a code chip anyway. */

/* **The widest diagram is wider than the prose, on purpose, and this is what lets it be.**
   The five share one scale so they read as a set, and that scale is set by the RPCarrier
   diagram: at 1.2744 its *drawn* edges land on the blockquote's text, 623.4px against a
   622.7px target and 0.4px off the text's own left edge. The Airport diagram is naturally
   wider and draws 673.4px against a 658.7px measure, so it hangs 7.4px past each side.

   The alternative was to cap the scale at whatever keeps the Airport diagram inside the
   measure, which costs the other four the alignment for the sake of one. So the figure
   reaches into the spacer instead. Not `wide`, which is the vocabulary for reaching over
   the *band* and is 1053px here — a 673px drawing centred in that sits 190px right of its
   neighbours and stops being one of them. This is the smallest thing that works: a few
   pixels of overhang each side, the diagram still centred on the prose's own axis like
   the other four.

   The overhang is stated once rather than derived, because the two numbers it sits between
   — the scale and the widest diagram's natural width — both live in the markup, and a
   `calc()` here could only restate them. 12px each side against a 7.4px need leaves room
   to retune either without coming back here. */
.article-body figure:has(> .diagram) {
  --diagram-overhang: 12px;
  margin-inline: calc(-1 * var(--diagram-overhang));
}

.diagram {
  display: block;
  /* Drawn at its natural size and shrunk to fit a narrow screen — the
     responsive-image pattern, and the reason for the width/height pair in the
     markup. Scaling *up* to the measure would enlarge the type with it and put
     a 14px label at 19px on one diagram and 16px on another. */
  max-width: 100%;
  height: auto;
  margin: 0 auto;
}

/* An entity type is a box; the ramp's structural neutral, the same stop a table
   hairline and a section rule take. */
.diagram .box {
  fill: none;
  stroke: var(--color-rule);
  stroke-width: 1.25;
}

/* A morphism. Ink-mute, one stop back from the prose: the arrows are the
   diagram's connective tissue and the names in the boxes are what is being
   read. */
.diagram .arrow {
  fill: none;
  stroke: var(--color-ink-mute);
  stroke-width: 1.25;
}

.diagram .head {
  fill: var(--color-ink-mute);
  stroke: none;
}

/* A check mark says "this square commutes" and an ellipsis stands in for the
   attributes not drawn. Both are annotations on the diagram rather than part
   of it, so both take ink-dim — the stop a subsection number takes, for the
   same reason: an address, not a word in the sentence. */
.diagram .check {
  fill: none;
  stroke: var(--color-ink-dim);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.diagram .dot {
  fill: var(--color-ink-dim);
}

/* Identifiers set in the publication's own mono, which is what makes a
   `Coupon` in a diagram, a `Coupon` in prose and a \mathtt{Coupon} in a
   formula the same word. */
.diagram text {
  font-family: var(--text-family-mono);
  fill: var(--color-ink);
}

.diagram .edge {
  fill: var(--color-ink-mute);
}
