/* =========================================================================
   Ludic Group — components
   One rule per block from _build/blocks.py, matching the live site's layout.
   Same breakpoint ladder as foundation.css: 36 / 48 / 64 / 80rem.
   Same naming: block__element--modifier.
   ========================================================================= */

/* ---------- home hero ---------------------------------------------------
   The live site's hero: a full-bleed looping background video with an
   off-white card floating over it, holding the circle mark, the h1 and one
   button. Original geometry: 619px tall, a 1.25fr/1fr grid inside an 80rem
   container, card padding 3rem 2.5rem, card background #f9fbf7.
   ----------------------------------------------------------------------- */
.hero--home { position: relative; overflow: hidden; background: var(--black); }

.hero__video,
.hero__video-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The poster sits under the video, so the hero is never a black hole while
   the video buffers and still reads correctly if it cannot play at all. */
.hero__video-poster { z-index: 0; }
.hero__video { z-index: 1; }

.hero__inner {
  position: relative;
  z-index: 2;
  display: grid;
  padding-block: var(--space-lg);
}
@media (min-width: 64rem) {
  /* align-items: start stops the card stretching to the full row height and
     leaving dead space under the button. */
  .hero__inner { grid-template-columns: 1.15fr 1fr; padding-block: var(--space-2xl); align-items: start; }
}

.hero__card {
  background: var(--header-cream);
  padding: var(--space-md) var(--space-sm);
}
@media (min-width: 48rem) { .hero__card { padding: var(--space-lg) var(--space-md); } }

.hero__mark { width: 4rem; height: 4rem; margin-bottom: var(--space-sm); }
@media (min-width: 48rem) { .hero__mark { width: 6rem; height: 6rem; } }

/* No measure cap here. The card is already a narrow column, and capping the
   title as well left a third of the card empty to the right of every line.
   The title fills the card and wraps where the card ends. */
.hero__title { max-width: none; text-wrap: balance; }
.hero__card .prose__p { margin-top: var(--space-xs); }

/* Not rendered by default — the live page hides its footage credit and the
   client asked for the same. Styling kept for the day one is wanted: a white
   line floating on pale footage is unreadable, so it gets a dark chip. */
.hero__credit {
  position: relative;
  z-index: 2;
  display: inline-block;
  margin-bottom: var(--space-xs);
  padding: 0.25rem 0.5rem;
  border-radius: 2px;
  background: rgb(0 0 0 / 0.6);
  color: var(--white);
  font-size: var(--text-xs);
}

/* ---------- interior page hero ------------------------------------------ */
.hero--page { padding-block: var(--space-lg); }
@media (min-width: 64rem) { .hero--page { padding-block: var(--space-xl); } }

.hero--page .hero__inner { display: grid; gap: var(--space-md); padding-block: 0; }
@media (min-width: 64rem) {
  .hero--page .hero__inner--split { grid-template-columns: 1.1fr 1fr; gap: var(--space-lg); align-items: center; }
  .hero--page .hero__inner:not(.hero__inner--split) .hero__content { max-width: 48rem; }
}

.page-hero__logo { height: 3rem; width: auto; margin-bottom: var(--space-sm); }
@media (min-width: 48rem) { .page-hero__logo { height: 4.5rem; } }

.page-hero__media img,
.page-hero__media picture { width: 100%; }

.hero__content .prose__p { margin-top: var(--space-xs); }

/* ---------- brand tiles -------------------------------------------------
   Three colour tiles butted together with no gap — the live site's
   .collection-list-business, 350px tall inside the 80rem container, with the
   logo centred and the description revealed on hover. A hover-only reveal is
   unreachable by touch, so below 64rem js/site.js reveals whichever tile is
   nearest the middle of the viewport, and the reveal is bound to
   :focus-visible as well as :hover.
   ----------------------------------------------------------------------- */
/* White field behind the tile row (client's call — the live site runs cream
   here, but the statement above it is white and they wanted the two to read
   as one band). */
.section--brand-tiles { background: var(--white); padding-block: var(--space-md); }

.brand-tiles { display: grid; margin: 0; padding: 0; list-style: none; }
@media (min-width: 64rem) { .brand-tiles { grid-template-columns: repeat(3, 1fr); } }

.brand-tile { display: flex; }

.brand-tile__link {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 15rem;
  padding: var(--space-lg) var(--space-md);
  text-decoration: none;
  overflow: hidden;
  transition: background-color var(--duration) var(--ease);
}
@media (min-width: 64rem) { .brand-tile__link { min-height: 21.875rem; } }

/* Logo and copy occupy the SAME grid cell. At rest the logo is visible and
   the copy is not; on hover they swap. Stacking them in one cell rather than
   one above the other is what lets each be positioned independently —
   neither reserves space that pushes the other around.

   All three logo rows are centred on the tile's vertical axis. */
.brand-tile__logo-row,
.brand-tile__body {
  grid-area: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

/* Advisory and Media Studio are single-line wordmarks and sit dead centre.
   SmartLab is a stacked lockup — the node graphic rises above the word — so
   centring its whole artwork drops the word well below the other two. It is
   nudged UP by half the height difference, which lands its baseline on the
   same line as Media Studio's.

   Every SVG here is cropped to its ink box, so rendered height is
   width x (viewBox height / viewBox width):

     smartlab      94.25 / 267.50 = 0.35234 of its width
     media studio  35.00 / 389.38 = 0.08989 of its width

   The two width rules below are in the ratio 14.4 / 21 = 0.6857, and their
   percentage caps 64 / 93 = 0.6882 agree to a quarter of a percent, so
   h_media / h_smartlab = 0.372 in either regime. Shifting up by
   (h_smartlab - h_media) / 2 = 0.314 x h_smartlab puts the two bottoms on one
   line, and a percentage translateY resolves against the element's OWN
   height — so this holds as the tiles resize, with no fixed pixel value to
   go stale. Change a width rule and this has to be re-derived. */
.brand-tile--smartlab .brand-tile__logo { transform: translateY(-31.4%); }

/* Tile colours are the live site's and do not change on hover — the copy
   fading in is the whole affordance. NOTE: white on #5b8edb is 3.31:1, below
   AA for body text. It is here by explicit client direction (white SmartLab
   copy, no colour shift); checks.py records it as an accepted exception with
   the measured value rather than pretending it passes. */
.brand-tile--advisory .brand-tile__link { background: var(--bold-blue); color: var(--white); }
.brand-tile--smartlab .brand-tile__link { background: var(--smartlab-blue); color: var(--white); }
.brand-tile--media-studio .brand-tile__link { background: var(--yellow); color: var(--black); }

/* Sized to reproduce the live optical scale. The original draws all three
   450x198 assets at 388px wide inside a 426.7px tile — 0.8628 px per SVG
   unit. These SVGs have been cropped to their ink box, so each one is set to
   its own cropped width x 0.8628:
     advisory     304.25u -> 262.5px -> 16.4rem
     smartlab     267.50u -> 230.8px -> 14.4rem
     media studio 389.38u -> 335.9px -> 21.0rem
   Change a viewBox and these numbers have to be recomputed.

   The percentage is the fallback for narrow tiles, and it resolves against
   the link's CONTENT box — tile width minus 2 x 2rem of side padding, so
   362.7px at a 426.7px tile, not 426.7px. The caps were first written
   against the tile width, which made them the smaller value at every real
   width: the rem targets above never applied and every logo rendered ~14%
   under the live size. Percentages here are of 362.7px:
     262.5 / 362.7 = 72.4% -> 73
     230.8 / 362.7 = 63.6% -> 64
     335.9 / 362.7 = 92.6% -> 93 */
.brand-tile__logo { height: auto; }
.brand-tile--advisory .brand-tile__logo { width: min(16.4rem, 73%); }
.brand-tile--media-studio .brand-tile__logo { width: min(21rem, 93%); }
.brand-tile--smartlab .brand-tile__logo { width: min(14.4rem, 64%); }

.brand-tile__body {
  margin: 0 auto;
  max-width: 34ch;
  font-size: var(--text-base);
  line-height: 1.6;
  text-align: center;
}

/* The swap. Below the desktop breakpoint there is no hover, so js/site.js
   marks the tile nearest the middle of the viewport [data-in-view] and that
   one swaps instead. Both start visible and the script opts into hiding the
   copy, so with JavaScript off every description is simply on show. */
.brand-tiles[data-reveal] .brand-tile__body { opacity: 0; }
.brand-tiles[data-reveal] .brand-tile__logo-row,
.brand-tiles[data-reveal] .brand-tile__body {
  transition: opacity var(--duration) var(--ease);
}
.brand-tiles[data-reveal] .brand-tile__link:hover .brand-tile__body,
.brand-tiles[data-reveal] .brand-tile__link:focus-visible .brand-tile__body,
.brand-tiles[data-reveal] .brand-tile[data-in-view] .brand-tile__body { opacity: 1; }

.brand-tiles[data-reveal] .brand-tile__link:hover .brand-tile__logo-row,
.brand-tiles[data-reveal] .brand-tile__link:focus-visible .brand-tile__logo-row,
.brand-tiles[data-reveal] .brand-tile[data-in-view] .brand-tile__logo-row { opacity: 0; }

/* ---------- feature split ----------------------------------------------- */
.feature { display: grid; gap: var(--space-md); align-items: center; }
@media (min-width: 64rem) {
  .feature { grid-template-columns: 1fr 1fr; gap: var(--space-lg); }
  .feature--flip .feature__media { order: 2; }
}

.feature__media { margin: 0; }
.feature__image { width: 100%; }
.feature__title { margin-bottom: var(--space-xs); }

/* A sentence-case line under the title, where the live page puts a long
   descriptor that would shout as an all-caps eyebrow. */
.feature__subhead {
  margin-bottom: var(--space-xs);
  font-size: var(--text-md);
  line-height: 1.5;
  color: var(--dark-grey);
}
.section[data-tone="black"] .feature__subhead { color: var(--blue-on-dark); }

.feature__badges-note { margin-top: var(--space-sm); font-size: var(--text-sm); }

.feature__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-sm);
  margin-top: var(--space-2xs);
  padding: 0;
  list-style: none;
}
.badge { display: inline-flex; }
.badge img { height: 1.875rem; width: auto; }

/* ---------- people ------------------------------------------------------ */
.people { display: grid; gap: var(--space-md); }
@media (min-width: 48rem) { .people { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .people { grid-template-columns: repeat(3, 1fr); gap: var(--space-lg); } }

/* No background and no forced square: the headshots are cut out on white,
   so a tinted box behind them just draws a grey rectangle around the subject. */
/* No background and no crop: the headshots are cut out on white, so a tinted
   box behind them draws a grey rectangle around the subject. A fixed box with
   object-fit: contain keeps the names aligned across the row even though the
   source files differ in height (503 vs 494 px). */
/* The portrait is wrapped in <picture> for its WebP source. display:contents
   takes that wrapper out of the layout so the sizing below still resolves
   against the card, not against an inline box in between. */
.person picture { display: contents; }

.person__portrait {
  width: 100%;
  max-width: 13.75rem;
  height: 13.75rem;
  object-fit: contain;
  object-position: center top;
  margin-bottom: var(--space-xs);
}

/* The live pages rule off each name before the bio. */
.person__name {
  font-size: var(--text-md);
  font-weight: 600;
  padding-bottom: var(--space-2xs);
  margin-bottom: var(--space-xs);
  border-bottom: 1px solid var(--line);
}
.person__bio { font-size: var(--text-base); line-height: 1.7; }
.person__link { display: inline-block; margin-top: var(--space-2xs); font-size: var(--text-sm); }

/* ---------- podcast ----------------------------------------------------- */
.podcast { display: grid; gap: var(--space-md); align-items: center; }
@media (min-width: 64rem) { .podcast { grid-template-columns: 0.8fr 1fr; gap: var(--space-lg); } }

.podcast__art { margin: 0; }
.podcast__art img, .podcast__art picture { width: 100%; }

.podcast__title { margin-bottom: var(--space-xs); }

/* No cover art: heading full width, then copy beside the listen row. */
@media (min-width: 64rem) {
  .podcast--noart { grid-template-columns: 1fr; }
  .podcast--noart .podcast__body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--space-2xl);
    align-items: start;
  }
  .podcast--noart .eyebrow,
  .podcast--noart .podcast__title { grid-column: 1 / -1; }
  .podcast--noart .podcast__aside { margin-top: 0; }
  .podcast--noart .podcast__subhead { margin-top: 0; }
}

.podcast__aside { margin-top: var(--space-sm); }

.podcast__subhead {
  margin-top: var(--space-sm);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.podcast__platforms {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  margin: var(--space-2xs) 0 0;
  padding: 0;
  list-style: none;
}
.platform { display: inline-flex; }
.platform img { height: 2.5rem; width: auto; }

/* ---------- cards ------------------------------------------------------- */
.cards { display: grid; gap: var(--space-xs); margin: 0; padding: 0; list-style: none; }
@media (min-width: 48rem) { .cards--2, .cards--3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .cards--3 { grid-template-columns: repeat(3, 1fr); } }

.card {
  display: flex;
  flex-direction: column;
  padding: var(--space-sm);
  background: var(--white);
  border: 1px solid var(--line);
}
.section[data-tone="cream"] .card { border-color: transparent; }

.card__index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem; height: 2rem;
  margin-bottom: var(--space-xs);
  background: var(--black);
  color: var(--white);
  font-size: var(--text-sm);
}

.card__icon { width: 3rem; height: 3rem; margin-bottom: var(--space-xs); }
.card__title { font-size: var(--text-md); font-weight: 600; margin-bottom: var(--space-2xs); }
.card__body { flex: 1; font-size: var(--text-base); line-height: 1.7; }
.card__more { margin-top: var(--space-xs); font-size: var(--text-sm); }

/* ---------- the 7 Cs ---------------------------------------------------- */
.seven-cs { display: grid; gap: var(--space-xs); margin: 0; padding: 0; list-style: none; }
@media (min-width: 48rem) { .seven-cs { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .seven-cs { grid-template-columns: repeat(3, 1fr); } }

.seven-c { padding: var(--space-sm); background: var(--white); }
.seven-c__icon { width: auto; height: 4.5rem; margin-bottom: var(--space-xs); }

.seven-c__title {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: var(--space-2xs);
}
.seven-c__body { font-size: var(--text-base); line-height: 1.7; }

/* ---------- closing CTA ------------------------------------------------- */
.cta { max-width: 44rem; }
.cta__title { margin-bottom: var(--space-xs); }
.cta .button { margin-top: var(--space-sm); }

/* ---------- contact ----------------------------------------------------- */
.contact { display: grid; gap: var(--space-lg); }
@media (min-width: 64rem) { .contact { grid-template-columns: 0.8fr 1fr; align-items: start; } }

.contact__title { margin-bottom: var(--space-xs); }

.contact__list {
  display: grid;
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 2px solid var(--black);
}
.contact__list dt { margin-top: var(--space-xs); font-size: var(--text-xs); font-weight: 600; }
.contact__list dd { margin: 0; line-height: 1.7; }

.form-card { padding: var(--space-sm); background: var(--cream); }
@media (min-width: 48rem) { .form-card { padding: var(--space-md); } }

.form-card__title { font-size: var(--text-lg); font-weight: 400; margin-bottom: var(--space-2xs); }
.form-card__intro { margin-bottom: var(--space-sm); }

.form { display: grid; gap: var(--space-sm); }

.form__notice { padding: var(--space-xs); background: var(--white); font-size: var(--text-sm); line-height: 1.6; }

.form__row { display: grid; gap: var(--space-sm); }
@media (min-width: 36rem) { .form__row { grid-template-columns: repeat(2, 1fr); } }

.form__field { display: grid; gap: 0.375rem; }
.form__label { font-size: var(--text-sm); font-weight: 600; }
.form__optional { font-weight: 400; color: var(--dark-grey); }

/* The live site's input: 2px solid black, square corners, white fill. */
.form__input {
  width: 100%;
  padding: 0.625rem 0.75rem;
  background: var(--white);
  border: 2px solid var(--control-line);
  border-radius: 0;
  font: inherit;
  font-size: var(--text-base);
  color: var(--ink);
}
.form__input--area { resize: vertical; min-height: 8rem; }
.form__input--select { appearance: none; padding-right: 2.5rem; }

.form__field--check { display: flex; align-items: flex-start; gap: 0.625rem; }
.form__check { margin-top: 0.3rem; width: 1.125rem; height: 1.125rem; accent-color: var(--black); }
.form__check-label { font-size: var(--text-sm); line-height: 1.6; }
.form__legal { font-size: var(--text-sm); }

/* =========================================================================
   INSIGHTS INDEX
   Mirrors the live /insights page — the Ludic Insights bar, the Roboto Slab
   heading, an inline Search field, a wrapping row of plain-text tags, and a
   two-column grid of white cards on cream with the format label and title
   ABOVE the image. What is added is the design the original never had: the
   controls sit in one bordered panel instead of floating loose, the cards
   are a fixed rhythm rather than ragged, and there is a real hover, a live
   result count and an empty state.
   ========================================================================= */
/* The lockup sits on the cream band with a full-width rule under it, and the
   copy below runs the full measure — as the live site has it. */
/* Equal air above and below the lockup, so the rule underneath reads as a
   deliberate margin rather than the logo resting on it. */
.insights-bar {
  background: var(--cream);
  padding-top: var(--space-sm);
}
.insights-bar__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-sm); padding-bottom: var(--space-sm); }
.insights-bar__logo { height: 1.3125rem; width: auto; }
.insights-bar__count { font-size: var(--text-xs); color: var(--dark-grey); }
.insights-bar__rule { height: 1px; margin: 0; border: 0; background: var(--black); }

/* --- intro --- */
.insights-intro { background: var(--cream); padding-block: var(--space-md) var(--space-md); }
@media (min-width: 64rem) { .insights-intro { padding-block: var(--space-lg) var(--space-lg); } }

.insights-intro__inner { display: grid; gap: var(--space-sm); }

.insights-intro__title {
  font-family: "Roboto Slab", Georgia, serif;
  font-size: clamp(1.5rem, 2.8vw, var(--text-xl));
  font-weight: 400;
  line-height: var(--leading-snug);
  max-width: 60ch;
}
.insights-intro__lede { font-size: var(--text-base); line-height: var(--leading-body); max-width: 60ch; }

/* --- the filter panel --- */
/* One continuous cream field from the lockup bar down to the last card, with
   the white panel and the white cards sitting on it. A white strip between
   the panel and the grid read as a seam. */
.insights-controls { background: var(--cream); padding-bottom: var(--space-lg); }

.filters {
  display: grid;
  gap: 0;
  border: 2px solid var(--black);
  background: var(--white);
}

.filters__row {
  display: grid;
  gap: var(--space-2xs);
  padding: var(--space-sm);
  border-bottom: 1px solid var(--line);
}
.filters__row:last-of-type { border-bottom: 0; }
@media (min-width: 48rem) {
  .filters__row { grid-template-columns: 7rem 1fr; gap: var(--space-sm); align-items: baseline; }
}

.filters__label,
.filters__legend {
  margin: 0;
  font-family: "Roboto Slab", Georgia, serif;
  font-size: var(--text-md);
  font-weight: 500;
  line-height: 1.3;
}

.filters__input {
  width: 100%;
  max-width: 30rem;
  height: 2.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--white);
  border: 2px solid var(--control-line);
  border-radius: 0;
  font: inherit;
  font-size: var(--text-base);
  color: var(--ink);
}
.filters__input::placeholder { color: var(--mid-grey); }

/* Tags are plain text on the live site, in a wrapping row. They are buttons
   here because they now filter; the resting state keeps the original's look,
   and the active state is a filled chip so it does not rely on colour alone. */
.chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }

.chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.375rem;
  padding: 0.3125rem 0.75rem;
  background: var(--white);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: "Archivo", sans-serif;
  font-size: var(--text-sm);
  line-height: 1.4;
  cursor: pointer;
  transition: background-color var(--duration) var(--ease),
              border-color var(--duration) var(--ease),
              color var(--duration) var(--ease);
}
.chip:hover { border-color: var(--black); }
.chip[aria-pressed="true"] { background: var(--black); border-color: var(--black); color: var(--white); }

.chip__count { font-size: var(--text-xs); color: var(--mid-grey); }
.chip[aria-pressed="true"] .chip__count { color: rgb(255 255 255 / 0.7); }

.filters__status {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  background: var(--cream);
  border-top: 1px solid var(--line);
}
.filters__count { font-size: var(--text-sm); color: var(--dark-grey); }
.filters__clear { font-size: var(--text-sm); font-weight: 600; }

/* --- the grid --- */
.section--insights { background: var(--cream); padding-block: 0 var(--space-xl); }

.insight-grid { display: grid; gap: var(--space-sm); margin: 0; padding: 0; list-style: none; }
@media (min-width: 48rem) { .insight-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 80rem) { .insight-grid--3 { grid-template-columns: repeat(3, 1fr); } }

.insight-card[hidden] { display: none; }

.insight-card__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--white);
  transition: transform var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
}
.insight-card__inner:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 28px rgb(0 0 0 / 0.10);
}
@media (prefers-reduced-motion: reduce) { .insight-card__inner:hover { transform: none; } }

/* Text first, image second — the live site's card order. */
.insight-card__text {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: var(--space-sm);
}

.insight-card__type {
  font-family: "Archivo", sans-serif;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.4;
}

.insight-card__title {
  font-family: "Roboto Slab", Georgia, serif;
  font-size: var(--text-md);
  font-weight: 400;
  line-height: var(--leading-snug);
  /* Two lines keeps every card's text block the same height, so the images
     below them line up across the row. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
/* The underline belongs to the LINK and nowhere else. Underlining the
   heading as well drew a second rule a couple of pixels below the first —
   the "double underline". */
.insight-card__link {
  text-decoration: none;
  transition: color var(--duration) var(--ease);
}
/* Stretch the link over the card so the whole card is the target, without
   nesting interactive elements. */
.insight-card__link::after { content: ""; position: absolute; inset: 0; }
.insight-card__inner:hover .insight-card__link,
.insight-card__link:focus-visible { color: var(--blue); text-decoration: underline; }

.insight-card__summary {
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--dark-grey);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Card art is authored at 1200x627. object-fit keeps that ratio whatever the
   source happens to be, so the current 826x601 exports crop to it and future
   1200x627 files fit exactly. No zoom on hover. */
/* Every cover in the CMS export is 825x600 (a few are 826x601). The box is
   set to that ratio and the image is CONTAINed rather than covered, so the
   whole artwork shows — several of these covers carry titles and logos right
   at the edge and a cover-crop clipped them. An image with a different ratio
   letterboxes onto the cream instead of losing its edges. */
.insight-card__media { margin-top: auto; background: var(--cream); }
.insight-card__media img,
.insight-card__media picture { width: 100%; }
.insight-card__media img { aspect-ratio: 825 / 600; object-fit: contain; }

.insight-empty { margin-top: var(--space-md); font-size: var(--text-md); }

/* ---------- article ----------------------------------------------------- */
.article { padding-block: var(--space-md) var(--space-xl); }

.breadcrumb { margin-bottom: var(--space-sm); }
.breadcrumb__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-xs);
  color: var(--dark-grey);
}
.breadcrumb__list li + li::before { content: "/"; margin-right: 0.5rem; color: var(--mid-grey); }
.breadcrumb__list a { color: var(--dark-grey); text-decoration: none; }
.breadcrumb__list a:hover { color: var(--black); text-decoration: underline; }
.breadcrumb__list [aria-current="page"] { color: var(--black); }

.article__type { font-size: var(--text-base); font-weight: 600; margin-bottom: var(--space-2xs); }

.article__title {
  font-family: "Roboto Slab", Georgia, serif;
  font-size: clamp(1.5rem, 2.8vw, var(--text-xl));
  font-weight: 400;
  line-height: var(--leading-snug);
  margin-bottom: var(--space-xs);
}

.article__summary { font-size: var(--text-md); line-height: 1.6; }
.article__meta { margin-top: var(--space-xs); font-size: var(--text-sm); color: var(--dark-grey); }

.article__figure { margin: var(--space-md) 0 0; }
.article__figure img, .article__figure picture { width: 100%; }

.article__listen, .article__download { margin-top: var(--space-md); }
.article__download { padding: var(--space-sm); background: var(--cream); }
.article__download-note { margin-top: var(--space-2xs); font-size: var(--text-base); }

.article__body { margin-top: var(--space-md); }

.article__footer { margin-top: var(--space-lg); padding-top: var(--space-sm); border-top: 1px solid var(--line); }
.article__footer-title { font-size: var(--text-xs); font-weight: 600; margin-bottom: var(--space-2xs); }

.tag-pills { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
.tag-pill {
  display: inline-block;
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--black);
  font-size: var(--text-sm);
  text-decoration: none;
  transition: background-color var(--duration) var(--ease), color var(--duration) var(--ease);
}
.tag-pill:hover { background: var(--black); color: var(--white); }

/* ---------- legal pages -------------------------------------------------
   Privacy policy and terms of use. These reuse .article__body for the prose
   itself — same job, same measure — so there is one set of body-copy rules
   on this site rather than two that drift.
   ----------------------------------------------------------------------- */
.legal { padding-block: var(--space-lg) var(--space-xl); }
.legal__title { margin-bottom: var(--space-2xs); }
.legal__dateline {
  font-size: var(--text-sm);
  color: var(--dark-grey);
}

/* --- article prose ---
   These style body copy generated from Markdown, so they are element
   selectors inside .article__body rather than classed. */
.article__body > :first-child { margin-top: 0; }

.article__body h2 {
  font-family: "Archivo", sans-serif;
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: var(--leading-snug);
  margin: var(--space-md) 0 var(--space-2xs);
}
.article__body h3 {
  font-size: var(--text-md);
  font-weight: 600;
  margin: var(--space-sm) 0 var(--space-2xs);
}

.article__body p { margin-bottom: var(--space-xs); font-size: var(--text-base); line-height: var(--leading-body); }
.article__body ul, .article__body ol { margin: 0 0 var(--space-xs); padding-left: 1.35em; }
.article__body li { margin-bottom: 0.4em; line-height: var(--leading-body); }
.article__body a { text-decoration: underline; }
.article__body strong { font-weight: 600; }

/* Tables in body copy. Scoped to the prose because the rest of the site has
   no tables at all — this is for the processor list in the privacy policy. */
.table-scroll { overflow-x: auto; margin-bottom: var(--space-xs); }
.table-scroll:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.article__body table {
  border-collapse: collapse;
  width: 100%;
  min-width: 30rem;
  font-size: var(--text-sm);
}
.article__body th,
.article__body td {
  padding: 0.625rem 0.75rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
  line-height: 1.5;
}
.article__body th {
  font-weight: 600;
  border-bottom: 2px solid var(--black);
  white-space: nowrap;
}
.article__body tbody tr:last-child td { border-bottom: 0; }

.article__body blockquote {
  margin: var(--space-md) 0;
  padding-left: var(--space-xs);
  border-left: 2px solid var(--black);
  font-size: var(--text-md);
  line-height: 1.5;
}

.article__body img { margin: var(--space-md) 0; }
.article__body hr { margin: var(--space-md) 0; border: 0; border-top: 1px solid var(--line); }
