/* Hand-written layer. The class rules that carry the design's repeated inline
   styles are generated by tools/build_site.py and appended after this file, so
   anything here can be overridden by them if the selectors ever collide.

   @font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/plex-mono-400.08f580a2.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/plex-mono-500.a3c2ae5d.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:300;font-display:swap;src:url(/assets/fonts/plex-sans-300.33efd2d1.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/plex-sans-400.56068293.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/plex-sans-500.fc86dbdc.woff2) format('woff2')} is replaced at build time with @font-face rules pointing at the
   content-hashed woff2 files, because those filenames change whenever a font
   changes. */

@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/plex-mono-400.08f580a2.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/plex-mono-500.a3c2ae5d.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:300;font-display:swap;src:url(/assets/fonts/plex-sans-300.33efd2d1.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/plex-sans-400.56068293.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/plex-sans-500.fc86dbdc.woff2) format('woff2')}

/* Carried over verbatim from the design's <style> block. */
body { margin: 0; background: #05070a; }
a { color: #7fc4d0; text-decoration: none; border-bottom: 1px solid #2b4a52; }
a:hover { color: #a9dbe4; border-bottom-color: #5f97a4; }
::selection { background: #1d3a44; color: #eaf4f7; }
input, textarea, button { font-family: inherit; }

/* Bypass Blocks. Both languages carried the string from the first build and
   no page rendered a link, so a keyboard reader crossed the nav strip on every
   page: four controls before <main> on the home page, two elsewhere.

   Parked off the left edge rather than clipped, and the difference is not
   cosmetic. The fidelity harness collects every laid-out element holding text
   and skips anything parked past the viewport's left or top edge, the same
   rule that hides the honeypot. Measured both ways at 1440: off the edge it
   reports 279 vs 279 and compares all 279; clipped at the origin it reports
   279 vs 280, mismatches at index 0, compares nothing, and still prints
   "style diffs 0". The clipped spelling would have read as green while
   checking no element at all.

   Fixed rather than absolute, and that was a measured correction: absolute
   scrolls with the document, so calling focus() from 40px down put the
   revealed link at viewport y -28, off the top of the screen. The navbar is
   fixed for the same reason. */
.skiplink {
  position: fixed;
  left: -9999px;
  top: 12px;
  z-index: 80;
  padding: 10px 16px;
  border-bottom: 0;
  border-radius: 4px;
  background: #7fc4d0;
  color: #06121a;
  font-size: 15px;
  letter-spacing: 0.01em;
}
.skiplink:focus { left: clamp(18px, 5vw, 44px); }
.skiplink:hover { color: #06121a; }

/* The mirror of the langbar, same line, other side. Same type, same border,
   same translucent backing, so the top of the page reads as one strip rather
   than two competing ones. */
.navbar {
  position: fixed;
  top: 20px;
  left: clamp(18px, 5vw, 44px);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  background: rgba(5, 7, 10, 0.72);
  padding: 0 10px;
  border: 1px solid #16202b;
  /* Room for the items still to come. Beyond this the row scrolls rather than
     colliding with the language buttons on the other side, which is what it
     would do at 375px once Naryu and the third product are in it. The reserve
     is the language switcher, 110px, plus a gap.

     border-box because max-width otherwise bounds the content box and the
     padding and border sit outside it, which put the row 22px wider than the
     limit and back over the buttons. The same omission produced a 78px
     control on the technical page. */
  box-sizing: border-box;
  max-width: calc(100% - clamp(18px, 5vw, 44px) * 2 - 126px);
  overflow-x: auto;
  scrollbar-width: none;
}

.navbar::-webkit-scrollbar { display: none; }

.navbar a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: #93a7b1;
  border-bottom: none;
  white-space: nowrap;
}

.navbar a:hover,
.navbar a:focus-visible { color: #dfeff3; }

.navbar .sep { color: #33454f; }

/* The one call to action on the page, reusing the site's only action style
   rather than inventing a second one: same border, same fill on hover as the
   form's submit and the technical page's back button. It has to beat the
   generic `a:hover` above, which would otherwise repaint the text and leave a
   bottom border fighting the box. Attribute selector, so it outranks the type
   selector without !important. */
[data-r="cta"]:hover,
[data-r="cta"]:focus-visible {
  background: #7fc4d0;
  color: #06121a;
  border-color: #7fc4d0;
}

[data-r="msvg"] { display: none; }
[data-r="invgrid"] { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); }
[data-r="sysgrid"] { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); }
@media (max-width: 1024px) { [data-r="sysgrid"] { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 900px)  { [data-r="invgrid"] { grid-template-columns: repeat(3, minmax(0,1fr)); } }
@media (max-width: 767px)  { [data-r="sysgrid"] { grid-template-columns: minmax(0,1fr); } }
@media (max-width: 700px)  { [data-r="invgrid"] { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 640px) {
  [data-r="dsvg"] { display: none; }
  [data-r="msvg"] { display: block; }
  [data-r="hero"] { padding-top: 80px !important; padding-bottom: 56px !important; }
  [data-r="sec"] { padding-top: 56px !important; padding-bottom: 56px !important; }
  [data-r="h1"] { font-size: 28px !important; }
  [data-r="lede"] { font-size: 18px !important; }
  [data-r="quote"] { font-size: 20px !important; }
  [data-r="invn"] { font-size: 22px !important; }
  [data-r="invd"] { font-size: 11px !important; }
}
@media (orientation: landscape) and (max-height: 500px) {
  [data-r="hero"] { padding-top: 72px !important; padding-bottom: 48px !important; }
  [data-r="sec"] { padding-top: 52px !important; padding-bottom: 52px !important; }
}

/* The page shell, previously an inline style on the outermost div. */
.page {
  background: linear-gradient(180deg, #05070a 0%, #070b12 35%, #09101a 70%, #0a121e 100%);
  color: #dfe7ec;
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 300;
  min-height: 100vh;
  padding-bottom: 80px;
  overflow-x: clip;
}

/* Language switcher. The design used spans with role="button", which are not
   reachable by keyboard; these are real buttons carrying the same visuals. */
.langbar {
  position: fixed;
  top: 20px;
  right: clamp(18px, 5vw, 44px);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 2px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  background: rgba(5, 7, 10, 0.72);
  padding: 0 4px;
  border: 1px solid #16202b;
}
.langbar button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: 0;
  padding: 0;
  font-size: inherit;
  /* A button does not inherit these from the page the way the design's span
     did, and the browser default would render the labels one weight heavier
     and centre-aligned. */
  font-weight: inherit;
  text-align: start;
  letter-spacing: inherit;
  cursor: pointer;
  color: #67818d;
  text-decoration: none;
}
.langbar button[aria-current="true"] { color: #dfeff3; }
.langbar .sep { color: #33454f; }

/* Placed after the base .langbar rules on purpose. A media query adds no
   specificity, so the same declaration written earlier in the file loses to
   the one written later; the first attempt at this sat above the base rule and
   changed nothing while looking correct in the source.

   On a wide screen the fixed switcher sits in the margin beside the text. On a
   phone the column is the full width, so it sat over the paragraphs for the
   whole scroll, and at 72% opacity the words showed through it, which reads as
   a rendering fault rather than as a control. */
@media (max-width: 640px) {
  .langbar,
  .navbar {
    background: #05070a;
    transition: transform 180ms linear, opacity 180ms linear;
  }
  .langbar[data-hidden="true"],
  .navbar[data-hidden="true"] {
    transform: translateY(-140%);
    opacity: 0;
    pointer-events: none;
  }
}

/* The design declared focus styling through a style-focus attribute, which no
   browser reads, so the page shipped with no visible focus indicator at all.
   These are the same colours, wired to the real pseudo-class. */
:focus-visible {
  outline: 2px solid #7fc4d0;
  outline-offset: 3px;
}
.field:focus-visible {
  outline: none;
  border-bottom-color: #6fb3c1;
  box-shadow: 0 1px 0 0 #6fb3c1;
}
.submit:hover {
  background: #7fc4d0;
  color: #06121a;
  border-color: #7fc4d0;
}

/* The way back, at the end of a long page as well as the top. Unscoped on
   purpose: the technical page and every blog post use it, and it was scoped
   under .tech while blog.css claimed to be reusing it, so the blog rendered a
   plain link and the comment said otherwise. Same shape as the form's submit control, which
   is the one action style the site has; no arrow glyph, because the font
   subset carries U+2191 and U+2193 but not U+2190. */
.backbuttonrow {
  margin: 26px 0 0;
}

.backbutton {
  display: inline-flex;
  align-items: center;
  /* Scoped here rather than set globally: the design export is written for
     content-box throughout, and switching that everywhere would move geometry
     the fidelity harness is there to protect. Without it, min-height 48 plus
     the padding and border rendered a 78px control. */
  box-sizing: border-box;
  background: transparent;
  border: 1px solid #3d6b77;
  color: #a9dbe4;
  font-size: 14px;
  /* Stated, not inherited: the paragraph around it carries the body's line
     height, which pushed the control to 78px against an intended 48. */
  line-height: 1.2;
  letter-spacing: 0.06em;
  padding: 14px 32px;
  min-height: 48px;
  text-decoration: none;
  transition: background-color 160ms linear, color 160ms linear;
}

.backbutton:hover,
.backbutton:focus-visible {
  background: #7fc4d0;
  color: #06121a;
  border-color: #7fc4d0;
}


/* Reserved before Turnstile renders, so the widget arriving does not push the
   submit button down. 67px, not 65px: the design's placeholder was a 65px box
   with a 1px dashed border on each side, so it occupied 67px of the column. */
.turnstile {
  width: 300px;
  max-width: 100%;
  min-height: 67px;
}

.formstatus {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: #7fc4d0;
  min-height: 16px;
}
.formstatus[data-state="error"] { color: #d8926b; }

.submit[aria-disabled="true"] { opacity: 0.5; cursor: default; }
.submit[aria-disabled="true"]:hover {
  background: transparent;
  color: #a9dbe4;
  border-color: #3d6b77;
}

/* Scroll reveal is applied from JavaScript, never from CSS, so that a visitor
   without JavaScript sees the content rather than a blank page. The transition
   is declared here only so that reduced-motion can switch it off. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}


/* Repeated declarations lifted out of the design's inline
   styles. Declaration text is byte-identical to the source. */
.dgl{font-family:'IBM Plex Mono',monospace;font-size:11px;line-height:1.5;color:#b6c6cf}
.invcell{border-top:1px solid #16202b;padding:20px 18px 22px 0}
.invdesc{margin-top:10px;font-size:13px;line-height:1.55;color:#8b9ca7;max-width:26ch;text-wrap:pretty}
.stack5{display:grid;gap:5px}
.dgtag{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.14em;color:#648793}
.invnum{font-family:'IBM Plex Mono',monospace;font-size:26px;font-weight:400;color:#dfe9ee;letter-spacing:-0.02em;line-height:1.1}
.eyebrow{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:0.2em;text-transform:uppercase;color:#648793;margin-bottom:22px}
.link{color:#7fc4d0;border-bottom:1px solid #2b4a52}
.cta{display:inline-flex;align-items:center;box-sizing:border-box;background:transparent;border:1px solid #3d6b77;color:#a9dbe4;font-size:14px;letter-spacing:0.06em;line-height:1.2;padding:16px 34px;min-height:52px;text-decoration:none;transition:background-color 160ms linear,color 160ms linear,border-color 160ms linear}
.sec{max-width:1120px;margin:0 auto;padding:clamp(60px,9vh,100px) clamp(20px,6vw,80px);border-top:1px solid #131c26}
.kicker{font-family:'IBM Plex Mono',monospace;font-size:11px;color:#60838f;letter-spacing:0.16em;margin-bottom:14px}
.h3lg{margin:0;font-size:clamp(21px,2.2vw,26px);font-weight:400;color:#eef4f7;letter-spacing:-0.01em;max-width:20ch}
.body22{margin:0 0 22px;font-size:17px;line-height:1.78;color:#a9b8c2;max-width:66ch;text-wrap:pretty}
.body22w{margin:0 0 22px;font-size:17px;line-height:1.78;color:#a9b8c2;max-width:68ch;text-wrap:pretty}
.body30w{margin:0 0 30px;font-size:17px;line-height:1.78;color:#a9b8c2;max-width:68ch;text-wrap:pretty}
.eyebrow2{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:0.16em;text-transform:uppercase;color:#648793;margin-bottom:18px}
.body18{margin:0 0 18px;font-size:17px;line-height:1.78;color:#a9b8c2;max-width:66ch;text-wrap:pretty}
.body26{margin:0 0 26px;font-size:17px;line-height:1.78;color:#a9b8c2;max-width:66ch;text-wrap:pretty}
.pullrule{margin:0;font-size:clamp(19px,2vw,23px);line-height:1.55;color:#cfe3e8;border-left:1px solid #2b3f49;padding-left:24px;max-width:56ch;font-weight:300;text-wrap:pretty}
.pullrule34{margin:0 0 34px;font-size:clamp(19px,2vw,23px);line-height:1.55;color:#cfe3e8;border-left:1px solid #2b3f49;padding-left:24px;max-width:52ch;font-weight:300;text-wrap:pretty}
.railcol{display:grid;gap:22px;align-content:start;border-left:1px solid #16202b;padding-left:18px}
.colhead{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.2em;color:#648793;padding-bottom:10px;border-bottom:1px solid #24323d;margin-bottom:16px}
.splitrow{border-top:1px solid #16202b;padding:38px 0;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr));gap:clamp(24px,4vw,64px);align-items:start}
.pull{margin:0;font-size:clamp(20px,2.1vw,26px);line-height:1.5;color:#cfe3e8;font-weight:300;max-width:44ch;text-wrap:pretty}
.body0{margin:0;font-size:17px;line-height:1.78;color:#a9b8c2;text-wrap:pretty}
.dsvg{max-width:460px;display:block;overflow:visible}
.msvg{display:block}
.h3sm{margin:0 0 18px;font-size:20px;font-weight:400;color:#e4edf1;letter-spacing:-0.01em}
.dgl16{font-family:'IBM Plex Mono',monospace;font-size:11px;line-height:1.6;color:#b6c6cf}
.fieldrow{display:grid;gap:8px}
.fieldlbl{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:0.14em;text-transform:uppercase;color:#6b8b97}
.h2{margin:0 0 26px;font-size:clamp(27px,3.2vw,38px);font-weight:400;letter-spacing:-0.015em;color:#eef4f7;max-width:22ch}
.h2free{margin:0 0 26px;font-size:clamp(27px,3.2vw,38px);font-weight:400;letter-spacing:-0.015em;color:#eef4f7}
.twocol{margin-top:clamp(64px,9vw,104px);display:grid;grid-template-columns:repeat(auto-fit,minmax(min(360px,100%),1fr));gap:clamp(36px,5vw,72px);align-items:start}
.meta11{font-family:'IBM Plex Mono',monospace;font-size:11px;color:#6d8792}
.metarow{border-top:1px solid #131c26;padding-top:22px;display:grid;gap:12px;font-family:'IBM Plex Mono',monospace;font-size:12px;color:#8ea3ad;letter-spacing:0.03em}
.invnumalt{font-family:'IBM Plex Mono',monospace;font-size:26px;font-weight:400;color:#cf9159;letter-spacing:-0.02em;line-height:1.1}
.field{background:transparent;border:none;border-bottom:1px solid #24323d;color:#e2edf1;font-size:16px;padding:11px 0;min-height:44px;outline:none}
.fieldarea{background:transparent;border:none;border-bottom:1px solid #24323d;color:#e2edf1;font-size:16px;padding:11px 0;outline:none;resize:vertical;line-height:1.6}
.submit{justify-self:start;background:transparent;border:1px solid #3d6b77;color:#a9dbe4;font-size:14px;letter-spacing:0.06em;padding:14px 32px;min-height:48px;cursor:pointer;transition:background-color 160ms linear,color 160ms linear}

/* Technical document page. Every rule is scoped under .tech, which only the
   technical page carries, so none of it can reach the two design pages and the
   fidelity harness has nothing new to measure. Everything else that page uses
   is a class already lifted out of the design's own inline styles. */

.tech .tech-head {
  border-top: none;
  /* The fixed row occupies 20px to 66px. This clears it with room to breathe,
     rather than the 6px the old value left, which read as a collision. */
  padding-top: clamp(104px, 12vh, 132px);
}

.tech .backlink {
  margin: 0 0 30px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.08em;
}

.tech h1.h2 {
  margin-bottom: 18px;
}

.tech .techlinks {
  margin: 22px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
}

.tech .techlinks span {
  color: #648793;
}

.tech ul {
  margin: 0 0 22px;
  padding-left: 18px;
  max-width: 66ch;
}

.tech li {
  margin-bottom: 12px;
  font-size: 17px;
  line-height: 1.78;
  color: #a9b8c2;
  text-wrap: pretty;
}

/* 4.60:1. A marker is arguably decoration rather than text, but the project's
   rule is that everything clears AA, and arguing the exception costs more than
   the two shades. */
.tech li::marker {
  color: #5e8393;
}

/* The lead-in names the mechanism; the sentence after it explains the choice.
   Weight would fight the page, so the separation is carried by colour. */
.tech li strong {
  font-weight: 400;
  color: #dfe9ee;
}

.tech .h3sm {
  margin-top: 4px;
}

.tech .pullrule {
  margin: 26px 0 0;
}

/* Diagrams sit under the prose that explains them rather than beside it: this
   page is a single column, and a figure that has to be read next to a sentence
   is a figure that does not survive a phone. */
.tech .diagram {
  margin: 34px 0 0;
}

/* Writing. Every rule is scoped under .post, which only the blog pages carry,
   so none of it can reach the two design pages or the technical page, and the
   fidelity harness has nothing new to measure. The shared shapes it borrows,
   .tech-head, .backlink, .backbutton and the rest, already live in
   technical.css and are reused rather than restated. */

.post .tech-head {
  border-top: none;
  /* The fixed row occupies 20px to 66px. This clears it with room to breathe,
     rather than the 6px the old value left, which read as a collision. */
  padding-top: clamp(104px, 12vh, 132px);
}

.post .backlink {
  margin: 0 0 30px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.08em;
}

.post h1.h2 {
  margin-bottom: 18px;
  max-width: 24ch;
}

/* Stacked .sec blocks each carry 81px of vertical padding, so the header, the
   body and the closing note sat 162px apart and the page read as mostly empty.
   The header keeps its top padding; everything after it flows. */

.post .tech-head { padding-bottom: 30px; }

.post article.sec,
.post .closing {
  padding-top: 0;
  padding-bottom: 0;
  border-top: none;
}

.post .listing {
  padding-top: 0;
  border-top: none;
}

/* Index */

.post .postlist {
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: 720px;
}

.post .postrow {
  border-top: 1px solid #16202b;
  padding: 34px 0;
}

.post .postrow:last-child {
  border-bottom: 1px solid #16202b;
}

.post .postdate {
  margin-bottom: 12px;
  text-transform: none;
  letter-spacing: 0.08em;
}

.post .postrow h2 {
  margin: 0 0 14px;
  max-width: 30ch;
}

/* The title is the link, so it should not also carry the underline the body
   links use. The colour shift on hover is enough at this size. */
.post .postrow h2 .link {
  color: #eef4f7;
  border-bottom: none;
}

.post .postrow h2 .link:hover {
  color: #a9dbe4;
}

.post .postrow .body17 {
  margin: 0;
  max-width: 64ch;
}

/* Article */

.post article p {
  margin: 0 0 22px;
  font-size: 17px;
  line-height: 1.78;
  color: #a9b8c2;
  max-width: 66ch;
  text-wrap: pretty;
}

.post article h2 {
  margin: 44px 0 18px;
  font-size: 20px;
  font-weight: 400;
  color: #e4edf1;
  letter-spacing: -0.01em;
  max-width: 34ch;
}

.post article blockquote {
  margin: 30px 0;
  padding-left: 24px;
  border-left: 1px solid #2b3f49;
  font-size: clamp(19px, 2vw, 23px);
  line-height: 1.55;
  font-weight: 300;
  color: #cfe3e8;
  max-width: 52ch;
  text-wrap: pretty;
}

.post article ul {
  margin: 0 0 22px;
  padding-left: 18px;
  max-width: 66ch;
}

.post article li {
  margin-bottom: 12px;
  font-size: 17px;
  line-height: 1.78;
  color: #a9b8c2;
  text-wrap: pretty;
}

.post article li::marker {
  color: #5e8393;
}

.post article strong {
  font-weight: 400;
  color: #dfe9ee;
}

/* Two controls on one row, and they wrap rather than overflow on a phone. */
.post .backbuttonrow {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 30px 0 0;
}

.post .sourcenote {
  margin: 40px 0 0;
  padding-top: 22px;
  border-top: 1px solid #131c26;
  color: #6d8792;
  max-width: 66ch;
  line-height: 1.7;
}

/* Figures, drawn in CSS rather than SVG. A diagram made of boxes and borders
   reflows on a phone; an SVG has to be drawn twice, once per width, which is
   what the technical page pays for its eight figures.

   All four carry a bottom margin. Body paragraphs set only a bottom margin, so
   a figure followed by a paragraph had no space between them at all, which was
   invisible for a while because every figure so far happened to be followed by
   a heading and headings bring their own 44px. Measured: 0px before a
   paragraph, 44px before a heading. The 22px here collapses against the 44 and
   changes nothing in the heading case. */

.post .fig {
  margin: 34px 0 22px;
  max-width: 620px;
}

.post .fig .row {
  display: grid;
  grid-template-columns: minmax(0, 190px) 1fr;
  gap: 14px;
  align-items: center;
}

.post .fig .box {
  border: 1px solid #33454f;
  padding: 9px 12px;
  font-size: 13px;
  color: #b6c6cf;
}

.post .fig .box.k { border-color: #4d8593; color: #8fd0dc; }
.post .fig .box.out { border-color: #5d7b88; color: #e2edf1; }
.post .fig .box.warn { border-color: #c88a52; color: #cf9159; }

.post .fig .note {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: #6d8792;
}

/* The arrow is the font's own U+2193. The subset carries it, and carries no
   right arrow and no box drawing, which is why these flows run downward. */
.post .fig .arr {
  font-family: 'IBM Plex Mono', monospace;
  color: #5e8393;
  font-size: 13px;
  line-height: 1;
  padding: 5px 0 5px 88px;
}

.post .fig .foot {
  border-top: 1px solid #131c26;
  margin-top: 22px;
  padding-top: 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: #6d8792;
  line-height: 1.75;
}

.post .fig .foot .hi { color: #8fd0dc; }

/* A findings table in the monospace voice. Deliberately not a fake terminal
   transcript: an invented "$ command" would turn a claim into evidence that
   was never produced, which is the failure the writing is about. */
.post .table {
  margin: 34px 0 22px;
  max-width: 660px;
  border: 1px solid #1b2732;
}

.post .table .head {
  border-bottom: 1px solid #1b2732;
  padding: 9px 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #648793;
}

.post .table table {
  border-collapse: collapse;
  width: 100%;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
}

.post .table td {
  padding: 9px 14px;
  border-top: 1px solid #131c26;
  vertical-align: top;
  color: #a9b8c2;
  line-height: 1.6;
}

.post .table tr:first-child td { border-top: none; }

.post .table .kind {
  color: #648793;
  white-space: nowrap;
  width: 1%;
}

.post .table .was { color: #cf9159; }

/* A calibration plot, drawn the same way and for the same reason: positioned
   borders rather than SVG, so there is one drawing instead of one per width.

   The geometry only holds because the plot's aspect ratio is fixed. At 5 by 3,
   a rise of y percent of the height is 0.6y percent of the width, so a segment
   between two points is sqrt(dx^2 + (0.6*dy)^2) long and sits at
   -atan(0.6*dy / dx). Every width and angle below was computed that way from
   five points: (10,10) (30,25) (50,37) (72,47) (90,53), with the first sitting
   on the diagonal because low confidence is where the system was honest.
   Change the aspect ratio and every number here becomes wrong. */

.post .chart { margin: 34px 0 22px; max-width: 620px; --gutter: 92px; }

/* Three columns: the y label, the plot, and a reserved gutter the two
   annotations are painted into. The gutter is not decoration either. The wedge
   between the two lines is only a few pixels tall at the left of a phone-width
   plot, so a horizontal label placed inside it crosses one line or the other
   at every position. Measured at 375px: the wedge admits 4px and the label
   needs 10.5. The third column has no element in it; it exists to stop the
   labels overflowing the figure. */
.post .chart .grid {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) var(--gutter);
  column-gap: 10px;
  row-gap: 8px;
}

.post .chart .ylab {
  grid-column: 1;
  grid-row: 1;
  align-self: start;
  text-align: right;
}

.post .chart .xlab { grid-column: 2; grid-row: 2; }

.post .chart .plot {
  grid-column: 2;
  grid-row: 1;
  position: relative;
  aspect-ratio: 5 / 3;
  border-left: 1px solid #33454f;
  border-bottom: 1px solid #33454f;
}

/* What it claimed: the line where stated confidence equals observed accuracy.
   Corner to corner, so its length is sqrt(1 + 0.6^2) of the width. */
.post .chart .claimed {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 116.62%;
  border-top: 1px dashed #52707c;
  transform-origin: 0 0;
  transform: rotate(-30.96deg);
}

.post .chart .seg {
  position: absolute;
  border-top: 2px solid #cf9159;
  transform-origin: 0 0;
}

.post .chart .s1 { left: 10%; bottom: 10%; width: 21.93%; transform: rotate(-24.23deg); }
.post .chart .s2 { left: 30%; bottom: 25%; width: 21.26%; transform: rotate(-19.80deg); }
.post .chart .s3 { left: 50%; bottom: 37%; width: 22.80%; transform: rotate(-15.26deg); }
.post .chart .s4 { left: 72%; bottom: 47%; width: 18.36%; transform: rotate(-11.31deg); }

.post .chart .dot {
  position: absolute;
  width: 7px;
  height: 7px;
  margin: 0 0 -3px -3px;
  border-radius: 50%;
  background: #cf9159;
}

.post .chart .p1 { left: 10%; bottom: 10%; }
.post .chart .p2 { left: 30%; bottom: 25%; }
.post .chart .p3 { left: 50%; bottom: 37%; }
.post .chart .p4 { left: 72%; bottom: 47%; }
.post .chart .p5 { left: 90%; bottom: 53%; }

/* The distance between the two lines at the right edge, which is the whole
   point of the drawing. */
.post .chart .gap {
  position: absolute;
  left: 90%;
  bottom: 53%;
  height: 37%;
  border-left: 1px solid #52707c;
}

.post .chart .gap::before,
.post .chart .gap::after {
  content: "";
  position: absolute;
  left: -4px;
  width: 8px;
  border-top: 1px solid #52707c;
}

.post .chart .gap::before { top: 0; }
.post .chart .gap::after { bottom: 0; }

.post .chart .lab {
  position: absolute;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  line-height: 1;
  color: #6d8792;
  white-space: nowrap;
}

/* Rotated to run parallel to the diagonal and anchored ten percent of the
   height below it, which keeps the same clearance at every width. An earlier
   version pushed it off the line by a fixed 15px instead; that is a tenth of
   the height on a desktop and a fifth of it on a phone, and at 375px the label
   sat on top of the curve. A fixed offset inside a box that scales is a bug
   waiting for a narrow screen. Anchored here the wedge is wide enough: the
   curve is 4.5% below and the diagonal 4.3% above. */
.post .chart .lab.claim {
  left: 55%;
  bottom: 45%;
  transform-origin: 0 100%;
  transform: rotate(-30.96deg);
}

/* Both sit in the gutter, level with the thing they name: the bracket's middle
   and the last point on the curve. */
.post .chart .lab.over {
  left: 100%;
  margin-left: 10px;
  bottom: 68%;
}

.post .chart .lab.hap {
  left: 100%;
  margin-left: 10px;
  bottom: 50%;
  color: #cf9159;
}

/* On a phone the y label costs more than it is worth beside the plot, so it
   moves above it and the plot takes the width back. */
@media (max-width: 560px) {
  .post .chart { --gutter: 84px; }

  .post .chart .grid {
    grid-template-columns: minmax(0, 1fr) var(--gutter);
  }

  .post .chart .lab { font-size: 9.5px; }

  .post .chart .ylab {
    grid-column: 1;
    grid-row: 1;
    text-align: left;
    margin-bottom: 6px;
  }

  .post .chart .plot { grid-column: 1; grid-row: 2; }
  .post .chart .xlab { grid-column: 1; grid-row: 3; }
}

/* The permission figure. Boxes and rules again, no SVG.

   The connectors are drawn rather than typed. The font subset carries U+2193
   and no right arrow, which is why the earlier flows run downward; this one
   runs across, so the heads are CSS triangles and the missing glyph never
   comes up. The dashed box and the dashed connector are the same statement in
   two places: that step exists and is not allowed to run.

   The bracket underneath spans ALARM through ACT and its stubs want to sit
   under the middle of each box. With three equal boxes and two fixed gaps
   that lands at 22% of the span from either end, near enough at every width
   the figure is used at. Its label sits below the rule rather than punched
   into it, because the page background is a gradient and a solid patch behind
   the text would show a seam at some scroll position. */

.post .flow { margin: 34px 0 22px; max-width: 620px; --conn: 34px; }

.post .flow .grid {
  display: grid;
  grid-template-columns: 1fr var(--conn) 1fr var(--conn) 1fr;
  align-items: stretch;
}

.post .flow .fbox {
  grid-row: 1;
  border: 1px solid #33454f;
  padding: 15px 8px;
  text-align: center;
}

.post .flow .fbox.no {
  border-style: dashed;
  border-color: #c88a52;
}

.post .flow .ft {
  font-size: 14px;
  letter-spacing: 0.06em;
  color: #e2edf1;
}

.post .flow .fn {
  margin-top: 7px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: #6d8792;
}

.post .flow .fbox.no .ft,
.post .flow .fbox.no .fn { color: #cf9159; }

.post .flow .conn {
  grid-row: 1;
  align-self: center;
  position: relative;
  height: 0;
  border-top: 1px solid #33454f;
}

.post .flow .conn::after {
  content: "";
  position: absolute;
  right: -1px;
  top: -4px;
  border-left: 6px solid #33454f;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
}

.post .flow .conn.dash {
  border-top-style: dashed;
  border-top-color: #5d7b88;
}

.post .flow .conn.dash::after { border-left-color: #5d7b88; }

.post .flow .human {
  grid-column: 3 / 6;
  grid-row: 2;
  position: relative;
  height: 40px;
  margin: 0 22% 26px;
  border-left: 1px solid #33454f;
  border-bottom: 1px solid #33454f;
  border-right: 1px solid #33454f;
}

/* The head points up, into the step a person is the only way to reach. */
.post .flow .human::after {
  content: "";
  position: absolute;
  right: -4px;
  top: -6px;
  border-bottom: 6px solid #33454f;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
}

.post .flow .human span {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 7px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: #8fd0dc;
  white-space: nowrap;
}
