/* 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 five share one scale, and that is the whole rule.** The widest of them — the
   Airport schema — is drawn exactly as wide as the prose measure, and every other diagram
   takes the same factor, so they come out narrower in proportion to how much less they
   have to draw. Type, box heights and stroke are therefore identical across all five, and
   only the widths differ.

   The alternative — sizing each diagram to the measure — is what makes a set stop being
   one: a three-node triangle would carry the same width as a seven-node schema and set
   its type half again as large. Nothing here enforces that; it is a property of the
   markup's generator, `system/tools/changing-the-frame-diagrams.py`, which derives the one
   scale from the widest diagram and splices all five into the post with `--write`. The
   check is that a node label measures the same number of pixels in every diagram on the
   page. The generator also holds one `PITCH` for a row of boxed nodes, so the two diagrams
   that draw Coupon → Market → Ticket are one width; they were 609px and 560px.

   THE SHRINK HAS TO BE ONE FACTOR TOO, and `max-width: 100%` alone is not. It shrinks
   only the diagrams wider than the measure, each by its own amount, so at a laptop's
   window the three small ones stayed at 1.238 while the two wide ones went to 1.17 and
   1.09, and the labels ran from 14px to 16px on one page — the same defect as sizing each
   to the measure, arriving from the narrow side. So every diagram carries its share of the
   widest, `--diagram-share`, viewBox width over the widest's 532 units, written beside the
   viewBox it derives from, and its width is that share of whatever the widest gets: the
   measure when the measure is narrower than the widest's natural size, the natural size
   otherwise. The custom property is the one kind of style attribute the site allows — a
   number the markup computed and the stylesheet consumes (CLAUDE.md § Style) — and it
   is unbounded, which is why it is not a class. Measured: 1.238 on all five at 1440 and
   900, 1.085 on all five at 1100, 0.630 on all five at 375.

   Nothing overhangs any more, so there is no rule here for it. An earlier factor put the
   RPCarrier diagram's edges on the blockquote's text and let the widest hang into the
   spacer; the two cannot both hold, because one scale cannot land that diagram on 622.7px
   and keep the Airport diagram inside 658.7px at the same time. */

.diagram {
  display: block;
  /* The widest diagram's natural width — its `width` attribute, the drawing at the shared
     scale. One number, stated once: what every diagram is a share of. Scaling *up* past it
     would enlarge the type with it, which is what the min() refuses. */
  --diagram-measure: 658.7px;
  width: calc(min(100%, var(--diagram-measure)) * var(--diagram-share, 1));
  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);
}
