:root {
  --paper: white;
  --ink: black;
  --muted: #555;
  --faint: #bbb;
  --rule: #000;
  --accent: #000;
  --accent-warm: #000;
}

/* The body hugs the dossier instead of the window: Safari's shrink-to-fit
   measures the document width against the paper, so a window-wide body
   makes every print scale with whatever size the window happened to be. */
html, body {
  inline-size: fit-content;
  margin-inline: auto;
  min-height: 100%;
  background: white;
  color: black;
  font-size: 8.5pt;
  line-height: 13pt;
  margin: 0;
  padding: 0;
}

@page {
  size: A4;
  margin: 15mm 15mm 20mm;

  @bottom-center {
    content: "JF Bastien · build & scale platforms · page " counter(page) " of " counter(pages);
    font-family: "Berkeley Mono", "Dossier Mono Supplement";
    font-size: 8.5pt;
    color: var(--muted);
  }
}

a {
  color: black;
  text-decoration: none;
}

/* A fixed print measure, exactly A4's content width: Safari lays print
   out at the window width and fits it to the paper, so an auto-width
   page makes the scale follow whatever size the window happened to be.
   A constant document width makes the fit factor constant. */
.page {
  display: block;
  inline-size: 100ch;
  max-width: none;
  margin-inline: auto;
  padding: 0;
}

/* .page is a block in print, so restore the panel separation the
   screen grid gap provided; without it adjacent box borders merge. */
.page > * + *,
main > * + * {
  margin-block-start: 1lh;
}

main,
.records,
.artifact-ledger {
  display: block;
}

/* Panels keep the screen box-and-straddling-title grammar; sections that
   span a page break get a complete box on every fragment. The box is a
   real border, not shadow ink: at least one Safari print path culls one
   strip of an inset box-shadow while painting the other three, and
   shadows are background ink a Print Backgrounds toggle may drop
   wholesale. The padding hands back the border's 1px so the interior —
   wrapping, pagination, the ch grid — is unchanged, and the straddling
   title follows its padding-box corner. */
.panel {
  break-inside: auto;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  border: 1px solid var(--rule);
  box-shadow: none;
  padding: calc(1lh - 1px) calc(var(--panel-pad-x) - 1px);
}

.panel__title {
  inset-block-start: -1px;
  inset-inline-start: calc(var(--panel-pad-x) - 1px);
}

/* WebKit's print pipeline paints a clipped pseudo-element as invisible
   glyphs when it carries its own color; inherited ink at reduced opacity
   is the faint gray (27% black on white is exactly #bbb) and paints on
   every engine. Alt text is also restated away for the same pipeline. */
.record-index li::before,
.dispatch-list li::before,
.artifact-venue::after {
  content: var(--leader-dots);
}

.record-index li::before,
.dispatch-list li::before,
.artifact-venue::after,
.artifact-group__title::after,
.series-list > li::before {
  color: inherit;
  opacity: 0.27;
}

.artifact-group__title::after {
  content: var(--rule-glyphs);
}

.masthead,
.record-index {
  break-inside: avoid;
}

.dispatch {
  break-inside: avoid-page;
  page-break-inside: avoid;
}

/* The register is one atomic table; Chromium ignores break-inside:
   avoid-page on cloned-border boxes, so pin the break explicitly. */
#patent-register {
  break-before: page;
}

/* Deliberate whitespace on the prior page: without this break,
   Education straddles the page boundary and splits mid-register. */
#education {
  break-before: page;
}

/* WebKit clips a forced-break fragment's paint at its border-box top, so
   the straddling title must overhang inside the box: half a line of
   transparent top border makes room for it, and padding gives back what
   the missing 1px top border no longer takes. The rule the title
   straddles is the ::before's own border, drawn at the padding edge; its
   paper background also masks the side rails' rise through the
   transparent region, keeping closed corners. */
#education,
#patent-register {
  border-block-start: 0.5lh solid transparent;
  padding-block-start: 1lh;
}

#education::before,
#patent-register::before {
  content: "";
  position: absolute;
  inset-block-start: -0.5lh;
  inset-inline: -1px;
  block-size: 0.5lh;
  background: var(--paper);
  border-block-end: 1px solid var(--rule);
}

.masthead .location span::before {
  color: var(--ink);
}

.record {
  break-inside: auto;
  grid-template-columns: var(--record-head-cols, 22ch) minmax(0, 1fr);
  column-gap: var(--record-gap, 2ch);
}

/* Real borders, not the screen's absolutely positioned glyph rules: an
   out-of-flow rule strands as a full glyph run on the previous page when
   its record's content is pushed across a break. A border degrades better
   (Chromium drops it at the fragment start), and atomic records were
   tried and rejected: Chromium then refuses to break between records and
   pushes whole sections. */
.record--service + .record--service,
.record--education + .record--education {
  margin-block-start: 1lh;
  border-block-start: 1px solid var(--faint);
}

.record--service + .record--service::before,
.record--education + .record--education::before {
  content: none;
}

.record--education {
  break-inside: avoid;
}

.bullet-list li + li {
  margin-block-start: 0;
}

.artifact-group + .artifact-group {
  margin-block-start: 1lh;
}

.artifact-ledger > li + li {
  margin-block-start: 0;
}

.artifact-ledger > li {
  break-inside: avoid;
}

.record--series .record__body {
  margin-block-start: 0;
}

.record--series {
  break-inside: avoid;
}

/* Two independently packed columns, not CSS multicol (WebKit's print
   pipeline ignores columns) and not a row grid (coupled row heights
   waste a line for every wrapped partner). The renderer counts each
   title's printed lines — exact in a monospace measure — and sets the
   balanced column height. */
.docket-list {
  display: flex;
  flex-flow: column wrap;
  block-size: calc(var(--docket-lines) * 1lh);
  column-gap: 4ch;
}

.docket-list > li {
  inline-size: calc((100% - 4ch) / 2);
}

.docket-list > li {
  break-inside: avoid;
}

.docket-list a {
  grid-template-columns: var(--docket-code-cols, 6ch) minmax(0, 1fr);
}

.docket-list a::before {
  content: none;
}

.doc-title {
  grid-column: 2;
}

.series-list {
  columns: auto;
  display: grid;
}

.series-list > li {
  grid-template-columns: var(--series-index-cols, 2ch) max-content minmax(1ch, 1fr) max-content;
  break-inside: avoid;
}

.series-list > li::before {
  content: var(--leader-dots);
  grid-column: 3;
  grid-row: 1;
  overflow: hidden;
  white-space: nowrap;
}

.series-title {
  grid-column: 2;
}

.series-url {
  grid-column: 4;
  justify-self: end;
  white-space: nowrap;
}

.record__head {
  break-after: avoid;
  break-inside: avoid;
}

/* Keep a section's first row with its straddling title. A panel that starts
   low on a page would otherwise print its section title alone at the bottom
   edge — an orphaned heading — and flow the first row onto the next page;
   break-before: avoid moves the break above the whole panel instead. */
.records > .record:first-child,
.artifact-group:first-of-type {
  break-before: avoid;
}

.record--patent {
  grid-template-columns: var(--patent-id-cols, 15ch) minmax(0, 1fr);
  column-gap: 2ch;
  row-gap: 0;
  padding-block: 0;
}

/* Print keeps a dense patent register: a quarter-line above each
   hairline clears the previous row's descenders, and the following
   ID row's capitals sit safely under it. A real border, not an inset
   shadow: PDF rasterizers paint phantom side edges for offset shadows. */
.record--patent + .record--patent {
  margin-block-start: 0.25lh;
  border-block-start: 1px solid var(--faint);
}

.record--patent + .record--patent::before {
  content: none;
}

.record--patent .record__title {
  grid-column: 1;
}

.record--patent .record__body {
  grid-column: 2;
}

.record--compact:not(.record--series),
.record--patent {
  break-inside: avoid;
}

h1, h2, h3, h4 {
  break-after: avoid;
}

p, li {
  orphans: 3;
  widows: 3;
}

.print-only {
  display: block;
}

.site-footer {
  display: none;
}

.endmark {
  display: none;
}