/* ═══════════════════════════════════════════════════════════════════════════
   /blog — the index.  Concept: THE CONTENTS PAGE.

   Not a card grid. The index is set the way the contents page of a printed
   volume is set: a masthead that says how much writing there is, one lead
   article at display scale, then entries in four editorial weights laid on an
   asymmetric twelve-column measure, each numbered in the margin in monospace.
   Filtering does not open a panel or navigate — it re-sets the contents in
   place, which is the one thing a printed contents page cannot do and the
   reason this page is worth building rather than templating.

   WHY FOUR WEIGHTS AND NOT ONE CARD. A uniform grid tells the reader every
   article is the same size, which is false and is the tell of a CMS. The four
   weights are:

     lead      one per page. Full measure, Orbitron title, cover to the side
     feature   seven columns, cover above the title
     standard  five columns, dropped half a rhythm for the asymmetry
     note      full measure, no image at all — a ruled line of type

   An author never picks one. `data-blg-w` exists as an override for the
   generator, and everything without it falls into a five-beat rhythm off
   :nth-child that always fills its row (7+5, 12, 5+7). So a badly ordered feed
   still composes. See "the rhythm" below.

   THE PLATE is the interaction. `note` and `standard` entries carry no image in
   the flow, so hovering (or focusing) one raises the article's cover on a small
   plate that tracks the cursor — the /projects dossier mechanism, reused
   deliberately rather than reinvented, because the reader has already learned
   it once on this site. It is a fine-pointer, non-reduced-motion, ≥992px
   affordance and it is decoration in the strict sense: aria-hidden, and no
   information lives only there.

   MARKUP CONTRACT — what this sheet expects blog/init.js and the generator to
   emit. Line 1 of content.html is fixed by BLOG-PLATFORM-ARCHITECTURE §4:

   <main data-taxi="" class="main-w"><div data-page="blog" data-taxi-view=""
     class="taxi-w blog"><script>document.documentElement.classList.add('blgjs')
     </script><link rel="stylesheet" href="/assets/css/blog.css"/>

     a.blg-skip                              → #blg-index, first focusable
     header.blg-mast[data-nav-theme-target="dark"]
       p.blg-crumb · h1.blg-h1 · p.blg-lede
       p.blg-count > b[data-blg-count]         ← the live figure, never animated
     nav.blg-filters[data-blg-filters]       → shown only under .blgjs
       button.blg-chip[data-blg-topic][aria-pressed]
         span.blg-chip-t · span.blg-chip-n
     p.blg-status[role="status"][data-blg-status]
     section#blg-index.blg-index[tabindex="-1"]
       ol.blg-list[role="list"][data-blg-list]
         li.blg-card[data-blg-topics="a b"][data-blg-shot][data-blg-w?]
           a.blg-card-a[data-taxi-ignore][href="/blog/<slug>"]
             span.blg-card-n · span.blg-card-k · h2.blg-card-t
             span.blg-card-x · span.blg-card-m ( time + span )
             span.blg-shot[aria-hidden="true"] > img
               ^ INSIDE the anchor, and last in the DOM. <a> has a transparent
                 content model, so a heading and a picture are both valid in it
                 and the whole entry is one target. It is last so the reading
                 order is title-then-picture; CSS `order` puts it on top.
       p.blg-empty[data-blg-empty]           → no result for the active filter
     div.blg-plate[data-blg-plate][aria-hidden="true"] > img
     nav.blg-pager > a.blg-page … span.blg-page.is-here
     script src="/assets/js/blog/init.js" defer

   MOTION CONTRACT. Every hidden start state on this page lives under `.blgjs`,
   which an inline one-liner on line 1 sets pre-paint. No class → no JS → the
   contents render settled, complete and fully visible; the filter bar simply is
   not there, and an unfiltered list is the correct no-JS state.

   1rem is not 16px — html{font-size} is a fluid function of viewport width
   (css-main.html), so every rem here is a proportional design unit and every
   minimum is written max(<rem>, <px>). No shadows anywhere: depth on this site
   is a tone step plus a 1px rim. Namespace is `blg-*`, scoped under
   .taxi-w.blog, and nothing here may style the shared nav or footer.
   ═══════════════════════════════════════════════════════════════════════════ */

.taxi-w.blog {
  --blg-bg: #fafcfb;            /* the paper — the same white /projects uses */
  --blg-surface: #eef4f1;       /* THE tone step. One value, and depth is this */
  --blg-surface-2: #e3ece7;     /* the same step, one further — hover only */
  --blg-ink: #040504;           /* = --color--dark-green · 17.6:1 on the paper */
  --blg-ink-soft: #10120f;
  --blg-dim: #535450;           /* = --color--dark-green-tint-2 · 7.41:1 */
  --blg-line: rgba(4, 5, 4, .07);
  --blg-rim: #cbd3ce;
  /* ONE accent, and it is never text here: #21f1a8 measures ~1.5:1 on paper.
     It fills shapes — the lead's rule, the active chip's ground, the plate's
     bezel. --blg-mint-ink is the same hue taken down until it clears 4.5:1 on
     the paper, and it is the ONLY mint value allowed to carry a word. It is a
     page-local derivation; the global token is not redefined. */
  --blg-mint: var(--color--lime, #21f1a8);
  --blg-mint-ink: #0c7a55;
  --blg-on-mint: #10120f;
  --blg-mono: ui-monospace, SFMono-Regular, Menlo, 'Courier New', monospace;
  --blg-ease: cubic-bezier(.215, .61, .355, 1);   /* the signature curve */
  --blg-rhythm: 6.5rem;
  --blg-measure: 62ch;

  /* what the shared footer band paints itself with — see css-main.html */
  --page-bg: var(--blg-bg);

  background-color: var(--blg-bg);
  color: var(--blg-ink);
  display: block;
  position: relative;
}

/* ---------------------------------------------------------------- resets

   :where() so the margin reset weighs nothing. Written the obvious way,
   `.taxi-w.blog p { margin: 0 }` scores 0,1,1 and silently out-specifies every
   single-class margin-top below it — the bug whatido.css documents at
   .wid-index, where the gap under a heading measured one pixel. list-style and
   padding stay at real specificity because those must beat the shared sheet's
   element rules; margins do not. */
.taxi-w.blog :is(ul, ol) { padding: 0; list-style: none; }
.taxi-w.blog li { list-style: none; }
.taxi-w.blog li::marker { content: none; }
:where(.taxi-w.blog) :where(p, h1, h2, h3, ul, ol, figure, dl, dd) { margin: 0; }
.taxi-w.blog img { display: block; max-width: 100%; }

/* One focus treatment for everything focusable on the page, and never
   `outline: none` on :focus — that out-specifies the global :focus-visible ring
   and deletes it, a bug this repository has already fixed twice. Ink, not mint:
   mint on paper is ~1.5:1 and a focus indicator is the one graphic a reader
   must always be able to see. */
.taxi-w.blog :where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--blg-ink);
  outline-offset: 3px;
  border-radius: .25rem;
}

/* ------------------------------------------------------------------ shell */

.blg-wrap {
  width: 100%;
  max-width: var(--fluid-container);
  margin-inline: auto;
  padding-inline: var(--padding--container);
}

/* Invisible until focused, then a real, visible control — the .cs-skip and
   .esy-skip contract, kept identical so the first Tab on any page of this site
   does the same thing in the same place. */
.blg-skip {
  position: fixed;
  top: 1rem;
  left: 50%;
  z-index: 20;
  transform: translate(-50%, -150%);
  opacity: 0;
  pointer-events: none;
  background-color: var(--blg-ink);
  color: #edf5f3;
  font-family: var(--blg-mono);
  font-size: max(.6875rem, 11px);
  letter-spacing: .06em;
  text-transform: uppercase;
  text-decoration: none;
  padding: .625rem 1rem;
  border-radius: .375rem;
  transition: transform .25s var(--blg-ease), opacity .25s linear;
}
/* Page-scoped so it beats the catch-all ring above (0,3,1 vs 0,2,0) — an ink
   ring on an ink pill is a focus indicator you cannot see. */
.taxi-w.blog .blg-skip:focus-visible {
  transform: translate(-50%, 0);
  opacity: 1;
  pointer-events: auto;
  outline: 2px solid var(--blg-mint);
  outline-offset: 2px;
}

/* --------------------------------------------------------------- masthead */

.blg-mast {
  position: relative;
  padding-top: calc(var(--on-t-nav-spacer, 5.375rem) + 2.5rem);
  padding-bottom: 3.25rem;
}

.blg-crumb {
  display: flex;
  align-items: baseline;
  gap: .625rem;
  font-family: var(--blg-mono);
  font-size: max(.625rem, 10px);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--blg-dim);
}
/* A crumb link is a flex item, so it is NOT an inline target and the 24 px
   floor applies in full — measured at 320px it was 27x11 before this. */
.blg-crumb a {
  display: inline-flex;
  align-items: center;
  min-height: max(1.5rem, 24px);
  color: var(--blg-dim);
  text-decoration: none;
  transition: color .3s linear;
}
.blg-crumb a:hover, .blg-crumb a:focus-visible { color: var(--blg-ink); }
.blg-crumb b { color: var(--blg-ink); font-weight: 500; }

/* The one Orbitron word on the page. Orbitron is wide and geometric, so it is
   spent here and on the lead article's title and nowhere else — a second
   display heading would flatten the hierarchy the whole page is built on. */
.blg-h1 {
  margin-top: 2.25rem;
  font-family: Orbitron, Arial, sans-serif;
  font-size: clamp(2.5rem, 6.2vw, 5.25rem);
  line-height: .92;
  letter-spacing: -.06em;
  text-transform: uppercase;
  font-weight: 700;
  font-variation-settings: normal;
  color: var(--blg-ink);
}

/* The mint slab under the wordmark. A filled shape, which is the only thing
   #21f1a8 is allowed to be on paper. It is drawn as a background so it cannot
   add a box the reveal has to animate separately. */
.blg-h1 em {
  font-style: normal;
  background-image: linear-gradient(var(--blg-mint), var(--blg-mint));
  background-size: 100% .18em;
  background-position: 0 88%;
  background-repeat: no-repeat;
}

.blg-lede {
  margin-top: 1.75rem;
  max-width: 44rem;
  color: var(--blg-dim);
  font-family: Quicksand, Arial, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.55;
  font-weight: 400;
}
.blg-lede b {
  font-family: var(--blg-mono);
  font-size: .9em;
  font-weight: 400;
  color: var(--blg-ink);
}

/* Monospace because it is a count, and monospace is this site's "this is a
   measurement" signal. It updates when a filter is applied. */
.blg-count {
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--blg-line);
  font-family: var(--blg-mono);
  font-size: max(.625rem, 10px);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--blg-dim);
}
.blg-count b { color: var(--blg-ink); font-weight: 400; }

/* ---------------------------------------------------------------- filters

   THE CHIP BAR ONLY EXISTS WHEN JS DOES. This is the inverse of the pre-paint
   gate and it is deliberate: a filter control with no runtime is an inert
   button in the tab order, which is a worse defect than its absence. With no
   JS the reader gets the complete contents, unfiltered, which is the correct
   and complete state — nothing is hidden and nothing is unreachable. */
.blg-filters { display: none; }
.blgjs .blg-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  padding-bottom: 2.25rem;
}

.blg-chip {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  /* max() so the target clears 24 CSS px at every width the fluid root scale
     passes through, including the narrow band where 1rem is at its smallest */
  min-height: max(2.25rem, 32px);
  padding: .4rem .9rem;
  border: 1px solid var(--blg-rim);
  border-radius: 999px;
  background-color: transparent;
  color: var(--blg-dim);
  font-family: 'Quicksand Label', Arial, sans-serif;
  font-size: max(.75rem, 12px);
  line-height: 1;
  letter-spacing: .02em;
  cursor: pointer;
  transition: background-color .3s var(--blg-ease), border-color .3s var(--blg-ease), color .3s linear;
}
.blg-chip:hover { border-color: var(--blg-ink); color: var(--blg-ink); }

/* The count rides in monospace — it is a figure, and it never animates. */
.blg-chip-n {
  font-family: var(--blg-mono);
  font-size: max(.625rem, 10px);
  color: var(--blg-dim);
  transition: color .3s linear;
}

/* Active state is a filled mint ground with ink type on it (16.4:1), NOT a
   colour promotion on the label — a chip whose only "on" signal is a hue is
   invisible to anyone who cannot see the hue, and mint as small text fails
   outright. The rim goes ink too, so the state survives forced-colours and a
   greyscale print. */
.blg-chip[aria-pressed="true"] {
  background-color: var(--blg-mint);
  border-color: var(--blg-ink);
  color: var(--blg-on-mint);
}
.blg-chip[aria-pressed="true"] .blg-chip-n { color: var(--blg-on-mint); }

/* A polite live region. Small, real text — never a visually-hidden-only
   announcement, because a sighted keyboard user filtering a list also needs to
   be told what happened. */
.blg-status {
  min-height: 1.25rem;
  padding-bottom: 1.5rem;
  font-family: var(--blg-mono);
  font-size: max(.625rem, 10px);
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--blg-dim);
}

/* ------------------------------------------------------------------ index */

/* The skip link's destination. tabindex="-1" is not a tab stop — it only lets
   the skip link genuinely move focus rather than merely scroll. The ring is
   drawn deliberately; it is never `outline: none`. */
.blg-index { padding-bottom: var(--blg-rhythm); }
.blg-index:focus-visible { outline: 2px solid var(--blg-ink); outline-offset: .75rem; }

.blg-list {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: 2rem;
  row-gap: 4rem;
  border-top: 1px solid var(--blg-line);
  padding-top: 2.75rem;
}

.blg-card { position: relative; grid-column: 1 / -1; min-width: 0; }
/* Filtered out. `display: none` rather than a visual hide, for the same reason
   PAGE-KIT §5 gives for alternates: not rendered is a finished state, hidden is
   not — and a parked card would still be a tab stop. Note that :nth-child is
   structural, so the five-beat rhythm below does NOT reshuffle when a filter is
   applied; only the margin numbers renumber, because counters skip boxes that
   are not generated. */
.blg-card[hidden] { display: none; }

/* The whole entry is one link. `<a>` has a transparent content model, so a
   heading inside it is valid; what must never appear inside is a second
   interactive element, and none of the four weights has one. */
.blg-card-a {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: .875rem;
  text-decoration: none;
  color: inherit;
}
/* The cover is LAST in the DOM and FIRST on the page. `order` moves the box,
   never the reading order, so a screen reader gets the title before the
   picture while the eye gets the picture first — which is what makes an entry
   read as an article rather than as a row in a list. The lead overrides this
   with named areas at ≥992px, where explicit placement ignores `order`. */
.blg-shot { order: -1; }

/* The margin number. counter() rather than a value in the markup, so the
   generator cannot emit a wrong one and a filtered view renumbers itself. */
.blg-list { counter-reset: blg-n; }
.blg-card { counter-increment: blg-n; }
.blg-card-n {
  font-family: var(--blg-mono);
  font-size: max(.625rem, 10px);
  letter-spacing: .08em;
  color: var(--blg-dim);
}
.blg-card-n::before { content: counter(blg-n, decimal-leading-zero); }

.blg-card-k {
  font-family: 'Quicksand Label', Arial, sans-serif;
  font-size: max(.6875rem, 11px);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--blg-mint-ink);   /* the derived mint — this one carries text */
}

.blg-card-t {
  font-family: Quicksand, Arial, sans-serif;
  font-size: 1.375rem;
  line-height: 1.14;
  letter-spacing: -.015em;
  font-weight: 500;
  color: var(--blg-ink);
  /* The underline is the hover, and it is drawn as a background so the title
     can grow to three lines without the rule detaching from the last one. */
  background-image: linear-gradient(var(--blg-ink), var(--blg-ink));
  background-size: 0 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
  transition: background-size .45s var(--blg-ease);
}
.blg-card-a:hover .blg-card-t,
.blg-card-a:focus-visible .blg-card-t { background-size: 100% 1px; }

.blg-card-x {
  max-width: var(--blg-measure);
  color: var(--blg-dim);
  font-family: Quicksand, Arial, sans-serif;
  font-size: 1rem;
  line-height: 1.6;
}

.blg-card-m {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .75rem;
  font-family: var(--blg-mono);
  font-size: max(.625rem, 10px);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--blg-dim);
}
.blg-card-m > * + *::before { content: "·"; margin-right: .75rem; }

/* The cover. Never transformed on hover — images on this site do not move;
   the frame does. The reveal is a clip that opens from the bottom edge plus a
   small settle on the picture inside it, which is the "subtle image reveal"
   and the only thing on this page that animates on scroll. */
.blg-shot {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: .75rem;
  border: 1px solid var(--blg-line);
  background-color: var(--blg-surface);
  aspect-ratio: 16 / 10;
}
.blg-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity .8s var(--blg-ease), transform .9s var(--blg-ease);
}

/* --------------------------------------------------------------- the rhythm

   `data-blg-w` is the generator's override. Everything without it takes the
   five-beat rhythm, which is arranged so every beat fills its row:

       1        lead                     12
       5n+2     feature   7  ┐
       5n+3     standard  5  ┘ = 12
       5n+4     note                     12
       5n+5     standard  5  ┐
       5n+6     feature   7  ┘ = 12

   All of the desktop-only column work is inside the ≥992px block below; up to
   991px every weight is one full-measure entry, differing only in whether it
   carries a cover, which is the right answer on a phone. */

/* Each weight is ONE selector, and all four weigh the same (0,3,0) — the
   attribute override and the :nth-child fallback are alternatives inside one
   :is(), so which weight applies is decided by source order and never by an
   accident of specificity. */

/* ---- lead ------------------------------------------------------------- */

/* One per page, and the only entry that gets Orbitron. The mint rule above it
   is a filled shape, which is the only thing #21f1a8 may be on paper. */
.blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child)::before {
  content: "";
  display: block;
  width: 3.5rem;
  height: .375rem;
  margin-bottom: 1.5rem;
  background-color: var(--blg-mint);
}
.blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-t {
  font-family: Orbitron, Arial, sans-serif;
  font-size: clamp(1.625rem, 2.9vw, 2.5rem);
  line-height: 1.04;
  letter-spacing: -.045em;
  text-transform: uppercase;
  font-weight: 700;
  font-variation-settings: normal;
  max-width: 24ch;
  background-size: 0 2px;
}
.blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-a:hover .blg-card-t,
.blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-a:focus-visible .blg-card-t { background-size: 100% 2px; }
.blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-x { font-size: 1.0625rem; line-height: 1.6; }

/* ---- feature ---------------------------------------------------------- */

.blg-card:is([data-blg-w="feature"], :not([data-blg-w]):nth-child(5n+2), :not([data-blg-w]):nth-child(5n+6)) .blg-card-t { font-size: 1.75rem; font-weight: 600; max-width: 20ch; }
/* 16:9 rather than 16:10 — at seven columns the taller crop put a 500px plate
   over a two-line title and the entry read as a picture with a caption. */
.blg-card:is([data-blg-w="feature"], :not([data-blg-w]):nth-child(5n+2), :not([data-blg-w]):nth-child(5n+6)) .blg-shot { aspect-ratio: 16 / 9; }

/* ---- standard --------------------------------------------------------- */

/* A squarer plate and a smaller title, so the five-column entry beside a
   seven-column one is a different object rather than the same object shrunk. */
.blg-card:is([data-blg-w="standard"], :not([data-blg-w]):nth-child(5n+3), :not([data-blg-w]):nth-child(5n+5)) .blg-shot { aspect-ratio: 4 / 3; }
.blg-card:is([data-blg-w="standard"], :not([data-blg-w]):nth-child(5n+3), :not([data-blg-w]):nth-child(5n+5)) .blg-card-t { font-size: 1.4375rem; max-width: 22ch; }

/* ---- note ------------------------------------------------------------- */

/* No image in the flow at all: a ruled line of type. The cursor plate is what
   shows the cover here, and only to a fine pointer on a wide screen. */
.blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) {
  border-top: 1px solid var(--blg-line);
  border-bottom: 1px solid var(--blg-line);
  padding-block: 2rem;
}
.blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-shot { display: none; }
.blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-card-t { font-size: 1.5rem; font-weight: 500; }

/* ------------------------------------------------------------ empty state */

.blg-empty {
  grid-column: 1 / -1;
  padding: 3rem 0;
  max-width: var(--blg-measure);
  color: var(--blg-dim);
  font-family: Quicksand, Arial, sans-serif;
  font-size: 1rem;
  line-height: 1.6;
}
.blg-empty[hidden] { display: none; }

/* ------------------------------------------------------------- the plate */

/* Decoration in the strict sense: aria-hidden, pointer-events:none, and no
   information lives only here. Fixed rather than absolute so the transform the
   loop writes is a viewport coordinate and needs no scroll read. The bezel is
   mint — a filled shape again — and there is no shadow: the plate reads as an
   object because of its rim and its tone step, exactly like every other
   surface on this site. */
.blg-plate {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 8;
  width: 15rem;
  opacity: 0;
  pointer-events: none;
  display: none;
  padding: .3125rem;
  border-radius: 1rem;
  background-color: var(--blg-mint);
  will-change: transform, opacity;
}
.blg-plate img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: .6875rem;
  background-color: var(--blg-surface);
}
/* Only ever mounted where it is wanted: a fine pointer, a wide viewport, and
   JS. Touch and keyboard-only readers lose nothing, because they were never
   shown it and every entry carries its own type. */
@media (hover: hover) and (pointer: fine) and (min-width: 992px) {
  .blgjs .blg-plate { display: block; }
}

/* ---------------------------------------------------------------- pager */

.blg-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  padding-block: 0 var(--blg-rhythm);
  border-top: 1px solid var(--blg-line);
  padding-top: 2rem;
}
.blg-page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: max(2.25rem, 32px);
  min-height: max(2.25rem, 32px);
  padding: 0 .75rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-family: var(--blg-mono);
  font-size: max(.6875rem, 11px);
  letter-spacing: .04em;
  text-decoration: none;
  color: var(--blg-dim);
  transition: color .3s linear, border-color .3s var(--blg-ease);
}
a.blg-page:hover { color: var(--blg-ink); border-color: var(--blg-rim); }
/* Current page: ink type and a real rim, never a faded one. Marking the
   current item by dropping opacity on its text is the rule whatido.css
   documents and it fails 3:1 on every ground here. */
.blg-page.is-here { color: var(--blg-ink); border-color: var(--blg-ink); font-weight: 500; }

/* ------------------------------------------------------------------ motion

   ONE reveal, used everywhere on this page, and nothing else. The hidden start
   state exists only once .blgjs is on <html>, set pre-paint by the inline
   one-liner on line 1 of content.html. No class → no JS → the contents render
   settled and complete. */
.blgjs .blg-rise { opacity: 0; transform: translateY(1.125rem); }
.blgjs .blg-rise.is-in {
  opacity: 1;
  transform: none;
  transition: opacity .75s var(--blg-ease), transform .75s var(--blg-ease);
  transition-delay: calc(var(--i, 0) * 60ms);
}

/* The cover reveal, one step subtler than the type's: the frame is already
   drawn (so the layout never shifts) and only the picture inside it settles. */
.blgjs .blg-rise .blg-shot img { opacity: 0; transform: scale(1.04); }
.blgjs .blg-rise.is-in .blg-shot img { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .taxi-w.blog *,
  .taxi-w.blog *::before,
  .taxi-w.blog *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  /* Every element reaches its settled, visible state whether or not the
     observer ever fires: the reveals are simply not hidden, the covers are
     opaque and untransformed, and the plate is not mounted at all (init.js
     also never starts its loop). Nothing on this page waits on an animation. */
  .blgjs .blg-rise,
  .blgjs .blg-rise .blg-shot img { opacity: 1; transform: none; }
  .blgjs .blg-rise.is-in { transition: none; }
  .blgjs .blg-plate { display: none; }
}

/* -------------------------------------------------------------- desktop */

@media screen and (min-width: 992px) {
  /* THE LEAD: type on the left, plate on the right, and the plate spans every
     row so it is as tall as the type beside it. Explicit placement, so the
     `order: -1` that lifts the cover on every other entry is simply ignored
     here — one mechanism per width, not two fighting. */
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-a {
    grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
    grid-template-areas:
      "n     shot"
      "kick  shot"
      "title shot"
      "ex    shot"
      "meta  shot";
    column-gap: 3rem;
    row-gap: .75rem;
    align-items: start;
  }
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-n { grid-area: n; }
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-k { grid-area: kick; }
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-t { grid-area: title; margin-top: .25rem; }
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-x { grid-area: ex; margin-top: .5rem; }
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-m { grid-area: meta; margin-top: .5rem; }
  /* The plate keeps its own proportion and the row takes whichever side is
     taller. Stretching it to the type's height was the version before this and
     it opened a 400px hole under a five-line headline. */
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-shot { grid-area: shot; aspect-ratio: 5 / 4; align-self: start; }

  .blg-card:is([data-blg-w="feature"], :not([data-blg-w]):nth-child(5n+2), :not([data-blg-w]):nth-child(5n+6)) { grid-column: span 7; }

  /* Dropped a rhythm so the two entries in a row never align at the top. This
     is the asymmetry, and it is padding rather than a translate so it cannot
     overlap its neighbour and costs nothing to animate. */
  .blg-card:is([data-blg-w="standard"], :not([data-blg-w]):nth-child(5n+3), :not([data-blg-w]):nth-child(5n+5)) { grid-column: span 5; padding-top: 3.25rem; }

  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) { grid-column: 1 / -1; }

  /* The note is four columns of a printed contents page: number, the type at
     the measure, and the metadata pushed to the far edge. */
  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-card-a {
    grid-template-columns: 3.5rem minmax(0, 7fr) minmax(0, 4fr);
    grid-template-areas:
      "n kick  meta"
      "n title meta"
      "n ex    meta";
    column-gap: 2rem;
    row-gap: .5rem;
    align-items: baseline;
  }
  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-card-n { grid-area: n; }
  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-card-k { grid-area: kick; }
  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-card-t { grid-area: title; }
  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-card-x { grid-area: ex; }
  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) .blg-card-m {
    grid-area: meta;
    justify-content: flex-end;
    text-align: right;
  }

  /* An entry the plate is currently showing gets a real tone step behind it —
     depth by a step and a rim, never by a shadow. */
  .blg-card.is-lit { background-color: var(--blg-surface); }
}

/* ------------------------------------------------------------- responsive */

@media screen and (max-width: 991px) {
  .taxi-w.blog { --blg-rhythm: 4.5rem; }
  .blg-mast { padding-top: calc(var(--on-t-nav-spacer, 5.375rem) + 1.5rem); padding-bottom: 2.5rem; }
  .blg-list { row-gap: 3.25rem; }
  /* The cover leads on a narrow screen: a full-width frame above a two-line
     title reads as an article, where the same frame beside it reads as a
     thumbnail. */
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-a { gap: 1rem; }
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-shot { aspect-ratio: 16 / 10; }
}

@media screen and (max-width: 767px) {
  .blg-card-t { font-size: 1.25rem; }
  .blg-card:is([data-blg-w="lead"], :not([data-blg-w]):first-child) .blg-card-t { letter-spacing: -.035em; max-width: none; }
  .blg-card:is([data-blg-w="note"], :not([data-blg-w]):nth-child(5n+4)) { padding-block: 1.5rem; }
  .blg-shot { border-radius: .625rem; }
}
