/* ---------------------------------------------------------------------------
   /guides/ — page layout.

   Only what makes a guide a guide. Everything that reconciles HATCH with this
   site — typography, the radius scale, surfaces — is in `hatch-bridge.css`,
   which every HATCH page loads.

   The one thing worth stating: HATCH's bento is a dashboard component, and its
   reference pages are dashboards, so tiles butt straight into whatever follows
   because on a dashboard nothing follows. A reading page needs its sections
   separated, so the rhythm below is the site's own rather than the bento's
   internal gap.
--------------------------------------------------------------------------- */

/* --- 1. Room between the parts ------------------------------------------- */
/* The eyebrow stays at the page's top left — it labels the page, and a label
   belongs where reading starts. The heading and the sentence under it are
   centred over the illustration that follows, which is symmetrical.

   No `text-align` on the header itself: `.h-eyebrow` is inline-flex, so it
   would be pulled to the centre with everything else. Only the two blocks that
   should move are told to. */
/* A masthead, left-aligned. Centred type reads as a marketing page; a
   publication's contents page starts at the left margin and stays there, and
   everything below this — the feature, the posters, the note — is on that same
   line. It was the only centred block on a page of left-aligned ones. */
/* `.guide-head` was the guides index's own head. It is `art__head` now. */

/* HATCH places its six tiles by hand (a…f). A guide has four to six depending
   on how many of its steps are built, so these span and let the grid place
   them — the anchor is still chosen first, as its guidelines ask. */

@media (max-width: 1120px) {
}
@media (max-width: 720px) {
}

/* A footnote, not a card. It qualifies everything above it rather than adding
   to it, so it sits under a rule at the page's own width and takes none of the
   elevation the tiles use to say "read me". HATCH's `.callout` was the wrong
   component twice over: an 11px bold centred chart annotation. */
.guide-note {
  margin: 0; padding: 1.5rem 0 0;
  border: 0; box-shadow: none; background: none;
  border-top: 1px solid var(--border-hairline);
}
/* Footnote size and colour. The LABEL is in caps; the sentences are not.
   Sixty words of uppercase loses the word shapes a reader scans by and is
   measurably slower to read — the caps do the "this is a footnote" job on the
   label, where it is three words, and the note stays readable underneath. */
.guide-note__label {
  margin: 0 0 0.5rem;
  font: 500 0.6875rem/1 var(--font-display);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-tertiary);
}
.guide-note p {
  margin: 0; max-width: none;
  font: 400 0.8125rem/1.7 var(--font-sans);
  letter-spacing: .005em;
  color: var(--text-tertiary); text-wrap: pretty;
}

/* The lime tile is one decision, not a poster. Without a chart inside it, the
   footer's `margin-top:auto` opened a hole the height of the tile. */

@media (max-width: 720px) {
  .guide-head { margin-bottom: 2.5rem; }
}

/* --- The figure explanation ----------------------------------------------
   The note is in the card's HTML, so a crawler and a screen reader both have
   it whether or not anyone hovers. Only the presentation changes with width.

   At desktop it covers the card on hover and on keyboard focus. Below that a
   hover state is unreachable, so the `?` opens HATCH's drawer from the bottom
   edge — the same component the app bar uses, moved up from 640px to tablet
   because that is where a pointer stops being reliable. */

.fig { position: relative; }
.fig__more {
  width: 1.75rem; height: 1.75rem; flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: rgba(var(--ink-rgb), .06); color: var(--text-secondary);
  font: 500 0.8125rem/1 var(--font-display); cursor: pointer;
  transition: background var(--dur-micro) var(--ease-out);
}
.fig__more:hover { background: rgba(var(--ink-rgb), .12); color: var(--text-primary); }

.fig__note { display: none; }
.fig__note p { margin: 0 0 0.875rem; font: var(--type-body-sm); line-height: 1.6; color: var(--text-secondary); }
.fig__note p:last-child { margin-bottom: 0; }
.fig__note b {
  display: block; margin-bottom: 0.1875rem;
  font: 500 0.6875rem/1 var(--font-display); letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-tertiary);
}

@media (min-width: 64rem) {
  .fig__note {
    display: block; position: absolute; inset: 0; z-index: 2;
    padding: var(--s-8); border-radius: inherit; overflow-y: auto;
    background: var(--surface-card);
    opacity: 0; visibility: hidden;
    transform: translateY(0.375rem);
    transition: opacity var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out),
                visibility var(--dur-base);
  }
  .fig:hover .fig__note,
  .fig:focus-within .fig__note { opacity: 1; visibility: visible; transform: none; }
}

/* Below desktop the whole card opens the sheet, not just the affordance — a
   thumb is aimed at the card, and a 28px target inside it is a smaller thing
   to hit than the thing it describes. The `?` stays because it is the keyboard
   control and the only visible sign the card holds more.

   HATCH's controls "compress on press", so the card presses down rather than
   lifting the way it does under a pointer. */
@media (max-width: 63.9375rem) {
  .fig { cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .fig:active {
    transform: translateY(0.1875rem) scale(0.995);
    box-shadow: var(--sh-xs);
  }
  .fig:active .fig__more { background: rgba(var(--ink-rgb), .12); }
}

/* HATCH sends the drawer to the bottom edge below 640px. A tablet has no
   hover either, so it comes from the bottom there too. */
@media (max-width: 63.9375rem) {
  .fig-sheet {
    top: auto; bottom: 0; left: 0; right: 0;
    width: 100%; height: auto; max-height: 86dvh;
    border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
    transform: translateY(101%);
  }
  .fig-sheet 
}
.fig-sheet__body p { margin: 0 0 1rem; font: var(--type-body); color: var(--text-secondary); }
.fig-sheet__body p:last-child { margin-bottom: 0; }
.fig-sheet__body b {
  display: block; margin-bottom: 0.25rem;
  font: 500 0.6875rem/1 var(--font-display); letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-tertiary);
}

/* --- A tile left alone at the end takes the row -------------------------- */
@media (min-width: 70rem) { }
@media (min-width: 45rem) and (max-width: 69.9375rem) { }

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
@media (max-width: 45rem) {
}

@media (max-width: 40rem) {
}

/* Built against not-yet-built needs to read at a glance: a solid fill on a
   hatched track, which is what the texture is for. */

/* --- The guides index ----------------------------------------------------
   Two tiles to a row rather than three: four guides make a square, and a
   fourth column would leave one of them alone. */

/* The guides index: each tile names the calculator it explains. */

/* --- The featured guide --------------------------------------------------
   The index behaves like a publication: one guide leads with its image, the
   rest are cards. It is the most recently written one, not a decorative band —
   a hero on a listing page that promotes nothing is the thing every blog does
   and nobody reads. */
/* --- Guides that are written but are not the feature -----------------------
   The feature is one guide at full width with its poster beside the copy;
   these are the same object at card scale, stacked rather than side by side.
   A picture is what a written guide earns -- the unwritten ones below stay in
   the register, because a poster on an article that does not exist promises
   one.

   Two columns, and the card-grid spacing rule: a grid of cards gets twice the
   gap a grid of anything else would. */
.gposts {
  /* Two across, not four. At four the posters came out about 230px wide and
     read as thumbnails beside a feature image five times their size; the whole
     point of giving each written guide a poster was that it is a picture.

     Column counts are stated rather than fitted, as before: four cards divide
     by two and by one, and never leave an orphan. `auto-fit` chose three at
     around 1000px and stranded the fourth. */
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: clamp(2.5rem, 4vw, 3.5rem);
  margin: 0 0 clamp(3.5rem, 6vw, 5rem);
}
@media (min-width: 44rem) { .gposts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* `.gpost`, not `.gcard`. HATCH defines a `.gcard` of its own -- glass
   background, backdrop-filter, 32px padding, 24px radius -- so every one of
   these arrived wearing a frosted card nobody asked for, on a page whose
   feature has no ground at all. Ours are entries on the page, not boxes.

   The lesson is the name: `_ds/hatch/components.css` is a namespace, and a
   class invented without checking it is a collision waiting to render. Guard
   42 checks the whole of our CSS against it now. */
.gpost { display: flex; flex-direction: column; }
.gpost__copy { display: flex; flex-direction: column; flex: 1; }
.gpost__art {
  display: block; overflow: hidden;
  background: var(--surface-sunken);
}
.gpost__art img { display: block; width: 100%; height: auto; }
.gpost__where {
  display: block; margin-top: 1.375rem;
  font: var(--type-mono-sm); color: var(--text-tertiary);
}
.gpost__copy h2 {
  margin: 0.5rem 0 0;
  font: 500 clamp(1.375rem, 2.2vw, 1.875rem)/1.14 var(--font-display);
  letter-spacing: -0.028em; text-wrap: balance;
}
.gpost__copy h2 a { color: inherit; text-decoration: none; }
.gpost__copy h2 a:hover { text-decoration: underline; text-underline-offset: 4px; }
/* `:not(.gpost__go)` because the link is a <p> too, and `.gpost__copy p`
   scores 0-1-1 against the link rule's 0-1-0 -- so the prose margin was
   overriding `margin-top: auto` and the link sat wherever the paragraph above
   it ended. Three cards, three different lines. */
.gpost__copy p:not(.gpost__go) {
  margin: 0.875rem 0 0; max-width: 44ch;
  font: var(--type-body-sm); color: var(--text-secondary); text-wrap: pretty;
}
.gpost__go { margin: auto 0 0; padding-top: 1.375rem; }
.gpost__go a {
  font: 500 0.875rem/1.2 var(--font-display); color: var(--text-primary);
  text-decoration-color: var(--raw-ink-40); text-underline-offset: 5px;
}
.gpost__go a:hover { text-decoration-color: currentColor; }

/* --- The lead ---------------------------------------------------------------
   The poster runs the full width of the page and the copy sits under it, which
   is the shape the other four repeat at half width. One form, two scales: a
   reader learns it once at the top and reads the rest without relearning it.

   Full bleed out of the page's column. The 50vw/-50vw pair is the standard
   escape from a centred measure, and `100vw` is deliberate here rather than
   `100cqw` -- there is no container query on this column, and a scrollbar's
   width on a poster is a pixel nobody sees. */
/* A rule under the whole lead. Poster, heading and copy read as one unit
   because a line closes them, and the four below start after it rather than
   simply appearing further down the page. Hairline, not a border: this
   separates, it does not frame. */
.lead {
  margin: 0 0 clamp(3.5rem, 7vw, 6rem);
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
  border-bottom: 1px solid var(--border-hairline);
}
.lead__art {
  display: block;
  width: 100vw; margin-left: calc(50% - 50vw);
  overflow: hidden;
}
.lead__art img { display: block; width: 100%; height: auto; }

/* The copy runs the width of the page under the poster, in two columns: the
   heading on the left, the note and the link on the right. A heading alone
   left the right half of a full-bleed opening empty, and body text set to
   1280px is not the answer -- a line of prose stops being readable somewhere
   around 75 characters. Two columns fill the width AND keep both measures. */
.lead__copy {
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  column-gap: clamp(2rem, 5vw, 4.5rem); align-items: start;
}
.lead__where {
  grid-column: 1 / -1;
  display: block;
  font: var(--type-mono-sm); color: var(--text-tertiary);
}
.lead__copy h2 {
  grid-column: 1; margin: 0.75rem 0 0;
  font: 500 clamp(1.875rem, 4vw, 3.5rem)/1.02 var(--font-display);
  letter-spacing: -0.04em; text-wrap: balance;
}
.lead__copy h2 a { color: inherit; text-decoration: none; }
.lead__copy h2 a:hover { text-decoration: underline; text-underline-offset: 6px; }
.lead__copy p {
  grid-column: 2; margin: 1.375rem 0 0; max-width: 46ch;
  font: var(--type-body-sm); line-height: 1.65; color: var(--text-secondary);
  text-wrap: pretty;
}
.lead__go { grid-column: 2; margin-top: 1.5rem; }

/* One column when there is not room for two, and the heading leads. */
@media (max-width: 52rem) {
  .lead__copy { grid-template-columns: minmax(0, 1fr); }
  .lead__copy h2, .lead__copy p, .lead__go { grid-column: 1; }
}
.lead__go a {
  font: 500 0.875rem/1.2 var(--font-display); color: var(--text-primary);
  text-decoration-color: var(--raw-ink-40); text-underline-offset: 5px;
}
.lead__go a:hover { text-decoration-color: currentColor; }

/* --- A card with a poster ------------------------------------------------
   The poster IS the hero, cropped to the slot. One asset, two jobs. */

@media (max-width: 60rem) {
  .feature { grid-template-columns: 1fr; gap: 1.75rem; margin-bottom: 3.5rem; }
}

/* --- The source graph ----------------------------------------------------
   A loose constellation of every calculator and every office that publishes a
   rule it computes with. Not a chart — no axes, no alignment, no hover. The
   lines draw themselves in once and the nodes drift.

   Ink at low alpha throughout: this is structure, not emphasis, and the fill
   ladder puts weight on hue rather than darkness. Nothing here is lime — no
   single dependency is the decision on this page. */
.sg { margin: 0 0 3.5rem; }

/* Full bleed out of the page's column, and NOT a coloured band. It has no
   ground of its own — the lines and the labels sit on the page the way the
   answers cover's portraits do, so the section opens with a drawing rather
   than with a panel laid across it.

   Height comes from an aspect ratio rather than a fixed clamp, which is the
   other half of the same change. A fixed height plus `overflow: hidden` was
   cropping the top row of labels: the upper nodes sit at 13% of the band and a
   label hangs above its node, so at 160px that label started at -5px and the
   band ate it. There is nothing to crop now, and nothing to crop it with.

   `width: 100%` is stated even though `100vw` already sets it, because
   `aspect-ratio` on a box with an `auto` width will shrink the WIDTH to hold
   the ratio the moment anyone adds a height cap. That cost an afternoon on the
   answers cover. */
.sg__band {
  width: 100vw; margin-left: calc(50% - 50vw);
  aspect-ratio: 4 / 1;   /* the viewBox's own ratio: no stretch at all here */
  position: relative;    /* the label overlay hangs off this */
}
.sg__band svg { display: block; width: 100%; height: 100%; }

/* Taller as it narrows, so the three bands of nodes keep room to be three
   bands. The drawing stretches to fit — `preserveAspectRatio="none"` — and
   only the curves stretch: the dots are screen-space round caps and the labels
   are HTML, so neither distorts. */
@media (max-width: 52rem) { .sg__band { aspect-ratio: 3 / 1; } }
@media (max-width: 34rem) { .sg__band { aspect-ratio: 2 / 1; } }

/* Four paths per connection: the faint line itself, and three stacked
   highlights travelling along it together. The dash pattern needs the real
   curve length — a fixed guess makes a short line flicker and a long one crawl
   — so the generator measures each one and passes it as `--len`.

   A stroke cannot carry a gradient ALONG its own path, so the soft ends are
   built by stacking instead: a 56px dash at low alpha, a 26px at middling, a
   10px core at full. Painted in that order they read as one highlight that
   fades in at the leading edge and out at the trailing one — gathering out of
   the faint line and dissolving back into it, rather than sliding along it
   like a tick.

   `--lead` is what keeps the three centred on each other: each layer starts
   half its own excess length early and ends half of it late, so all three
   midpoints sit at the same place at every moment of the cycle. Drop it and
   the layers fan apart into three separate dashes.

   Each layer's width and opacity ride in as `--w` and `--o` alongside its
   `--dash`, so the whole ladder lives in one array in the generator rather
   than half here and half there. */
.sg__line {
  fill: none;
  stroke: rgba(var(--ink-rgb), .18);
  stroke-width: 1;
  /* The parent stretches non-uniformly, which would make a horizontal line and
     a vertical one different thicknesses. This pins the hairline at 1px in
     screen space, whichever way the curve runs. It is deliberately NOT set on
     the pulse below: a dash pattern on a non-scaling stroke is measured in
     screen pixels, and `--len` is in user units. */
  vector-effect: non-scaling-stroke;
}
.sg__pulse {
  --core: 10px;
  --lead: calc((var(--dash) - var(--core)) / 2);
  fill: none;
  stroke: var(--lime-700);
  stroke-linecap: round;
  stroke-dasharray: var(--dash) calc(var(--len) * 1px);
  stroke-dashoffset: calc(var(--len) * 1px + var(--lead));
  stroke-width: var(--w);
  opacity: var(--o);
  animation: sg-pulse var(--dur) linear var(--delay) infinite;
}
@keyframes sg-pulse { to { stroke-dashoffset: calc(var(--lead) - var(--dash)); } }

.sg__node { animation: sg-float var(--dur) ease-in-out var(--delay) infinite; }
@keyframes sg-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(calc(var(--drift) * -1)); }
}

/* Round caps on a zero-length path, in screen space — see `node` in
   source-graph.js. `stroke-width` IS the diameter in px. */
.sg__dot {
  fill: none;
  stroke: rgba(var(--ink-rgb), .55);
  stroke-linecap: round;
  stroke-width: 8;
}
.sg__node--tool .sg__dot { stroke: var(--text-primary); stroke-width: 12; }

/* HTML over the drawing, positioned in percentages that match the stretched
   viewBox exactly. `translate(-50%, -100%)` puts the label's baseline row above
   its dot; the extra rem clears the dot itself. */
.sg__labels { position: absolute; inset: 0; pointer-events: none; }
.sg__label {
  position: absolute;
  transform: translate(-50%, calc(-100% - 0.5rem));
  white-space: nowrap;
  font: 500 0.8125rem/1 var(--font-display);
  letter-spacing: -0.01em;
  color: var(--text-tertiary);
}
.sg__label--tool {
  font: 500 0.9375rem/1 var(--font-display);
  color: var(--text-primary);
  transform: translate(-50%, calc(-100% - 0.625rem));
}

/* Decoration is the first thing that should stop moving. */
@media (prefers-reduced-motion: reduce) {
  .sg__pulse { display: none; }
  .sg__node { animation: none; }
}

.sg__cap {
  max-width: 46rem; margin: 1.25rem auto 0; text-align: center;
  font: var(--type-body-sm); line-height: 1.6; color: var(--text-tertiary);
}

/* The constellation runs everywhere. The labels come off only when they start
   colliding with each other, which is at 34rem, not 52rem.

   The old 52rem came from when these were SVG <text>: they scaled with the
   drawing, so 13px landed near 8px on a tablet and had to go. They are HTML in
   an overlay now and render at 13px at every width — that reasoning expired
   when they moved and the breakpoint outlived it. Measured at 768: twelve
   labels, no pair overlapping, none off screen. At 375 six pairs collide, so
   there they still go. */
@media (max-width: 34rem) {
  .sg { margin-bottom: 2.5rem; }
  .sg__labels { display: none; }
  .sg__dot { stroke-width: 7; }
  .sg__node--tool .sg__dot { stroke-width: 10; }
}

/* --- The guides register --------------------------------------------------
   The unwritten guides, listed rather than boxed. Four cards whose whole
   content was a title, one line and the words "not written yet" is a box drawn
   around an absence — and four of them in a grid is the shape the answers root
   was rebuilt to escape. The written guide keeps its poster above; that is
   where a card earns itself. */
.gidx {
  list-style: none; margin: 0; padding: 0;
  border-top: 1px solid var(--border-hairline);
}
.gidx__item { border-bottom: 1px solid var(--border-hairline); }
.gidx__item > a,
.gidx__row {
  display: grid; grid-template-columns: 2.5rem minmax(0, 1fr) auto;
  align-items: baseline; gap: 0.75rem 1rem;
  padding: 1.875rem 0.25rem 0.5rem;
  text-decoration: none; color: inherit;
  transition: background var(--dur-micro) var(--ease-out);
}
.gidx__item > a:hover { background: rgba(var(--ink-rgb), .028); }
.gidx__no {
  font: 500 0.75rem/1.4 var(--font-mono); color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.gidx__title { display: block; font: 500 1.0625rem/1.3 var(--font-display); color: var(--text-primary); text-wrap: pretty; }
.gidx__sub { display: block; margin-top: 0.5rem; font: var(--type-caption); color: var(--text-secondary); text-wrap: pretty; }
.gidx__meta { display: flex; align-items: baseline; gap: 1rem; justify-self: end; white-space: nowrap; }
.gidx__where { font: var(--type-mono-sm); color: var(--text-tertiary); }
/* Named rather than linked, and marked as such — the rule the figures follow,
   applied to pages. */
.gidx__soon { font: var(--type-caption); color: var(--text-tertiary); }
.gidx__go { font: 400 0.9375rem/1 var(--font-mono); color: var(--text-tertiary); }
.gidx__item > a:hover .gidx__go { color: var(--text-primary); }
.gidx__calc {
  margin: 1rem 0 1.875rem; padding-left: 3.25rem;
  font: var(--type-caption); color: var(--text-tertiary);
}
.gidx__calc a { color: var(--text-secondary); }

@media (max-width: 40rem) {
  .gidx__item > a, .gidx__row { grid-template-columns: 2rem minmax(0, 1fr); padding: 1.5rem 0.25rem 0.25rem; }
  .gidx__meta { grid-column: 2; justify-self: start; margin-top: 0.375rem; }
  .gidx__calc { margin: 0.875rem 0 1.5rem; padding-left: 2.75rem; }
}

/* What the figure means, and why anyone would want it. Two labelled lines per
   guide, which is what an index of guides is for — a reader choosing between
   them has not read any of them yet, and a title plus a subtitle does not help
   them choose. The labels are the narrow column; the sentences run beside. */
.gidx__note {
  display: grid; grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: 0.875rem 1.25rem; margin-top: 0.875rem;
  font: var(--type-caption); color: var(--text-secondary); text-wrap: pretty;
}
.gidx__k {
  font: var(--type-mono-xs);
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-tertiary);
}
@media (max-width: 40rem) {
  /* The label sits over its sentence rather than beside it — at 375px a
     6.5rem column leaves the sentence about twenty characters wide. */
  .gidx__note { grid-template-columns: minmax(0, 1fr); gap: 0.375rem; }
  .gidx__k { margin-top: 0.625rem; }
}

/* ---------------------------------------------------------------------------
   The all-jurisdictions comparison page.

   Deliberately a table and not the eight cards the calculator carries: this
   page is read to rank, and ranking down a column is what a table is for. The
   card strip on the calculator answers a different question and keeps its
   cards.
--------------------------------------------------------------------------- */
.cmp { display: grid; gap: 3rem; }
/* The compare page had a private eyebrow, title and lede of its own -- three
   more names for what art__head already does. Only the call to action is left,
   and it needs no header wrapper to sit in. */
.cmp-actions { margin: 0.25rem 0 0; }
.cmp-cta { font-weight: 600; }

.cmp-case { display: grid; gap: 0.875rem; }
.cmp-case h2 { margin: 0; text-wrap: balance; }
.cmp-note { margin: 0; max-width: 68ch; color: var(--text-secondary); }

/* The ranked table. Digits line up or the ranking is not readable. */
.cmp-table { width: 100%; }
.cmp-table .cmp-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.cmp-table tr.is-best { background: var(--surface-accent, rgba(var(--ink-rgb), .04)); }
.cmp-table tr.is-best td { font-weight: 600; }
.cmp-code {
  font: 500 0.75rem/1 var(--font-mono); letter-spacing: 0.06em;
  color: var(--text-tertiary); margin-right: 0.4rem;
}
.cmp-na { color: var(--text-tertiary); font-style: italic; }

.cmp-lede { margin: 0; max-width: 68ch; }

.cmp-reasons, .cmp-sources { margin: 0; padding-left: 1.1rem; display: grid; gap: 0.6rem; }
.cmp-sources { list-style: none; padding-left: 0; }
.cmp-sources li { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: baseline; }
.cmp-when { color: var(--text-tertiary); font-size: 0.875rem; }

@media (max-width: 40rem) {
  .cmp { gap: 2.25rem; }
}
