/* ------------------------------------------------------------------
   Visual reproduction of mgaudet.ca.

   Every value here was read from the live site — either from its own
   site.css rules or from computed styles probed in-browser — not guessed.

   Key live values:
     body            #f0f5f1 bg, #263333 ink, 20.5px Crimson Pro, line-height 1.8
     #header         y=85, centred, then .page-divider (1px #fff)
     .site-title     Inconsolata 40px/1.8, letter-spacing 0, #263333
     #topNav a       Inconsolata 22px/1.2, letter-spacing 2px, capitalize
     .entry-title    Merriweather 1.2em (30px), weight 300, letter-spacing 2px,
                     capitalize, rgba(38,51,51,.9), line-height 1.2em
     .meta           #242d2d, centred, italic date
     article/.body   700px wide at x=290, .body margin 17px top
   ------------------------------------------------------------------ */

@import url("/css/fonts.css");

* { box-sizing: border-box; }

html { -webkit-font-smoothing: antialiased; }

body {
  margin: 0;
  background-color: #f0f5f1;
  color: #263333;
  /* Crimson Pro replaced minion-pro on 2026-10-29: Minion Pro cannot be
     self-hosted (docs/font-licensing.md). It is set at 20.5px, not 19px,
     because its x-height is 0.96x Minion's at the same nominal size — at 19px
     it measured a full optical step small. At 20.5px the x-height is 1.04x
     Minion's and the measure is ~84 characters per 700px line instead of ~90. */
  font-family: "Crimson Pro", Georgia, serif;
  font-weight: 400;
  font-size: 20.5px;
  line-height: 1.8em;
  font-style: normal;
}

a, a:link, a:visited { color: #9d0202; }
a:hover, a:active { color: #d60004; }

/* ---------- masthead ----------
   Live geometry:
     #canvas    (205,0,870) padding-top:85px
     #header    (290,85,700,104)  margin-bottom:0
     .page-divider.top-divider (290,219,700,2) margin-top:30px
     #page      (290,255,700)  margin 34px 0
   The 870px #canvas and 700px #header are what centre the content; the
   masthead is NOT full-bleed even though it looks like it. */
#canvas {
  max-width: 870px;
  margin: 0 auto;
  padding-top: 85px;
}

/* ---------- masthead: canvas-style-masthead-logo-left ----------
   The live body class list includes `canvas-style-masthead-logo-left`. Its
   consequences, verified by probe:
     #upper-logo             display:none in this canvas style
     #lower-logo             (290, 85, 280, 72) float:left, shrink-to-fit
     #lower-logo h1.logo     display:inline-block, text-align:start
     #topNav                 (294, 157, 696, 32), nav text centred inside it

   So the site title sits at the LEFT content edge and the nav occupies the
   line below. An earlier version centred the title in a 700px div, which put
   it 290px right of live — 40px out on a 1280px viewport, on the most visible
   element of every page. It looked plausible in isolation; only a side-by-side
   comparison showed it. */
#header {
  position: relative;
  /* 700px is what makes #lower-logo start at x=290 inside the 870px #canvas.
     It is NOT full-bleed; the masthead only looks full width. */
  max-width: 700px;
  margin: 0 auto;
  text-align: start;
}

#lower-logo {
  float: left;
  margin: 0;
  /* Live computes line-height:0 here (the logo rules set 0 and the h1
     restores 1.8em). Keeping the inherited 34.2px adds an extra line box and
     pushes the nav onto a third line. */
  line-height: 0;
}

#lower-logo h1.logo {
  margin: 0;
  display: inline-block;
  width: auto;
  font-family: Inconsolata, Menlo, monospace;
  font-weight: 700;
  font-size: 40px;
  line-height: 1.8em;
  letter-spacing: 0;
  text-transform: none;
  text-align: start;
}

/* Live: rgba(38,51,51,.9) — same ink, slightly lighter than solid. */
#lower-logo h1.logo a {
  color: rgba(38, 51, 51, .9);
  text-decoration: none;
}
#lower-logo h1.logo a:hover { color: #c70000; }

/* #header owns two floats, so it must clear them or the divider rides up. */
#header::after { content: ""; display: table; clear: both; }

/* Mobile-only control; the desktop nav is #topNav. The live page hides
   #topNav below 760px and shows #mobileMenuLink (a centred "Menu"), so this
   disclosure is the non-JS equivalent of that control. */
#mobileMenu { display: none; }

/* #topNav: Inconsolata 22px, 2px tracking, capitalised */
/* Flex rather than inline-block: inline-block adds a text-space (~10px) per
   newline between <li> elements, which is enough to wrap this nav inside its
   700px column. flex+gap gives exact, whitespace-independent spacing. */
#topNav {
  /* Live #topNav is float:right but occupies the full 700px column (x=294,
     w=696), so the nav is on its own line below the logo with its text
     centred. `clear: both` after the floated #lower-logo is what puts it on
     that line; width:100% then makes the centring match. */
  clear: both;
  width: 100%;
}

#topNav nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 30px;
  font-family: Inconsolata, Menlo, monospace;
  font-size: 22px;
  line-height: 1.2em;
  letter-spacing: 2px;
  text-transform: capitalize;
}

#topNav nav ul li {
  display: block;
  margin: 0;
  /* Six items measure 546px of text + 5x30px gaps = 696px: it only just fits
     the 700px column, so nothing may add width here. */
  white-space: nowrap;
}

#topNav nav ul li a,
#topNav nav ul li summary {
  display: inline-block;
  padding: 2px 0;
  margin: 0;
  color: #263333;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  white-space: nowrap;
}

#topNav nav ul li a:hover,
#topNav nav ul li summary:hover {
  color: #a30003;
  border-bottom-color: #fff;
}

#topNav nav ul li.active-link a {
  color: #6b6b6b;
  border-bottom-color: #fff;
}

/* "Projects" is a folder of external GitHub Pages sites, not a page. It is a
   native <details> disclosure so keyboard, touch and screen readers can open it
   without JS and without a focusable `href="#"` that goes nowhere. The closed
   summary must measure exactly like the other nav links (same font, padding and
   transparent underline): the six items are 696px of a 700px column. */
#topNav li.has-dropdown { position: relative; }

#topNav li.has-dropdown details { display: inline-block; }

#topNav li.has-dropdown summary {
  cursor: pointer;
  list-style: none;
}

#topNav li.has-dropdown summary::-webkit-details-marker { display: none; }

#topNav li.has-dropdown ul.subnav {
  display: none;
  position: absolute;
  left: 0;
  top: 100%;
  z-index: 10;
  min-width: 260px;
  padding: 8px 0;
  margin: 0;
  background: #f0f5f1;
  text-align: left;
  font-size: 16px;
  letter-spacing: 1px;
  border: 1px solid #e2e8e2;
}

#topNav li.has-dropdown details[open] ul.subnav { display: block; }

#topNav li.has-dropdown ul.subnav li { display: block; }

#topNav li.has-dropdown ul.subnav li a {
  display: block;
  margin: 0;
  padding: 4px 14px;
  border-bottom: none;
}

/* .page-divider is a white rule (2px total height incl. rounding), not grey */
.page-divider {
  max-width: 700px;
  margin: 0 auto;
  border-bottom: 1px solid #fff;
}
.page-divider.top-divider {
  margin-top: 30px;
}
#page {
  position: relative;
  max-width: 700px;
  margin: 34px auto;
}

/* ---------- article ---------- */
.article-wrapper { margin: 0; }

article {
  width: 100%;
  max-width: 700px;
  margin: 0 auto;
}

article header {
  text-align: center;
  /* live: article > header has margin-bottom:45px, which is the whole gap
     between the meta line and the first paragraph. */
  margin-bottom: 45px;
}

/* title: Merriweather 1.2em of 19px = 30px */
.collection-type-blog article .entry-title,
article .entry-title {
  font-family: Merriweather, Georgia, serif;
  font-weight: 300;
  font-style: normal;
  font-size: 30px;
  line-height: 1.2em;
  letter-spacing: 2px;
  text-transform: capitalize;
  color: rgba(38, 51, 51, .9);
  margin: 0 0 9px;
}

article .entry-title a,
article .entry-title a:link,
article .entry-title a:visited {
  color: rgba(38, 51, 51, .9);
  text-decoration: none;
}
article .entry-title a:hover { color: #d60004; }

article header .meta {
  color: #242d2d;
  /* The body size, not a fixed one: the byline is the body face set straight
     after the title, and it read small the moment the body grew. */
  font-size: 20.5px;
  line-height: 1.8em;
  margin-bottom: 0;
}

/* Live: `article .date, article .author, article .entry-source` are
   font-size .85em (16.15px of 19px), letter-spacing 2px, italic. Only the
   italic was reproduced before, so the byline rendered 2.85px too tall and
   12px too wide and sat 5px off -- small, but it is the line under every
   title on every list page. */
article header .meta .date,
article header .meta .author {
  font-size: .85em;
  letter-spacing: 2px;
  font-style: italic;
}

/* The date and author are LINKS (they link to the permalink / author page),
   so without this they inherit the site-wide link colour and underline and the
   byline renders red-underlined under every title.

   Live: `.collection-type-blog article .meta, article .meta a { color: #242d2d }`
   — the link colour is reasserted to the meta ink, and there is no underline.
   Measured: live rgb(36,45,45) vs local rgb(157,2,2) before this rule. */
article header .meta a,
article header .meta a:link,
article header .meta a:visited {
  color: #242d2d;
  text-decoration: none;
}

article header .meta a:hover { color: #d60004; }

/* The live body carries .hide-article-author, so only the date shows in the
   byline. Without this the " / Matthew Gaudet" adds a separator and changes the
   line's width and centring. */
.hide-article-author article header .meta .author { display: none; }
article header .meta span + span { margin-left: .5em; }
article header .meta span + span:before {
  content: "/";
  padding-right: .5em;
}

/* The header's 45px margin-bottom supplies the gap; .body itself has none. */
article .body.entry-content {
  margin: 0 auto 24px;
  padding-top: 0;
  max-width: 100%;
}

.body p { margin: 0 0 1.8em; }

.body h1, .body h2, .body h3, .body h4, .body h5, .body h6 {
  margin: 1em 0 .3em;
}

/* Live sets `overflow-wrap: break-word` on body text (measured on
   /blog/2014/1/14/... at 390px). Without it a raw URL used as link text —
   `http://en.wikipedia.org/wiki/Self_(programming_language)`, 486px at 20.5px —
   pushes the document to 557px wide on a phone and the page scrolls sideways.
   `break-word`, not `anywhere`: it only breaks a word that cannot fit, so
   normal wrapping and min-content widths are untouched. */
.body, .page-content { overflow-wrap: break-word; }

.body h1 {
  font-family: Merriweather, Georgia, serif;
  font-weight: 300;
  font-size: 1.3em;
  line-height: 1.3em;
  letter-spacing: 1px;
  text-transform: capitalize;
  color: rgba(38, 51, 51, .9);
}

.body h2 {
  font-family: Merriweather, Georgia, serif;
  font-weight: 300;
  font-size: 1.2em;
  line-height: 2.2em;
  letter-spacing: 1.11px;
  text-transform: none;
  color: rgba(38, 51, 51, .9);
}

.body h3 {
  font-family: Merriweather, Georgia, serif;
  font-weight: 300;
  font-size: .9em;
  line-height: 1.5em;
  letter-spacing: 1px;
  color: rgba(37, 50, 50, .9);
}

.body ul, .body ol, .body dl {
  margin: 1em 0;
  /* live list markers start at x=330 in a column beginning at 290 => 40px */
  padding-left: 40px;
}

/* Live list items have margin 0. Adding the usual 0.4em rhythm here is
   enough to push everything below down by 15px per list -- which accumulates
   to ~600px of drift over a long article. */
.body li { margin: 0; }

/* pandoc emits "loose" lists: each <li> wraps its text in a <p>. Those inner
   paragraphs otherwise carry the global 1.8em bottom margin, inflating every
   list item by ~34px.

   The live site has NO paragraph wrappers inside list items at all -- its
   list text is a direct text node of the <li>. So the inner <p> must be
   display:inline to reproduce line-box behaviour exactly; margin:0 alone
   still leaves the <p> as a block and changes item height by one line on
   multi-paragraph items. */
.body li > p {
  display: inline;
  margin: 0;
}

/* A genuinely multi-paragraph list item must stay block-level. */
.body li > p + p {
  display: block;
  margin-top: 1em;
}

.body li > p:only-child { display: inline; }

.body li > ul,
.body li > ol {
  margin-top: 0;
  margin-bottom: 0;
}

.body blockquote {
  margin: 1em 0;
  padding: 0 0 0 1em;
  border: 1px solid #263333;
  border-width: 0 0 0 1px;
  font-style: italic;
}

.body a { color: #9d0202; text-decoration: underline; }

.body img { max-width: 100%; height: auto; }

/* Responsive images are wrapped in <picture> so the browser can pick a WebP
   source while keeping the original <img> as fallback. No display override:
   `display: contents` is the tidy choice for descendant selectors, but
   Chromium's full-page (captureBeyondViewport) shot does not paint an image
   inside a contents-picture, which makes every diff show a blank box. The
   default inline wrapper keeps the measured layout (verified against live:
   same 700px images, same page heights) and paints normally. */

/* live: 525px wide inside the 700px column (75%), 19px vertical margins */
.body hr {
  border: 0;
  border-top: 1px solid #263333;
  margin: 19px auto;
  width: 75%;
  opacity: .35;
}

.body code {
  font-family: Inconsolata, Menlo, monospace;
  /* 500, not 400: Inconsolata's 400 is a light, narrow cut that reads a step
     under a 20.5px serif — that is the "anemic" code. .95em, not .88em: at .88
     the x-height measured 0.92x the body's, and in the shipped setting it
     measures 8.91px against the body's 8.61px (1.03x) — matched, not small.
     Both measurements are in docs/font-licensing.md. */
  font-weight: 500;
  font-size: .95em;
}

.body pre {
  font-family: Inconsolata, Menlo, monospace;
  font-weight: 400;
  /* 16px, not 15: the body grew 7.9% and a fixed block size would have shrunk
     relative to it. Blocks stay 400 — the heavier cut exists to compensate for
     running inside prose, which a paneled block does not do. */
  font-size: 16px;
  line-height: 1.5;
  background: #e9efe9;
  padding: 16px;
  overflow-x: auto;
  margin: 1.8em 0;
}

/* A block keeps the 400 face: `.body code` is 500 so inline code holds up
   against 20.5px prose, but inside a panel that extra weight just looks bold. */
.body pre code {
  /* `inherit`, not the .95em `.body code` carries: inside a block the `pre`
     size (16px) is the truth. Without this the .95em compounds and blocks
     render at 15.2px. */
  font-size: inherit;
  font-weight: 400;
  background: none;
  padding: 0;
}

.body table { border-collapse: collapse; margin: 1.8em 0; }
.body th, .body td { border: 1px solid #cfd6cf; padding: 8px 12px; }

.body iframe, .body figure { max-width: 100%; }

/* ---------- embeds (YouTube / Vimeo / SlideShare / Giphy) ----------
   The converter keeps `embed-block-wrapper embed-block-provider-X` and the
   `<iframe>`, but Squarespace's aspect ratio lived in an INLINE STYLE on the
   wrapper --
       <div class="embed-block-wrapper embed-block-provider-YouTube"
            style="padding-bottom:56.5%">
   -- which the HTML->Markdown round trip dropped. Without it the wrapper has
   height 0 and the iframe falls back to its own width/height attributes
   (`width="200" height="113"`), so every embed rendered at 200x113 in the
   middle of a 670px column. That is a visible, wrong-sized video on 39 posts.

   The ratio is implied by the provider, so restoring it in CSS fixes all of
   them at once rather than re-deriving per-embed values. */
.body .embed-block-wrapper iframe { max-width: 100%; }

.body .embed-block-wrapper {
  position: relative;
  width: 100%;
  height: 0;
  /* 16:9 is what YouTube emits (56.5%), and is right for Vimeo too. */
  padding-bottom: 56.5%;
}

.body .embed-block-wrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* SlideShare renders a taller box on the live site and is not 16:9. */
.body .embed-block-provider-SlideShare { padding-bottom: 68%; }

/* ---------- image shortcode and embeds ----------
   Blog/technical images are emitted by `shortcodes/figure.html`, not by the
   Squarespace image-block scaffold: the 151-character wrapper class, the
   hidden "View fullsize" lightbox label and `sizes=` without a `srcset` are
   gone, and alt text is explicit. The 1.8em rhythm is the article's. */
.body .image-figure,
.body .embed-block-wrapper {
  display: block;
  max-width: 100%;
  margin: 1.8em 0;
}

.body .image-figure img {
  display: block;
  max-width: 100%;
  height: auto;
}
/* Live's figure box ends at the caption text; the 21.6px bottom margin of its
   caption paragraph sits outside the figure, before the next block's 1.8em
   rhythm. Without this, that margin collapses into the figure's bottom margin
   and every image pair loses 21.6px, which accumulated to ~380px over this
   24-image post. */
.body .image-figure.has-caption { margin-bottom: calc(1.8em + 21.6px); }

/* R11: the one 2-up gallery is a grid, not Squarespace's unwrapped columns.
   Live at 1280px: two 333px columns at x=290 and x=657 inside 700px, i.e.
   (700 - 34) / 2. Below 640px the source's `sizes` switches to 100vw and the
   rows stack. */
.body .gallery {
  display: grid;
  align-items: start;
  gap: 34px;
  margin: 1.8em 0;
}
.body .gallery-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.body .gallery .image-figure { margin: 0; }

/* Five standalone-page images retain their original aspect-ratio crop. The
   images are absolutely positioned inside a padding-bottom box, so the box
   must stay; only the vendor class names were renamed. */
.image-shape {
  position: relative;
  overflow: hidden;
}
@media (max-width: 640px) {
  .body .gallery-cols-2 { grid-template-columns: 1fr; }
}

/* Live's caption wrapper: 18px of padding below the image, left-aligned body
   text colour, and a 12px/20.16px paragraph inside a 19px inheriting block.
   Measured on /blog/2015/12/25/japan-part-2-day-tour/; the old italic grey
   centred caption was a guess and visibly wrong against the reference. */
.body figcaption {
  padding-top: 18px;
  text-align: left;
}
.body .image-caption {
  display: block;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: inherit;
}
.body figcaption p {
  margin: 0 0 21.6px;
  font-size: 12px;
  line-height: 20.16px;
}

/* ---------- promoted block (featured image) ----------
   `.special-content` is Squarespace's "Promoted Block": the image between a post
   header and `.body`. It is NOT inside `.body`, so none of the `.body` image
   rules reach it -- and until this fix nothing styled it at all, so the
   preserved Squarespace scaffolding rendered on UA defaults only:

     figure    UA `margin: 1em 40px` shrank the column to 700-80 = 620px
     <img>     kept its intrinsic width (1680px on
               /blog/2026/9/15/youve-got-the-wrong-guy/), overflowing the ratio
               box, which carries inline `overflow: hidden` -- so the picture was
               clipped to its left third
     ratio box its inline `padding-bottom` (96.67%) is a percentage of *width*,
               so with the <img> still in flow it was added *below* the image:
               599px of dead space on that post, under the crop.

   That combination -- a cropped picture and a page far too long -- was reported
   as a visual bug and is why the block is now generated markup
   (layouts/partials/promoted.html) instead of preserved HTML.

   Live, measured at 1280px on /blog/2026/9/15/youve-got-the-wrong-guy/ (no
   caption) and /blog/2014/3/5/current-status/ (with one):
     .special-content   margin 0 0 45px, and its children's margins stay inside
                        it (live block height = image + caption + caption margin)
     figure             margin auto, max-width = the image's intrinsic width:
                        a 500px photo stays 500px inside the 700px column
     image              full width of the figure, height from the ratio
     gap after image    18px, reserved even when the caption is empty (live's
                        `.image-caption-wrapper` has padding-top 18px around an
                        empty div, so an uncaptioned block is 400+18 = 418px)
     caption            12px / 1.68, #263333, left-aligned, and its paragraph
                        still carries the global 1.8em bottom margin -- which
                        live leaves inside the block: 459.8px = 400 image +
                        38.2 caption area + 21.6 margin. */
.special-content {
  margin: 0 0 45px;
  display: flow-root;          /* keep the caption's 1.8em margin inside */
}
.special-content .promoted-image {
  margin: auto;
  max-width: 100%;
  /* The 18px live keeps under the image, caption or not. With a caption the gap
     belongs above the text instead, so the block total stays 18px longer than
     the image either way. */
  padding-bottom: 18px;
}
.special-content .promoted-image:has(figcaption) { padding-bottom: 0; }
.special-content .promoted-image img {
  display: block;
  width: 100%;
  height: auto;
}
.special-content .promoted-image figcaption {
  padding-top: 18px;
  font-size: 12px;
  line-height: 1.68;
  color: #263333;
  margin: 0 0 1.8em;
}

/* ---------- promoted gallery ----------
   The other promoted-block type: a square photo grid. Live at 1280px on
   /blog/2020/4/10/a-sad-sign-of-pandemic/ is 700x480 -- two rows of 220px
   squares, a 20px gutter, and 20px after the last row. At 390px it is
   356x564: three rows of 168px squares in two columns, the same gutter and
   trailing 20px, plus 17px of live's mobile `.sqs-block` padding above and
   below. Live cover-crops each photo from a 50% focal point with inline
   left/top offsets; `object-fit: cover` reproduces that crop without JS. */
.special-content .promoted-gallery {
  display: grid;
  grid-template-columns: repeat(var(--promoted-columns, 3), minmax(0, 1fr));
  gap: 20px;
  padding-bottom: 20px;
}
.special-content .promoted-gallery-item { margin: 0; }
.special-content .promoted-gallery-link {
  display: block;
  aspect-ratio: 1 / 1;
  overflow: hidden;
}
.special-content .promoted-gallery-link img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 767px) {
  /* Live's mobile promoted block: 15px below instead of 45px, and the gallery
     block picks up `.sqs-block`'s 17px top/bottom padding. */
  .special-content { margin-bottom: 15px; }
  .special-content:has(.promoted-gallery) { padding: 17px 0; }
  .special-content .promoted-gallery {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---------- list pages ----------
   The front page, /blog/ and /technical/ are the live site's Squarespace
   `view-list` blog template: FULL posts, not excerpts. Measured on live at
   1280px, first post of `/`:
     #page                 (290, 255, 700)
     .article-list         (305, 255, 670)   <- max-width 670, centred
     article               (305, 255, 670)
     article header        margin 0 auto 45px, text-align centre
     h1.entry-title        Merriweather 30/300, centred, h=36 for one line
     article .body         (305, 381, 670)   <- full width, no indent
     article + article     margin-top 41px
   No per-item title restyling and no excerpt box: anything that re-scopes the
   title here is how it ended up bold and red instead of matching a post page. */
.article-list {
  max-width: 670px;
  margin: 0 auto;
}

/* Consecutive posts are separated by 41px, not by a margin on a wrapper div.
   Live: `article + article { margin-top: 41px }`. */
.article-list article + article { margin-top: 41px; }

/* List articles are wider than the article column and must not inherit the
   700px cap that single posts use. */
.article-list article { max-width: 100%; }

/* ---------- pagination ----------
   Live: `nav.pagination` margin 30px 0, padding 7.5px 0, 1px white rules top
   and bottom, each link 50% wide, the older link floated right. */
nav.pagination {
  margin: 30px 0;
  padding: 7.5px 0;
  border: 1px solid #fff;
  border-width: 1px 0;
}

nav.pagination a {
  display: block;
  width: 50%;
  float: left;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

nav.pagination a.older-posts {
  float: right;
  text-align: right;
}

nav.pagination::after { content: ""; display: table; clear: both; }

@media (max-width: 760px) {
  /* Live at 390px: #canvas padding 0 17px (it is padding-top:85px only on
     desktop), #header padding-top 30px + margin-bottom 30px, the title is
     float:none and fills the 356px content column at x=17, and #page sits at
     y=222 after the collapsed 30px header/divider/page margins. */
  #canvas { padding: 0 17px; }
  #header { padding-top: 30px; margin-bottom: 30px; }
  #lower-logo { float: none; }
  #lower-logo h1.logo { width: 100%; font-size: 40px; }
  #topNav { display: none; }

  /* Live #mobileMenuLink is 26.4px of text plus 15px top and 17px bottom,
     i.e. the 58.4px block above the header. */
  #mobileMenu { display: block; text-align: center; }
  #mobileMenu > summary {
    display: block;
    padding: 15px 0 17px;
    font-family: Inconsolata, Menlo, monospace;
    font-size: 16px;
    font-weight: 400;
    line-height: 26.4px;
    letter-spacing: 2px;
    cursor: pointer;
    list-style: none;
  }
  #mobileMenu > summary::-webkit-details-marker { display: none; }
  #mobileMenu .mobileNav ul {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    margin: 0 0 18px;
    padding: 0;
    list-style: none;
  }
  /* Live's mobile drawer uses Inconsolata 16px ink, not the global red
     underlined link treatment. */
  #mobileMenu .mobileNav a,
  #mobileMenu .mobileNav summary {
    color: rgb(38, 51, 51);
    font-family: Inconsolata, Menlo, monospace;
    font-size: 16px;
    letter-spacing: 2px;
    text-decoration: none;
  }
  #mobileMenu .mobileNav a:hover { color: #c70000; }

  #page { margin: 30px auto; }
  .page-divider.top-divider { height: 2px; }
  .collection-type-blog article .entry-title,
  article .entry-title {
    font-size: 22.8px;
    margin-bottom: 6.84px;
    letter-spacing: 2px;
  }
  article > header { margin-bottom: 19px; }
  /* Live's body is one .sqs-layout wrapper; at mobile that wrapper starts the
     first paragraph 17px below the body box. The unwrapped body has no such
     child, so put the offset on the body itself. */
  article .body.entry-content { margin-top: 17px; padding-top: 17px; }
  /* The canvas now owns the 17px gutter; adding it here again made the
     article 18px narrower than live on every mobile page. */
  article, .article-list { padding: 0; }
}

/* --- static pages (/about, /archives) -------------------------------------
   The heading rules above are all scoped to `.body`, which is the article
   wrapper. Standalone pages use `.page-content` instead, so their headings need
   their own rules or they fall back to the article treatment (Merriweather,
   uppercase h3) rather than the text-block treatment.

   `.page-content` replaced `.sqs-html-content` in R4: that was Squarespace's
   text-block wrapper, it survived in exactly two places (/about's content and
   the /archives headings), and keeping it meant two heading scopes with the
   same specificity, where the winner was "whichever rule is later in this
   file". There is now one static-page scope.

   These are the BODY FACE, not Merriweather: on the live site the h1/h2/h3
   inside a text block are the body face at a larger size, weight 400, no text
   transform. Measured on /about and /archives at 1280px, back when the body
   was minion-pro at 19px — the em ratios are the measurement, the px are one
   body size's arithmetic and have moved with the swap to 20.5px:
     h1  1.3em (was 24.7px of 19) / 1.3em, ls 1px, rgba(38,51,51,.9)
     h3  .9em  (was 17.1px of 19) / 1.5em, ls 1px, rgba(37,50,50,.9)
   An earlier version of this file used Merriweather + uppercase h3 here, which
   is the *article title* treatment, not the text-block treatment. */
.page-content h1,
.page-content h2,
.page-content h3 {
  font-family: "Crimson Pro", Georgia, serif;
  font-weight: 400;
  font-style: normal;
  letter-spacing: 1px;
  text-transform: none;
  margin: 1em 0 .3em;
}

.page-content h1 {
  font-size: 1.3em;
  line-height: 1.3em;
  color: rgba(38, 51, 51, .9);
  /* Live puts each Squarespace text block in its own 17px-padded box, so the
     gap between the "About" heading block and the first paragraph is 17px +
     17px = 34px. The content is now one flow (no per-block wrappers), so the
     block boundary is modelled as margin on the heading that starts it. */
  margin-bottom: 34px;
}

.page-content h2 {
  font-size: 1.2em;
  line-height: 2.2em;
  letter-spacing: 1.11px;
  color: rgba(38, 51, 51, .9);
  /* Same 34px block boundary, before the heading. Adjacent margins collapse
     with the preceding list's 1em bottom margin, hence 34 rather than 34+19. */
  margin-top: 34px;
}

.page-content h3 {
  font-size: .9em;
  line-height: 1.5em;
  color: rgba(37, 50, 50, .9);
}

/* Live static-page links are ink-red with NO underline (measured
   `text-decoration-line: none` on /about). `.body a` underlines them, and on a
   static page the old markup also sat inside `.body`; scope the correction to
   the static-page wrapper instead of weakening the article rule. */
.page-content a,
.page-content a:link,
.page-content a:visited { text-decoration: none; }

.page-content a:hover { text-decoration: underline; }

/* ---------- /archives ---------- */
/* Live: a 700px row with two 333px columns at x=290 and x=657, i.e.
   (700 - 34) / 2 with a 34px gap. The old implementation reproduced
   Squarespace's -17px row bleed + 50% floats + 17px block padding; a grid
   gives the same geometry with two declarations. */
.archive-page {
  max-width: 700px;
  margin: 0 auto;
  padding: 0 0 60px;
}

.archives-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 34px;
  align-items: start;
}

.archives-column { min-width: 0; }

.archive-block { padding: 0; }

/* The heading block is 32.1px tall and the archive block starts 34px below
   it; live gets the same 35px from 17px of bottom + 17px of top block padding.
   Keep the measured gap, not the mechanism. */
.archive-block--archive { padding-top: 34px; }

/* Live Squarespace rule: `.sqs-html-content > *:first-child { margin-top: 0 }`
   and `> *:last-child { margin-bottom: 0 }`. An h1 that is the only child of
   its block therefore has NO vertical margin at all.

   This is load-bearing for /archives: without it the "Blog" / "Tech Blog"
   headings carry a 7.41px bottom margin, which pushes every archive entry in
   both columns down by 15px relative to live. Found by geomdiff, not by eye.

   The same rule the live text-block wrapper carried, now on the one
   static-page scope. */
.page-content > *:first-child { margin-top: 0; }
.page-content > *:last-child { margin-bottom: 0; }

/* `layout-index` + `multicolumns`: the *item list* is multi-column (200px
   columns, 60px gap), while the page itself is 2 columns wide. At 333px of
   available width that resolves to a single column, so this stays at 1. It is
   stated explicitly rather than omitted so nobody "fixes" it back to 2 and
   halves the entry width. */
.archive-block-wrapper {
  margin: 0 0 1.4em;
}

.archive-group-list,
.archive-item-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.archive-group {
  break-inside: avoid;
  margin: 0 0 1.4em;
}

/* Live: 1.4em = 26.6px, link colour #9d0202, display block. */
.archive-group-name-link {
  display: block;
  font-size: 1.4em;
  line-height: 1.4em;
  text-decoration: none;
}

.archive-item-list {
  display: block;
  margin: 1.4em 0 2.8em;
  font-size: 1em;
  line-height: 1.4em;
}

.archive-item {
  margin: 0 0 1.4em;
}

/* Titles inherit the body ink and are NOT underlined. Live uses
   `.archive-item-link { color: inherit !important }`, but the reason we needed
   it is specificity: the site-wide `a:link` rule is (0,1,1) and beats a bare
   `.archive-item-link` (0,1,0), so every archive entry rendered red and
   underlined. Matching the pseudo-class here beats the global rule on merit
   rather than by copying the `!important`. */
.archive-item-link,
.archive-item-link:link,
.archive-item-link:visited {
  display: block;
  margin-right: 5px;
  color: inherit;
  text-decoration: none;
}

.archive-item-link:hover { color: #d60004; }

.archive-item-date-after {
  display: block;
  opacity: .7;
}

.archive-empty { font-style: italic; }

@media (max-width: 760px) {
  .archives-grid { grid-template-columns: 1fr; gap: 0; }
  .archives-column + .archives-column { margin-top: 34px; }
  .archive-page { padding: 0 0 40px; }
}

/* ---------- gallery project pages ----------
   Live `/photography-2013`, `/photography-2014`, `/snow-shoeing` and
   `/moving-days` are `collection-type-gallery.view-list` with
   `project-layout-left-sidebar`. Measured at 1280px on /photography-2013:
     #page 700px at x=290; .project margin-left 400px -> 300px image column
     at x=690; .project-meta floats back out by -400px at 366px, putting the
     title at x=290. h2.project-title is Source Sans Pro 22.8px on a 50.16px
     line, 34.2px padding-bottom, 34.2px margin-bottom, 1px white underline.
     Image width 300px, captions 19px minion, .image-desc padding-top 9.5px.
   Below 767px the sidebar collapses: margin-left 0, meta floats none. */
#projectPages .project { margin-bottom: 34px; }
#projectPages .project-meta { margin-bottom: 15px; }
#projectPages .image-meta { margin-bottom: 2em; }
#projectPages .image-meta .image-title { margin-bottom: 0; }
#projectPages .image-meta .image-desc { padding-top: .5em; }
#projectPages h2.project-title {
  margin: 0 0 1.5em;
  padding-bottom: 1.5em;
  border-bottom: 1px solid #fff;
  font-family: "Source Sans Pro", sans-serif;
  font-size: 1.2em;
  font-weight: 400;
  line-height: 2.2em;
  text-align: left;
  display: inline-block;
}
#projectPages .image-list img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 auto 1em;
}
.project-layout-left-sidebar #projectPages .project { margin-left: 400px; }
.project-layout-left-sidebar #projectPages .project .project-meta {
  margin-left: -400px;
  width: 366px;
  float: left;
}
@media (max-width: 767px) {
  .project-layout-left-sidebar #projectPages .project { margin-left: 0; }
  .project-layout-left-sidebar #projectPages .project .project-meta {
    margin-left: 0;
    width: auto;
    float: none;
  }
}

/* Live utility class used by /valentines-day and /new-page copy. */
.text-align-center { text-align: center; }

/* /fatherhood is the one standalone page whose text blocks sit in a 6-column
   cell. Live: the 700px #page contains a 367px centred column at x=457, and
   the text inside it is 333px at x=474 (17px block padding each side). The h1
   is a separate full-width block, so its centred text stays at the same centre;
   only the copy is narrow. The 86px h1-to-first-paragraph gap is live's block
   boundary, not the 34px used on /about. */
.page-content.narrow {
  max-width: 333px;
  margin: 0 auto;
}
.page-content.narrow h1 { margin-bottom: 86px; }

/* Simple-page image blocks. Live's `.sqs-block-image-figure` has margin 0 and
   always carries an 18px caption wrapper (even with no caption); a captioned
   one adds a 20.16px line for the text. The body's generic figure margin
   (1.8em) is article rhythm and must not apply here. */
.page-content .image-figure { margin-top: 0; margin-bottom: 0; }
.page-content .image-caption-wrapper { padding-top: 18px; min-height: 18px; }
.page-content .image-caption-paragraph {
  margin: 0;
  font-size: 14px;
  line-height: 20.16px;
}

/* /new-page's Flickr embed is a raw 1024px-wide image in a 700px column: live
   lets it overflow horizontally (scrollWidth 1314), and the block sits 55.6px
   below the previous figure. Reproduce the geometry rather than "fixing" a
   live layout bug that the screenshot gate would then flag. */
.page-content .flickr-embed { margin-top: 55.6px; line-height: 0; }
.page-content .flickr-embed img { max-width: none; }
.page-content .image-caption { margin-top: 0; }
/* Mid-block h1: a text block's first child is the "heading starts the page"
   case (34px gap); an h1 after an empty paragraph is still inside the same
   block and keeps live's .3em bottom margin (7.41px). /valentines-day. */
.page-content h1:not(:first-child) { margin-bottom: .3em; }

/* ---------- promoted-gallery lightbox ----------
   A native <dialog> is the modal: it is in the top layer, makes the article
   inert, and Escape closes it without custom key plumbing. The dialog fills
   the viewport rather than floating a box, so the photo gets the whole screen
   and the controls sit in fixed bands above and below it (nothing overlaps
   the image at 390px). /js/lightbox.js creates the element; without JS the
   gallery links still open the original images. */
html.lightbox-open,
html.lightbox-open body { overflow: hidden; }

.lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0 20px;
  border: 0;
  /* Solid, not 97%: a transparent scrim let the article ghost through and
     made the viewer look like two pages stacked. */
  background: #0c1111;
  color: #f0f5f1;
  overflow: hidden;
}
.lightbox[open] {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
}
.lightbox::backdrop { background: rgba(12, 17, 17, .8); }

.lightbox-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 44px;
  padding: 14px 0 6px;
}
.lightbox-counter {
  font-family: Inconsolata, Menlo, monospace;
  font-size: 14px;
  line-height: 1;
  letter-spacing: .14em;
  color: rgba(240, 245, 241, .82);
}
.lightbox button {
  appearance: none;
  display: grid;
  place-items: center;
  border: 1px solid rgba(240, 245, 241, .16);
  border-radius: 2px;
  background: rgba(240, 245, 241, .12);
  color: inherit;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
.lightbox button:hover,
.lightbox button:focus-visible {
  background: rgba(240, 245, 241, .24);
  border-color: rgba(240, 245, 241, .4);
}
.lightbox button:focus-visible { outline: 2px solid #f0f5f1; outline-offset: 2px; }
.lightbox-close {
  width: 40px;
  height: 40px;
  padding: 0;
  font-family: Inconsolata, Menlo, monospace;
  font-size: 26px;
  line-height: 1;
}
.lightbox-figure {
  grid-row: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  width: 100%;
  max-width: 1200px;
  min-height: 0;
  margin: 0 auto;
}
.lightbox-image {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  /* Head band (44px + padding) + foot band (48px + padding) + margins. */
  max-height: calc(100dvh - 170px);
  object-fit: contain;
  background: #0b0f0f;
  box-shadow: 0 18px 60px rgba(0, 0, 0, .5);
}
.lightbox-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 10px 0 18px;
}
.lightbox-nav {
  width: 48px;
  height: 48px;
  padding: 0;
}
.lightbox-nav svg { display: block; width: 20px; height: 20px; }
.lightbox [hidden] { display: none; }

/* The live counter/caption update without a page change; announce the change
   to screen readers without adding another visible line to the viewer. */
.lightbox-status {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 600px) {
  .lightbox { padding: 0 12px; }
  .lightbox-head { padding-top: 10px; }
  .lightbox-image { max-height: calc(100dvh - 150px); }
  .lightbox-nav { width: 44px; height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  .lightbox button { transition: none; }
}
