/**
 * Grid forked from The Monospace Web
 * By Oskar Wickström
 *
 * Forked for jenny-portfolio: light-only tokens, self-hosted Commit Mono,
 * the grid with its round(down) fallbacks, and no debug or image-padding code.
 * Forked 2026-11-08 from https://github.com/owickstrom/the-monospace-web/
 *
 * The upstream MIT notice is reproduced here in full, because the licence
 * requires it to travel with copies and substantial portions:
 *
 * Copyright 2024 Oskar Wickström
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

@font-face {
  font-family: "Commit Mono";
  src: url("/assets/fonts/CommitMonoV143-VF.woff2") format("woff2");
  font-weight: 200 700;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Commit Mono";
  src: url("/assets/fonts/CommitMonoV143-VF.woff2") format("woff2");
  font-weight: 200 700;
  font-style: italic;
  font-variation-settings: "ital" 1;
  font-display: block;
}

:root {
  color-scheme: light;
  --paper: #f4f4ed;
  --ink: #14140f;
  --accent: #b83d05;
  --accent-bright: #ff9767;
  --sky-1: #dfe5e7;
  --sky-2: #c5d5e5;
  --sky-3: #9ec8e4;
  --sky-4: #2157cc;

  --line-height: 1.5rem;
  --column: 80ch;
  --border-thickness: 2px;
  --font-family: "Commit Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-bold: 600;

  font-family: var(--font-family);
  font-weight: var(--font-weight-regular);
  font-style: normal;
  font-variant-numeric: tabular-nums lining-nums;
  font-size: 16px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
  font-weight: var(--font-weight-regular);
}

* + * {
  margin-top: var(--line-height);
}

html {
  display: flex;
  width: 100%;
  min-height: 100%;
  margin: 0;
  padding: 0;
  flex-direction: column;
  align-items: center;
  background: var(--paper);
  color: var(--ink);
}

body {
  position: relative;
  width: 100%;
  max-width: var(--column);
  margin: 0;
  padding: calc(var(--line-height) * 2) 2ch;
  line-height: var(--line-height);
  overflow-x: hidden;
  font-weight: var(--font-weight-regular);
}

@supports (width: calc(round(down, 100%, 1ch))) {
  body {
    max-width: calc(min(var(--column), round(down, 100%, 1ch)));
  }
}

.sheet {
  /*
   * The sheet's own font size, named so the palette strip can borrow it: a ch
   * resolves against the font of the element that uses it, and the footer sets
   * a smaller one, so the strip would otherwise inset itself short of the text.
   */
  --sheet-font-size: 1rem;
  --sheet-pad-x: 3ch;
  --swatch-size: 1.25rem;
  --sheet-pad-y: var(--line-height);
  position: relative;
  width: 100%;
  padding: var(--sheet-pad-y) var(--sheet-pad-x);
  background: var(--paper);
  border: var(--border-thickness) solid var(--sky-2);
}

/*
 * The sky: a poster image and a canvas, both fixed behind the sheet at
 * z-index -1. The gradient underneath is the fallback when there is no 2D
 * context; without JavaScript the poster covers it, and the canvas covers the
 * poster once it has painted.
 */
.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(180deg, var(--sky-1) 0%, var(--paper) 55%);
}

.sky-poster,
.sky-canvas {
  position: fixed;
  top: 0;
  left: 0;
  display: block;
  margin: 0;
  border: 0;
}

.sky-poster {
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}

.sky-canvas {
  z-index: -1;
  image-rendering: pixelated;
}

.sky--fallback {
  background:
    url("/assets/sky-poster.svg") center / cover no-repeat,
    linear-gradient(180deg, var(--sky-1) 0%, var(--paper) 55%);
  image-rendering: pixelated;
}

.sky--fallback .sky-canvas {
  display: none;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1ch;
  border-bottom: var(--border-thickness) solid var(--sky-4);
  padding-bottom: calc(var(--line-height) / 2);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.site-header nav {
  display: flex;
  flex-wrap: wrap;
  gap: 2ch;
}

.site-header nav > * + * {
  margin-top: 0;
}

/* The strip is paper furniture, so the menu and the footer draw over it. */
.site-header nav,
.site-header nav a,
.site-footer a {
  position: relative;
  z-index: 1;
}

/*
 * The strip is printed behind the type, so a link that crosses a square paints
 * its own backing in paper: invisible on the paper, a highlight over a square.
 * The shadow widens the backing by a hair without the layout shift padding
 * would cause. The nav's own box stays unpainted, so squares between its links
 * show through.
 */
.site-header nav a,
.site-footer a {
  background-color: var(--paper);
  box-shadow: 0 0 0 0.1em var(--paper);
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/*
 * Header links sit in the accent at rest and answer hover in ink, so the one
 * ink link is the section the reader is on. The :link/:visited cases are
 * spelled out so no other link rule in the sheet can repaint the header.
 */
.site-header a,
.site-header a:link,
.site-header a:visited {
  color: var(--accent);
  font-weight: var(--font-weight-medium);
  text-decoration: none;
}

.site-header a:hover,
.site-header a:link:hover,
.site-header a:visited:hover {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: var(--border-thickness);
}

.site-header a[aria-current="page"],
.site-header a[aria-current="page"]:link,
.site-header a[aria-current="page"]:visited {
  color: var(--ink);
}

.site-header a[aria-current="page"]:hover {
  color: var(--ink);
}

a {
  color: var(--accent);
  font-weight: inherit;
  text-decoration-thickness: var(--border-thickness);
  text-underline-offset: calc(var(--line-height) / 4);
}

/*
 * No visited state. It only ever told the reader where they had been, which
 * the nav already does, and its equal specificity let it paint over component
 * colours such as the header's ink.
 */

/*
 * Links answer hover the way the nav does, in ink, so the two read as one
 * behaviour across the site.
 */
a:hover {
  color: var(--ink);
  text-decoration-thickness: calc(var(--border-thickness) * 2);
}

:focus-visible {
  outline: var(--border-thickness) solid var(--ink);
  outline-offset: calc(var(--line-height) / 4);
  box-shadow: 0 0 0 calc(var(--border-thickness) * 2) var(--accent-bright);
}

::selection {
  background: var(--accent-bright);
  color: var(--ink);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: var(--font-weight-bold);
  margin: calc(var(--line-height) * 2) 0 var(--line-height);
  line-height: var(--line-height);
}

h1 {
  font-size: 2rem;
  line-height: calc(2 * var(--line-height));
  margin-bottom: calc(var(--line-height) * 2);
  text-transform: uppercase;
}

/*
 * The title keeps one step to whatever follows it, on every page: a project's
 * category line, the front page's tagline, Now's date, the work tab bar, or the
 * first paragraph of About. The title drops its own bottom margin, or the two
 * margins would collapse back to the larger one.
 */
h1 {
  margin-bottom: 0;
}

main h1 + * {
  margin-top: var(--line-height);
}

h2 {
  font-size: 1rem;
  text-transform: uppercase;
}

p,
ul,
ol,
blockquote,
pre,
figure,
table {
  margin: 0 0 var(--line-height);
}

p {
  word-break: break-word;
  overflow-wrap: break-word;
  hyphens: auto;
}

strong,
b,
th {
  font-weight: var(--font-weight-bold);
}

em,
i {
  font-style: italic;
  font-weight: inherit;
}

.lede {
  font-weight: var(--font-weight-regular);
  max-width: 56ch;
}

.portrait {
  float: right;
  width: 12rem;
  height: 12rem;
  margin: 0 0 var(--line-height) 2ch;
  border: var(--border-thickness) solid var(--sky-2);
  object-fit: cover;
}

.meta {
  font-size: 0.875rem;
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/*
 * The featured cards rule off like a table: one light rule above every card.
 * Cards in a row share a height, so the rules line up. No card rules off its
 * own bottom — the run ends where the page does.
 */
.featured {
  list-style: none;
  padding: 0;
  align-items: stretch;
}

.card {
  display: flex;
  flex-direction: column;
  border-top: var(--border-thickness) solid var(--sky-2);
  padding-top: calc(var(--line-height) / 2);
}

/* The card sets its own rhythm; a paragraph's default bottom margin would
   otherwise open twice the gap under the meta line that the title has above it. */
.card > * {
  margin: 0;
}

.card > * + * {
  margin-top: calc(var(--line-height) / 4);
}

.card a {
  font-weight: var(--font-weight-bold);
  text-decoration: none;
}

.card a:hover {
  text-decoration: underline;
}

.card .meta {
  color: color-mix(in srgb, var(--ink) 75%, var(--paper));
}

.card-blurb {
  font-size: 0.875rem;
}

.card-blurb p {
  margin: 0;
}

.card-blurb p + p {
  margin-top: calc(var(--line-height) / 2);
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 2ch;
  padding: 0;
  margin: 0 0 calc(var(--line-height) / 2);
  list-style: none;
  border-bottom: var(--border-thickness) solid var(--sky-4);
  padding-bottom: calc(var(--line-height) / 2);
}

.filters li {
  margin: 0;
}

.filters a {
  font-weight: var(--font-weight-medium);
}

.filters a[aria-current="page"] {
  color: var(--ink);
  text-decoration: none;
}

.catalogue th,
.catalogue td {
  border: 0;
  border-bottom: var(--border-thickness) solid var(--sky-2);
}

.catalogue .meta {
  width: 6ch;
  white-space: nowrap;
  font-size: 0.75rem;
}

/* The whole catalogue names each row's category; a category page does not. */
.catalogue-category {
  width: 10ch;
  white-space: nowrap;
  font-size: 0.875rem;
}

/*
 * Now is the same table as the catalogue, two columns wide: what the reader
 * is doing on the left, the blurb on the right. The first column carries the
 * short section names, so it gets the fixed width the year column has.
 */
.now-table th:first-child,
.now-table td:first-child {
  width: 14ch;
  white-space: nowrap;
}

.now-table td:first-child {
  font-weight: var(--font-weight-medium);
}

.catalogue-title a {
  font-weight: var(--font-weight-medium);
}

.catalogue-blurb {
  font-size: 0.875rem;
}

.catalogue-blurb p {
  margin: 0;
}

.catalogue-blurb p + p {
  margin-top: calc(var(--line-height) / 2);
}

.cover {
  margin: 0 0 calc(var(--line-height) * 1.5);
}

.cover img {
  width: 100%;
  height: auto;
  border: var(--border-thickness) solid var(--sky-2);
}

.links,
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 2ch;
  padding: 0;
  list-style: none;
}

.links {
  border-top: var(--border-thickness) solid var(--sky-2);
  padding-top: calc(var(--line-height) / 2);
}

/*
 * Project links open on the same rule they close on, like the About page's
 * primary links. A list that ends the page drops its closing rule; see below.
 */
.primary-links,
.ruled {
  border-bottom: var(--border-thickness) solid var(--sky-2);
  padding-bottom: calc(var(--line-height) / 2);
}

.links a {
  font-weight: var(--font-weight-medium);
}

/* The tags' own margin sets the gap under the links, not the list's. */
article > .links {
  margin-bottom: 0;
}

.tags {
  font-size: 0.875rem;
  color: color-mix(in srgb, var(--ink) 75%, var(--paper));
  /*
   * The tags are the article's last line: the gap above them matches the padding
   * inside the band the links sit in, so the rule under the links reads as the
   * rule between the two, and the article ends without one of its own.
   */
  margin-top: calc(var(--line-height) / 2);
  gap: 0;
}

/* The same separator the meta line uses, so the tags read as one line. */
.tags li + li::before {
  content: "·";
  margin: 0 1ch;
}

/*
 * A rule separates one row from the next, so a run never closes on one: the
 * last catalogue row, the last disclosure in a career section, the links that
 * end an article all stop without a line of their own. A rule left at the end
 * reads as one more row beginning beneath it.
 */
.primary-links:last-child,
.career-section > :last-child,
.catalogue tbody tr:last-child > *,
article > :last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

/*
 * The page ends on whatever its last element is: a table, a list of links, a
 * paragraph of prose. The gap to the footer's rule is the page's own padding,
 * so it is the same size whatever that element happens to be. Padding is used
 * here because it cannot collapse through the last element's bottom margin.
 */
main {
  padding-bottom: calc(var(--line-height) / 2);
}

main > :last-child,
main > :last-child > :last-child {
  margin-bottom: 0;
}

.site-footer {
  display: flex;
  flex-wrap: wrap;
  gap: 2ch;
  margin-top: 0;
  border-top: var(--border-thickness) solid var(--sky-4);
  padding-top: calc(var(--line-height) / 2);
  font-size: 0.875rem;
  font-weight: var(--font-weight-regular);
}

.site-footer a {
  margin-left: auto;
  font-weight: inherit;
}

.site-footer > * + * {
  margin-top: 0;
}

/*
 * The palette strip: the eight tokens as 1x1 squares, butted edge to edge with
 * no gap, in one row that never wraps and never shrinks. It is taken out of
 * flow and set into the paper, so it is furniture printed on the sheet rather
 * than content: the header and the footer wrap and measure as though it were
 * not there, and anything that needs the space draws over it. Vertically it
 * sits on the text line it belongs to, so it reads as part of that line.
 * Colours are assigned in token order, so the palette lives in one place.
 */
.swatches {
  /*
   * The squares inherit --swatch-size from the sheet, so the strip and the
   * paper's own inset stay one number. The strip never takes room from the
   * flow: the header and the footer lay out as if it were not there.
   */
  --swatch-inset-y: calc(var(--sheet-pad-y) + (var(--line-height) - var(--swatch-size)) / 2);
  font-size: var(--sheet-font-size);
  position: absolute;
  right: var(--sheet-pad-x);
  display: flex;
  flex-wrap: nowrap;
  justify-content: flex-end;
  pointer-events: none;
}

.swatches > * {
  display: block;
  flex: 0 0 var(--swatch-size);
  width: var(--swatch-size);
  height: var(--swatch-size);
}

.swatches > * + * {
  margin-top: 0;
}

.swatches > :nth-child(1) { background: var(--paper); }
.swatches > :nth-child(2) { background: var(--ink); }
.swatches > :nth-child(3) { background: var(--accent); }
.swatches > :nth-child(4) { background: var(--accent-bright); }
.swatches > :nth-child(5) { background: var(--sky-1); }
.swatches > :nth-child(6) { background: var(--sky-2); }
.swatches > :nth-child(7) { background: var(--sky-3); }
.swatches > :nth-child(8) { background: var(--sky-4); }

.site-header .swatches {
  top: var(--swatch-inset-y);
}

/*
 * The footer strip runs the other way, deep blue to paper, and hangs off the
 * left edge so the palette closes the sheet the way the header opens it.
 */
.site-footer .swatches {
  right: auto;
  bottom: var(--swatch-inset-y);
  left: var(--sheet-pad-x);
  flex-direction: row-reverse;
}

hr {
  position: relative;
  display: block;
  height: var(--line-height);
  margin: calc(var(--line-height) * 1.5) 0;
  border: none;
  color: var(--ink);
}

hr::after {
  display: block;
  content: "";
  position: absolute;
  top: calc(var(--line-height) / 2 - var(--border-thickness));
  left: 0;
  width: 100%;
  height: 0;
  border-top: calc(var(--border-thickness) * 3) double var(--ink);
}

ul,
ol {
  padding: 0 0 0 2ch;
}

ul {
  list-style-type: square;
}

ol {
  list-style-type: none;
  counter-reset: item;
}

ol li::before {
  content: counters(item, ".") ". ";
  counter-increment: item;
  font-weight: var(--font-weight-medium);
}

ol ul,
ol ol,
ul ol,
ul ul {
  padding: 0 0 0 3ch;
  margin: 0;
}

li {
  margin: 0;
  padding: 0;
}

li::marker {
  line-height: 0;
  font-weight: var(--font-weight-regular);
}

blockquote {
  border-left: var(--border-thickness) solid var(--accent);
  padding-left: calc(var(--line-height) / 2);
  font-weight: var(--font-weight-regular);
}

code,
pre {
  font-family: var(--font-family);
  font-size: 0.875rem;
}

code {
  font-weight: var(--font-weight-medium);
  background: var(--sky-3);
}

pre {
  white-space: pre;
  overflow-x: auto;
  overflow-y: hidden;
  padding: var(--line-height);
  background: var(--sky-1);
}

pre code {
  background: transparent;
}

img,
video,
canvas {
  display: block;
  max-width: 100%;
  height: auto;
}

img {
  font-style: italic;
  color: color-mix(in srgb, var(--ink) 65%, var(--paper));
}

table {
  width: 100%;
  border-collapse: collapse;
}

/*
 * The body starts one half-step below the last bar, which is the same gap the
 * footer keeps above its rule, so the sheet is framed evenly top and bottom.
 * The body's own first element gives up its margin too, or its heading would
 * collapse through the block and push the body back down again.
 */
nav + *,
nav + * > :first-child {
  margin-top: 0;
}

/* Now's table keeps a larger step under the date line than the work pages do
   under their filter bar. */
.catalogue.now-table {
  margin-top: calc(var(--line-height) * 1.5);
}

th,
td {
  border: var(--border-thickness) solid var(--ink);
  padding:
    calc(var(--line-height) / 2)
    calc(1ch - var(--border-thickness) / 2)
    calc((var(--line-height) / 2) - var(--border-thickness));
  line-height: var(--line-height);
  vertical-align: top;
  text-align: left;
}

th {
  font-weight: var(--font-weight-bold);
}

@supports (width: calc(round(down, 100%, 1ch))) {
  table {
    width: calc(round(down, 100%, 1ch));
  }
}

.grid {
  --grid-cells: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 1ch;
  width: 100%;
  margin-bottom: var(--line-height);
}

.grid > * {
  flex: 0 0 calc((100% - (1ch * (var(--grid-cells) - 1))) / var(--grid-cells));
  margin-top: 0;
}

.grid:has(> :nth-child(2)) { --grid-cells: 2; }
.grid:has(> :nth-child(3)) { --grid-cells: 3; }

@supports (width: calc(round(down, 100%, 1ch))) {
  .grid {
    width: calc(round(down, 100%, 1ch));
  }

  .grid > * {
    flex-basis: calc(round(down, (100% - (1ch * (var(--grid-cells) - 1))) / var(--grid-cells), 1ch));
  }
}

/*
 * The Work tabs are ordinary links to real pages, and the career page is the
 * narrow record on the site's normal shell.
 */
.tab-bar {
  margin-bottom: calc(var(--line-height) * 1.5);
}

.tab-bar ul {
  display: flex;
  flex-wrap: wrap;
  gap: 2ch;
  padding: 0 0 calc(var(--line-height) / 2);
  margin: 0;
  list-style: none;
  border-bottom: var(--border-thickness) solid var(--sky-4);
}

.tab-bar li {
  margin: 0;
}

.tab-bar a {
  font-weight: var(--font-weight-medium);
}

.tab-bar a[aria-current="page"] {
  color: var(--ink);
  text-decoration: none;
}

.career-section > * + * {
  margin-top: var(--line-height);
}

.career-section h2 {
  /* A section break is one row's gap, not the two steps a heading takes by
     default: the previous section already ended without a closing rule. */
  margin-top: var(--line-height);
  border-bottom: var(--border-thickness) solid var(--sky-2);
  padding-bottom: calc(var(--line-height) / 2);
  margin-bottom: calc(var(--line-height) / 2);
}

/*
 * The heading's own half-line sets the gap under its rule, or the section's
 * standard paragraph gap would win and leave the first row sitting twice as
 * far down as every row below it.
 */
.career-section h2 + * {
  margin-top: 0;
}

/*
 * The career record folds each role, each qualification and each skill group
 * into a native <details>, the monospace-web disclosure: the summary is the
 * index line the reader scans, and the marker is a solid triangle that turns
 * to point down when the record opens. No JavaScript is involved, and the
 * whole record is still in the markup and in the print output.
 */
.career-disclosure {
  border-bottom: var(--border-thickness) solid var(--sky-2);
  padding-bottom: calc(var(--line-height) / 2);
}

.career-disclosure + .career-disclosure {
  margin-top: calc(var(--line-height) / 2);
}

.career-disclosure > summary {
  cursor: pointer;
  font-weight: var(--font-weight-medium);
}

.career-disclosure > summary::marker {
  content: "\25B6\00A0";
}

.career-disclosure[open] > summary::marker {
  content: "\25BC\00A0";
}

/*
 * One gap under every summary, whatever the record holds: 12px between the
 * index line and the dates, the prose or the skills. The gap rides on the
 * first child rather than the summary's margin-bottom so it does not depend
 * on margin collapsing through the details slot.
 */
.career-disclosure[open] > summary {
  margin-bottom: 0;
}

.career-disclosure > :not(summary) {
  margin: 0;
}

.career-disclosure > summary + * {
  margin-top: 0;
}

.career-disclosure[open] > summary + * {
  margin-top: calc(var(--line-height) / 2);
}

.career-disclosure > * + * {
  margin-top: calc(var(--line-height) / 4);
}

.career-meta {
  margin: 0;
}

.career-meta .meta,
.career-meta .career-sep,
.career-meta .career-note {
  margin: 0;
  color: color-mix(in srgb, var(--ink) 75%, var(--paper));
}

.career-meta .career-dates {
  margin: 0;
}

.career-dates,
.career-note {
  font-size: 0.875rem;
  color: color-mix(in srgb, var(--ink) 75%, var(--paper));
}

.career-body {
  margin-top: calc(var(--line-height) / 2);
}

.career-body > * + * {
  margin-top: calc(var(--line-height) / 2);
}

.career-body p,
.career-body ul {
  margin: 0;
}

/*
 * Skills read as a list, wrapped into two columns when the sheet is wide
 * enough for two readable ones and one when it is not.
 */
.skill-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0 2ch;
  margin: 0;
  padding-left: 2ch;
  list-style-type: square;
}

.skill-list li {
  flex: 1 1 30ch;
  margin: 0;
}

@media print {
  :root {
    --line-height: 1rem;
    --border-thickness: 1px;
  }

  html,
  body {
    background: #fff;
    color: #000;
  }

  body {
    max-width: none;
    padding: 0;
    font-size: 8pt;
  }

  * + * {
    margin-top: 0.2rem;
  }

  h1 {
    font-size: 1.25rem;
    line-height: 1.35rem;
    margin: 0 0 0.25rem;
  }

  h2 {
    font-size: 0.8rem;
    margin: 0.6rem 0 0.15rem;
    padding-bottom: 0.05rem;
  }

  h3 {
    font-size: 0.85rem;
    margin: 0.25rem 0 0.05rem;
  }

  .sheet {
    border: 0;
    padding: 0;
  }

  .sky,
  .tab-bar,
  .site-header,
  .site-footer {
    display: none;
  }

  .career-disclosure + .career-disclosure {
    margin-top: 0.5rem;
  }

  .career-disclosure {
    break-inside: avoid;
  }

  /* Closed disclosures would print as bare summaries, so open them on paper. */
  details::details-content {
    content-visibility: visible;
  }

  details > *:not(summary) {
    display: block;
  }

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

  @page {
    size: letter;
    margin: 0.45in;
  }
}

/*
 * Narrow viewports. The site is one column above this, so the featured grid
 * and the catalogue keep their reading order and simply stop sitting side by
 * side. The header and filters move to their own lines rather than leaving a
 * single word orphaned on a second row.
 */
@media screen and (max-width: 760px) {
  .grid > * {
    flex-basis: 100%;
  }
}

@media screen and (max-width: 560px) {
  .sheet {
    --sheet-pad-x: 2ch;
    --sheet-pad-y: calc(var(--line-height) / 2);
    --swatch-size: 1rem;
  }

  .site-header {
    flex-direction: column;
    align-items: flex-start;
    gap: calc(var(--line-height) / 4);
  }

  .site-footer {
    gap: 1ch;
    font-size: 0.75rem;
  }

  .site-header nav {
    gap: 1ch;
    font-size: 0.8125rem;
  }

  .filters {
    gap: 1.5ch;
    font-size: 0.75rem;
  }

  .links,
  .tags {
    gap: 1ch;
  }
}

@media screen and (max-width: 640px) {
  .catalogue,
  .catalogue tbody,
  .catalogue tr,
  .catalogue td {
    display: block;
    width: 100%;
  }

  .catalogue thead {
    display: none;
  }

  .catalogue tr {
    border-bottom: var(--border-thickness) solid var(--sky-2);
    padding: calc(var(--line-height) / 2) 0;
  }

  .catalogue td {
    border-bottom: 0;
    padding: 0;
  }

  .catalogue td:first-child {
    width: auto;
    white-space: normal;
  }

  .catalogue .meta {
    width: auto;
    white-space: normal;
  }

  .catalogue td + td {
    margin-top: calc(var(--line-height) / 4);
  }
}
