/* Editorial theme. No framework, no JS. Blocks bring their own CSS, inlined
   per page, so a page only pays for what it renders. */

:root {
  --bg: #fdfdfc;
  --fg: #1a1a1a;
  --accent: #2563eb;

  /* **Derived from the two colours the blog actually chose**, never fixed.
     `base.html` overrides `--bg` and `--fg` per blog, and custom properties
     resolve at use rather than at declaration, so these follow whatever it set.

     They used to be literals here plus a second set of literals under
     `prefers-color-scheme: dark`, which decided the greys independently of the
     palette -- and got it wrong for every blog either way. Five of the 27 chose
     a dark background (altcodex, tokenrift, betbriefs, bodyartmags,
     playingwise): they got this file's *light* tint, so every checklist, note,
     stats and CTA block rendered as a pale card with white text on it. The
     other 22 chose white and, in a reader's dark mode, got a near-black tint
     card on a white page. The `--bg`/`--fg` in that media query were dead the
     whole time, because the per-blog `:root` comes later and always won. */
  --tint: color-mix(in oklab, var(--bg), var(--fg) 5%);
  --rule: color-mix(in oklab, var(--bg), var(--fg) 13%);
  --muted: color-mix(in oklab, var(--bg), var(--fg) 55%);
  --muted-strong: color-mix(in oklab, var(--bg), var(--fg) 74%);
  /* Reading measure for a single column of prose. */
  --measure: 34rem;
  /* Outer container. Rail and card layouts need real width; the single-column
     editorial layout deliberately stays narrow. */
  --container: calc(var(--measure) + var(--gutter) * 2);
  --gutter: 1.5rem;

  /* -- the style bundle ---------------------------------------------------
     `Blog.style` picks a bundle in models.py and `base.html` writes it into a
     second :root after this one, which wins on every token it sets. These are
     the EDITORIAL values, which is what all 27 sites shipped before the bundle
     existed -- so a blog on a style this file has not heard of still renders
     what it always rendered, rather than nothing.

     Each of these was a literal here, the same on every site. That is why the
     estate measured 100% identical footers and 92.9% identical mastheads while
     differing in colour, typeface and rail: the pigment varied and the voice
     did not. */
  --heading-weight: 700;
  --heading-track: -.028em;
  --heading-scale: 1;
  /* Tracking and scale ease off on a list title: a headline set at -.04em is
     tight, and the same figure on a 1.2rem row is cramped. Derived rather than
     given, so a bundle sets one number and both follow. */
  --heading-track-sm: calc(var(--heading-track) * .5);
  --heading-scale-sm: calc(1 + (var(--heading-scale) - 1) * .5);
  /* A list title has to out-weigh the excerpt sitting under it, and at a
     display weight of 400 it does not: size alone separates them and the row
     stops scanning. The light styles keep their light headline and give the
     lists a floor. Derived for the same reason as the two above -- a bundle
     sets the display weight and everything else follows from it. */
  --heading-weight-sm: max(var(--heading-weight), 600);
  --label-transform: uppercase;
  --label-track: .09em;
  --label-weight: 650;
  --label-variant: normal;
  --label-scale: 1;
  --radius: 6px;
  --radius-sm: 4px;
  --chip-radius: 999px;
  --rule-w: 1px;
  --edge-w: 2px;
  --body-size: 18px;
  --leading: 1.68;
  --rhythm: 1;
  --masthead-dir: row;
  --masthead-cross: baseline;
  --topicbar-bg: var(--tint);
  --topicbar-justify: flex-start;
  --footer-nav-dir: column;
  --ornament: "";
  --ornament-size: 0;
  --ornament-gap: 0;
  --footer-bg: transparent;
  --footer-fg: inherit;
  --standfirst-face: var(--font-body);
  --standfirst-size: 1.13rem;
  --standfirst-weight: 400;
  --kicker-fill: transparent;
  --kicker-ink: var(--mark);
  /* The second accent falls back to the brand colour, which is what every blog
     had before it existed. `base.html` overrides it per blog. */
  --mark: var(--accent);
}

/* No `prefers-color-scheme` block. A blog picks its own background and text
   colour in Site, the way a publication picks its paper -- five of the 27 have
   chosen a dark one deliberately -- and inverting that to follow a reader's OS
   would be the theme overruling the brand. The block that used to be here did
   not do that either: it could never reach `--bg` or `--fg`, and all it managed
   was to hand every page a set of greys belonging to the other scheme. */

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-optical-sizing: var(--font-optical, none);
  /* Set here rather than on each heading: axes a face does not carry are
     ignored, so one declaration covers the display face without listing every
     element that uses it. */
  font-variation-settings: var(--font-variation, normal);
  font-size: var(--body-size);
  line-height: var(--leading);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; }

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

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

/* Wide shells: the rail and card layouts were designed edge-to-edge. */
.layout-digest, .layout-grid, .layout-stream { --container: 78rem; --gutter: 2rem; }
.layout-stream { --container: 62rem; }

.skip { position: absolute; left: -9999px; }
.skip:focus { left: var(--gutter); top: .5rem; background: var(--fg); color: var(--bg); padding: .5rem .8rem; border-radius: 4px; z-index: 10; }

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

.masthead { border-bottom: var(--rule-w) solid var(--rule); }
/* Geometry, off the body class rather than the style bundle: wanting a split
   masthead and an ornamented heading are unrelated decisions, and welding them
   into one preset meant no combination could give you both. */
.masthead-centred .masthead__inner { flex-direction: column; align-items: center; }
.masthead-stacked .masthead__inner { flex-direction: column; align-items: flex-start; }
.masthead-centred .topicbar__inner,
.masthead-centred .seasonbar__inner { justify-content: center; }
/* Split or stacked is the bundle's call: `column` puts the name over the
   sections the way a masthead does, and `--masthead-cross` decides whether that
   stack is centred or ranged left. Nothing else moves -- it is the same markup
   and the same flex container. */
.masthead__inner { display: flex; flex-direction: row; align-items: baseline;
  justify-content: space-between;
  gap: calc(1.5rem * var(--rhythm)); padding-block: calc(1.15rem * var(--rhythm));
  flex-wrap: wrap; }
.masthead__brand { display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--font-heading); font-size: calc(1.14rem * var(--heading-scale-sm));
  font-weight: var(--heading-weight); letter-spacing: var(--heading-track-sm);
  text-decoration: none; }
.brand__icon { flex-shrink: 0; color: var(--accent); }
.masthead__brand img { display: block; }
.masthead__nav { display: flex; gap: 1.15rem; flex-wrap: wrap;
  font-size: calc(.81rem * var(--label-scale)); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant); }
.masthead__nav a { color: var(--muted-strong); text-decoration: none; }
.masthead__nav a:hover { color: var(--accent); }
/* About and Contact are pages, not sections. Set apart by a rule and a lighter
   weight rather than by a second nav, so the masthead stays one row at every
   width -- the sections already wrap, and a second flex row would wrap twice.

   The rule was `.masthead__nav-page:first-of-type`, which never matched
   anything on any of the 27 sites: `:first-of-type` counts element type and
   not class, so it wanted a page link that was also the first <a> in the nav,
   and the first <a> is always Home. Only the lighter weight ever shipped. The
   adjacent-sibling form asks the question that was meant -- the first page
   link after a section link -- since the pages are always last and contiguous.

   `margin-left: auto` does the pushing in a row masthead and nothing in a
   column one, where the nav shrink-wraps and centres; the rule carries the
   distinction in both. */
.masthead__nav a:not(.masthead__nav-page) + .masthead__nav-page {
  margin-left: auto; padding-left: 1.15rem;
  border-left: var(--rule-w) solid var(--rule); }
.masthead__nav-page { color: var(--muted); }

/* An author archive is a page about a person, so it leads with the face rather
   than with the count. Falls back to the plain listing header when there is no
   avatar, which is every author on the estate today. */
.author__header { display: flex; gap: 1.25rem; align-items: flex-start; }
.author__avatar { border-radius: 50%; flex: none; object-fit: cover; }
.author__role { color: var(--muted-strong); font-size: .9rem; margin: .1rem 0 .5rem; }

/* -- shared bits --------------------------------------------------------- */

.article__kickers { display: flex; gap: .9rem; margin: 0 0 .3rem; }
/* The one mannerism that appeared five times above the fold on every site.
   Tracked caps, small caps or simply bold sentence case is a bundle decision
   now, and it is shared by the kicker, the nav, the topic bar, the contents
   label, the rail titles and "Keep reading" -- one rule, six places. */
.kicker { display: inline-block; font-size: calc(.71rem * var(--label-scale));
  font-weight: var(--label-weight); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant);
  color: var(--accent); text-decoration: none; margin-bottom: .5rem; }

.meta { display: flex; gap: .9rem; flex-wrap: wrap; margin: 0; font-size: .8rem; color: var(--muted); }
.meta__updated { font-style: italic; }

.empty { color: var(--muted); font-style: italic; list-style: none; padding: 2rem 0; }

/* -- home ---------------------------------------------------------------- */

/* The slogan, in the size a slogan is. It is still the homepage h1 -- the site's
   own name for itself, and what Google has indexed on 27 domains -- but it used
   to be set at clamp(2rem, 6vw, 2.9rem) and own the top third of the column on
   every visit, with the newest article starting below the fold. */
.siteline { padding-block: calc(1.5rem * var(--rhythm)) calc(1rem * var(--rhythm)); }
.siteline__title { margin: 0; font-family: var(--font-heading);
  font-weight: var(--label-weight); font-size: calc(.86rem * var(--label-scale));
  letter-spacing: var(--label-track); text-transform: var(--label-transform);
  font-variant-caps: var(--label-variant); color: var(--muted-strong); }
.siteline__note { margin: .35rem 0 0; font-size: .95rem; color: var(--muted);
  max-width: var(--measure); }

/* A run of posts under a heading that links to the archive it samples. The
   heading carries the style bundle's flourish, which is what keeps a homepage
   expressing its blog's design now that the billboard is gone. */
.home__section { padding-block: calc(1.25rem * var(--rhythm)) 0; }
.sectionhead { display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; padding-block: calc(1.6rem * var(--rhythm)) .4rem;
  border-top: var(--edge-w) solid var(--fg); }
.sectionhead__title { margin: 0; font-family: var(--font-heading);
  font-weight: var(--heading-weight-sm); font-size: calc(1.1rem * var(--heading-scale-sm));
  letter-spacing: var(--heading-track-sm); }
.sectionhead__more { flex: none; font-size: calc(.75rem * var(--label-scale));
  font-weight: var(--label-weight); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant);
  color: var(--muted); text-decoration: none; }
.sectionhead__more:hover { color: var(--accent); }


.lead { padding-block: 0 calc(2rem * var(--rhythm));
  border-bottom: var(--rule-w) solid var(--rule); }
.lead--media { display: grid; grid-template-columns: 1.1fr 1fr; gap: 2rem;
  align-items: center; }
.lead__link { text-decoration: none; display: block; }
.lead__title { font-family: var(--font-heading); font-weight: var(--heading-weight);
  font-size: calc(clamp(1.7rem, 5vw, 2.5rem) * var(--heading-scale)); line-height: 1.12;
  letter-spacing: var(--heading-track); margin: 0; }
.lead__link:hover .lead__title { color: var(--accent); }
.lead__excerpt { margin: .7rem 0 .9rem; color: var(--muted-strong); }

.lead__media { display: block; }
.lead__media img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3;
  object-fit: cover; border-radius: var(--radius); }

.row__media img { display: block; width: 100%; height: auto;
  aspect-ratio: 3 / 2; object-fit: cover; border-radius: var(--radius-sm); }

/* -- what the style bundle marks with ------------------------------------- */

/* The second accent has one job: marking. What is current, what is new, the
   ornament above a section. It falls back to the brand colour, so a blog that
   has not set one is unchanged -- which is the difference between this and the
   `--secondary` that was removed for being emitted everywhere and read by
   nothing. */
.kicker--mark { color: var(--mark); }
/* Filled or plain is a bundle decision. On a bleed hero a plain kicker
   disappears into the photograph, which is what a chip is for. */
.article__bleedwords .kicker { background: var(--kicker-fill); color: var(--kicker-ink);
  padding: .28rem .6rem; border-radius: var(--radius-sm); }

/* **The headings inside an article were never given the display face.** The
   title, the list titles and the section heads all read `--font-heading`; the
   `## ` headings a writer types came out of `md.py` and inherited the body
   font, so on every blog with a display serif the article's own structure was
   set in something else. Nobody noticed because seven of the eight presets pair
   a serif with a sans that look alike at 1.8rem. */
.article__body h2, .article__body h3, .article__body h4 {
  font-family: var(--font-heading); font-weight: var(--heading-weight-sm);
  letter-spacing: var(--heading-track-sm);
}
.article__body h2 { font-size: calc(1.6rem * var(--heading-scale-sm)); line-height: 1.18;
  margin: 2.4rem 0 .8rem; }
.article__body h3 { font-size: calc(1.2rem * var(--heading-scale-sm)); line-height: 1.25;
  margin: 1.8rem 0 .5rem; }

/* An ornament cannot be conditionally absent: `content: ""` still generates a
   box, so the size and the gap collapse with it. */
.article__body h2::before {
  content: var(--ornament); display: block; color: var(--mark);
  font-size: var(--ornament-size); margin-bottom: var(--ornament-gap);
  line-height: 1;
}

/* A drop cap is a decision about the article, not about the type: an essay
   wants one and a reference page does not, whatever face either is set in. */
.article--dropcap .article__body > .blk-prose:first-of-type > p:first-of-type::first-letter {
  float: left; font-family: var(--font-heading);
  font-weight: var(--heading-weight); font-size: 4em; line-height: .82;
  padding: .06em .09em 0 0; color: var(--mark);
}

/* -- the bleed hero -------------------------------------------------------- */

/* Edge to edge, with the headline set inside the picture. It escapes the wrap
   with a viewport-width trick rather than by living outside `main`, because the
   article has to stay one element for the grid below to place its parts. */
.article__bleed { position: relative; margin: 0; }
.article__bleed img {
  display: block; width: 100%; height: clamp(22rem, 58vh, 34rem);
  object-fit: cover;
}
.article__bleed::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(8,10,6,.82) 0%, rgba(8,10,6,.3) 40%, transparent 68%);
}
.article__bleedwords {
  position: absolute; z-index: 1; left: 0; right: 0; bottom: 0;
  padding-block: var(--gutter);
}
/* A headline set inside a photograph is a display headline: at the article
   title's own size it read as a caption on 1512px of picture. Not a knob -- it
   follows from the layout, and a bleed that did not do this would just be a
   large image with small type on it. */
.article__bleedwords .article__title {
  color: #fff; text-wrap: balance;
  font-size: calc(clamp(2.4rem, 6.2vw, 4.4rem) * var(--heading-scale));
  line-height: 1.02;
}
.article__bleedwords .kicker { color: #fff; opacity: .82; }

/* -- the margin column ----------------------------------------------------- */

.article--magazine { display: grid; column-gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: minmax(0, var(--measure)) minmax(0, 15rem);
  align-content: start; }
.article--magazine > * { grid-column: 1; }
/* The column starts where the prose does. It is a grid child, so without this
   it began at the top of the grid and its rule sat flush against the bottom of
   the hero -- touching the photograph while the article it belongs to started
   67px lower. Matched to `.article__header`'s own top padding rather than
   guessed, so the two columns move together if the rhythm changes. */
.article__margin {
  grid-column: 2; grid-row: span 30; position: sticky; top: 2rem;
  align-self: start; display: block;
  margin-top: calc(3rem * var(--rhythm)); padding-top: .3rem;
  border-top: var(--edge-w) solid var(--mark);
}
.article__margin dl { margin: 0 0 1.6rem; }
.article__margin dt { font-size: calc(.68rem * var(--label-scale));
  font-weight: var(--label-weight); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant);
  color: var(--muted); margin-bottom: .1rem; }
.article__margin dd { margin: 0 0 .85rem; font-size: .88rem;
  font-weight: var(--label-weight); letter-spacing: .04em; text-transform: uppercase; }

@media (max-width: 62rem) {
  .article--magazine { grid-template-columns: minmax(0, 1fr); }
  .article__margin { grid-column: 1; grid-row: auto; position: static; }
  .article__margin > * { display: inline; margin-top: 0; }
}

/* -- listings ------------------------------------------------------------ */

.listing__header { padding-block: calc(3rem * var(--rhythm)) calc(1.5rem * var(--rhythm)); border-bottom: var(--edge-w) solid var(--fg); }
.listing__title { font-family: var(--font-heading); font-weight: var(--heading-weight);
  font-size: calc(clamp(1.8rem, 5vw, 2.4rem) * var(--heading-scale)); line-height: 1.1;
  letter-spacing: var(--heading-track); margin: 0; }
.listing__description { margin: .7rem 0 0; font-size: 1.02rem; color: var(--muted-strong); max-width: 32rem; }
.listing__count { margin: .5rem 0 0; font-size: .82rem; color: var(--muted); }

.rows { list-style: none; margin: 0; padding: 0; }
.row { padding-block: calc(1.5rem * var(--rhythm)); border-bottom: var(--rule-w) solid var(--rule); }
/* Separators sit BETWEEN items -- a trailing rule leaves a stray line above
   the footer. */
.rows .row:last-child { border-bottom: none; }
.row--media { display: grid; grid-template-columns: 120px 1fr; gap: 1.1rem; align-items: start; }
.row--media .row__media img { aspect-ratio: 4 / 3; }
.row--compact.row--media { grid-template-columns: 72px 1fr; gap: .9rem; align-items: center; }
.row a { text-decoration: none; }
.row__title { font-family: var(--font-heading); font-weight: var(--heading-weight-sm);
  font-size: calc(1.2rem * var(--heading-scale-sm)); line-height: 1.3;
  letter-spacing: var(--heading-track-sm); margin: 0 0 .4rem; }
.row a:hover .row__title { color: var(--accent); }
.row__excerpt { margin: 0 0 .55rem; font-size: .93rem; color: var(--muted-strong); }
.row--compact { padding-block: .95rem; }
.row--compact .row__title { font-size: 1.05rem; margin-bottom: .25rem; }

/* -- article ------------------------------------------------------------- */

.article { padding-bottom: 3.5rem; }
/* **One column: the headline, the picture and the prose share an edge.**
   `.shell__body .article__body` constrained only the prose -- and
   `base.html` emits `.shell__body` whether or not there is a shell -- so the
   header, the standfirst and the featured image ran to the container while the
   first paragraph ran to the measure. On a feature page that put the standfirst
   200px wider than the paragraph under it, and it was true of every layout: the
   two widths just happened to be close enough not to look wrong. */
.article > * { max-width: var(--measure); }
.article__header { padding-block: calc(3rem * var(--rhythm)) calc(1.75rem * var(--rhythm)); }
.article__title { font-family: var(--font-heading); font-weight: var(--heading-weight);
  font-size: calc(clamp(1.9rem, 5.5vw, 2.65rem) * var(--heading-scale)); line-height: 1.11;
  letter-spacing: var(--heading-track); margin: 0; }
/* The standfirst was the body face at 1.13rem on every blog. It is the one
   sentence between a headline and the article, and a publication that sets its
   headlines in a display face usually sets this in it too. */
.article__standfirst { margin: .9rem 0 0; font-family: var(--standfirst-face);
  font-size: var(--standfirst-size); font-weight: var(--standfirst-weight);
  line-height: 1.42; color: var(--muted-strong); text-wrap: pretty; }
.article__meta { margin-top: 1.15rem; padding-top: 1.15rem; border-top: var(--rule-w) solid var(--rule); }
.article__body { --bleed: 0px; }
.article__body > :first-child { margin-top: 0; }

.article--page .article__header { padding-block: 3rem 1.25rem; }

/* The featured image, shown on the post rather than only in the card that
   links to it. Full bleed of the measure, because a photograph indented to
   the text column reads as an illustration of a paragraph. */
.article__lead { margin: 0 0 2rem; }
/* A magazine opening: the photograph before the headline and at the width of
   the article rather than the measure, so it reads as the piece beginning
   rather than as an illustration of the first paragraph. */
.article--feature .article__header { padding-block: calc(1.75rem * var(--rhythm)) calc(1.5rem * var(--rhythm)); }
/* The hero is the one thing that breaks the column, which is what makes it a
   magazine opening rather than a large picture. It also needs its own space
   above: the header used to provide that, and on a feature the header comes
   after -- so the photograph sat flush against the topic bar. */
.article > .article__lead--hero { max-width: none; }
.article__lead--hero { margin: calc(2rem * var(--rhythm)) 0 1.5rem; }
/* Capped against the viewport, not just the column: 2:1 across a wide body is
   470px of photograph, and with a centred masthead above it the headline lands
   below the fold. A feature opens with a picture; it does not consist of one. */
.article__lead--hero img { aspect-ratio: 2 / 1; object-fit: cover;
  max-height: 42vh; }
/* An article with nothing beside it can afford to start higher. */
.article--essay .article__header,
.article--feature .article__header { padding-top: calc(2rem * var(--rhythm)); }
.article__lead img { display: block; width: 100%; height: auto; border-radius: var(--radius); }
.article__credit,
.blk-image figcaption.credit { margin: .5rem 0 0; font-size: .78rem; color: var(--muted); }
.article__credit a,
.blk-image figcaption.credit a { color: inherit; text-decoration: underline; }

.toc { margin: 0 0 calc(2.25rem * var(--rhythm)); padding: 1.1rem 1.3rem; background: var(--tint); border-radius: var(--radius); }
.toc__label { margin: 0 0 .55rem; font-size: calc(.71rem * var(--label-scale));
  font-weight: var(--label-weight); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant);
  color: var(--muted); }
.toc ol { margin: 0; padding-left: 1.15rem; font-size: .91rem; }
.toc li { margin-bottom: .28rem; }
.toc li.toc__l3 { padding-left: .85rem; }
.toc li.toc__l4 { padding-left: 1.7rem; }
.toc a { color: var(--muted-strong); text-decoration: none; }
.toc a:hover { color: var(--accent); text-decoration: underline; }

.related { margin-top: calc(3rem * var(--rhythm)); padding-top: 1.4rem; border-top: var(--rule-w) solid var(--rule); }
.related__title { margin: 0 0 .8rem; font-size: calc(.78rem * var(--label-scale));
  font-weight: var(--label-weight); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant);
  color: var(--muted); }
.related ul { list-style: none; margin: 0; padding: 0; }
.related li { padding-block: .6rem; border-bottom: var(--rule-w) solid var(--rule);
  display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; }
.related li:last-child { border-bottom: none; }
.related a { font-family: var(--font-heading); font-weight: var(--heading-weight-sm); text-decoration: none; }
.related a:hover { color: var(--accent); }
.related__topic { flex-shrink: 0; font-size: .78rem; color: var(--muted); }

.byline { display: flex; gap: 1rem; align-items: flex-start; margin-top: calc(3rem * var(--rhythm)); padding-top: 1.5rem; border-top: var(--rule-w) solid var(--rule); }
.byline img { border-radius: 50%; flex-shrink: 0; }
.byline__name { margin: 0 0 .2rem; font-weight: 650; font-size: .93rem; }
.byline__role { display: block; font-weight: 400; font-size: .8rem; color: var(--muted); margin-top: .1rem; }
.byline__bio { margin: 0; font-size: .88rem; color: var(--muted); }

/* -- footer -------------------------------------------------------------- */

/* A colophon rather than a footer: the panel is a `color-mix` of the blog's own
   two colours, and because this redefines `--bg` and `--fg` to them, every grey
   inside it -- `--tint`, `--rule`, `--muted` -- follows on its own. A literal
   dark panel would have clashed with the five blogs that chose a dark page. */
.footer { border-top: var(--rule-w) solid var(--rule); margin-top: calc(2rem * var(--rhythm));
  padding-block: calc(2.25rem * var(--rhythm)) calc(1.5rem * var(--rhythm));
  --bg: var(--footer-bg); --fg: var(--footer-fg);
  background: var(--footer-bg); color: var(--footer-fg); }
.footer__inner { display: flex; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
.footer__name { margin: 0; font-family: var(--font-heading); font-weight: var(--heading-weight);
  letter-spacing: var(--heading-track-sm); }
.footer__tagline { margin: .25rem 0 0; font-size: .87rem; color: var(--muted); max-width: 22rem; }
/* A column of links beside the name, or a row under it. The 27 built footers
   measured 100% identical, which is the cheapest network tell on the estate. */
.footer__nav { display: flex; flex-direction: var(--footer-nav-dir); flex-wrap: wrap;
  gap: .35rem 1.15rem; font-size: .87rem; }
.footer__nav a { color: var(--muted-strong); text-decoration: none; }
.footer__nav a:hover { color: var(--accent); }
.footer__legal { margin-top: 1.75rem; font-size: .78rem; color: var(--muted); }
.footer__legal p { margin: 0; }
.footer__disclaimer { margin: 0 0 .7rem !important; max-width: 46rem; line-height: 1.55; }

@media (max-width: 640px) {
  body { font-size: calc(var(--body-size) - 1px); }
  .lead--media { grid-template-columns: 1fr; gap: 1.1rem; }
  .lead__media img { aspect-ratio: 16 / 10; }
  .sectionhead { padding-block: calc(1.2rem * var(--rhythm)) .4rem; }
  .masthead__inner { padding-block: calc(.9rem * var(--rhythm)); }
  .row--media { grid-template-columns: 96px 1fr; gap: .8rem; }
  .row--compact.row--media { grid-template-columns: 64px 1fr; }
}

/* -- two-tier header ------------------------------------------------------ */

.masthead__nav .count, .topicbar__inner .count {
  font-size: .78em; color: var(--muted); margin-left: .3em;
}
.topicbar { border-top: var(--rule-w) solid var(--rule); background: var(--topicbar-bg); }
.topicbar__inner { display: flex; gap: 1.15rem; flex-wrap: wrap;
  justify-content: var(--topicbar-justify);
  padding-block: calc(.6rem * var(--rhythm));
  font-size: calc(.81rem * var(--label-scale)); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant); }
.topicbar__inner a { color: var(--muted-strong); text-decoration: none; }
.topicbar__inner a:hover { color: var(--accent); }

/* -- the year -------------------------------------------------------------- */

.seasonbar { border-top: var(--rule-w) solid var(--rule); background: var(--tint); }
.seasonbar__inner {
  display: flex; gap: clamp(.6rem, 2.4vw, 2rem); justify-content: center;
  list-style: none; margin: 0; padding-block: calc(.55rem * var(--rhythm));
  font-size: calc(.72rem * var(--label-scale)); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant);
  font-weight: var(--label-weight); color: var(--muted);
}
.seasonbar li { position: relative; }
.seasonbar .is-now { color: var(--mark); }
.seasonbar .is-now::after {
  content: ""; position: absolute; left: 50%; bottom: -.38rem;
  width: 4px; height: 4px; margin-left: -2px; border-radius: 50%;
  background: var(--mark);
}
@media (max-width: 40rem) {
  .seasonbar__inner { justify-content: flex-start; overflow-x: auto; }
}

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

/* Right by default, which is where a publication puts a rail: the eye starts
   at the top left, so a left rail sits a list of secondary links at the natural
   reading origin and pushes the lead story off it. Left is still available --
   it is what all 11 rail blogs shipped -- and `none` hides it without deleting
   the widgets that configure it. */
.shell { display: grid; grid-template-columns: minmax(0, 1fr) 17rem; gap: 3.5rem;
  align-items: start; }
.shell--left { grid-template-columns: 17rem minmax(0, 1fr); }
.shell--left .shell__rail { order: -1; }
/* Prose outside an article -- a search result, a listing description -- keeps
   the measure too. The article itself is constrained by `.article > *`, one
   rule for every part of it, so the header and the body cannot disagree. */
.shell__body .blk-prose { max-width: var(--measure); }
.shell__rail { position: sticky; top: 1.5rem; padding-top: calc(2.5rem * var(--rhythm)); }
.rail__widget { margin-bottom: calc(2rem * var(--rhythm)); }
.rail__title { margin: 0 0 .7rem; font-size: calc(.78rem * var(--label-scale));
  font-weight: var(--label-weight); letter-spacing: var(--label-track);
  text-transform: var(--label-transform); font-variant-caps: var(--label-variant);
  color: var(--muted); }
.rail__list { list-style: none; margin: 0; padding: 0; font-size: .92rem; line-height: 1.4; }
.rail__list li { padding-block: .6rem; border-bottom: var(--rule-w) solid var(--rule); }
.rail__list li:last-child { border-bottom: none; }
.rail__list li:first-child { border-top: var(--rule-w) solid var(--rule); }
.rail__list a { color: inherit; text-decoration: none; }
.rail__list a:hover { color: var(--accent); }
.rail__chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.chip { padding: .25rem .65rem; border: var(--rule-w) solid var(--rule); border-radius: var(--chip-radius);
  font-size: .82rem; text-decoration: none; color: var(--muted-strong); }
.chip:hover { border-color: var(--accent); color: var(--accent); }
.rail__search input { width: 100%; padding: .5rem .7rem; font: inherit; font-size: .9rem;
  border: var(--rule-w) solid var(--rule); border-radius: var(--radius); background: var(--bg); color: var(--fg); }
.rail__cta { font-size: .9rem; color: var(--muted-strong); margin: 0;
  padding: .9rem 1rem; background: var(--tint); border-radius: var(--radius); }

/* -- text list with right-aligned date ------------------------------------ */

.row__head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.row__date { flex-shrink: 0; font-size: .8rem; color: var(--muted); white-space: nowrap; }

/* -- card grid ------------------------------------------------------------ */

/* The section already spaces itself; the grid's own padding on top of that
   pushed the card row past the fold, so the homepage showed one article and a
   reader had to scroll to learn there were others. */
.home__section .grid { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  padding-block: 0; }
.grid { list-style: none; margin: 0; padding: 0; display: grid; gap: calc(2rem * var(--rhythm));
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); padding-block: calc(2rem * var(--rhythm)); }
/* **The cards line up on their parts, not on their tops.** A tile is a subgrid
   of the row's tracks, so every card's kicker sits on the kicker line, every
   title on the title line and every meta on the meta line -- whatever any one
   card happens to be missing. Before this the parts were nested inside a link
   and the kicker was conditional, so one post without a topic pushed its whole
   card up out of step with the two beside it. */
.grid { grid-template-rows: repeat(5, auto); }
.tile { display: grid; grid-row: span 5; grid-template-rows: subgrid; row-gap: 0; }
.tile a { text-decoration: none; }
.tile__media { margin-bottom: .8rem; }
/* An empty slot, so a card with no picture keeps the row it would have used. */
.tile__media--none { display: block; }
.tile__kicker { margin: 0 0 .3rem; min-height: 1lh; }
.tile__excerpt:empty { margin: 0; }
.tile__media img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2;
  object-fit: cover; border-radius: var(--radius); }
.tile__title { font-family: var(--font-heading); font-weight: var(--heading-weight-sm);
  font-size: calc(1.14rem * var(--heading-scale-sm)); line-height: 1.3;
  letter-spacing: var(--heading-track-sm); margin: 0; }
.tile a:hover .tile__title { color: var(--accent); }
.tile__excerpt { margin: .35rem 0 .6rem; font-size: .93rem; color: var(--muted-strong); }
.tile .meta { align-self: end; }

@media (max-width: 900px) {
  .shell, .shell--left { grid-template-columns: 1fr; gap: 0; }
  /* One column: the rail goes under the article whichever side it takes on a
     wide screen, so `--left` has to give up its pull to the front. */
  .shell__rail, .shell--left .shell__rail { position: static; padding-top: 1.5rem;
    order: 2; border-top: var(--rule-w) solid var(--rule); }
  .row__head { display: block; }
  .row__date { display: block; margin-top: .2rem; }
}

/* **There is no rule under a title, and the tokens for one are gone.**
   A 3.4rem stub of accent shipped under every h1 on all 27 sites, and it became
   a style-bundle decision -- a stub, a full-width hairline, a thick bar, or
   nothing. Then every place it could live acquired a structural rule that does
   the same job: the hero went when the homepage got a composition, a listing
   header draws its own `border-bottom`, `.sectionhead` draws a `border-top`,
   and an article's meta line draws one under the standfirst. In each case the
   flourish became a second horizontal rule a few lines from the first.

   So `--title-rule-w`, `-h`, `-gap` and `-color` are deleted rather than left
   unread on ten bundles. A token nothing reads is `color_secondary` again. */

/* -- search page ---------------------------------------------------------- */

.searchpage__form { margin: 1.5rem 0 .5rem; }
.searchpage__form input { width: 100%; padding: .7rem .9rem; font: inherit; font-size: 1rem;
  border: var(--rule-w) solid var(--rule); border-radius: var(--radius); background: var(--bg); color: var(--fg); }
.searchpage__form input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.searchpage__status { font-size: .84rem; color: var(--muted); margin: .6rem 0 0; }
.masthead__search input { padding: .35rem .7rem; font: inherit; font-size: .84rem;
  border: var(--rule-w) solid var(--rule); border-radius: var(--radius); background: var(--bg); color: var(--fg); width: 11rem; }

/* Paid-link disclosure. Quiet but not hidden: it has to be legible to a reader
   who is looking for it, and the one thing it must never be is absent. */
.disclosure {
  margin: calc(2rem * var(--rhythm)) 0 0;
  padding-top: 0.9rem;
  border-top: var(--rule-w) solid var(--rule);
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--muted);
}
.disclosure p { margin: 0 0 0.4rem; }
.disclosure p:last-child { margin-bottom: 0; }

/* A sponsored link reads as a link, and carries no extra decoration: dressing
   it up as a button is the thing that makes a paid link look like an ad and an
   ad look like a recommendation. */
.partner-link { text-decoration-style: solid; }
