/*
 * The public blog — the reading experience.
 *
 * Loaded AFTER brand.css and site.css, and it depends on both. The chrome (header, footer),
 * the tokens, the type scale and the whole shared `k-*` language — grounds, cover, heads,
 * device, lists, buttons, close, the reveal contract — come from there (docs/design-system.md).
 * This file adds only what long-form reading needs and what a listing of articles needs, and
 * every component here reads the `--k-*` palette the enclosing ground sets, so the same
 * markup is right on paper, on wash and on ink.
 *
 * Rules, the same ones site.css holds itself to:
 *   - No colour literal. Every colour is a token from brand.css or a color-mix() of tokens,
 *     so a tint follows its base through both themes without a second definition.
 *   - Nothing is defined only inside a media query or a [data-theme] block.
 *   - Fluid from 360px up. Nothing scrolls the body sideways.
 *   - Motion follows the contract exactly: the base state is fully visible, only
 *     `.k-motion …` hides, only inside (prefers-reduced-motion: no-preference), and the
 *     article body itself is never inside a reveal — a reader who came from a search result
 *     is here for the text, and the text does not make an entrance.
 *
 * ------------------------------------------------------------------- the reading argument
 *
 * A reading column is not a marketing page, and this file is where the two are allowed to
 * differ. Three decisions carry the article body, and they survived the redesign intact:
 *
 * 1. MEASURE. `--bl-measure: 62ch`. Typographic convention is 45-75 characters per line;
 *    below 45 the eye returns too often, above 75 it loses the start of the next line on the
 *    return sweep. The unit needs a caveat: `ch` is the width of a ZERO, and in Instrument
 *    Sans a zero is noticeably wider than the average letter — 68ch measured out at about 83
 *    real characters when this was checked in a browser, which is well past the ceiling. 62ch
 *    lands at roughly 74, the long end of the range, which is right for a screen where the
 *    reader sits further from the page than from a book. It is still stated in `ch` rather
 *    than pixels so the measure follows the font size instead of drifting every time the type
 *    scale changes.
 *
 * 2. A SEPARATE TYPE SCALE FOR THE BODY. The site's `--step-0` is set for interface text and
 *    marketing copy read in glances. An article is read for six minutes, and 16px is too small
 *    for that. `--bl-body` starts at 1.06rem on a phone and reaches 1.2rem on a desktop — a
 *    deliberate step above the rest of the site — with `line-height: 1.72`, which is looser
 *    than the site's 1.6 because a longer measure needs more room between lines to stop the
 *    eye jumping a line on the return sweep. Everything that is NOT running text — the
 *    masthead, the listing, the furniture — uses the site's scale, which is what makes the
 *    blog unmistakably the same site.
 *
 * 3. RHYTHM FROM ONE UNIT. Every vertical gap in the prose is a multiple of `--bl-rhythm`,
 *    which is derived from the body size rather than set in pixels. Headings get more space
 *    above than below, so a heading belongs to the text under it rather than floating
 *    between two paragraphs.
 *
 * Sections: 1. tokens  2. masthead (+ the hero image)  3. doors  4. the feed (listing)
 *           5. prose  5b. the author's furniture (notes, pull quotes, figures, takeaways)
 *           6. acquisition  7. tags & pager  8. empty & preview  9. wide screens  9b. print
 *           10. motion
 */

/* ------------------------------------------------------------------------- 1. tokens */

:root {
  /* The reading column. See the argument above. */
  --bl-measure: 62ch;

  /* A type scale for prose, one step warmer than the site's interface scale. */
  --bl-body:      clamp(1.06rem, 1.01rem + 0.24vw, 1.2rem);
  --bl-lead:      clamp(1.16rem, 1.06rem + 0.5vw, 1.4rem);
  --bl-h2:        clamp(1.45rem, 1.25rem + 0.95vw, 2.05rem);
  --bl-h3:        clamp(1.18rem, 1.06rem + 0.55vw, 1.5rem);
  --bl-small:     clamp(0.84rem, 0.81rem + 0.14vw, 0.92rem);

  /* One vertical unit. Every gap in the prose is a multiple of it. */
  --bl-rhythm: 1.55em;

  /* Surfaces that belong to the blog. Mixes, not new colours, so both themes follow. */
  --bl-quote-rule: color-mix(in srgb, var(--accent) 55%, transparent);
  --bl-code-bg: color-mix(in srgb, var(--ink) 6%, var(--surface-alt));
  /*
   * The two track tints. They are the SEMANTIC blue and green — --accent and --ok — and not the
   * raw brand --blue and --leaf they used to be, because they are read as TEXT: the door's
   * eyebrow (`.bl-door__eyebrow`) is 13px at weight 600 in the track's own colour, so the pair
   * has to clear the 4.5:1 AA asks of body text on the page in both themes.
   *
   * --blue measured 4.17:1 on --paper and 3.83:1 on the dark surface, and --leaf measured
   * 1.99:1 on --paper — "For students" was a pale green on near-white. This is the same failure
   * brand.css already fixed for --accent, which is why its comment says --blue "is untouched and
   * still right for a large heading or a graphic accent, where 3:1 is the bar": these were the
   * two places left reading it at body size.
   *
   * --accent and --ok are the palette's AA-safe blue and green and are BOTH redefined per theme,
   * so the doors now follow the theme instead of holding one hue across both. Nothing here is a
   * new colour, and schools still reads blue while students still reads green.
   * BlogTrackTintContrastTest measures the pair and is the reason this cannot drift back.
   */
  --bl-schools: var(--accent);
  --bl-students: var(--ok);
  --bl-preview-bg: var(--warn-wash);

  /* The author's furniture — notes, pull quotes, figures, the takeaways box. Each reads one
     tint and one wash, and every wash is the tint mixed into the surface at the same 9%, so
     the four notes sit at the same volume in both themes and none of them shouts. The tints
     are the site's four semantic colours; nothing here is a new hue. */
  --bl-note-info:     var(--accent);
  --bl-note-success:  var(--ok);
  --bl-note-warning:  var(--warn);
  --bl-note-danger:   var(--danger);
  --bl-note-mix:      9%;
  --bl-note-rule-mix: 28%;
  /* `--bl-note-wash` and `--bl-note-rule` are NOT declared here, on purpose. A custom property
     is substituted on the element that declares it, so a `color-mix()` written at `:root`
     is computed at `:root` — where `--bl-note-tint` is unset and its fallback resolves — and
     what inherits down to `.bl-note--warning` is already a fixed blue. They live on `.bl-note`,
     where the variant's tint is in scope. The same goes for the pull quote's bar, which reads
     `--k-hi`, a property `.k-section` sets below `:root`. */

  /* A figure's plate while the picture is still on its way down the line, and its edge. */
  --bl-figure-plate:  var(--surface-alt);
  --bl-figure-edge:   var(--border-soft);

  /* The takeaways box: the wash ground the site uses for evidence, ruled like a register. */
  --bl-takeaways-bg:   var(--surface-alt);
  --bl-takeaways-rule: var(--border);

  /* The hero. How far the lede figure hangs over the seam between ink and paper, and the
     plate that holds its place before the bytes arrive. */
  --bl-hero-overhang: clamp(2.6rem, 7vw, 5.5rem);
  --bl-hero-plate:    color-mix(in srgb, var(--paper) 14%, var(--ink));
}

/* The reading column. It is the ordinary shell, so the article starts on the page's own left
   axis — the axis the masthead's h1, the "Read next" head below it, the site header and the
   footer all share. The measure itself is held by the blocks inside (`.bl-prose`,
   `.bl-callout`, `.bl-filed`, `.bl-product` each cap at `--bl-measure`), so an article at
   1440px is still a column of text rather than a line stretched across a monitor — it is
   simply a column that lines up with everything else on the page instead of floating in the
   middle of the viewport on an axis nothing else uses. */
.shell--reading {
  max-width: 1180px;
}

/* ----------------------------------------------------------------------- 2. masthead */

/* The article's cover. It is a `.k-cover` — ink, the glow, the staggered entrance — with
   the h1 sized for a headline that is a sentence rather than a slogan: post titles run to
   seventy characters, and the site's step-4 would put that on five lines. */
.bl-masthead .k-cover__grid {
  align-items: end;
}

.bl-masthead h1 {
  font-size: clamp(2rem, 1.35rem + 2.7vw, 3.7rem);
  max-width: 24ch;
  text-wrap: balance;
}

.bl-masthead .k-cover__lede {
  max-width: 56ch;
}

/* The byline: who, when, how long. Dates and durations in the mono face — the numerals do
   the structural work here, the way they do everywhere else in the language. */
.bl-byline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .4rem 1.4rem;
  margin: 1.8rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--k-rule-soft);
  font-size: var(--step--1);
  color: var(--k-soft);
}

.bl-byline > span {
  display: inline-flex;
  align-items: baseline;
  gap: .45rem;
}

.bl-byline b {
  font-weight: 600;
  color: var(--k-text);
}

.bl-byline time,
.bl-byline .bl-byline__mono {
  font-family: var(--font-mono);
  font-size: .8rem;
  font-variant-numeric: tabular-nums;
  color: var(--k-faint);
}

/* ------------------------------------------------------------------- the hero image
 *
 * An article's main picture has two honest shapes, and the masthead picks by the picture.
 *
 * THE LEDE FIGURE (`.bl-masthead--lede`) is the default: a landscape picture (or one whose
 * size is unknown) sits under the standfirst at the width of the reading column and hangs
 * over the seam between the ink cover and the paper body. The headline keeps the whole
 * measure, which a seventy-character title needs; the picture is read *after* the argument
 * has been stated, in the order an article is read; and it lands on the page's one left axis,
 * so it is plainly the article's picture rather than a card beside it. The old treatment —
 * a framed thumbnail in the cover's aside, bottom-aligned against a four-line title — read as
 * a device in a cover, because that is what every other cover on the site puts there.
 *
 * THE ASIDE (`.bl-masthead--aside`) survives for a portrait or square picture, which at the
 * column's width would be a wall a reader has to scroll past before the first sentence. It
 * stays beside the headline, capped, on the cover's grid.
 *
 * Both are the same markup (`.k-cover__aside > figure.bl-hero > img`); only the masthead's
 * modifier changes, and the template chooses it from the intrinsic width and height it
 * already prints for layout stability. Neither is inside anything that hides: the cover's
 * own stagger lifts the aside, and only under the motion contract's two gates.
 *
 * On a slow line the picture is the heaviest thing on the page. Its `width`/`height` reserve
 * the box, and the plate behind it is a flat colour, so the reader gets the headline and the
 * first paragraph in their final positions while the bytes are still arriving. */
.bl-hero {
  margin: 0;
}

.bl-hero img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--bl-hero-plate);
  border-radius: var(--radius-lg);
  border: 1px solid var(--k-rule);
  box-shadow: var(--shadow-lg);
}

.bl-hero figcaption {
  margin-top: .75rem;
  font-size: var(--step--1);
  color: var(--k-soft);
}

/* The lede figure. The cover's grid becomes one column, the text keeps the solo measure,
   and the figure follows it. The cover gives up its bottom padding and its clipping so the
   figure can hang over the seam; the clip-path keeps the glow inside the cover's own box on
   every other side, which is what the overflow was there for. The masthead is raised so the
   overhang paints over the body section that follows it in the DOM. */
.bl-masthead--lede {
  overflow: visible;
  clip-path: inset(0 0 calc(-1 * var(--bl-hero-overhang)) 0);
  z-index: 1;
  padding-bottom: 0;
}

.bl-masthead--lede .k-cover__grid {
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

.bl-masthead--lede .k-cover__grid > div:first-child {
  max-width: 60rem;
}

.bl-masthead--lede .k-cover__aside {
  /* The figure's width is stated in the prose's own `ch`, so it lines up with the column it
     introduces: `ch` is measured against the font size of the element it is used on. */
  font-size: var(--bl-body);
  max-width: calc(var(--bl-measure) + 6rem);
  margin-top: clamp(2rem, 5vw, 3.2rem);
  margin-bottom: calc(-1 * var(--bl-hero-overhang));
}

/* The half of the figure standing on paper would lose a pale border; the shadow carries it
   on both grounds, and the plate is the cover's own ink lightened rather than the body's
   paper, so a picture still loading reads as part of the cover it belongs to. */
.bl-masthead--lede .bl-hero img {
  border-color: color-mix(in srgb, var(--paper) 16%, transparent);
}

/* The body under a lede figure starts the overhang further down, so the first paragraph
   keeps the same distance from the picture that it has from the byline on a plain post. */
.bl-masthead--lede + .bl-body {
  padding-top: calc(var(--bl-hero-overhang) + clamp(2.5rem, 6vw, 4rem));
}

/* The aside, for a portrait or square picture: capped so it cannot tower over the headline,
   and set to the end of the row like every cover's device. */
.bl-masthead--aside .k-cover__aside {
  max-width: 26rem;
  justify-self: end;
  width: 100%;
}

@media (max-width: 940px) {
  .bl-masthead--aside .k-cover__aside {
    max-width: 22rem;
    justify-self: start;
  }
}

/* ------------------------------------------------------------------------- 3. doors */

/* The two front doors, as a register. This is the archive's most important element and it
   lives in the cover, because a bursar and a seventeen-year-old share no vocabulary and the
   first thing either of them needs is to be told which half of the page is not for them.
   Rows ruled like the shared ledger, inside the cover's device — every other cover on the
   site answers its aside with a card, and this one used to answer it with a bare list on
   navy. The device resets the `--k-*` palette for its contents, so these rules read the
   card's palette here and would read a section's anywhere else. */
.bl-doors {
  list-style: none;
  margin: 0;
  padding: 0 1.1rem;
}

/* The rows are the whole card, so the last one has no rule of its own: the device's own
   edge closes the register. */
.bl-doors > li:last-child .bl-door { border-bottom: 0; }

.bl-door {
  --bl-door-tint: var(--k-hi);
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "eyebrow go"
    "heading go"
    "audience go";
  column-gap: 1.2rem;
  padding: 1.15rem 0 1.25rem .9rem;
  border-bottom: 1px solid var(--k-rule);
  color: var(--k-text);
  text-decoration: none;
  transition: padding-left .2s ease;
}

.bl-door::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.15rem;
  bottom: 1.25rem;
  width: 3px;
  border-radius: 2px;
  background: var(--bl-door-tint);
  opacity: .55;
  transition: opacity .2s ease;
}

.bl-door:hover { padding-left: 1.3rem; }
.bl-door:hover::before,
.bl-door[aria-current="page"]::before { opacity: 1; }

.bl-door--schools  { --bl-door-tint: var(--bl-schools); }
.bl-door--students { --bl-door-tint: var(--bl-students); }

.bl-door__eyebrow {
  grid-area: eyebrow;
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bl-door-tint);
}

.bl-door__heading {
  grid-area: heading;
  margin-top: .25rem;
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1rem + 1vw, 1.6rem);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.bl-door__audience {
  grid-area: audience;
  margin-top: .35rem;
  font-size: var(--step--1);
  color: var(--k-soft);
}

.bl-door__go {
  grid-area: go;
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  font-family: var(--font-mono);
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
  color: var(--k-faint);
  white-space: nowrap;
}

.bl-door__go::after {
  content: "→";
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 650;
  color: var(--k-hi);
  transition: transform .2s ease;
}

.bl-door:hover .bl-door__go::after { transform: translateX(4px); }

/* The strip under a listing's cover carries the subjects: static, because it is an index a
   reader may be scanning for a word, and an index must not move. Chips read the ground. */
.k-strip .bl-taglist { gap: .45rem .55rem; }

/* ---------------------------------------------------------------------- 4. the feed */

/* The listing. Where the old language had a grid of cards, this is a register: every entry
   is a ruled row with the date and the track in a key column and the headline in the
   display face, and the newest entry on page one leads at a size up. A list of articles is
   really a list, and it reads like one. */
.bl-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--k-rule);
}

.bl-entry {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: .35rem 2rem;
  padding: 1.4rem 0 1.5rem;
  border-bottom: 1px solid var(--k-rule);
  transition: padding-left .2s ease;
}

.bl-entry:hover,
.bl-entry:focus-within {
  padding-left: .4rem;
}

.bl-entry__key {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem .8rem;
  margin: 0;
  font-family: var(--font-mono);
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
  color: var(--k-faint);
}

.bl-entry__body {
  display: grid;
  gap: .5rem;
  align-content: start;
  min-width: 0;
}

.bl-entry__title {
  font-size: clamp(1.25rem, 1.05rem + 1vw, 1.7rem);
  line-height: 1.15;
  letter-spacing: -0.015em;
  max-width: 30ch;
  margin: 0;
}

.bl-entry__title a {
  color: var(--k-text);
  text-decoration: none;
}

.bl-entry__title a:hover {
  color: var(--k-hi);
}

/* The whole row is the link. The anchor's pseudo-element covers the row, so there is one tab
   stop per article and nothing is nested inside the anchor that would break the heading
   outline. Tag links sit above the overlay so they stay clickable. */
.bl-entry__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.bl-entry .bl-taglist { position: relative; z-index: 1; }

.bl-entry__excerpt {
  margin: 0;
  max-width: 62ch;
  color: var(--k-soft);
  font-size: var(--step-0);
  line-height: 1.55;
}

.bl-entry__media {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--k-rule-soft);
  background: var(--surface-alt);
}

.bl-entry__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The newest entry leads: a size up, its standfirst at the lede size, its picture beside it. */
.bl-entry--lead {
  padding-block: 1.8rem 2rem;
}

.bl-entry--lead .bl-entry__title {
  font-size: clamp(1.7rem, 1.2rem + 2.2vw, 2.7rem);
  font-weight: 750;
  max-width: 24ch;
}

.bl-entry--lead .bl-entry__excerpt {
  font-size: var(--step-1);
  line-height: 1.45;
  max-width: 52ch;
}

@media (min-width: 760px) {
  .bl-entry {
    grid-template-columns: 9.5rem minmax(0, 1fr);
  }

  .bl-entry__key {
    flex-direction: column;
    align-items: flex-start;
    gap: .45rem;
    padding-top: .35rem;
  }

  .bl-entry--media {
    grid-template-columns: 9.5rem minmax(0, 1fr) minmax(10rem, 16rem);
  }

  .bl-entry--lead.bl-entry--media {
    grid-template-columns: 9.5rem minmax(0, 1fr) minmax(12rem, 22rem);
  }
}

/* The track badge: a small tinted mark so, on the archive and the tag pages, "which of these
   is for me" is answered before the headline is read. */
.bl-badge {
  --bl-badge-tint: var(--k-hi);
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-family: var(--font-display);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--k-soft);
}

.bl-badge::before {
  content: "";
  width: .5rem;
  height: .5rem;
  border-radius: 2px;
  background: var(--bl-badge-tint);
}

.bl-badge--schools  { --bl-badge-tint: var(--bl-schools); }
.bl-badge--students { --bl-badge-tint: var(--bl-students); }

/* Ticked facts inside a device read the device's own palette, not the ground's: a white
   window on the ink cover would otherwise print its facts in the cover's pale text. */
.k-device .k-checks li { color: var(--k-dev-soft, var(--text-soft)); }
.k-device .k-checks li::before { color: var(--ok); }
.k-device .bl-callout__link { margin-left: .35rem; }

/* The end of a listing: what a reader is told after the last row. */
.bl-feed__end {
  margin: 1.4rem 0 0;
  font-size: var(--step--1);
  color: var(--k-faint);
}

/* ------------------------------------------------------------------------- 5. prose */

.bl-prose {
  font-size: var(--bl-body);
  line-height: 1.72;
  color: var(--text);
  /* Never wider than the measure, even if a parent is. This is the rule that makes the
     article readable at 1440px, and it is stated here rather than only on the shell so a
     future full-width layout cannot lose it. */
  max-width: var(--bl-measure);
  /* A hyphenated long word beats a line that scrolls sideways on a 360px screen. */
  overflow-wrap: break-word;
}

.bl-prose > * { margin-block: 0 var(--bl-rhythm); }
.bl-prose > :last-child { margin-bottom: 0; }

.bl-prose h2,
.bl-prose h3,
.bl-prose h4 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  line-height: 1.15;
  /* More space above than below: a heading belongs to the text beneath it. */
  margin-block: calc(var(--bl-rhythm) * 1.7) calc(var(--bl-rhythm) * 0.42);
  max-width: 28ch;
}

.bl-prose > h2:first-child,
.bl-prose > h3:first-child { margin-top: 0; }

.bl-prose h2 { font-size: var(--bl-h2); }
.bl-prose h3 { font-size: var(--bl-h3); }
.bl-prose h4 { font-size: calc(var(--bl-body) * 1.08); font-weight: 700; }

.bl-prose a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.bl-prose a:hover { text-decoration-thickness: 2px; }

.bl-prose strong { color: var(--text); }

.bl-prose ul,
.bl-prose ol {
  padding-inline-start: 1.35em;
}

.bl-prose li { margin-bottom: .45em; }
.bl-prose li:last-child { margin-bottom: 0; }
.bl-prose li::marker { color: var(--text-faint); }

.bl-prose li > ul,
.bl-prose li > ol { margin-block: .45em; }

/* A quotation an author writes with `>`: somebody else's words, set apart and attributed.
   It keeps the body face and the body size, sits behind a tinted left rule, and is NOT a
   pull quote — a pull quote (below) is the article's own sentence lifted out and set in the
   display face. The two are visibly different things because they are different things. */
.bl-prose blockquote:not(.bl-pullquote__quote) {
  margin-inline: 0;
  padding: .35em 0 .35em 1.25em;
  border-inline-start: 3px solid var(--bl-quote-rule);
  color: var(--text-soft);
  font-size: calc(var(--bl-body) * 1.02);
}

.bl-prose blockquote:not(.bl-pullquote__quote) > * { margin-block: 0 calc(var(--bl-rhythm) * .6); }
.bl-prose blockquote:not(.bl-pullquote__quote) > :last-child { margin-bottom: 0; }

/* An attribution in a <cite> inside the quotation. */
.bl-prose blockquote:not(.bl-pullquote__quote) cite {
  display: block;
  margin-top: .3em;
  font-style: normal;
  font-size: var(--bl-small);
  color: var(--text-soft);
}

.bl-prose code {
  font-family: var(--font-mono);
  font-size: .88em;
  background: var(--bl-code-bg);
  border: 1px solid var(--border-soft);
  border-radius: 5px;
  padding: .1em .35em;
}

/* Wide content scrolls inside its own container. The page body never scrolls sideways. */
.bl-prose pre {
  overflow-x: auto;
  background: var(--bl-code-bg);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  font-size: .9rem;
  line-height: 1.55;
}

.bl-prose pre code {
  background: none;
  border: 0;
  padding: 0;
  font-size: inherit;
}

.bl-prose img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
  display: block;
}

.bl-prose hr {
  border: 0;
  border-top: 1px solid var(--border-soft);
  margin-block: calc(var(--bl-rhythm) * 1.6);
}

/* A table is the one thing that legitimately exceeds the measure. It gets its own scroll
   container so it can, without taking the page with it. */
.bl-prose table {
  display: block;
  overflow-x: auto;
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step-0);
}

.bl-prose th,
.bl-prose td {
  border: 1px solid var(--border-soft);
  padding: .55rem .75rem;
  text-align: start;
  vertical-align: top;
}

.bl-prose thead th {
  background: var(--surface-alt);
  font-family: var(--font-display);
}

/* The body sits on paper under an ink masthead; this is the breathing room between the two,
   and between the last paragraph and the furniture that follows it. */
.bl-body {
  padding-block: clamp(2.5rem, 6vw, 4rem) 0;
}

/* ------------------------------------------------------- 5b. the author's furniture
 *
 * What an author can put in the reading column besides paragraphs: a note, a pull quote, a
 * figure, a takeaways box. The markup comes from the markdown extension and is fixed; this
 * is the whole of its design. Four rules hold across all of it:
 *
 *   - Each caps at the measure, like the callout. Only a `--wide` figure may pass it.
 *   - The meaning of a note is carried by its glyph and its title; colour reinforces, and a
 *     reader who cannot tell blue from green still knows which one they are looking at.
 *   - Nothing here animates. Furniture in the reading column is simply there.
 *   - None of it is the product callout. The callout is white with a shadow and a mark; the
 *     notes are a tinted wash with no shadow, the takeaways box is the evidence wash, the
 *     pull quote is type between two rules. A reader learns in one article which of these
 *     is the company talking and which is the author.
 */

/* Notes. A tinted wash, a tinted rule on the left, the glyph in the margin — the shape the
   site's callouts share, at the volume of an aside. The variants change one property. */
.bl-note {
  --bl-note-tint: var(--bl-note-info);
  /* Mixed here, not at `:root`, so the tint the variant sets one rule down takes part. */
  --bl-note-wash: color-mix(in srgb, var(--bl-note-tint) var(--bl-note-mix), var(--surface));
  --bl-note-rule: color-mix(in srgb, var(--bl-note-tint) var(--bl-note-rule-mix), var(--surface));
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr);
  gap: .2rem .9rem;
  align-items: start;
  max-width: var(--bl-measure);
  margin-block: calc(var(--bl-rhythm) * 1.15);
  padding: 1rem 1.2rem 1.05rem 1.1rem;
  background: var(--bl-note-wash);
  border: 1px solid var(--bl-note-rule);
  border-inline-start: 3px solid var(--bl-note-tint);
  border-radius: 0 var(--radius) var(--radius) 0;
  color: var(--text);
  /* The box is measured in the body's own size so its `ch` is the column's `ch`; the text
     inside steps down one notch, which is what makes it read as an aside. */
  font-size: var(--bl-body);
  line-height: 1.6;
}

.bl-note--info    { --bl-note-tint: var(--bl-note-info); }
.bl-note--success { --bl-note-tint: var(--bl-note-success); }
.bl-note--warning { --bl-note-tint: var(--bl-note-warning); }
.bl-note--danger  { --bl-note-tint: var(--bl-note-danger); }

.bl-note__mark {
  display: inline-flex;
  width: 1.25rem;
  height: 1.25rem;
  /* Sits on the first line's x-height, whether or not there is a title. */
  margin-top: .2em;
  color: var(--bl-note-tint);
}

.bl-note__mark svg {
  width: 100%;
  height: 100%;
}

.bl-note__body {
  min-width: 0;
  font-size: .94em;
}

.bl-note__body > * { margin-block: 0 .6em; }
.bl-note__body > :last-child { margin-bottom: 0; }

.bl-note__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(var(--bl-body) * .98);
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text);
  margin-bottom: .3em;
}

.bl-note__body ul,
.bl-note__body ol { padding-inline-start: 1.2em; }

.bl-note__body li { margin-bottom: .3em; }

/* Plain --accent, which is the stronger blue now.
   This used to reach for --accent-hover, because the link colour of the day measured 3.8:1 on
   the light wash — short of 4.5 for text — while the hover shade cleared it. --accent resolves
   to --blue-dark since the light theme was corrected for contrast, so the ordinary link colour
   is the accessible one and the hover is free to be the darkest step. Keeping --accent-hover
   here would paint a link inside a note in --ink and take away the one cue that it is a link.
   No literal colour in this comment: the stylesheet is asserted to hold no hex at all. */
.bl-note__body a { color: var(--accent); }

.bl-note__body code {
  background: color-mix(in srgb, var(--surface) 60%, transparent);
}

/* The pull quote. The article's own sentence, lifted out and set large in the display face
   between two hairlines, with the language's tinted bar above it — the metric's bar over
   words instead of a number. It is ink, not soft: this is the line the author most wants
   read. The source, when there is one, sits under it in the small size. */
.bl-pullquote {
  /* The language's tinted bar (the metric's) laid over words, and the section's rule. Read
     here so the section's `--k-hi` is in scope, not `:root`'s fallback. */
  --bl-pull-bar:  var(--k-hi, var(--accent));
  --bl-pull-rule: var(--k-rule, var(--border));
  max-width: var(--bl-measure);
  margin-block: calc(var(--bl-rhythm) * 1.5);
  margin-inline: 0;
  padding-block: 1.4rem 1.3rem;
  border-block: 1px solid var(--bl-pull-rule);
}

.bl-pullquote::before {
  content: "";
  display: block;
  width: 2.4rem;
  height: 4px;
  border-radius: 2px;
  margin-bottom: 1.1rem;
  background: var(--bl-pull-bar);
}

.bl-pullquote__quote {
  margin: 0;
  padding: 0;
  border: 0;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 1.12rem + 1.1vw, 1.95rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.018em;
  color: var(--text);
  text-wrap: balance;
}

.bl-pullquote__quote > p { margin-block: 0 .5em; }
.bl-pullquote__quote > p:last-child { margin-bottom: 0; }

.bl-pullquote__source {
  margin-top: .9rem;
  font-size: var(--bl-small);
  color: var(--text-soft);
}

.bl-pullquote__source::before {
  content: "— ";
  color: var(--text-faint);
}

/* A figure in the running text. The picture on a plate, an edge, a caption in the small
   size. The intrinsic width and height on the image are the layout; the plate is what the
   reader sees in the reserved box while the bytes come down the line. */
.bl-figure {
  max-width: var(--bl-measure);
  margin-block: calc(var(--bl-rhythm) * 1.3);
  margin-inline: 0;
}

.bl-figure__img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--bl-figure-plate);
  border: 1px solid var(--bl-figure-edge);
  border-radius: var(--radius);
}

.bl-figure__caption {
  margin-top: .65rem;
  font-size: var(--bl-small);
  line-height: 1.5;
  color: var(--text-soft);
  max-width: 62ch;
}

/* The takeaways box. What to leave with, as a numbered register on the evidence wash: mono
   numerals in the margin, hairlines between the items, the title in the kicker's voice.
   Numbered on purpose — a reader coming back for one point can cite it. */
.bl-takeaways {
  max-width: var(--bl-measure);
  margin-block: calc(var(--bl-rhythm) * 1.4);
  padding: 1.2rem 1.4rem 1.1rem;
  background: var(--bl-takeaways-bg);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  color: var(--text);
}

.bl-takeaways__title {
  margin: 0 0 .7rem;
  font-family: var(--font-display);
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ok);
}

.bl-takeaways ul,
.bl-takeaways ol {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: bl-takeaway;
}

/* Not a grid: an item's content is inline (`<strong>` beside bare text, or a `<p>`), and a
   grid would set each inline run in its own cell. The numeral sits in the padding. */
.bl-takeaways li {
  position: relative;
  margin: 0;
  padding: .7rem 0 .7rem 2.6rem;
  border-top: 1px solid var(--bl-takeaways-rule);
  font-size: calc(var(--bl-body) * .96);
  line-height: 1.55;
}

.bl-takeaways li::before {
  counter-increment: bl-takeaway;
  content: counter(bl-takeaway, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: calc(.7rem + .3em);
  font-family: var(--font-mono);
  font-size: .76rem;
  font-variant-numeric: tabular-nums;
  /* Soft, not faint: the faint tone measures 2.7:1 on the light wash. */
  color: var(--text-soft);
}

.bl-takeaways li > p { margin: 0; }
.bl-takeaways li strong { color: var(--text); }

/* A container whose type nobody has defined (`::: warnign`) is rendered as a plain block so
   the author's words survive. It should read as ordinary paragraphs, at the prose rhythm. */
.bl-prose .bl-block > * { margin-block: 0 var(--bl-rhythm); }
.bl-prose .bl-block > :last-child { margin-bottom: 0; }

/* Anything from this section that lands first or last in a prose block should not double
   up its gap with the block's own rhythm. */
.bl-prose > .bl-note:first-child,
.bl-prose > .bl-pullquote:first-child,
.bl-prose > .bl-figure:first-child,
.bl-prose > .bl-takeaways:first-child { margin-top: 0; }

.bl-prose > .bl-note:last-child,
.bl-prose > .bl-pullquote:last-child,
.bl-prose > .bl-figure:last-child,
.bl-prose > .bl-takeaways:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------------ 6. acquisition */

/* The in-article callout: one thing said in a box with a coloured edge — the shared
   callout's shape, carrying the product's real mark. It interrupts as little as an aside
   can: surface, a hairline, the product's tint on the left, and one link. */
.bl-callout {
  --k-tint: var(--leaf);
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  gap: 1rem 1.1rem;
  align-items: start;
  margin-block: clamp(1.8rem, 5vw, 2.8rem);
  padding: 1.15rem 1.3rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-left: 4px solid var(--k-tint);
  border-radius: 0 var(--radius) var(--radius) 0;
  box-shadow: var(--shadow);
  max-width: var(--bl-measure);
  color: var(--text);
}

.bl-callout__icon {
  width: 3rem;
  height: 3rem;
  border-radius: 11px;
  object-fit: contain;
}

.bl-callout__body { min-width: 0; }

.bl-callout__eyebrow {
  margin: 0 0 .15rem;
  font-family: var(--font-display);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ok);
}

.bl-callout h3 {
  font-size: var(--step-1);
  margin-bottom: .4rem;
}

.bl-callout p {
  margin-bottom: .75rem;
  color: var(--text-soft);
  font-size: var(--step--1);
  line-height: 1.55;
}

.bl-callout__link {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-family: var(--font-display);
  font-weight: 650;
  font-size: var(--step--1);
  color: var(--accent);
  text-decoration: none;
}

.bl-callout__link span { transition: transform .2s ease; }
.bl-callout__link:hover { text-decoration: underline; }
.bl-callout__link:hover span { transform: translateX(4px); }

/* The end-of-post product card is a device — a window onto the product, with the same
   proof points the product page prints, so the claim here and the claim there can never
   diverge — and a caption under it that says so. The article hands the reader over here. */
.bl-product {
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
  max-width: var(--bl-measure);
}

.bl-product__head {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr);
  gap: 1rem 1.1rem;
  align-items: center;
  margin-bottom: 1rem;
}

.bl-product__head img {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 12px;
  object-fit: contain;
}

.bl-product__eyebrow {
  margin: 0 0 .15rem;
  font-family: var(--font-display);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ok);
}

.bl-product h2 {
  font-size: var(--step-2);
  margin: 0;
}

.bl-product__tagline {
  margin: .25rem 0 0;
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--text-soft);
}

.bl-product__summary {
  margin: 0 0 1rem;
  color: var(--text-soft);
  font-size: var(--step--1);
  line-height: 1.6;
}

.bl-product .k-checks { margin-bottom: .2rem; }

.bl-product__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .7rem;
  padding: .9rem 1.1rem 1rem;
  border-top: 1px solid var(--k-dev-rule, var(--border-soft));
}

.bl-product__actions .k-btn {
  padding: .7rem 1.2rem;
  font-size: var(--step--1);
}

/* --------------------------------------------------------------- 7. tags and pager */

.bl-taglist {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  padding: 0;
  margin: 0;
}

.bl-taglist a {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .3rem .75rem;
  border: 1px solid var(--k-rule, var(--border));
  border-radius: 999px;
  color: var(--k-soft, var(--text-soft));
  font-size: var(--bl-small);
  text-decoration: none;
  transition: border-color .18s ease, color .18s ease;
}

.bl-taglist a:hover {
  border-color: var(--k-hi, var(--accent));
  color: var(--k-hi, var(--accent));
}

.bl-taglist a i {
  font-style: normal;
  font-family: var(--font-mono);
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
  color: var(--k-faint, var(--text-faint));
}

.bl-taglist--small a {
  padding: .18rem .6rem;
  border-color: var(--k-rule-soft, var(--border-soft));
}

/* "Filed under", at the end of an article: a labelled hairline and the chips. */
.bl-filed {
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
  max-width: var(--bl-measure);
}

.bl-filed .k-rule {
  margin-block: 0 1rem;
}

.bl-filed__heading {
  font: inherit;
  margin: 0;
}

/* Previous / next, and a position readout in the mono face. */
.bl-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: clamp(1.8rem, 5vw, 2.6rem);
}

.bl-pager .k-btn {
  padding: .65rem 1.15rem;
  font-size: var(--step--1);
}

.bl-pager__position {
  margin: 0;
  flex: 1 1 auto;
  text-align: center;
  font-family: var(--font-mono);
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
  color: var(--k-faint);
}

/* A spent direction stays in place so the row does not reflow between pages. */
.bl-pager__spent {
  opacity: .38;
  pointer-events: none;
}

/* ------------------------------------------------------------ 8. empty and preview */

/* What a listing shows when there is nothing behind it: a written state, not a blank. */
.bl-empty {
  max-width: 40rem;
  padding: clamp(1.6rem, 5vw, 2.4rem) 0 0;
  border-top: 1px solid var(--k-rule);
}

.bl-empty__eyebrow {
  margin: 0 0 .6rem;
  font-family: var(--font-mono);
  font-size: .78rem;
  color: var(--k-faint);
}

.bl-empty h3 {
  font-size: var(--step-2);
  margin-bottom: .7rem;
}

.bl-empty p {
  color: var(--k-soft);
  margin-bottom: 1.4rem;
  max-width: 52ch;
}

.bl-empty__row {
  display: flex;
  flex-wrap: wrap;
  gap: .8rem;
}

/* The preview banner an author sees over an unpublished post. */
.bl-preview {
  background: var(--bl-preview-bg);
  border-bottom: 1px solid var(--warn);
  color: var(--warn);
  padding-block: .8rem;
  font-size: var(--bl-small);
  line-height: 1.5;
}

.bl-preview strong { color: var(--warn); }

/* -------------------------------------------------------------------- 9. wide screens */

@media (min-width: 1180px) {
  /* At desktop the article's furniture may breathe past the measure — the callout and the
     product card are objects, not prose, and a 62ch card beside a 62ch column reads as an
     indent rather than an element. The text column itself never moves. */
  .bl-callout,
  .bl-product,
  .bl-filed {
    max-width: calc(var(--bl-measure) + 4rem);
  }

  .bl-callout,
  .bl-product {
    margin-inline: -2rem;
  }

  /* A wide figure is the one thing an author can put past the measure. It keeps the left
     axis — a picture that starts left of the text reads as a second column — and runs into
     the empty right third, which is what that space is for. Never past the shell. */
  .bl-figure--wide {
    max-width: none;
    width: calc(var(--bl-measure) + 14rem);
  }
}

/* ---------------------------------------------------------------------- 9b. print */

/* On paper the furniture keeps its shape and loses its ground: a tinted wash prints as a
   grey block and a hairline does the same work for nothing. The hero prints where it stands. */
@media print {
  .bl-note,
  .bl-takeaways {
    background: none;
    border-color: var(--ink);
    break-inside: avoid;
  }

  .bl-note__mark { color: var(--ink); }

  .bl-pullquote {
    break-inside: avoid;
    border-color: var(--ink);
  }

  .bl-pullquote::before { background: var(--ink); }

  .bl-figure { break-inside: avoid; }

  .bl-figure__img,
  .bl-hero img {
    box-shadow: none;
    border-color: var(--ink);
    background: none;
  }

  .bl-masthead--lede {
    clip-path: none;
  }

  .bl-masthead--lede .k-cover__aside {
    margin-bottom: 0;
  }

  .bl-masthead--lede + .bl-body {
    padding-top: clamp(2.5rem, 6vw, 4rem);
  }
}

/* -------------------------------------------------------------------------- 10. motion */

/* The byline and the hero enter with the rest of the cover — the same stagger the shared
   cover uses, behind the same two gates: `.k-motion` (added by script once the observer
   exists) and the reduced-motion query. With either gate closed, nothing here applies and
   the masthead is simply there. Nothing in the article body ever animates. */
@media (prefers-reduced-motion: no-preference) {
  .k-motion .bl-masthead .bl-byline {
    opacity: 0;
    transform: translateY(22px);
    animation: k-lift .8s cubic-bezier(.2, .65, .25, 1) .62s forwards;
  }

  .k-motion .bl-doors .bl-door {
    opacity: 0;
    transform: translateY(14px);
    animation: k-lift .7s cubic-bezier(.2, .65, .25, 1) forwards;
    animation-delay: calc(.45s + var(--i, 0) * .14s);
  }
}

@media (prefers-reduced-motion: reduce) {
  .bl-entry,
  .bl-door,
  .bl-callout__link span,
  .bl-door__go::after {
    transition: none;
  }
}
