/* compare-generator/resources/css/item-card.css */
/* ============================================================
   Item-cards pages (Deals / New Releases) - v2 redesign
   Design language ported from the home (v2) mockup: tokens
   --brand, Archivo + IBM Plex Mono, dark gradient hero with a
   dot pattern, overlapping stat strip and clean white cards.
   Per-page accents (sky for new releases, warm for deals) are
   layered in latest.css / deals.css, loaded after this file.
   ============================================================ */

.itemCardsPageBody,
.latestPageBody,
.dealsPageBody {
  /* Brand - each token reads a per-site override (--site-*) with the
     generator's neutral defaults as fallback, so the look is correct out of
     the box and recolorable from a consuming repo's metadata CSS. */
  --brand:        var(--site-brand,        #01579B);
  --brand-bright: var(--site-brand-bright, #0288D1);
  --brand-sky:    var(--site-brand-sky,    #29B6F6);
  --brand-dark:   var(--site-brand-dark,   #013A6B);
  --brand-darker: var(--site-brand-darker, #002948);

  --ink:        #0c2233;
  --ink-soft:   #4a6075;
  --ink-faint:  #738aa0;
  --bg:         #eef3f8;
  --bg-2:       #f6f9fc;
  --surface:    #ffffff;
  --line:       #dde6ef;
  --line-soft:  #e9f0f6;

  --blue-50:  #eaf3fb;
  --warm:      #f2682c;
  --warm-soft: #fde9df;

  --radius:    1.8rem;
  --radius-sm: 1.2rem;

  --font: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, monospace;

  /* Page accent: overridden per page (sky for new releases, warm for deals) */
  --accent:      var(--brand);
  --accent-soft: var(--blue-50);

  font-family: var(--font);
  color: var(--ink);
  background:
    radial-gradient(110rem 50rem at 0% 0%, rgba(2, 136, 209, 0.10), transparent 60%),
    radial-gradient(90rem 46rem at 100% 8%, rgba(41, 182, 246, 0.10), transparent 58%),
    linear-gradient(180deg, #edf5ff 0%, #f8fbff 42%, #ffffff 100%);
}

/* ============================================================
   HERO (compact, gradient + dot pattern) - mirrors .homeHero
   ============================================================ */
.itemCardsHero {
  position: relative;
  overflow: hidden;
  color: #fff;
  margin: 0;
  padding: 0;
  width: 100%;
  /* Same base gradient as the navbar so there is no visible seam. */
  background:
    linear-gradient(180deg, transparent 0%, transparent 14%, var(--brand-dark) 64%, var(--brand-darker) 100%),
    linear-gradient(90deg, var(--brand) 0%, var(--brand-bright) 100%);
}
.itemCardsHero::before {
  content: "";
  position: absolute; inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, 0.16) 1.2px, transparent 1.2px);
  background-size: 2.6rem 2.6rem;
  -webkit-mask-image: radial-gradient(120% 95% at 82% 0%, #000 22%, transparent 72%);
          mask-image: radial-gradient(120% 95% at 82% 0%, #000 22%, transparent 72%);
  opacity: .5;
  pointer-events: none;
}
.itemCardsHeroInner {
  position: relative;
  max-width: 1500px;
  margin-inline: auto;
  padding: clamp(3.2rem, 4.5vw, 5.6rem) clamp(2.4rem, 4vw, 5.2rem) clamp(5rem, 5.5vw, 7.4rem);
  text-align: left;
}
.itemCardsHero h1,
.itemCardsHero h2 { text-align: left; text-transform: none; }
.itemCardsHero h1 {
  font-size: clamp(3.2rem, 5vw, 5.6rem);
  font-weight: 800; line-height: 1.02; letter-spacing: -0.03em;
  margin: 0 0 1.8rem; max-width: 18ch; text-wrap: balance;
  color: #fff; text-transform: none;
}
.itemCardsHeroHl { color: var(--brand-sky); }
.itemCardsHeroEyebrow {
  font-family: var(--mono);
  font-size: 1.25rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--brand-sky);
  display: inline-flex; align-items: center; gap: 1rem; margin-bottom: 2rem;
}
.itemCardsHeroDot {
  width: 0.7rem; height: 0.7rem; border-radius: 50%;
  background: var(--brand-sky);
  box-shadow: 0 0 0 0.4rem rgba(41, 182, 246, 0.25);
  animation: itemCardsHeroPulse 2.4s ease-in-out infinite;
}
@keyframes itemCardsHeroPulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
.itemCardsHeroSub {
  font-size: clamp(1.5rem, 1.7vw, 1.8rem); line-height: 1.5;
  color: #cfe4f4; max-width: 64ch; margin: 0 0 1.6rem; text-wrap: pretty; font-weight: 500;
}

/* Hero call to action: only rendered by pages that do NOT show the comparison
   table themselves (the item-type editorial page), so the reader learns above
   the fold that the whole catalog can be compared in one table. Solid white pill
   on the brand gradient: it is the only button in the hero, so it can take the
   strongest treatment available. */
.itemCardsHeroActions {
  display: flex;
  align-items: center;
  gap: 1.6rem;
  flex-wrap: wrap;
  margin: 0.6rem 0 2rem;
}
.itemCardsHeroCta {
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  padding: 1.4rem 2.6rem;
  border-radius: 999px;
  background: #fff;
  color: var(--brand-dark);
  font-size: var(--fs-lg);
  font-weight: 800;
  text-decoration: none;
  box-shadow: 0 1rem 2.4rem -0.8rem rgba(0, 20, 40, 0.55);
  transition: transform 0.14s ease, box-shadow 0.14s ease;
}
.itemCardsHeroCta:hover,
.itemCardsHeroCta:focus-visible {
  color: var(--brand-dark);
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: 0 1.4rem 3rem -0.8rem rgba(0, 20, 40, 0.6);
}
.itemCardsHeroCtaIcon {
  width: 1.6rem;
  height: 1.6rem;
  flex: none;
  color: var(--brand-bright);
}
.itemCardsHeroCtaArrow { transition: transform 0.16s ease; }
.itemCardsHeroCta:hover .itemCardsHeroCtaArrow { transform: translateX(0.3rem); }
.itemCardsHeroActionsNote {
  font-size: var(--fs-base);
  color: #cfe4f4;
}

/* Freshness chip: "Last updated <date>" on pages rebuilt from live data.
   Mirrors the JSON-LD dateModified and the sitemap lastmod of the same page. */
.itemCardsUpdated {
  display: inline-flex; align-items: center; gap: 0.7rem;
  margin: 0.4rem 0 0;
  padding: 0.6rem 1.2rem;
  border: 0.1rem solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  color: #cfe4f4;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.itemCardsUpdatedIcon { width: 1.4rem; height: 1.4rem; flex: none; color: var(--brand-sky); }
.itemCardsUpdated time { color: #fff; font-weight: 600; }

/* ============================================================
   STAT STRIP - overlaps the hero bottom edge
   ============================================================ */
.itemCardsPageMain,
.latestPageMain,
.dealsPageMain {
  /* Unify the main content width with the home (home-v2) layout: max-width
     124rem + the same clamp inline padding used by .homePulseLayout, the navbar
     and the footer, so every new-design page lines up on the same edges.
     (Comparison-table pages use a different full-width container and are
     unaffected — they don't use this element as their main column.) */
  width: 100%;
  max-width: 124rem;
  padding-inline: clamp(2rem, 4vw, 5.2rem);
  margin: 0 auto 3.4rem;
  font-family: var(--font);
}
.itemCardsStats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.6rem;
  margin-top: -4.4rem;
  position: relative;
  /* Sit above the hero (which has no stacking z-index) but BELOW the site
     header/nav (z-index:3) so the expanded mobile menu overlays the cards. */
  z-index: 1;
  margin-bottom: 2.8rem;
}
.itemCardsStat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 2rem 2.2rem;
  box-shadow: 0 1.8rem 4rem -2.8rem rgba(1, 40, 72, 0.45);
}
.itemCardsStat .n {
  font-size: clamp(2.6rem, 3vw, 3.4rem); font-weight: 800; letter-spacing: -0.02em;
  line-height: 1; color: var(--ink); display: flex; align-items: baseline; gap: 0.3rem;
}
.itemCardsStat .n .u { color: var(--brand-sky); }
.itemCardsStat.warm .n,
.itemCardsStat.warm .n .u { color: var(--warm); }
.itemCardsStat .l {
  font-family: var(--mono); font-size: 1.15rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: 1rem;
}
/* Four-stat strip (Deals): four across on wide screens, 2x2 below (see the
   responsive block, where the three-stat "stretch the last card" rule has to
   be neutralised so the fourth card does not end up alone). */
.itemCardsStats--four { grid-template-columns: repeat(4, 1fr); }

/* ============================================================
   ITEMS SHELL + GRID
   ============================================================ */
.itemCardsItemsShell,
.latestItemsShell,
.dealsItemsShell {
  width: 100%;
  max-width: none;
  box-sizing: border-box;
  font-family: var(--font);
}

.itemCardsPageMain .gallery,
.latestPageMain .gallery,
.dealsPageMain .gallery,
.brandProductsPanel .gallery,
.itemCardsItemsShell .gallery {
  margin: 0;
}

.itemCardsPageMain .grid-auto-lg,
.latestPageMain .grid-auto-lg,
.dealsPageMain .grid-auto-lg,
.itemCardsItemsShell .grid-auto-lg {
  --col-min-width: 22rem;
  --gallery-gap: 1.6rem;
  grid-template-columns: repeat(auto-fill, minmax(var(--col-min-width), 1fr));
}

/* ============================================================
   CARD - clean white, lifts on hover, name turns brand
   ============================================================ */
.itemCardsPageMain .gallery .cardV2,
.latestPageMain .gallery .cardV2,
.dealsPageMain .gallery .cardV2,
.itemCardsItemsShell .gallery .cardV2 {
  margin: 0;
  max-width: none;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: 0 1.6rem 3.6rem -2.8rem rgba(1, 40, 72, 0.4);
  overflow: hidden;
  /* Own stacking context: keeps internal chip/badge z-indexes scoped to the
     card so they never compete with the site header/nav at the root level. */
  isolation: isolate;
  transition: transform .3s cubic-bezier(.22,1,.36,1), box-shadow .3s, border-color .3s;
}

.itemCardsPageMain .gallery .cardV2:hover,
.latestPageMain .gallery .cardV2:hover,
.dealsPageMain .gallery .cardV2:hover,
.itemCardsItemsShell .gallery .cardV2:hover {
  transform: translateY(-0.5rem);
  border-color: color-mix(in oklab, var(--accent) 38%, var(--line));
  box-shadow: 0 2.2rem 4.2rem -2.8rem rgba(1, 40, 72, 0.4);
  background: var(--surface);
  cursor: pointer;
}

.itemCardsPageMain .galleryCardLink,
.latestPageMain .galleryCardLink,
.dealsPageMain .galleryCardLink,
.itemCardsItemsShell .galleryCardLink {
  display: block;
  text-decoration: none;
  color: inherit;
  height: 100%;
}

.itemCardsPageMain .top-info-gallery,
.latestPageMain .top-info-gallery,
.dealsPageMain .top-info-gallery,
.itemCardsItemsShell .top-info-gallery {
  border-right-width: 0;
  width: 100% !important;
  min-height: 100%;
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: var(--surface);
  text-align: left;
}

/* thumb */
.itemCardsPageMain .top-info-gallery .productImage,
.latestPageMain .top-info-gallery .productImage,
.dealsPageMain .top-info-gallery .productImage,
.itemCardsItemsShell .top-info-gallery .productImage {
  position: relative;
  overflow: hidden;
  height: 17rem;
  margin: 0;
  padding-top: 0.6rem;
  border-bottom: 1px solid var(--line-soft);
  background:
    radial-gradient(12rem 8.6rem at 22% 18%, rgba(255,255,255,0.6), transparent 70%),
    linear-gradient(160deg, #f4f8fc 0%, #eaf1f8 100%);
}
.itemCardsPageMain .top-info-gallery .productImage img,
.latestPageMain .top-info-gallery .productImage img,
.dealsPageMain .top-info-gallery .productImage img,
.itemCardsItemsShell .top-info-gallery .productImage img {
  margin-top: 1rem;
  margin-bottom: 0.8rem;
  max-height: 13rem;
  max-width: 13rem;
}

/* deal badge - top-left orange pill */
.itemCardsPageMain .productImage > .discount,
.latestPageMain .productImage > .discount,
.dealsPageMain .productImage > .discount,
.itemCardsItemsShell .productImage > .discount {
  position: absolute;
  top: 1rem; left: 1rem; right: auto; bottom: auto;
  margin: 0;
  background: var(--warm);
  color: #fff;
  font-family: var(--mono);
  font-weight: 600;
  font-size: 1.1rem;
  letter-spacing: 0.02em;
  padding: 0.5rem 0.9rem;
  border-radius: 0.8rem;
  opacity: 1;
  box-shadow: 0 0.6rem 1.6rem -0.8rem rgba(1, 40, 72, 0.6);
  z-index: 2;
}

/* item-type chip - top-right mono pill, accent-tinted */
.itemCardsPageMain .itemType,
.latestPageMain .itemType,
.dealsPageMain .itemType,
.itemCardsItemsShell .itemType {
  top: 1rem; right: 1rem; left: auto; bottom: auto;
  background: var(--blue-50);
  color: var(--brand);
  font-family: var(--mono);
  font-weight: 700;
  font-size: var(--fs-2xs);
  line-height: 1.15;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border: 1px solid var(--line-soft);
  border-radius: 0.8rem;
  margin: 0;
  padding: 0.45rem 0.85rem;
  width: auto;
  max-width: calc(100% - 2rem);
  white-space: normal;
  display: inline-block;
  overflow-wrap: break-word;
  overflow: hidden;
  text-overflow: clip;
  opacity: 1;
  text-align: left;
  z-index: 3;
}

/* title block */
.itemCardsPageMain .top-info-gallery .itemTitle,
.latestPageMain .top-info-gallery .itemTitle,
.dealsPageMain .top-info-gallery .itemTitle,
.itemCardsItemsShell .top-info-gallery .itemTitle {
  margin: 1.4rem 1.5rem 0;
  min-height: auto;
  display: grid;
  gap: 0.5rem;
}

.itemCardsPageMain .itemPreviewBrand,
.latestPageMain .itemPreviewBrand,
.dealsPageMain .itemPreviewBrand,
.itemCardsItemsShell .itemPreviewBrand {
  margin: 0;
  min-height: 0;
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  order: -1;
}

.itemCardsPageMain .top-info-gallery h3,
.latestPageMain .top-info-gallery h3,
.dealsPageMain .top-info-gallery h3,
.itemCardsItemsShell .top-info-gallery h3 {
  padding: 0;
  margin: 0;
  font-size: 1.45rem;
  line-height: 1.28;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color .2s;
}
.itemCardsPageMain .gallery .cardV2:hover h3,
.latestPageMain .gallery .cardV2:hover h3,
.dealsPageMain .gallery .cardV2:hover h3,
.itemCardsItemsShell .gallery .cardV2:hover h3 {
  color: var(--accent);
}

/* price footer - separated by a top border; brand left, price right.
   Reserves a fixed line height so cards without a price keep the same
   total height and footer alignment as priced ones. */
.itemCardsPageMain .top-info-gallery div.fiatPrice,
.latestPageMain .top-info-gallery div.fiatPrice,
.dealsPageMain .top-info-gallery div.fiatPrice,
.itemCardsItemsShell .top-info-gallery div.fiatPrice {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  margin: 1.2rem 1.5rem 1.5rem;
  float: none;
  justify-content: flex-end;
  width: calc(100% - 3rem);
  max-width: calc(100% - 3rem);
  min-height: 3.1rem; /* 1.2rem padding-top + ~1.9rem price line */
  box-sizing: border-box;
  border-radius: 0;
  padding: 1.2rem 0 0;
  border-top: 1px solid var(--line-soft);
  background: transparent;
  color: var(--ink);
}
/* No price: the price row carries a global `.hide { display:none !important }`,
   so it collapses. Render an empty footer placeholder in its place so the card
   keeps the same height, footer separator and alignment as priced cards. */
.itemCardsPageMain .top-info-gallery:has(div.fiatPrice.hide)::after,
.latestPageMain .top-info-gallery:has(div.fiatPrice.hide)::after,
.dealsPageMain .top-info-gallery:has(div.fiatPrice.hide)::after,
.itemCardsItemsShell .top-info-gallery:has(div.fiatPrice.hide)::after {
  content: "";
  display: block;
  margin: 1.2rem 1.5rem 1.5rem;
  min-height: 3.1rem;
  box-sizing: border-box;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line-soft);
}

/* inline discount chip inside the price row (kept small + orange) */
.itemCardsPageMain .top-info-gallery div.fiatPrice .discount,
.latestPageMain .top-info-gallery div.fiatPrice .discount,
.dealsPageMain .top-info-gallery div.fiatPrice .discount,
.itemCardsItemsShell .top-info-gallery div.fiatPrice .discount {
  position: static;
  transform: none;
  margin: 0 auto 0 0;
  align-self: center;
  opacity: 1;
  font-family: var(--mono);
  font-weight: 600;
  font-size: var(--fs-2xs);
  padding: 0.3rem 0.6rem;
  border-radius: 0.6rem;
  background: var(--warm-soft);
  color: var(--warm);
  box-shadow: none;
  white-space: nowrap;
  z-index: 3;
}

.itemCardsPageMain .top-info-gallery .originalPrice,
.latestPageMain .top-info-gallery .originalPrice,
.dealsPageMain .top-info-gallery .originalPrice,
.itemCardsItemsShell .top-info-gallery .originalPrice {
  font-size: 1.2rem;
  color: var(--ink-faint);
  text-decoration: line-through;
  margin: 0;
}

.itemCardsPageMain .top-info-gallery .discountedPrice,
.latestPageMain .top-info-gallery .discountedPrice,
.dealsPageMain .top-info-gallery .discountedPrice,
.itemCardsItemsShell .top-info-gallery .discountedPrice {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--ink);
}
/* when discounted (a strike-through original is shown), the live price is warm */
.itemCardsPageMain .top-info-gallery .originalPrice:not(.hide) + .discountedPrice,
.latestPageMain .top-info-gallery .originalPrice:not(.hide) + .discountedPrice,
.dealsPageMain .top-info-gallery .originalPrice:not(.hide) + .discountedPrice,
.itemCardsItemsShell .top-info-gallery .originalPrice:not(.hide) + .discountedPrice {
  color: var(--warm);
}

/* ============================================================
   ENTRANCE ANIMATION
   ============================================================ */
@keyframes itemCardsRise {
  from { opacity: 0; transform: translateY(1.4rem); }
  to   { opacity: 1; transform: translateY(0); }
}

.itemCardsStats,
.itemCardsItemsShell,
.latestItemsShell,
.dealsItemsShell {
  animation: itemCardsRise 0.55s ease both;
}

.itemCardsPageMain .gallery .cardV2,
.latestPageMain .gallery .cardV2,
.dealsPageMain .gallery .cardV2,
.itemCardsItemsShell .gallery .cardV2 {
  animation: itemCardsRise 0.5s ease both;
}
.itemCardsPageMain .gallery .cardV2:nth-child(2n),
.latestPageMain .gallery .cardV2:nth-child(2n),
.dealsPageMain .gallery .cardV2:nth-child(2n),
.itemCardsItemsShell .gallery .cardV2:nth-child(2n) { animation-delay: 0.04s; }
.itemCardsPageMain .gallery .cardV2:nth-child(3n),
.latestPageMain .gallery .cardV2:nth-child(3n),
.dealsPageMain .gallery .cardV2:nth-child(3n),
.itemCardsItemsShell .gallery .cardV2:nth-child(3n) { animation-delay: 0.08s; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1000px) {
  .itemCardsStats--four { grid-template-columns: repeat(2, 1fr); }
  .itemCardsStats--four .itemCardsStat:nth-child(3) { grid-column: auto; }
}

@media (max-width: 760px) {
  .itemCardsPageMain,
  .latestPageMain,
  .dealsPageMain {
    width: calc(100% - 16px);
  }

  .itemCardsHeroInner {
    padding: 2.6rem 2rem 4.6rem;
  }
  .itemCardsHero h1 { font-size: 3.2rem; max-width: none; }
  .itemCardsHeroSub { font-size: 1.45rem; }
  /* The button takes the full width; its note would wrap into a third line
     under a title that is already tall on a phone, so it is dropped (the
     comparison block further down says the same thing). */
  .itemCardsHeroCta { width: 100%; justify-content: center; }
  .itemCardsHeroActionsNote { display: none; }

  .itemCardsStats {
    grid-template-columns: repeat(2, 1fr);
    margin-top: -3.6rem;
    gap: 1.2rem;
  }
  .itemCardsStat:nth-child(3) { grid-column: 1 / -1; }

  .itemCardsPageMain .grid-auto-lg,
  .latestPageMain .grid-auto-lg,
  .dealsPageMain .grid-auto-lg,
  .itemCardsItemsShell .grid-auto-lg {
    --col-min-width: 16rem;
  }
}

@media (max-width: 460px) {
  .itemCardsPageMain .grid-auto-lg,
  .latestPageMain .grid-auto-lg,
  .dealsPageMain .grid-auto-lg,
  .itemCardsItemsShell .grid-auto-lg {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  .itemCardsStats,
  .itemCardsItemsShell,
  .latestItemsShell,
  .dealsItemsShell,
  .itemCardsPageMain .gallery .cardV2,
  .latestPageMain .gallery .cardV2,
  .dealsPageMain .gallery .cardV2,
  .itemCardsItemsShell .gallery .cardV2 {
    animation: none;
  }
  .itemCardsHeroDot { animation: none; }
  .itemCardsHeroCta:hover,
  .itemCardsHeroCta:hover .itemCardsHeroCtaArrow { transform: none; }
}

/* ============================================================
   USE CASE LINKS SECTION
   Bidirectional internal link from category and product pages to the use case
   landings ("Used for" / "Good for these use cases"), placed at the end of the
   page. Generic and structural: a card grid that mirrors the use case landing
   index cards (accent bar + title + blurb + arrow).
   ============================================================ */
.useCaseLinks {
  width: 100%;
  margin-top: 4rem;
  padding: clamp(3rem, 4vw, 4.8rem) 0 clamp(3.4rem, 4vw, 5rem);
  border-top: 1px solid var(--line, #dde6ef);
  background:
    radial-gradient(90rem 40rem at 100% 0%, rgba(41, 182, 246, 0.06), transparent 60%),
    linear-gradient(180deg, #f6fbff 0%, transparent 100%);
}
.useCaseLinksInner {
  width: 100%;
  max-width: 124rem;
  margin-inline: auto;
  padding-inline: clamp(2rem, 4vw, 5.2rem);
}
.useCaseLinksEyebrow {
  font-family: var(--mono, monospace);
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brand-sky, #29B6F6);
  margin-bottom: 0.8rem;
}
.useCaseLinksHeading {
  font-family: var(--font);
  font-size: clamp(2rem, 2.4vw, 2.8rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink, #0c2233);
  margin: 0 0 2.2rem;
}
.useCaseLinksGrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(28rem, 1fr));
  gap: 1.6rem;
}
.useCaseLinksCard {
  --use-case: var(--brand, #01579B);
  position: relative;
  display: flex;
  align-items: center;
  gap: 1.4rem;
  padding: 2rem 2rem 2rem 2.4rem;
  background: var(--surface, #fff);
  border: 1px solid var(--line, #dde6ef);
  border-radius: var(--radius, 1.8rem);
  text-decoration: none;
  overflow: hidden;
  box-shadow: 0 1.6rem 3.6rem -3rem rgba(1, 40, 72, 0.4);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.useCaseLinksCard:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--use-case) 45%, var(--line, #dde6ef));
  box-shadow: 0 2.4rem 4.6rem -2.8rem rgba(1, 40, 72, 0.5);
}
.useCaseLinksCardAccent {
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: var(--use-case);
}
.useCaseLinksCardBody {
  flex: 1 1 auto;
  min-width: 0;
}
.useCaseLinksCardTitle {
  display: block;
  font-size: 1.7rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--ink, #0c2233);
}
.useCaseLinksCardText {
  display: block;
  margin-top: 0.5rem;
  font-size: 1.35rem;
  line-height: 1.45;
  color: var(--ink-soft, #4a6075);
}
.useCaseLinksCardArrow {
  flex: 0 0 auto;
  font-size: 1.8rem;
  font-weight: 700;
  color: var(--use-case);
  transition: transform 0.18s ease;
}
.useCaseLinksCard:hover .useCaseLinksCardArrow {
  transform: translateX(4px);
}

/* compare-generator/resources/css/stats.css */
/* ============================================================
   Statistics data report (/stats)
   Base look (hero, stat strip, surfaces, tokens) comes from
   item-card.css, loaded first. This file adds what only a data
   report needs: the charts, the tiles, the matrix, the data
   tables and the citation block.

   The page reads as an infographic and indexes as a document:
   every figure drawn here is also present as HTML text in the
   same block (a tile prints its own number, a matrix cell is a
   real <td>, a treemap cell is a real <a>), so nothing on this
   page depends on a crawler executing or rendering anything.

   COLOR. Two separate scales, do not mix them up:
   - The four SERIES slots encode identity in the line chart.
     They were validated as a set (lightness band, chroma floor,
     CVD separation). Two of them fail a 4.5:1 text contrast
     check on white, which is fine there because every chart
     ships with its data table and every line carries a direct
     label, so color is never the only channel. Do NOT swap a
     slot without re-validating the whole set: changing one hex
     changes every adjacent pair.
   - The ACCENT slots are section chrome only (chapter numeral,
     eyebrow, rules, tints). They never encode a value, so they
     are free to be picked for rhythm rather than for
     separability, and adding one does not touch the series set.

   TYPOGRAPHY: sizes use the shared --fs-* tokens, except inside
   the <svg>, where font-size is in viewBox user units (not CSS
   pixels) and must scale with the drawing, not with the page.
   ============================================================ */
.statsPageBody {
  --stats-series-1: var(--site-brand, #01579B);
  --stats-series-2: #f2682c;
  --stats-series-3: #1baf7a;
  --stats-series-4: #eda100;

  /* Sequential ramp for magnitude bars: one hue, light to dark. Magnitude is
     not identity, so a bar chart of shares must not use one color per row. */
  --stats-bar:      var(--site-brand, #01579B);
  --stats-bar-soft: color-mix(in oklab, var(--site-brand, #01579B) 18%, #ffffff);
  --stats-surface:  var(--surface, #ffffff);
  /* Star fill. Gold rather than the brand hue: a rating is a rating everywhere,
     and reusing the brand color here would read as another data series. */
  --stats-star:     #FFB020;

  --accent:      var(--brand);
  --accent-soft: var(--blue-50);

  /* The shared muted ink (#738aa0) measures 3.58:1 on white and this page uses
     it for about two hundred labels at 11 to 13px: table headers, captions,
     axis ticks, notes. That is a WCAG AA failure repeated two hundred times.
     Darkened here to 4.9:1, scoped to this page so no other page's look moves.
     The same fix belongs in the shared token, which is a call for the site. */
  --ink-faint: #5d738b;
}

/* ============================================================
   SECTION SHELL + CHAPTER HEADER
   ============================================================ */
.statsSection {
  /* Accent defaults to the brand; per-section overrides below. Everything in
     a section that carries color reads --sec, so recoloring a chapter is a
     one-attribute change. */
  --sec: var(--brand);
  --sec-ink: var(--brand);
  --sec-tint: var(--blue-50);
  margin-bottom: clamp(4.4rem, 6vw, 7.2rem);
  /* Anchor jumps must clear the fixed header AND the sticky section nav, or
     the heading someone linked to lands underneath them. */
  scroll-margin-top: 14rem;
}
.statsSection[data-accent="0"] { --sec: #5b7186; --sec-ink: #46596b; --sec-tint: #eef3f7; }
.statsSection[data-accent="2"] { --sec: #f2682c; --sec-ink: #c74a12; --sec-tint: #fdeee6; }
.statsSection[data-accent="3"] { --sec: #1baf7a; --sec-ink: #10805a; --sec-tint: #e6f7f0; }
.statsSection[data-accent="4"] { --sec: #eda100; --sec-ink: #a06c00; --sec-tint: #fdf3dd; }
.statsSection[data-accent="5"] { --sec: #7c5cd6; --sec-ink: #5b3fae; --sec-tint: #f0ebfd; }

.statsSectionHead {
  display: flex;
  align-items: flex-start;
  gap: clamp(1.4rem, 2vw, 2.2rem);
  margin-bottom: 1.4rem;
}
/* The chapter numeral. Outlined rather than filled so it reads as furniture,
   not as one of the figures the page is asking to be quoted on, and painted
   from data-chapter so it never lands in the document's text. */
.statsChapterNum::before,
.statsIndexChip::before,
.statsMethodologyNum::before { content: attr(data-chapter); }
.statsChapterNum {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: clamp(1.6rem, 2.2vw, 2.2rem);
  font-weight: 700;
  line-height: 1;
  color: var(--sec-ink);
  background: var(--sec-tint);
  border: 1px solid color-mix(in oklab, var(--sec) 28%, transparent);
  border-radius: var(--radius-sm);
  padding: 1.1rem 1.3rem;
  letter-spacing: -0.02em;
}
.statsSectionEyebrow {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sec-ink);
  margin: 0.2rem 0 0.6rem;
}
.statsSection > .statsSectionHead h2 {
  font-size: clamp(2.6rem, 3.2vw, var(--fs-6xl));
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0;
  text-align: left;
  text-transform: none;
  color: var(--ink);
  text-wrap: balance;
}
/* Full column width, no reading measure. The paragraphs of this page sit
   between full width visuals, and a text block that stops two thirds of the
   way across leaves a ragged edge down the whole report. The longer line is
   paid for with extra leading, which is what a wide measure needs to stay
   readable: the return sweep is what gets lost, not the line itself. */
.statsSectionIntro {
  font-size: var(--fs-lg);
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 0 0 2.4rem;
  text-wrap: pretty;
}
.statsSectionNote {
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--ink-faint);
  margin: 1.6rem 0 0;
}
/* Sub-heading inside a section (a second cut of the same subject). Some carry
   an id, because they used to be sections of their own and links to those
   anchors are already out there; they need the same header clearance. */
.statsSubHeading {
  scroll-margin-top: 14rem;
  font-size: var(--fs-2xl);
  font-weight: 700;
  color: var(--ink);
  margin: 4rem 0 1.6rem;
  text-align: left;
  text-transform: none;
  display: flex;
  align-items: center;
  gap: 1.2rem;
}
.statsSubHeading::before {
  content: "";
  width: 0.4rem;
  height: 1.8rem;
  border-radius: 999px;
  background: var(--sec);
  flex: 0 0 auto;
}

/* ============================================================
   ANCHOR INDEX - sticky, one row, scrolls sideways
   ============================================================ */
.statsIndex {
  position: sticky;
  top: var(--f-header-height, 8rem);
  z-index: 2;
  display: flex;
  flex-wrap: nowrap;
  gap: 0.8rem;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 1rem 0;
  margin-bottom: 3rem;
  /* Frosted rather than a solid fill: the page background is a gradient, so any
     opaque strip would leave a visible seam as it scrolls over it. */
  background: rgba(247, 250, 253, 0.96);
  box-shadow: 0 1rem 1.2rem -1.2rem rgba(12, 34, 51, 0.25);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
          backdrop-filter: blur(10px) saturate(1.1);
  /* The strip is wider than the screen at every size, so its right edge fades
     out: a chip cut clean in half reads as a layout bug, a fading one reads as
     "there is more this way". */
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 5rem), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 5rem), transparent);
}
.statsIndex::-webkit-scrollbar { display: none; }
.statsIndexChip {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.7rem 1.4rem;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.18s, border-color 0.18s, background 0.18s;
}
.statsIndexChip:hover {
  color: var(--brand);
  border-color: color-mix(in oklab, var(--brand) 45%, var(--line));
  background: var(--blue-50);
}
/* Scroll spy. Progressive enhancement: without JS the chips are a plain
   anchor list, which is what a crawler follows anyway. */
.statsIndexChip.is-current {
  color: #fff;
  background: var(--brand);
  border-color: var(--brand);
}
.statsIndexChip::before {
  font-size: var(--fs-2xs);
  opacity: 0.55;
  margin-right: 0.5rem;
}

/* ============================================================
   KEY FINDINGS - the quotable block, as a bento
   ============================================================ */
.statsFindings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1.6rem;
}
.statsFinding {
  grid-column: span 2;
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 2rem 2.2rem 2.2rem;
  font-size: var(--fs-md);
  line-height: 1.55;
  color: var(--ink-soft);
  text-wrap: pretty;
  overflow: hidden;
}
/* The two leading claims get half a row each: the block an LLM lifts should
   have a visible first sentence, not nine tiles of equal weight. */
.statsFinding--lead {
  grid-column: span 3;
  background: linear-gradient(180deg, var(--blue-50), var(--surface) 60%);
  border-color: color-mix(in oklab, var(--brand) 22%, var(--line));
  font-size: var(--fs-lg);
}
/* Tail tiles, widened so the last row closes. */
.statsFinding--wide { grid-column: span 6; }
.statsFinding--half { grid-column: span 3; }
.statsFinding::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 0.4rem;
  background: var(--brand);
  opacity: 0.85;
}
.statsFinding strong {
  color: var(--ink);
  font-weight: 700;
}
.statsFindingFigure {
  display: block;
  font-family: var(--mono);
  font-size: clamp(3rem, 4vw, 4.4rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--brand);
  margin: 0.6rem 0 1.2rem;
}
.statsFinding--lead .statsFindingFigure { font-size: clamp(3.8rem, 5.4vw, 6rem); }

/* ============================================================
   METRIC TILES - the adoption workhorse
   Replaces a stack of full width bars: same encoding (one hue,
   one shared 0-100% baseline) at four times the density, which
   is what lets a reader compare seven platforms in one glance
   instead of one scroll.
   ============================================================ */
/* A fixed column count, not auto-fit, and every tile the same width. The bar
   inside a tile is a share on a 0 to 100% scale, so two tiles of different
   widths would draw the same share at two different lengths. A ragged last row
   is a smaller price than a chart that lies. */
.statsTiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.4rem;
  margin: 0 0 2rem;
}
.statsTile {
  position: relative;
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 1.8rem 1.8rem 1.6rem;
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
a.statsTile:hover {
  border-color: color-mix(in oklab, var(--sec) 45%, var(--line));
  box-shadow: 0 0.8rem 2.2rem rgba(12, 34, 51, 0.09);
  transform: translateY(-0.2rem);
}
.statsTileTop {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.4rem;
  min-height: 3.2rem;
}
.statsTileIcon {
  width: 3.2rem;
  height: 3.2rem;
  object-fit: contain;
  flex: 0 0 auto;
}
/* Brand logos are wordmarks: a square box would shrink them to an illegible
   smudge, so they get a wide one and the artwork is pinned to its left edge. */
.statsTileBrandMark {
  width: 11rem;
  height: 3rem;
  object-fit: contain;
  object-position: left center;
  flex: 0 1 auto;
  min-width: 0;
}
.statsTileRank {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  /* On the tint, not on white: --sec-ink measures 4.45:1 over --sec-tint, just
     under the bar. Darkening it here costs nothing and clears it. */
  color: color-mix(in oklab, var(--sec-ink) 82%, #000000);
  background: var(--sec-tint);
  border-radius: 999px;
  padding: 0.35rem 0.8rem;
  flex: 0 0 auto;
}
.statsTileLabel {
  display: block;
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.3;
  margin-bottom: 0.6rem;
}
.statsTileValue {
  display: block;
  font-family: var(--mono);
  font-size: clamp(2.8rem, 3.4vw, 3.6rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--sec-ink);
  margin-bottom: 1.1rem;
}
.statsTileUnit { font-size: 0.5em; margin-left: 0.1em; }
.statsTileTrack {
  display: block;
  height: 0.7rem;
  border-radius: 999px;
  background: var(--line-soft);
  overflow: hidden;
}
.statsTileFill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--sec);
}
.statsTileFoot {
  display: block;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  color: var(--ink-faint);
  margin-top: 0.9rem;
  letter-spacing: 0.04em;
}

/* Third level, inside a sub-block. */
.statsMiniHeading {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--ink-soft);
  margin: 2.6rem 0 1.2rem;
  text-align: left;
  text-transform: none;
}

/* ============================================================
   PER-METRIC DETAIL CARDS
   One card per option of a group: where it stands, where it is
   going and where it lives. The sparkline is the only thing
   here that is not also printed as text, and the same figures
   are in the year table further down.
   ============================================================ */
.statsEcoGrid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.4rem;
  margin-bottom: 1.2rem;
}
.statsEcoCard {
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 1.8rem 1.8rem 1.4rem;
  transition: border-color 0.18s, box-shadow 0.18s;
}
a.statsEcoCard:hover {
  border-color: color-mix(in oklab, var(--sec) 45%, var(--line));
  box-shadow: 0 0.8rem 2.2rem rgba(12, 34, 51, 0.09);
}
.statsEcoHead { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.2rem; }
.statsEcoLogo { width: 3.4rem; height: 3.4rem; object-fit: contain; flex: 0 0 auto; }
.statsEcoName { font-size: var(--fs-md); font-weight: 700; color: var(--ink); line-height: 1.2; }
.statsEcoFigure { margin-bottom: 0.6rem; }
.statsEcoShare {
  font-family: var(--mono);
  font-size: clamp(2.6rem, 3vw, 3.2rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--sec-ink);
}
.statsEcoFacts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.4rem 1.2rem;
  margin: 1.4rem 0 0;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line-soft);
  font-size: var(--fs-sm);
}
.statsEcoFacts dt {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  align-self: center;
}
.statsEcoFacts dd { margin: 0; color: var(--ink-soft); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.statsEcoFacts dd b { color: var(--ink); font-family: var(--mono); font-weight: 700; }

/* ============================================================
   BAR ROWS - kept for the lists where a row carries a name, a
   logo and two numbers at once (brands)
   ============================================================ */
.statsBars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(34rem, 1fr));
  gap: 1rem;
  margin: 0 0 2rem;
}
.statsBarRow {
  display: block;
  text-decoration: none;
  color: inherit;
  padding: 1.2rem 1.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  transition: border-color 0.18s, box-shadow 0.18s;
}
a.statsBarRow:hover {
  border-color: color-mix(in oklab, var(--sec) 45%, var(--line));
  box-shadow: 0 0.6rem 1.8rem rgba(12, 34, 51, 0.07);
}
.statsBarLabel {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.2rem;
  margin-bottom: 0.9rem;
}
.statsBarName {
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--ink);
  min-width: 0;
}
/* Logo marks (feature, brand). Sized in a fixed box and contained, because the
   source files are a mix of square SVGs and wide raster wordmarks: without the
   box a wordmark would set the row height. */
.statsBarIcon {
  width: 2.8rem;
  height: 2.8rem;
  object-fit: contain;
  flex: 0 0 auto;
}
.statsTableIcon {
  width: 2rem;
  height: 2rem;
  object-fit: contain;
  vertical-align: -0.45rem;
  margin-right: 0.8rem;
}
/* Brand logos are wordmarks: give them a wide box and left-align the artwork,
   or a square box would shrink them to an illegible smudge. */
.statsBrandMark {
  width: 8.4rem;
  height: 2.4rem;
  object-fit: contain;
  object-position: left center;
  flex: 0 0 auto;
}
/* Country flags. Bordered because several are mostly white and would otherwise
   dissolve into the row. */
.statsFlag {
  width: 2.4rem;
  height: 1.8rem;
  object-fit: cover;
  border-radius: 0.2rem;
  border: 1px solid var(--line);
  vertical-align: -0.35rem;
  margin-right: 0.9rem;
}
.statsTableBrandMark {
  width: 7.6rem;
  height: 2rem;
  object-fit: contain;
  object-position: left center;
  vertical-align: -0.45rem;
  margin-right: 0.8rem;
}
.statsBarValue {
  font-family: var(--mono);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--sec-ink);
  white-space: nowrap;
}
.statsBarCount {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  color: var(--ink-faint);
  margin-left: 0.6rem;
  font-weight: 500;
}
.statsBarTrack {
  height: 0.7rem;
  border-radius: 999px;
  background: var(--line-soft);
  overflow: hidden;
}
.statsBarFill {
  height: 100%;
  border-radius: 999px;
  background: var(--sec);
}

/* ============================================================
   COMPATIBILITY MATRIX - a heatmap that is a real table
   The most linkable asset on the page, and it costs nothing in
   crawlability: the color is a background, the number inside
   the cell is the content.
   ============================================================ */
.statsMatrixShell {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow-x: auto;
}
.statsMatrix {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-sm);
}
.statsMatrix th,
.statsMatrix td {
  padding: 0.9rem 1rem;
  text-align: center;
  white-space: nowrap;
}
.statsMatrix thead th {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
  border-bottom: 1px solid var(--line);
  vertical-align: bottom;
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 1;
}
.statsMatrixColHead { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }
.statsMatrixColHead img { width: 2.2rem; height: 2.2rem; object-fit: contain; }
/* The row name stays put while the columns scroll: on a phone the numbers are
   meaningless without it. */
.statsMatrix tbody th {
  position: sticky;
  left: 0;
  z-index: 1;
  text-align: left;
  font-family: var(--font);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line-soft);
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
.statsMatrix tbody th a { color: inherit; text-decoration: none; }
.statsMatrix tbody th a:hover { color: var(--brand); text-decoration: underline; }
.statsMatrix tbody td {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  border-bottom: 1px solid #fff;
  border-right: 1px solid #fff;
}
/* Six steps, not a continuous ramp: banded color is read as a category, which
   is what a reader actually does with a heatmap ("high", "low"), and it keeps
   the text contrast decision to six known cases instead of a hundred.
   Dark ink on every step, and the ramp is capped well short of the solid
   accent so that holds for every accent hue. An earlier version flipped to
   white at the top two steps: fine on the blue chapter, unreadable on the green
   one (white on #5abe91 measures 2.3:1). Every rule carries the full
   `.statsMatrix tbody td` prefix on purpose: a bare `.statsMatrixLvl5` loses to
   the block above on specificity, which is how these cells once ended up dark
   on dark. */
.statsMatrix tbody td.statsMatrixLvl0 { background: #eef2f6; color: var(--ink-soft); }
.statsMatrix tbody td.statsMatrixLvl1 { background: color-mix(in oklab, var(--sec) 14%, #ffffff); color: var(--ink); }
.statsMatrix tbody td.statsMatrixLvl2 { background: color-mix(in oklab, var(--sec) 28%, #ffffff); color: var(--ink); }
.statsMatrix tbody td.statsMatrixLvl3 { background: color-mix(in oklab, var(--sec) 42%, #ffffff); color: var(--ink); }
.statsMatrix tbody td.statsMatrixLvl4 { background: color-mix(in oklab, var(--sec) 54%, #ffffff); color: var(--ink); }
.statsMatrix tbody td.statsMatrixLvl5 { background: color-mix(in oklab, var(--sec) 66%, #ffffff); color: var(--ink); }
.statsMatrix tbody td.statsMatrixNa { background: repeating-linear-gradient(135deg, #fafcfe, #fafcfe 4px, #f1f5f9 4px, #f1f5f9 8px); color: var(--ink-faint); font-weight: 400; }
/* A cell that answers a different question than its column header. It keeps the
   heat of its own share (it is a real number for that row) and carries a dashed
   outline plus the marker, so the reader sees the exception before reading the
   footnote. */
.statsMatrix tbody td.statsMatrixSub { box-shadow: inset 0 0 0 2px var(--ink-faint); }
/* The legend swatches are not table cells, so they need the plain classes. */
.statsMatrixLvl1 { background: color-mix(in oklab, var(--sec) 14%, #ffffff); }
.statsMatrixLvl2 { background: color-mix(in oklab, var(--sec) 28%, #ffffff); }
.statsMatrixLvl3 { background: color-mix(in oklab, var(--sec) 42%, #ffffff); }
.statsMatrixLvl4 { background: color-mix(in oklab, var(--sec) 54%, #ffffff); }
.statsMatrixLvl5 { background: color-mix(in oklab, var(--sec) 66%, #ffffff); }
.statsMatrixLegend {
  display: flex;
  align-items: center;
  gap: 0.6rem 2.8rem;
  flex-wrap: wrap;
  margin: 1.4rem 0 0;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.statsMatrixLegendRamp { display: inline-flex; align-items: center; gap: 0.7rem; }
.statsMatrixLegendScale { display: inline-flex; }
.statsMatrixLegendStep { width: 2.6rem; height: 1.2rem; }
.statsMatrixLegendStep:first-child { border-radius: 0.3rem 0 0 0.3rem; }
.statsMatrixLegendStep:last-child { border-radius: 0 0.3rem 0.3rem 0; }

/* ============================================================
   TREEMAP - the catalog, drawn to scale
   Area is the honest encoding for "share of a whole" across
   dozens of parts, and every cell is a link with its own text.
   ============================================================ */
.statsTreemapWrap { position: relative; }
.statsTreemapShell {
  border-radius: var(--radius-sm);
  overflow-x: auto;
  /* Explicit, not left to default. With overflow-x set and overflow-y visible,
     the spec makes overflow-y compute to auto: the horizontal scrollbar then
     eats height from a fixed aspect-ratio box, that overflows vertically, and
     a second scrollbar appears to scroll about fifteen pixels. */
  overflow-y: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
}
/* Hover readout for the cells too small to label themselves. */
.statsTreemapTip {
  position: absolute;
  z-index: 3;
  transform: translate(-50%, -125%);
  padding: 0.7rem 1.1rem;
  border-radius: var(--radius-sm);
  background: rgba(12, 34, 51, 0.94);
  color: #fff;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s;
}
.statsTreemapTip.show { opacity: 1; }
.statsTreemapTip.flipY { transform: translate(-50%, 25%); }
.statsTreemap {
  position: relative;
  width: 100%;
  /* The rectangles are squarified for a 3:2 box, so the container has to hold
     that ratio: any other shape stretches every cell and undoes the layout. */
  aspect-ratio: 3 / 2;
  /* Below this the cells are narrower than the words in them. The shell scrolls
     instead, which is what the tables on this page already do. */
  min-width: 108rem;
  overflow: hidden;
}
.statsTreemapCell {
  position: absolute;
  display: flex;
  flex-direction: column;
  /* Every label sits at the top of its cell. Anchoring the big ones to the
     floor and the small ones to the ceiling put the text at a different height
     in every rectangle, which is most of what made the map look unfinished. */
  justify-content: flex-start;
  /* Dark type on every cell. The ramp below is deliberately capped short of the
     solid accent so this always holds: flipping to white halfway down the scale
     would need a different step per accent hue, and a mid-lightness accent has
     no step where white is readable at this size. */
  color: var(--ink);
  gap: 0.2rem;
  padding: 0.9rem 1rem;
  overflow: hidden;
  text-decoration: none;
  border: 1px solid rgba(255, 255, 255, 0.85);
  transition: filter 0.18s;
}
.statsTreemapCell:hover { filter: brightness(1.12); }
.statsTreemapName {
  font-size: var(--fs-base);
  font-weight: 700;
  line-height: 1.15;
  text-wrap: balance;
  /* A cell is as wide as its share, not as wide as its name. Wrapping inside a
     word is better than a name sliced through the middle of a letter. */
  overflow-wrap: anywhere;
}
.statsTreemapValue {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  opacity: 0.85;
  letter-spacing: 0.04em;
}
/* Small cells fill from the top: when the name is taller than the cell,
   clipping the end of it still leaves it identifiable, clipping the start does
   not. The big cells keep their labels on the floor, which reads better.

   They also put the name and the share on ONE line instead of stacking them.
   Every cell has to show its share (it is the figure the section is about), and
   a stacked second line is the first thing a short cell clips. Inline, the two
   share the width the cell does have and wrap together. */
.statsTreemapCell--sm,
.statsTreemapCell--xs {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  /* Keeps the wrapped lines together at the top. Without it a name that takes
     two lines pushes its share to the floor of the cell, which reads as a
     figure belonging to something else. */
  align-content: flex-start;
  gap: 0 0.5rem;
}
.statsTreemapCell--sm { padding: 0.5rem 0.6rem; }
.statsTreemapCell--sm .statsTreemapName { font-size: var(--fs-sm); font-weight: 600; }
.statsTreemapCell--sm .statsTreemapValue { font-size: var(--fs-2xs); }
.statsTreemapCell--xs { padding: 0.25rem 0.35rem; }
.statsTreemapCell--xs .statsTreemapName { font-size: var(--fs-2xs); line-height: 1.15; font-weight: 600; }
.statsTreemapCell--xs .statsTreemapValue { font-size: var(--fs-2xs); }

/* ============================================================
   COLUMN CHART - counts across an ordered axis (years, rating
   buckets). Vertical bars, because the axis is ordered and the
   reader is looking for a shape, not a ranking.
   ============================================================ */
.statsColumns {
  display: flex;
  align-items: flex-end;
  gap: clamp(0.4rem, 1vw, 1.2rem);
  height: 26rem;
  padding: 0 0.4rem;
  border-bottom: 1px solid var(--line);
  margin-bottom: 1.2rem;
}
.statsColumn {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.statsColumnValue {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 0.5rem;
  white-space: nowrap;
}
.statsColumnBar {
  width: 100%;
  border-radius: 0.4rem 0.4rem 0 0;
  background: var(--sec);
  min-height: 0.3rem;
}
.statsColumn--partial .statsColumnBar {
  background: repeating-linear-gradient(135deg, var(--sec), var(--sec) 5px, color-mix(in oklab, var(--sec) 45%, #fff) 5px, color-mix(in oklab, var(--sec) 45%, #fff) 10px);
}
.statsColumn--partial .statsColumnValue { color: var(--ink-faint); font-style: italic; }
.statsColumnLabels {
  display: flex;
  gap: clamp(0.4rem, 1vw, 1.2rem);
  padding: 0 0.4rem;
  margin-bottom: 2rem;
}
.statsColumnLabel {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  color: var(--ink-faint);
  letter-spacing: 0.04em;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
}

/* ============================================================
   RANGE ROWS - min / median / max on one axis
   A single bar can only carry one number; a price needs three,
   and the spread between them is the interesting part.
   ============================================================ */
.statsRanges { margin: 0 0 1.2rem; }
.statsRange {
  display: grid;
  grid-template-columns: minmax(12rem, 18rem) 1fr minmax(8rem, auto);
  align-items: center;
  gap: 1.6rem;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--line-soft);
  text-decoration: none;
  color: inherit;
}
.statsRange:last-child { border-bottom: none; }
a.statsRange:hover .statsRangeName { color: var(--brand); }
.statsRangeName {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink);
  line-height: 1.25;
  /* Two lines rather than an ellipsis: the longest category names were being
     cut ("Temperature & Humidity Sen..."), and the row has the height for it. */
  overflow-wrap: anywhere;
}
.statsRangeTrack {
  position: relative;
  height: 1.8rem;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--line-soft), var(--line-soft));
}
.statsRangeSpan {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  height: 0.7rem;
  border-radius: 999px;
  background: color-mix(in oklab, var(--sec) 45%, #ffffff);
}
.statsRangeDot {
  position: absolute;
  top: 50%;
  width: 1.4rem;
  height: 1.4rem;
  margin-left: -0.7rem;
  transform: translateY(-50%);
  border-radius: 50%;
  background: var(--sec);
  border: 2px solid #fff;
  box-shadow: 0 0.1rem 0.4rem rgba(12, 34, 51, 0.25);
}
.statsRangeValue {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--sec-ink);
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.statsRangeAxis {
  display: grid;
  grid-template-columns: minmax(12rem, 18rem) 1fr minmax(8rem, auto);
  gap: 1.6rem;
  margin-bottom: 0.4rem;
}
.statsRangeAxisTicks { position: relative; height: 1.8rem; }
.statsRangeTick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  color: var(--ink-faint);
  white-space: nowrap;
}
.statsRangeLegend {
  display: flex;
  flex-wrap: wrap;
  gap: 1.6rem;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 1.6rem;
}
.statsRangeLegendItem { display: inline-flex; align-items: center; gap: 0.6rem; }
/* Dot plot: one value per row on a shared axis. */
.statsDumbbellDot {
  position: absolute;
  top: 50%;
  width: 1.3rem;
  height: 1.3rem;
  margin-left: -0.65rem;
  transform: translateY(-50%);
  border-radius: 50%;
  background: var(--sec);
  box-shadow: 0 0.1rem 0.3rem rgba(12, 34, 51, 0.22);
}
.statsRangeIcon,
.statsDumbbellIcon { width: 1.8rem; height: 1.8rem; object-fit: contain; margin-right: 0.7rem; vertical-align: -0.35rem; }
.statsRangeLegendDot { width: 1.1rem; height: 1.1rem; border-radius: 50%; background: var(--sec); }
.statsRangeLegendBar { width: 2.4rem; height: 0.6rem; border-radius: 999px; background: color-mix(in oklab, var(--sec) 45%, #ffffff); }

/* ============================================================
   STARS - fractional rating fill, no images
   ============================================================ */
.statsStars {
  position: relative;
  display: inline-block;
  font-size: 1.6rem;
  line-height: 1;
  letter-spacing: 0.12em;
  /* Empty track. Light enough to read as "not filled", dark enough to be seen. */
  color: #d5dfe9;
  white-space: nowrap;
}
.statsStarsFill {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--rating, 0) / 5 * 100%);
  overflow: hidden;
  white-space: nowrap;
  color: var(--stats-star);
}
.statsRatingValue {
  font-family: var(--mono);
  font-weight: 600;
  margin-left: 0.8rem;
  color: var(--ink);
}
.statsBucketRange {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-soft);
}
.statsCard .statsStars,
.statsFigureCard .statsStars {
  /* fit-content, not block: the fill is a percentage of this element's width,
     so a full-width block would spill the gold past the fifth star. */
  display: block;
  width: fit-content;
  font-size: 2rem;
  margin: -0.4rem 0 1rem;
}

/* ============================================================
   STACKED BAR - the overlap distribution
   ============================================================ */
.statsStack {
  display: flex;
  width: 100%;
  height: 6.4rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  margin-bottom: 1.6rem;
}
.statsStackPart {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  font-family: var(--mono);
  font-size: var(--fs-md);
  font-weight: 700;
  color: #fff;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
}
.statsStackPartCount {
  font-size: var(--fs-2xs);
  font-weight: 500;
  opacity: 0.82;
  letter-spacing: 0.04em;
}
/* Ordinal ramp, painted in the section's accent: the segments are steps on a
   scale (cheaper to dearer, fewer to more), so one hue getting darker is the
   encoding. A color per segment would read as four unrelated categories.

   The last step is --sec-ink, not --sec. Some accents are light hues (the gold
   one especially), and white type on a light accent is unreadable, so the only
   step that flips to white type is the one guaranteed dark enough for it. The
   three below it stay light enough for dark type at every accent. */
.statsStackPart0 { background: color-mix(in oklab, var(--sec) 14%, #ffffff); color: var(--ink); }
.statsStackPart1 { background: color-mix(in oklab, var(--sec) 36%, #ffffff); color: var(--ink); }
.statsStackPart2 { background: color-mix(in oklab, var(--sec) 60%, #ffffff); color: var(--ink); }
.statsStackPart3 { background: var(--sec-ink); color: #fff; }
.statsStackLegend {
  display: flex;
  flex-wrap: wrap;
  gap: 1.6rem;
  margin-bottom: 2.4rem;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}
.statsStackLegendItem { display: inline-flex; align-items: center; gap: 0.7rem; }
.statsStackSwatch {
  width: 1.2rem; height: 1.2rem; border-radius: 0.3rem;
  border: 1px solid rgba(12, 34, 51, 0.12);
}

/* ============================================================
   CHART
   ============================================================ */
.statsChartShell {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 2rem 1.6rem 1.2rem;
  margin-bottom: 2.4rem;
  overflow-x: auto;
}
.statsChart {
  display: block;
  width: 100%;
  min-width: 62rem;
  height: auto;
}
.statsChartGrid { stroke: #e4ecf3; stroke-width: 1; }
/* px inside the viewBox are user units, not CSS pixels: they scale with the
   drawing, which is exactly what chart labels must do. This is the one place
   the --fs-* tokens do not apply. */
.statsChartAxis { font-family: var(--mono); font-size: 13px; fill: #738aa0; }
.statsChartAxisPartial { fill: #a9bccd; font-style: italic; }
.statsChartLine { stroke-width: 2.6; stroke-linejoin: round; stroke-linecap: round; }
.statsChartLinePartial { stroke-width: 2.6; stroke-dasharray: 5 5; stroke-linecap: round; }
.statsChartDot { stroke-width: 2; }
.statsChartDotPartial { stroke-dasharray: 3 2; }
.statsChartSeriesLabel { font-family: var(--font); font-size: 14px; font-weight: 700; }

/* ============================================================
   DATA TABLES - the machine-readable half of every visual
   ============================================================ */
.statsFigure {
  margin: 0;
}
.statsFigureCaption {
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--ink-faint);
  margin: 0 0 1rem;
  text-wrap: pretty;
}
.statsTableShell {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow-x: auto;
}
/* Scroll affordance: a wide table only scrolls inside its own box, and without
   this the reader has no way to tell there are more columns to the right. The
   two `local` layers paint the surface edges and the two `scroll` layers the
   shadows, so the shadows fade out exactly when that end is reached. Pure CSS,
   no scroll listener. */
.statsTableShell,
.statsChartShell,
.statsMatrixShell,
.statsTreemapShell {
  background-image:
    linear-gradient(to right, var(--surface), rgba(255, 255, 255, 0)),
    linear-gradient(to left, var(--surface), rgba(255, 255, 255, 0)),
    linear-gradient(to right, rgba(12, 34, 51, 0.14), rgba(12, 34, 51, 0)),
    linear-gradient(to left, rgba(12, 34, 51, 0.14), rgba(12, 34, 51, 0));
  background-position: 0 0, 100% 0, 0 0, 100% 0;
  background-repeat: no-repeat;
  background-size: 3rem 100%, 3rem 100%, 1.2rem 100%, 1.2rem 100%;
  background-attachment: local, local, scroll, scroll;
}
.statsTable {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-base);
}
.statsTable th,
.statsTable td {
  padding: 1.1rem 1.8rem;
  text-align: left;
  border-bottom: 1px solid var(--line-soft);
  white-space: nowrap;
}
.statsTable th {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
}
.statsTable thead th { border-bottom: 1px solid var(--line); }
.statsTable tbody tr:last-child td { border-bottom: none; }
.statsTable tbody th { font-family: var(--font); font-size: var(--fs-base); text-transform: none; letter-spacing: 0; color: var(--ink); font-weight: 600; }
/* Numeric columns align right, HEADER INCLUDED: a right-aligned column sitting
   under a left-aligned header reads as two columns that do not belong together. */
.statsTable th.num,
.statsTable th.share { text-align: right; }
.statsTable td.num { font-family: var(--mono); text-align: right; font-variant-numeric: tabular-nums; }
.statsTable td.share { font-family: var(--mono); text-align: right; font-weight: 600; color: var(--brand); font-variant-numeric: tabular-nums; }
.statsTable .partial { color: var(--ink-faint); font-style: italic; }
.statsTable a { color: var(--brand); text-decoration: none; }
.statsTable a:hover { text-decoration: underline; }

/* Tables collapse so the visual above them can breathe. Content inside an open
   or closed <details> is still parsed and indexed, so the full sample behind
   every figure stays in the page for a crawler, a screen reader and anyone
   copying it into a spreadsheet. */
.statsDetails {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
  margin-top: 1.2rem;
}
.statsDetails > summary {
  cursor: pointer;
  padding: 1.4rem 1.8rem;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.statsDetails > summary::-webkit-details-marker { display: none; }
.statsDetails > summary::before {
  content: "";
  width: 1.6rem;
  height: 1.6rem;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 3h18v4H3zM3 10h18v4H3zM3 17h18v4H3z'/%3E%3C/svg%3E");
          mask: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 3h18v4H3zM3 10h18v4H3zM3 17h18v4H3z'/%3E%3C/svg%3E");
  opacity: 0.5;
}
.statsDetails > summary::after { content: "+"; margin-left: auto; color: var(--ink-faint); }
.statsDetails[open] > summary { border-bottom: 1px solid var(--line); }
.statsDetails[open] > summary::after { content: "-"; }
.statsDetails .statsTableShell { border: none; border-radius: 0; }
.statsDetails .statsFigureCaption { padding: 1.4rem 1.8rem 0; margin-bottom: 0; }

/* ============================================================
   FIGURE CARDS - the headline numbers of a section
   ============================================================ */
.statsCards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
  gap: 1.4rem;
  margin-bottom: 2.4rem;
}
.statsCard {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 2rem 2.2rem;
  overflow: hidden;
}
.statsCard::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 0.4rem;
  background: var(--sec);
}
.statsCardValue {
  font-family: var(--mono);
  font-size: clamp(3rem, 3.8vw, 4rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--sec-ink);
  display: block;
  margin-bottom: 0.8rem;
}
.statsCardLabel {
  font-size: var(--fs-md);
  line-height: 1.5;
  color: var(--ink-soft);
  display: block;
}
.statsCardCount {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  display: block;
  margin-top: 0.8rem;
}

/* ============================================================
   METHODOLOGY
   ============================================================ */
/* Separate cards rather than one gridded slab: the number of methodology notes
   is whatever the site writes, and a slab with an incomplete last row shows its
   own background through the empty cells. */
.statsMethodologyGrid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.4rem;
  margin-bottom: 2.4rem;
}
.statsMethodologyItem {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 2.2rem 2.4rem;
}
.statsMethodologyItem h3 {
  font-size: var(--fs-lg);
  font-weight: 700;
  margin: 0 0 0.8rem;
  color: var(--ink);
  text-align: left;
  text-transform: none;
  display: flex;
  align-items: center;
  gap: 0.9rem;
}
.statsMethodologyNum {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  color: var(--sec-ink);
  background: var(--sec-tint);
  border-radius: 0.5rem;
  padding: 0.3rem 0.6rem;
  flex: 0 0 auto;
}
.statsMethodologyItem p {
  font-size: var(--fs-sm);
  line-height: 1.65;
  color: var(--ink-soft);
  margin: 0;
  text-wrap: pretty;
}

/* ============================================================
   CITATION - the backlink mechanism
   ============================================================ */
.statsCite {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(2.4rem, 3vw, 3.4rem);
}
.statsCiteBlock { margin-bottom: 2.4rem; }
.statsCiteBlock:last-child { margin-bottom: 0; }
.statsCiteBlock h3 {
  font-size: var(--fs-lg);
  font-weight: 700;
  margin: 0 0 1rem;
  color: var(--ink);
  text-align: left;
  text-transform: none;
}
.statsCiteCode {
  display: block;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--ink);
  background: var(--bg-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 1.6rem 1.8rem;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0 0 1rem;
}
.statsCopyButton {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand);
  background: transparent;
  border: 1px solid color-mix(in oklab, var(--brand) 35%, var(--line));
  border-radius: 999px;
  padding: 0.7rem 1.6rem;
  cursor: pointer;
  transition: background 0.18s, color 0.18s;
}
.statsCopyButton:hover { background: var(--blue-50); }
.statsCopyButton[data-copied="true"] { background: var(--brand); color: #fff; border-color: var(--brand); }
.statsLicense {
  font-size: var(--fs-md);
  line-height: 1.6;
  color: var(--ink-soft);
  margin: 2.4rem 0 0;
  padding-top: 2rem;
  border-top: 1px solid var(--line-soft);
}

/* ============================================================
   DOWNLOADS
   ============================================================ */
.statsDownloads { display: flex; flex-wrap: wrap; gap: 1.2rem; margin-top: 1.6rem; }
.statsDownload {
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--brand);
  background: var(--surface);
  border: 1px solid color-mix(in oklab, var(--brand) 30%, var(--line));
  border-radius: 999px;
  padding: 1rem 1.8rem;
  text-decoration: none;
  transition: background 0.18s;
}
.statsDownload:hover { background: var(--blue-50); }

/* ============================================================
   FAQ
   ============================================================ */
.statsFaq {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}
.statsFaqItem { border-bottom: 1px solid var(--line-soft); padding: 2rem 2.4rem; }
.statsFaqItem:last-child { border-bottom: none; }
.statsFaqItem h3 {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 0.8rem;
  text-align: left;
  text-transform: none;
}
.statsFaqItem p { font-size: var(--fs-md); line-height: 1.6; color: var(--ink-soft); margin: 0; text-wrap: pretty; }

/* ============================================================
   RELATED LINKS
   ============================================================ */
.statsRelated { display: flex; flex-wrap: wrap; gap: 0.8rem; }
.statsRelatedChip {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-soft);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.8rem 1.6rem;
  text-decoration: none;
  transition: color 0.18s, border-color 0.18s, background 0.18s;
}
.statsRelatedChip img { width: 1.8rem; height: 1.8rem; object-fit: contain; }
.statsRelatedChip:hover {
  color: var(--brand);
  border-color: color-mix(in oklab, var(--brand) 45%, var(--line));
  background: var(--blue-50);
}

/* ============================================================
   HERO EXTRAS
   ============================================================ */
.statsHeroActions { display: flex; flex-wrap: wrap; gap: 1.2rem; margin-top: 2rem; }
.statsHeroAction {
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.10);
  border-radius: 999px;
  padding: 1rem 2rem;
  text-decoration: none;
  transition: background 0.18s, border-color 0.18s;
}
.statsHeroAction:hover { background: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.5); }
.statsHeroAction.primary { background: var(--brand-sky); border-color: var(--brand-sky); color: #06263c; }
.statsHeroAction.primary:hover { background: #fff; border-color: #fff; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1200px) {
  .statsTiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .statsEcoGrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .statsMethodologyGrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 1100px) {
  .statsFinding,
  .statsFinding--half { grid-column: span 3; }
  .statsFinding--lead,
  .statsFinding--wide { grid-column: span 6; }
}
@media (max-width: 820px) {
  .statsTiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .statsEcoGrid { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
  /* The band label does not fit inside a narrow segment (a 52px slice needs
     65px for "$50 to $100" and spills over its neighbour). The share stays
     inside; the legend right below names every band. */
  .statsStackPartCount { display: none; }
  .statsFindings { grid-template-columns: 1fr; }
  .statsFinding,
  .statsFinding--lead,
  .statsFinding--wide,
  .statsFinding--half { grid-column: span 1; }
  .statsCards { grid-template-columns: 1fr; }
  .statsMethodologyGrid { grid-template-columns: 1fr; }
  .statsBars { grid-template-columns: 1fr; }
  .statsStack { height: 5.2rem; }
  .statsStackPart { font-size: var(--fs-sm); }
  .statsTable th,
  .statsTable td { padding: 1rem 1.2rem; }
  /* The box keeps its 2:1 shape and scrolls; only the type comes down. */
  .statsTreemapName { font-size: var(--fs-sm); }
  .statsTreemapValue { font-size: var(--fs-2xs); }
  .statsTreemapCell { padding: 0.6rem 0.7rem; }
  .statsColumns { height: 20rem; }
  /* The track gets the full width and the name moves above it: on a phone a
     three column row leaves the axis about a hundred pixels to live in. */
  .statsRange {
    grid-template-columns: 1fr auto;
    grid-template-areas: "name value" "track track";
    gap: 0.6rem 1rem;
    padding: 1.2rem 0;
  }
  .statsRangeName { grid-area: name; white-space: normal; }
  .statsRangeValue { grid-area: value; }
  .statsRangeTrack { grid-area: track; }
  .statsRangeAxis { grid-template-columns: 1fr; }
  .statsMatrix tbody th { max-width: 12rem; white-space: normal; }
  .statsMatrix th,
  .statsMatrix td { padding: 0.8rem 0.7rem; }
}
@media (max-width: 600px) {
  .statsSectionHead { gap: 1.2rem; }
  .statsChapterNum { padding: 0.8rem 1rem; }
  .statsBarLabel { flex-direction: column; align-items: flex-start; gap: 0.4rem; }
  .statsChartShell { padding: 1.2rem 0.8rem 0.8rem; }
  .statsTile { padding: 1.4rem 1.4rem 1.2rem; }
  .statsTileValue { font-size: 2.6rem; }
  .statsRangeName { white-space: normal; }
}

/* Reduced motion: the only motion here is decorative. */
@media (prefers-reduced-motion: reduce) {
  a.statsTile:hover { transform: none; }
}

/* metadata/css/reports.css */
/* ============================================================
   Monthly market reports (/reports and /reports/<month>)

   The report wears the /stats clothes: item-card.css and
   stats.css are loaded first, and the page reuses their
   sections, chapter numerals, tiles, magnitude bars, figure
   tables and citation blocks. This file only adds what a report
   needs and a live stats page does not, plus the handful of
   utility classes the report markup asks for.

   Nothing here should restyle a stats component. If a rule
   below starts fighting stats.css, the fix belongs in the
   markup, not in an override.

   TYPOGRAPHY: sizes use the shared --fs-* tokens. Never hardcode
   a font-size in px here.
   ============================================================ */

/* ---------- Utilities the report markup uses ---------- */

/* Numeric table columns read as a column of magnitudes only when
   they line up on the right. */
.statsTable td.statsNum,
.statsTable th.statsNum { text-align: right; font-variant-numeric: tabular-nums; }

/* Direction of a change. Green for cheaper and red for pricier is
   the shopper's reading, not the trader's: on this site a falling
   price is the good news. Never the only channel, the sign is
   always printed with the number. */
.statsDown { color: #047857; font-weight: 600; }
.statsUp   { color: #b91c1c; font-weight: 600; }

/* Icon plus name inside a table row. */
.statsRowLabel { display: inline-flex; align-items: center; gap: 0.8rem; }

/* The sample riding along with a figure in the same cell. Recessive: it
   qualifies the number, it is not a second number competing with it. */
.statsMuted { color: var(--ink-faint); font-size: var(--fs-xs); }

.statsEmpty {
  padding: 3.2rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: var(--fs-md);
  text-align: center;
}

.statsNotes {
  margin: 0;
  padding-left: 2rem;
  color: var(--ink-soft);
  font-size: var(--fs-md);
  line-height: 1.7;
}
.statsNotes li { margin-bottom: 0.8rem; }
.statsNotes--limits { color: var(--ink-faint); }

/* A brand wordmark used as a bar-row icon: wide, not square. */
.statsBrandMark {
  width: auto;
  max-width: 7.6rem;
  height: 2rem;
  object-fit: contain;
  object-position: left center;
}

/* The month the report covers, marked in the year's column chart so
   the reader can place it without counting columns. */
.statsColumn--current .statsColumnBar {
  background: var(--brand);
  box-shadow: 0 0 0 0.2rem color-mix(in oklab, var(--brand) 25%, transparent);
}
.statsColumn--current .statsColumnValue { color: var(--brand); font-weight: 800; }

/* ---------- Part dividers ---------- */

/* The report answers two questions over two different universes: the products
   released this month, and every product we track. These banners are what tell
   the reader the universe just changed, so they have to read as a louder break
   than a chapter heading. */
.reportPart {
  margin: 6rem 0 3.2rem;
  padding: 3.2rem;
  border-radius: var(--radius);
  background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%);
  color: #fff;
}

.reportPart:first-of-type { margin-top: 4rem; }

.reportPartEyebrow {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.7;
}

.reportPartTitle {
  margin: 0.8rem 0 0;
  color: #fff;
  font-size: var(--fs-6xl);
  line-height: 1.15;
}

.reportPartIntro {
  margin: 1.2rem 0 0;
  max-width: 70ch;
  font-size: var(--fs-md);
  line-height: 1.6;
  opacity: 0.85;
}

/* Label that opens each run of chips in the sticky index, so the split is
   visible before the reader scrolls into it. */
.reportIndexPart {
  align-self: center;
  padding-left: 0.8rem;
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.reportIndexPart:not(:first-child) { border-left: 1px solid var(--line); margin-left: 0.8rem; }

@media (max-width: 700px) {
  .reportPart { margin: 4rem 0 2.4rem; padding: 2.4rem; }
  .reportPartTitle { font-size: var(--fs-5xl); }
}

/* ---------- Price comparison ---------- */

/* The headline of the price chapter: last month against this one, side by side.
   Two figures with an arrow between them state the comparison in one glance,
   which a row of four unrelated counters never did. */
/* A grid, not a row of boxes: the month labels share a row, the figures share
   the next one, and the delta hangs under the current month without dragging
   the other half of the pair down with it (which is what a flex row centred on
   two columns of different heights did). */
.reportCompare {
  display: grid;
  grid-template-columns: auto auto auto;
  align-items: center;
  justify-content: center;
  column-gap: 3.2rem;
  row-gap: 0.8rem;
  padding: 3.2rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  text-align: center;
}

.reportCompareWhen--was { grid-column: 1; grid-row: 1; }
.reportCompareValue--was { grid-column: 1; grid-row: 2; }
.reportCompareArrow { grid-column: 2; grid-row: 2; }
.reportCompareWhen--now { grid-column: 3; grid-row: 1; }
.reportCompareValue--now { grid-column: 3; grid-row: 2; }
.reportCompareDelta { grid-column: 3; grid-row: 3; }
.reportCompareCaption { grid-column: 1 / -1; grid-row: 4; }

/* First edition: one figure, one column. */
.reportCompare--single { grid-template-columns: auto; }
.reportCompare--single .reportCompareWhen--now,
.reportCompare--single .reportCompareValue--now,
.reportCompare--single .reportCompareDelta { grid-column: 1; }

.reportCompareWhen {
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.reportCompareValue {
  font-family: var(--mono);
  font-size: clamp(3.6rem, 6vw, 5.6rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--ink);
}

/* The earlier figure is context, not the headline: same size so the pair reads
   as one comparison, but recessive so the eye lands on the current month. */
.reportCompareValue--was { color: var(--ink-faint); }

.reportCompareDelta {
  font-size: var(--fs-base);
  font-weight: 600;
}

.reportCompareArrow {
  font-size: var(--fs-6xl);
  line-height: 1;
  color: var(--ink-faint);
}
.reportCompareArrow--down { color: #047857; }
.reportCompareArrow--up { color: #b91c1c; }

.reportCompareCaption {
  margin-top: 1.2rem;
  color: var(--ink-faint);
  font-size: var(--fs-sm);
  text-align: center;
}

.reportSpread {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 3.2rem;
  margin: 1.6rem 0 3.2rem;
}
.reportSpread > div { display: flex; align-items: baseline; gap: 0.8rem; }

.reportSpreadLabel {
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.reportSpreadValue {
  font-family: var(--mono);
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--ink);
}

/* ---------- Dumbbell ---------- */

/* The track, dots and axis come from stats.css (.statsRange*). What is added
   here is the second dot (where the category was) and the bar between them. */
.reportDotWas {
  display: inline-block;
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--ink-faint);
}
.reportDotWas--onTrack {
  position: absolute;
  top: 50%;
  margin-left: -0.6rem;
  transform: translateY(-50%);
}

/* Length of this bar IS the movement, so it carries the direction colour. */
.reportMove { background: color-mix(in oklab, var(--ink-faint) 35%, #ffffff); }
.reportMove--down { background: color-mix(in oklab, #047857 45%, #ffffff); }
.reportMove--up { background: color-mix(in oklab, #b91c1c 45%, #ffffff); }

.reportMoveDelta {
  display: block;
  margin-top: 0.2rem;
  font-size: var(--fs-2xs);
  font-weight: 700;
}

/* ---------- Price bands ---------- */

.reportBands { margin-bottom: 2.4rem; }

.reportBandRow {
  display: grid;
  grid-template-columns: minmax(8rem, 12rem) 1fr;
  align-items: center;
  gap: 1.6rem;
  margin-bottom: 0.8rem;
}

.reportBandWhen {
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: right;
}

.reportBandTrack {
  display: flex;
  height: 3.2rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.reportBandPart {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  overflow: hidden;
}

.reportBandPartLabel {
  color: #fff;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  white-space: nowrap;
}

/* Four bands, ordered cheapest to priciest: a sequential ramp, because the
   bands are an ordered scale and not four unrelated categories. */
.reportBandPart[data-band="0"],
.reportBandSwatch[data-band="0"] { background: #bfdbfe; }
.reportBandPart[data-band="1"],
.reportBandSwatch[data-band="1"] { background: #60a5fa; }
.reportBandPart[data-band="2"],
.reportBandSwatch[data-band="2"] { background: #2563eb; }
.reportBandPart[data-band="3"],
.reportBandSwatch[data-band="3"] { background: #1e3a8a; }

/* The lightest band cannot carry white text. */
.reportBandPart[data-band="0"] .reportBandPartLabel { color: #1e3a8a; }

.reportBandLegend {
  display: flex;
  flex-wrap: wrap;
  gap: 1.6rem;
  margin-top: 1.2rem;
  color: var(--ink-soft);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.reportBandLegendItem { display: inline-flex; align-items: center; gap: 0.6rem; }
.reportBandSwatch {
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 0.3rem;
}

@media (max-width: 700px) {
  .reportCompare { grid-template-columns: auto; row-gap: 0.4rem; padding: 2.4rem; }
  .reportCompareWhen--was { grid-column: 1; grid-row: 1; }
  .reportCompareValue--was { grid-column: 1; grid-row: 2; }
  .reportCompareArrow { grid-column: 1; grid-row: 3; transform: rotate(90deg); margin: 0.8rem 0; }
  .reportCompareWhen--now { grid-column: 1; grid-row: 4; }
  .reportCompareValue--now { grid-column: 1; grid-row: 5; }
  .reportCompareDelta { grid-column: 1; grid-row: 6; }
  .reportCompareCaption { grid-column: 1; grid-row: 7; }
  .reportSpread { gap: 1.6rem; }
  .reportBandRow { grid-template-columns: 1fr; gap: 0.4rem; }
  .reportBandWhen { text-align: left; }
}

/* ---------- Launch stat strip ---------- */

/* item-card.css pulls the stat strip up by -4.4rem so it overlaps the
   hero on Deals and New Releases. Inside a chapter it sits under the
   intro text, so that offset has to go or the cards land on the
   paragraph. It also flows instead of holding three fixed columns,
   because these strips carry four figures. */
.reportPageBody .reportStats {
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  margin-top: 0;
}

/* ---------- Index page: the latest edition ---------- */

/* The landing card leads with the STATE of the market, not with what launched,
   so the figures are the subject and the prose is the caption. */
.reportLatest {
  display: grid;
  gap: 2.4rem;
  margin-bottom: 4.8rem;
  padding: 3.2rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: linear-gradient(150deg, var(--blue-50) 0%, var(--surface) 55%);
}

.reportLatestBadge {
  display: inline-block;
  padding: 0.4rem 1.2rem;
  border-radius: 999px;
  background: var(--brand);
  color: #fff;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.reportLatestTitle {
  margin: 1.6rem 0 0;
  font-size: clamp(3.4rem, 5vw, 5rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
}
.reportLatestTitle a { color: inherit; text-decoration: none; }
.reportLatestTitle a:hover { color: var(--brand); }

.reportLatestLede {
  margin: 1.2rem 0 0;
  max-width: 62ch;
  color: var(--ink-soft);
  font-size: var(--fs-md);
  line-height: 1.6;
}

/* The figures sit on their own band, divided rather than boxed: three cards
   would compete with the report's own stat strip a click away. */
.reportLatestFigures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: 2.4rem;
  padding: 2.4rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.reportFigureValue {
  font-family: var(--mono);
  font-size: clamp(3rem, 4vw, 4rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--brand);
}

.reportFigureLabel {
  margin-top: 0.8rem;
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  line-height: 1.4;
}

.reportFigureDelta {
  margin-top: 0.6rem;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink-faint);
}

.reportLatestCta {
  justify-self: start;
  padding: 1.2rem 2.4rem;
  border-radius: 999px;
  background: var(--brand);
  color: #fff;
  font-size: var(--fs-base);
  font-weight: 600;
  text-decoration: none;
}
.reportLatestCta:hover { background: var(--brand-bright); }

/* ---------- Index page: the archive ---------- */

/* A table, not a grid of cards: twelve editions in, cards are a scroll, while a
   table lets a reader follow one figure down the months. That series is the
   reason to publish monthly at all. */
.reportArchiveTable th[scope="row"] a { color: var(--brand); text-decoration: none; font-weight: 700; }
.reportArchiveTable th[scope="row"] a:hover { text-decoration: underline; }

/* ---------- Press block ---------- */

.reportPress {
  margin-top: 4rem;
  padding: 3.2rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-2);
}
.reportPress .statsSubHeading { margin-top: 0; }

/* ---------- Archive ---------- */

.reportArchive {
  display: flex;
  flex-wrap: wrap;
  gap: 1.2rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.reportArchive a {
  display: inline-block;
  padding: 1rem 1.6rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--brand);
  font-size: var(--fs-base);
  font-weight: 600;
  text-decoration: none;
}
.reportArchive a:hover { background: var(--blue-50); }

/* ---------- Responsive ---------- */

@media (max-width: 700px) {
  .reportLatest { padding: 2.4rem; }
  .reportLatestFigures { gap: 1.6rem; }
}

/* Movement inside a treemap cell: the share is the figure, the delta rides
   beside it in the same line, dimmer, so the cell still reads share-first.
   Lives here and not in stats.css because only the reports treemap carries a
   month to compare against. */
.statsTreemapDelta {
  font-weight: 700;
  opacity: 0.75;
}
.statsTreemapCell--xs .statsTreemapDelta { display: none; }
