/* =============================================================
   HOME REDESIGN LAYER
   Loaded after screen.css. Everything here is prefixed hm- so it
   cannot collide with the article surfaces. Consumes tokens.css
   only, no literal colour values.

   Motion rules honoured throughout:
   - every scroll-driven block sits inside @supports
   - the CSS default is the finished state
   - every block has a prefers-reduced-motion override
   - only transform and opacity are animated
   - at most two animated elements at any scroll position
   ============================================================= */

/* ---- layout: the home template is full bleed. screen.css caps
   .site-main at --wide-width for article surfaces; every hm-
   section manages its own padding instead. ---- */

/* Full bleed is decided by what the template actually renders, not
   by a body class. Ghost gives custom routes (/fr/, /analyses/) no
   body class at all, so the class-based rules below are only a
   fallback for browsers without :has(). */
.site-main:has(> .hm-hero),
.site-main:has(> .lp-header),
.site-main:has(> .ms-page),
.site-main:has(> .err-page),
.site-main:has(> .page-template-body),
.site-main:has(> article > .pp-header) {
  max-width: none;
  padding: 0;
}

.home-template .site-main,
.page-the-metrics-shelf .site-main {
  max-width: none;
  padding: 0;
}

/* ---- utilities ---- */

.hm-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.hm-kicker {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--interface);
  margin-bottom: var(--space-md);
}

/* ---- film grain, whole site. Static texture is the default,
   the drift animation is the enhancement. ---- */

body::before {
  content: "";
  position: fixed;
  inset: -100%;
  z-index: 90;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: hm-grain 7s steps(9) infinite;
}

@keyframes hm-grain {
  0%, 100% { transform: translate(0, 0); }
  12% { transform: translate(-3%, 2%); }
  25% { transform: translate(2%, -4%); }
  37% { transform: translate(-4%, -2%); }
  50% { transform: translate(3%, 3%); }
  62% { transform: translate(-2%, 4%); }
  75% { transform: translate(4%, -3%); }
  87% { transform: translate(-3%, -3%); }
}

@media (prefers-reduced-motion: reduce) {
  body::before { animation: none; }
}

/* ---- measurement cursor, styled here, driven by home.js.
   Exists only when the script adds .hm-cursor-on for fine
   pointers with motion allowed. ---- */

body.hm-cursor-on,
body.hm-cursor-on a,
body.hm-cursor-on button { cursor: none; }

.hm-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 99;
  pointer-events: none;
  will-change: transform;
}
.hm-cursor .hm-cx,
.hm-cursor .hm-cy { position: absolute; background: rgb(var(--blue-rgb) / 0.55); }
.hm-cursor .hm-cx { width: 34px; height: 1px; left: -17px; top: 0; }
.hm-cursor .hm-cy { width: 1px; height: 34px; top: -17px; left: 0; }
.hm-cursor .hm-ring {
  position: absolute;
  width: 10px;
  height: 10px;
  left: -5px;
  top: -5px;
  border-radius: 50%;
  border: 1px solid var(--interface);
  transition: width var(--dur-fast) var(--ease-standard),
              height var(--dur-fast) var(--ease-standard),
              left var(--dur-fast) var(--ease-standard),
              top var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.hm-cursor .hm-read {
  position: absolute;
  left: 14px;
  top: 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--interface);
  white-space: nowrap;
}
.hm-cursor.hm-is-link .hm-ring {
  width: 34px;
  height: 34px;
  left: -17px;
  top: -17px;
  background: var(--interface-wash);
}
.hm-cursor.hm-is-data .hm-ring { border-color: var(--data); }
.hm-cursor.hm-is-data .hm-read { color: var(--data); }

/* ---- HERO ---- */

.hm-hero {
  position: relative;
  /* svh is the SMALL viewport: the height with the browser chrome
     showing. 100vh on iOS is the large viewport, so bottom aligned
     content ends up underneath the Safari toolbar, which is exactly
     how the hero number was getting clipped on a phone. The vh line
     stays first as the fallback for anything without svh.

     The header is sticky, so it occupies flow space and the hero
     starts below it. A full 100svh therefore overshot the fold by
     exactly the header height, at every width, which is the other
     half of why the buttons were never visible. */
  min-height: calc(100vh - var(--hdr-h));
  min-height: calc(100svh - var(--hdr-h));
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 0 var(--space-lg)
           max(5vh, calc(var(--space-md) + env(safe-area-inset-bottom)));
  overflow: hidden;
  background: var(--void);
}

.hm-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: linear-gradient(var(--rule) 1px, transparent 1px),
                    linear-gradient(90deg, var(--rule) 1px, transparent 1px);
  background-size: 72px 72px;
  opacity: 0.35;
  mask-image: radial-gradient(ellipse 90% 80% at 65% 40%, black 30%, transparent 75%);
}

.hm-field {
  position: absolute;
  top: 50%;
  right: -4vw;
  transform: translateY(-58%);
  width: min(78vh, 60vw);
  height: min(78vh, 60vw);
  opacity: 0.92;
  filter: drop-shadow(0 0 6px rgb(var(--data-blue-rgb) / 0.5));
}

.hm-dots circle {
  fill: var(--data);
  r: 2.4px;
  opacity: 0.7;
}

/* Default state: the true flattened fit. 37deg back toward level. */
.hm-fit {
  stroke: var(--data);
  stroke-width: 2.5;
  /* Full opacity. The blue implementation spec keeps the central estimate
     solid and reserves reduced opacity for the observation cloud, which sits
     at 0.7 and inside the 65 to 85 percent the spec asks for. */
  opacity: 1;
  transform: rotate(37deg);
  transform-origin: 250px 250px;
}

.hm-eyebrow {
  position: relative;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--interface);
  margin-bottom: 1vh;
}

.hm-number {
  position: relative;
  z-index: 2;
  font-family: var(--font-mono);
  font-weight: 700;
  /* nowrap: the counter overlay breaks if the span fragments across
     lines. The 64px floor keeps r = 0.14 on one line at 390px. */
  white-space: nowrap;
  font-size: clamp(64px, 17vw, 260px);
  line-height: 0.95;
  letter-spacing: -0.04em;
  color: var(--text-strong);
  font-variant-numeric: var(--numerals);
}

.hm-unit {
  color: var(--data);
  text-shadow: 0 0 22px rgb(var(--data-blue-rgb) / 0.4);
}

.hm-sub {
  position: relative;
  z-index: 2;
  max-width: 560px;
  margin-top: 2.5vh;
  font-size: var(--fs-base);
  color: var(--text-muted);
}
.hm-sub strong { color: var(--text); }

.hm-actions {
  position: relative;
  z-index: 2;
  display: flex;
  gap: var(--space-sm);
  margin-top: 3vh;
  flex-wrap: wrap;
}

.hm-btn {
  font-size: var(--fs-xs);
  font-weight: 600;
  padding: 14px 28px;
  border-radius: 2px;
  transition: background var(--dur-base) var(--ease-standard),
              color var(--dur-base) var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard);
}
.hm-btn-primary { background: var(--interface); color: var(--void); }
.hm-btn-primary:hover { background: var(--interface-hover); }
.hm-btn-ghost { border: 1px solid var(--interface-quiet); color: var(--interface); }
.hm-btn-ghost:hover { border-color: var(--interface); color: var(--interface-hover); }

/* Hero motion: the field relaxes off the diagonal and the caption
   counts down over the first 40vh of scroll. Two animated elements,
   the dot field and the fit line, plus the caption property. */

@property --hm-rv {
  syntax: "<integer>";
  inherits: false;
  initial-value: 14;
}

@supports (animation-timeline: scroll()) {
  .hm-dots circle {
    animation: hm-relax linear both;
    animation-timeline: scroll(root block);
    animation-range: 0vh 40vh;
  }
  .hm-fit {
    animation: hm-flatten linear both;
    animation-timeline: scroll(root block);
    animation-range: 0vh 40vh;
  }

  @supports (width: calc(round(down, 1.5px, 1px))) {
    .hm-rv i { visibility: hidden; }
    .hm-rv {
      position: relative;
    }
    .hm-rv::before {
      content: counter(hm-ri) "." counter(hm-rd, decimal-leading-zero);
      position: absolute;
      left: 0;
      top: 0;
      counter-reset: hm-ri calc(round(down, var(--hm-rv) / 100, 1))
                     hm-rd calc(mod(var(--hm-rv), 100));
      animation: hm-count linear both;
      animation-timeline: scroll(root block);
      animation-range: 0vh 40vh;
    }
  }
}

@keyframes hm-relax {
  from { transform: translate(var(--dx), var(--dy)); }
  to   { transform: none; }
}
@keyframes hm-flatten {
  from { transform: rotate(0deg); }
  to   { transform: rotate(37deg); }
}
@keyframes hm-count {
  from { --hm-rv: 100; }
  to   { --hm-rv: 14; }
}

@media (prefers-reduced-motion: reduce) {
  .hm-dots circle, .hm-fit { animation: none; }
  .hm-rv i { visibility: visible; }
  .hm-rv::before { content: none; animation: none; }
}

/* ---- TICKER. Time-based marquee, both track copies are in the
   markup so this needs no JavaScript. ---- */

.hm-ticker {
  display: flex;
  background: var(--void);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  overflow: hidden;
  padding: 16px 0;
}

.hm-track {
  display: flex;
  gap: 64px;
  padding-right: 64px;
  flex-shrink: 0;
  min-width: 100%;
  animation: hm-marquee 36s linear infinite;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  white-space: nowrap;
  font-variant-numeric: var(--numerals);
}
.hm-ticker:hover .hm-track,
.hm-ticker:focus-within .hm-track,
.hm-ticker.hm-paused .hm-track { animation-play-state: paused; }

/* Pause control. WCAG 2.2.2 is Level A and hover reaches neither a keyboard
   nor a touch screen. Rendered by home.js so it never appears when the
   marquee is not actually animating. */
.hm-pause {
  position: absolute;
  right: var(--space-2xs);
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--base);
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 11px;
  cursor: pointer;
}
.hm-pause:hover { color: var(--interface); border-color: var(--interface-quiet); }
.hm-track span { color: var(--text-muted); }
.hm-track span b {
  color: var(--data);
  font-weight: 700;
  text-shadow: 0 0 22px rgb(var(--data-blue-rgb) / 0.4);
}
.hm-dia { color: var(--interface-quiet); }

@keyframes hm-marquee { to { transform: translateX(-100%); } }

@media (prefers-reduced-motion: reduce) {
  .hm-track { animation: none; }
  .hm-track[aria-hidden] { display: none; }
}

/* ---- STATEMENT ---- */

.hm-statement {
  padding: 16vh var(--space-lg);
  max-width: var(--wide-width);
  margin: 0 auto;
}
.hm-statement h2 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(40px, 7.5vw, 110px);
  line-height: 0.98;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: var(--text-strong);
}
.hm-statement h2 .hm-accent { color: var(--interface); }
.hm-statement p {
  max-width: 620px;
  margin-top: 4vh;
  font-size: var(--fs-lg);
  color: var(--text-muted);
}

/* ---- SECOND ACT. Default: five readable stacked panels. With
   scroll-driven support: pinned, panels crossfade on the section's
   own view timeline. One panel visible at a time plus the progress
   bar, inside the two element budget. ---- */

.hm-act2 {
  background: var(--void);
  border-top: 1px solid var(--rule);
}

.hm-act2-sticky { padding: 12vh var(--space-lg); }

.hm-act2-head {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--interface);
  margin-bottom: 8vh;
}
.hm-act2-count { display: none; }
.hm-act2-bar { display: none; }

.hm-panel { max-width: var(--wide-width); margin: 0 auto 12vh; }
.hm-panel:last-of-type { margin-bottom: 0; }

.hm-panel .hm-k {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--data);
  text-shadow: 0 0 22px rgb(var(--data-blue-rgb) / 0.4);
}
.hm-panel h3 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(36px, 7vw, 104px);
  line-height: 1;
  text-transform: uppercase;
  color: var(--text-strong);
  margin-top: 2vh;
}
.hm-panel .hm-apps {
  margin-top: 3vh;
  font-size: var(--fs-base);
  color: var(--text-muted);
  max-width: 640px;
}
.hm-tags { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 2.5vh; }
/* The domain chips list where a question applies, which is not the
   same as where we have published. The label makes that explicit so
   the row cannot be read as a client list. */
.hm-tags .hm-tags-label {
  border: 0;
  padding: 6px 0;
  color: var(--text-faint);
  letter-spacing: 0.14em;
}
.hm-tags span {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--interface);
  border: 1px solid var(--interface-quiet);
  padding: 6px 12px;
  border-radius: 2px;
}
.hm-panel .hm-ref {
  margin-top: 3vh;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.hm-panel .hm-ref b { color: var(--text-muted); font-weight: 400; }

@supports (animation-timeline: view()) {
  .hm-act2 {
    height: 520vh;
    view-timeline: --hm-act2 block;
  }
  .hm-act2-sticky {
    position: sticky;
    top: 0;
    height: 100vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0 var(--space-lg);
  }
  .hm-act2-head {
    position: absolute;
    top: 11vh;
    left: var(--space-lg);
    right: var(--space-lg);
    margin: 0;
  }
  .hm-panel {
    position: absolute;
    left: var(--space-lg);
    right: var(--space-lg);
    top: 50%;
    margin: 0;
    transform: translateY(-44%);
    opacity: 0;
    animation: hm-panel-mid linear both;
    animation-timeline: --hm-act2;
  }
  /* contain ranges split the pinned scroll into five windows */
  .hm-panel:nth-of-type(1) { animation-name: hm-panel-first; animation-range: contain 0% contain 22%; }
  .hm-panel:nth-of-type(2) { animation-range: contain 18% contain 42%; }
  .hm-panel:nth-of-type(3) { animation-range: contain 38% contain 62%; }
  .hm-panel:nth-of-type(4) { animation-range: contain 58% contain 82%; }
  .hm-panel:nth-of-type(5) { animation-name: hm-panel-last; animation-range: contain 78% contain 100%; }

  .hm-act2-bar {
    display: block;
    position: absolute;
    bottom: 9vh;
    left: var(--space-lg);
    right: var(--space-lg);
    height: 1px;
    background: var(--rule);
  }
  .hm-act2-bar::after {
    content: "";
    display: block;
    height: 100%;
    background: var(--interface);
    transform: scaleX(0);
    transform-origin: 0 50%;
    animation: hm-bar linear both;
    animation-timeline: --hm-act2;
    animation-range: contain 0% contain 100%;
  }
}

@keyframes hm-panel-first {
  0% { opacity: 1; }
  80% { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes hm-panel-mid {
  0% { opacity: 0; }
  22% { opacity: 1; }
  78% { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes hm-panel-last {
  0% { opacity: 0; }
  20% { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes hm-bar {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .hm-act2 { height: auto; view-timeline: none; }
  .hm-act2-sticky { position: static; height: auto; overflow: visible; padding: 12vh var(--space-lg); }
  .hm-act2-head { position: static; margin-bottom: 8vh; }
  .hm-panel { position: static; opacity: 1; transform: none; animation: none; margin: 0 auto 12vh; }
  .hm-act2-bar { display: none; }
}

/* ---- EVIDENCE ROWS ---- */

.hm-evidence {
  max-width: var(--wide-width);
  margin: 0 auto;
  padding: 14vh var(--space-lg);
}

.hm-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  padding: 34px 18px;
  border-top: 1px solid var(--rule);
  transition: background var(--dur-base) var(--ease-standard),
              padding-left var(--dur-base) var(--ease-standard);
}
.hm-row:last-of-type { border-bottom: 1px solid var(--rule); }
.hm-row:hover { background: var(--card); padding-left: 34px; }
.hm-row h3 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(24px, 3.6vw, 52px);
  text-transform: uppercase;
  line-height: 1;
  color: var(--text-strong);
  transition: color var(--dur-base) var(--ease-standard);
}
.hm-row:hover h3 { color: var(--interface-hover); }
.hm-dom {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--text-faint);
  text-transform: uppercase;
  display: block;
  margin-bottom: 10px;
}
.hm-num {
  font-family: var(--font-mono);
  font-size: clamp(14px, 1.6vw, 22px);
  font-weight: 700;
  color: var(--data);
  font-variant-numeric: var(--numerals);
  flex-shrink: 0;
  text-transform: uppercase;
  text-shadow: 0 0 22px rgb(var(--data-blue-rgb) / 0.4);
}
.hm-q {
  display: block;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 8px;
}
.hm-all {
  display: inline-block;
  margin-top: var(--space-lg);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--interface);
}
.hm-all:hover { color: var(--interface-hover); }

/* floating figure preview, positioned by home.js */
.hm-preview {
  position: fixed;
  z-index: 70;
  width: 300px;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.94);
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
  will-change: transform;
}
.hm-preview.hm-on { opacity: 1; transform: scale(1); }
.hm-preview .hm-frame {
  background: var(--fig-paper);
  border: 1px solid rgb(var(--data-blue-rgb) / 0.25);
  border-radius: 3px;
  padding: 8px;
  box-shadow: 0 18px 50px rgb(var(--shadow-rgb) / 0.6);
}
.hm-preview img { display: block; width: 100%; height: auto; border-radius: 2px; }
@media (pointer: coarse) { .hm-preview { display: none; } }

/* ---- PRODUCTS ---- */

.hm-products {
  background: var(--void);
  border-top: 1px solid var(--rule);
  padding: 14vh var(--space-lg);
}
.hm-products-inner { max-width: var(--wide-width); margin: 0 auto; }

.hm-prod {
  display: block;
  /* No side padding, so the resting state sits on the page grid. The hover
     indent below supplies the movement that the 18px used to. */
  padding: 44px 0;
  border-top: 1px solid var(--rule);
  transition: background var(--dur-base) var(--ease-standard),
              padding-left var(--dur-base) var(--ease-standard);
}
.hm-prod:last-of-type { border-bottom: 1px solid var(--rule); }
.hm-prod:hover { background: var(--interface-wash); padding-left: 16px; }
.hm-prod h3 {
  font-family: var(--font-display);
  font-weight: 900;
  /* 62px, not 84px. The longest offer name is 31 characters and at 84
     it broke after the first word, landing on three lines. */
  font-size: clamp(26px, 4.4vw, 62px);
  line-height: 1.02;
  text-wrap: balance;
  text-transform: uppercase;
  color: var(--text-strong);
  transition: color var(--dur-base) var(--ease-standard);
}
.hm-prod:hover h3 { color: var(--interface-hover); }
.hm-prod p {
  margin-top: 14px;
  /* These are sales paragraphs, not captions. At --fs-sm under a title
     spanning the full row they left half the row empty and read small. */
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--text-muted);
  max-width: var(--content-width);
}
.hm-prod p + p { margin-top: 0.9em; }
.hm-go {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.2em;
  color: var(--interface);
  text-transform: uppercase;
  margin-top: 14px;
  display: inline-block;
}
.hm-prod-top { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
/* A flex item will not shrink below its longest word unless it is told
   it may. Without this the product name ran off the right edge on a
   320px phone instead of wrapping. */
.hm-prod-top h3 { min-width: 0; }

/* Narrow phones. MEASUREMENT set in Archivo Black measures 296px at
   34px, which is wider than a 320px screen has left once the section
   and the row have taken their padding. The row gives up its own inset
   first, and only below that does the billboard step down. Measured,
   not guessed: at 360px the word still fits at full size. */
@media (max-width: 400px) {
  .hm-prod { padding-left: 0; padding-right: 0; }
  .hm-prod:hover { padding-left: 0; }
  /* French runs longer than English. AUTOMATISATION is the longest
     product word on the site and it needs a smaller step than
     MEASUREMENT did, one breakpoint earlier. This is the one place the
     lang-fr body class set by the French layouts is load bearing. */
  .lang-fr .hm-prod h3 { font-size: 27px; }
}
@media (max-width: 359px) {
  .hm-prod h3 { font-size: 30px; }
  .lang-fr .hm-prod h3 { font-size: 26px; }
}
.hm-status {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: 2px;
  border: 1px solid var(--interface-quiet);
  white-space: nowrap;
}
.hm-live { color: var(--interface-hover); background: var(--interface-wash); }
.hm-soon { color: var(--data); border-color: rgb(var(--data-blue-rgb) / 0.3); }
.hm-dev { color: var(--text-muted); border-color: var(--rule); }

/* The unnamed teaser. Not a product row: no border, no hover, no
   link, because there is nothing to click yet and a dead link reads
   worse than no link at all. */
.hm-teaser {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 44px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  max-width: 720px;
}
.hm-teaser-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--data);
  border: 1px solid rgb(var(--data-blue-rgb) / 0.3);
  border-radius: 2px;
  padding: 6px 12px;
  white-space: nowrap;
}

/* ---- TOP SHELF BAND ------------------------------------------------
   It reuses .hm-products so the card, the status chip and the go line
   are the same components as Work with us rather than a second set that
   drifts. Two things change, and only two.

   One: it follows another .hm-products band, so 14vh of top padding on
   top of 14vh of bottom padding is a screen and a half of nothing on a
   phone. Halved here, not in the shared rule.

   Two: the shared rule paints --void and draws a top rule, which between
   two bands of the same colour reads as a divider rather than as the edge
   of a section. That is the correct reading, so it stays.

   ADJACENCY, NOT A CLASS. This was .hm-topshelf { padding-top: 7vh } while
   Top Shelf sat second. Martin moved it above the consulting cards on
   2026-08-21 and the reduction would have stayed on the wrong band: the one
   that now comes FIRST after the evidence section, with the doubled gap
   landing between it and Work with us instead. Written as adjacency it is
   correct in either order and needs no edit if the order changes again. */
.hm-products + .hm-products { padding-top: 7vh; }

/* The handoff link outside a card, which today means the footer. It takes
   .footer-cta there, so this only has to carry the failure state.
   partials/topshelf-handoff.hbs sets data-ts-error and rewrites the label
   when the token exchange fails, and deliberately does NOT navigate. An
   error nobody can see is the same as no error handling at all.

   --ui-red CARRIES ITS OWN GROUND. The footer is --void, where --ui-red
   measures 4.05:1 and fails AA, which block 8 of tokens.css states in so
   many words. .sg-state solved this the same way on the same band: give
   the error its own --surface ground and it reads 4.83. */
.ts-link[data-ts-error] {
  color: var(--ui-red);
  background: var(--surface);
  padding: 4px 8px;
  border-radius: 4px;
}
.ts-link[aria-busy="true"] { opacity: 0.6; }

/* The Top Shelf note on The Metrics Shelf. Deliberately not a .ms-entry:
   an entry is a metric with a verdict, and putting a product in that grid
   would say Top Shelf had been assessed by the same process, which is the
   one claim this page must never make loosely. It is an aside. */
.ms-topshelf {
  max-width: var(--wide-width);
  margin: var(--space-2xl) auto 0;
  padding: var(--space-lg) 0 0;
  border-top: 1px solid var(--rule);
}
.ms-topshelf p:not(.hm-kicker) {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  max-width: 68ch;
  margin-top: var(--space-2xs);
}
.ms-topshelf strong { color: var(--text); font-weight: 600; }
.ms-topshelf-go {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--interface);
  display: inline-block;
  margin-top: var(--space-sm);
}
/* The arrow is decoration, so it lives here rather than in the label. It was
   passed as label="... &rarr;" first, and {{label}} is an escaping stash, so
   it would have rendered as five literal characters on a live page. */
.ms-topshelf-go::after { content: " \2192"; }
.ms-topshelf-go:hover { color: var(--interface-hover); }

/* ---- SCROLL REVEALS. Finished state is the default, the entrance
   only exists where scroll-driven animation is supported. ---- */

@supports (animation-timeline: view()) {
  .hm-reveal {
    animation: hm-rise linear both;
    animation-timeline: view();
    animation-range: entry 5% entry 45%;
  }
}

@keyframes hm-rise {
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hm-reveal { animation: none; opacity: 1; transform: none; }
}

/* ---- responsive ---- */

@media (max-width: 760px) {
  .hm-hero {
    padding: 0 var(--space-md)
             max(4vh, calc(var(--space-md) + env(safe-area-inset-bottom)));
  }
  .hm-eyebrow { letter-spacing: 0.2em; }
  .hm-sub { margin-top: 2vh; }
  .hm-field { right: -30vw; opacity: 0.5; }
  .hm-statement, .hm-evidence, .hm-products { padding-left: var(--space-md); padding-right: var(--space-md); }
  .hm-act2-sticky { padding-left: var(--space-md); padding-right: var(--space-md); }
  .hm-row { flex-direction: column; align-items: flex-start; gap: var(--space-xs); }
}

/* ---- THE METRICS SHELF, filterable multi-domain index.
   Filtering is pure CSS. Without :has() every entry stays
   visible, which is the finished state. ---- */

.ms-page { padding-bottom: var(--space-2xl); }

.ms-header {
  padding: 16vh var(--space-lg) 6vh;
  max-width: var(--wide-width);
  margin: 0 auto;
}
.ms-title {
  font-family: var(--font-display);
  font-size: clamp(40px, 8vw, 120px);
  line-height: 0.98;
  text-transform: uppercase;
  color: var(--text-strong);
}
.ms-intent {
  max-width: 640px;
  margin-top: 3vh;
  font-size: var(--fs-base);
  color: var(--text-muted);
}
.ms-intent strong { color: var(--text); }

.ms-shelf {
  max-width: var(--wide-width);
  margin: 0 auto;
  padding: 0 var(--space-lg);
}

/* filter rails */
.ms-filters {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-md) 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--space-lg);
}
.ms-rail {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  flex-wrap: wrap;
}
.ms-rail-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-faint);
  min-width: 72px;
}
.ms-rail input { position: absolute; opacity: 0; pointer-events: none; }
.ms-rail label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  border: 1px solid var(--rule);
  padding: 6px 12px;
  border-radius: 2px;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}
.ms-rail label:hover { color: var(--interface-hover); border-color: var(--interface-quiet); }
.ms-rail input:checked + label {
  color: var(--interface-hover);
  border-color: var(--interface-quiet);
  background: var(--interface-wash);
}
/* 3px, not 2. The blue implementation spec sets a 3px minimum for focus
   indicators, and these were the only three focus rings in the theme. */
.ms-rail input:focus-visible + label { outline: 3px solid var(--interface); outline-offset: 2px; }

/* the index grid */
.ms-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: var(--space-xs);
}
.ms-entry {
  display: block;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: var(--space-sm) var(--space-sm) var(--space-xs);
  transition: border-color var(--dur-base) var(--ease-standard),
              background var(--dur-base) var(--ease-standard);
}
a.ms-entry:hover { border-color: var(--interface-quiet); background: var(--raised); }
a.ms-entry:hover h3 { color: var(--interface-hover); }
.ms-entry h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-base);
  color: var(--text-strong);
  line-height: 1.25;
  transition: color var(--dur-base) var(--ease-standard);
}
.ms-entry p {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 4px;
  min-height: 2.6em;
}
.ms-dom {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-faint);
  display: block;
  margin-bottom: 6px;
}
.ms-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: var(--space-2xs); }
.ms-chip {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 2px;
  border: 1px solid var(--rule);
  color: var(--text-muted);
}
.ms-chip.ms-ok { color: var(--interface-hover); border-color: var(--interface-quiet); background: var(--interface-wash); }
.ms-chip.ms-no { color: var(--text-faint); }
.ms-pending { opacity: 0.75; }

/* CSS-only filtering. One rule per option, both dimensions
   combine because each rule only hides what fails its own test. */
.ms-shelf:has(#d-hockey:checked)     .ms-entry:not([data-d="hockey"]),
.ms-shelf:has(#d-baseball:checked)   .ms-entry:not([data-d="baseball"]),
.ms-shelf:has(#d-football:checked)   .ms-entry:not([data-d="football"]),
.ms-shelf:has(#d-basketball:checked) .ms-entry:not([data-d="basketball"]),
.ms-shelf:has(#d-soccer:checked)     .ms-entry:not([data-d="soccer"]),
.ms-shelf:has(#d-tennis:checked)     .ms-entry:not([data-d="tennis"]),
.ms-shelf:has(#d-motorsport:checked) .ms-entry:not([data-d="motorsport"]),
.ms-shelf:has(#d-endurance:checked)  .ms-entry:not([data-d="endurance"]),
.ms-shelf:has(#d-biathlon:checked)   .ms-entry:not([data-d="biathlon"]),
.ms-shelf:has(#d-combine:checked)    .ms-entry:not([data-d="combine"]),
.ms-shelf:has(#q-reliability:checked) .ms-entry:not([data-q="reliability"]),
.ms-shelf:has(#q-selection:checked)   .ms-entry:not([data-q="selection"]),
.ms-shelf:has(#q-inference:checked)   .ms-entry:not([data-q="inference"]),
.ms-shelf:has(#q-physiology:checked)  .ms-entry:not([data-q="physiology"]),
.ms-shelf:has(#q-structure:checked)   .ms-entry:not([data-q="structure"]) {
  display: none;
}

/* the long-form assessments below the index */
.ms-full { margin-top: var(--space-2xl); }
.ms-full-head {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 var(--space-lg);
}
.ms-full .post-content {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 var(--space-lg);
}
/* anchor jumps land below the sticky header */
.ms-full .post-content h2,
.ms-full .post-content h3 { scroll-margin-top: 96px; }

@media (max-width: 760px) {
  .ms-header, .ms-shelf, .ms-full-head, .ms-full .post-content {
    padding-left: var(--space-md);
    padding-right: var(--space-md);
  }
}

/* ---- POST PAGE, the pp- layer. The reading column and every
   .post-content rule stay in screen.css, untouched. ---- */

.post-template .site-main {
  max-width: none;
  padding: 0;
}

.pp-header {
  background: var(--void);
  border-bottom: 1px solid var(--rule);
  padding: 14vh var(--space-lg) 7vh;
}
.pp-header-inner { max-width: var(--wide-width); margin: 0 auto; }
.pp-kicker {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--interface);
}
.pp-kicker:hover { color: var(--interface-hover); }
.pp-title {
  font-family: var(--font-display);
  font-size: clamp(34px, 6vw, 88px);
  line-height: 1.02;
  text-transform: uppercase;
  color: var(--text-strong);
  margin-top: var(--space-sm);
}
.pp-standfirst {
  max-width: var(--content-width);
  margin-top: var(--space-md);
  font-size: var(--fs-lg);
  line-height: 1.5;
  color: var(--text-muted);
}
.pp-meta {
  margin-top: var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-variant-numeric: var(--numerals);
}
.pp-meta a { color: var(--text); }
.pp-meta a:hover { color: var(--interface-hover); }
.pp-sep { color: var(--interface-quiet); margin: 0 10px; }

.pp-feature {
  max-width: 880px;
  margin: var(--space-xl) auto var(--space-lg);
  padding: 0 var(--space-lg);
}
.pp-frame {
  background: var(--fig-paper);
  border: 1px solid var(--data-line);
  border-radius: 3px;
  padding: 10px;
}
.pp-frame img { display: block; width: 100%; border-radius: 2px; }
.pp-caption {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  color: var(--text-faint);
  margin-top: var(--space-2xs);
  text-align: right;
}

/* body grid: content centered, TOC in the right rail on wide
   screens. Below 1280px the rail disappears and the column is
   exactly what ships today. */
/* The header block ends on a rule. Without this the first paragraph
   sits flush against it. */
.pp-body { position: relative; padding-top: var(--space-2xl); }
/* Page bodies align to the same grid as the bands around them. Ghost's
   .post-content centres itself, which left the About copy 258px right of
   the section headings above and below it. */
.pp-body { padding-left: var(--space-lg); padding-right: var(--space-lg); }
.pp-body .post-full {
  max-width: var(--wide-width);
  margin: 0 auto;
  padding: 0;
}
.pp-body .post-content {
  max-width: var(--content-width);
  margin-left: 0;
  margin-right: auto;
}
@media (max-width: 760px) {
  .pp-body { padding-left: var(--space-md); padding-right: var(--space-md); }
}
.pp-toc-rail { display: none; }

@media (min-width: 1280px) {
  .pp-toc-rail {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    right: max(var(--space-lg), calc(50% - 620px));
    width: 220px;
  }
  .pp-toc {
    position: sticky;
    top: 110px;
    border-left: 1px solid var(--rule);
    padding-left: var(--space-sm);
  }
  .pp-toc:not(:empty)::before {
    content: "Contents";
    display: block;
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--text-faint);
    margin-bottom: var(--space-xs);
  }
  .pp-toc a {
    display: block;
    font-size: var(--fs-xs);
    line-height: 1.4;
    color: var(--text-muted);
    padding: 5px 0;
    transition: color var(--dur-fast) var(--ease-standard);
  }
  .pp-toc a:hover { color: var(--interface-hover); }
  .pp-toc a.pp-on { color: var(--interface); }
}

/* anchor jumps clear the sticky site header */
.post-content h2, .post-content h3 { scroll-margin-top: 96px; }

/* standard Ghost image cards get the figure frame. HTML cards from
   published posts carry their own inline styles and are left alone. */
.post-content .kg-image-card .kg-image {
  background: var(--fig-paper);
  border: 1px solid var(--data-line);
  border-radius: 3px;
  padding: 8px;
}
.post-content .kg-card figcaption {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  color: var(--text-faint);
}

/* ---- ERROR PAGE ---- */

.err-page {
  min-height: 70vh;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  max-width: 880px;
  margin: 0 auto;
  padding: 0 var(--space-lg);
}
.err-code {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(96px, 20vw, 280px);
  line-height: 1;
  color: var(--data);
  text-shadow: 0 0 32px rgb(var(--data-blue-rgb) / 0.35);
  font-variant-numeric: var(--numerals);
}
.err-message {
  margin-top: var(--space-sm);
  font-size: var(--fs-lg);
  color: var(--text);
}
.err-note {
  margin-top: var(--space-2xs);
  margin-bottom: var(--space-lg);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  max-width: 480px;
}

@media (max-width: 760px) {
  .pp-header { padding: 12vh var(--space-md) 6vh; }
  .pp-feature { padding: 0 var(--space-md); }
}

/* ---- LIST PAGES: tag, author, and the paginated archive share
   one language with the home evidence rows. ---- */

.tag-template .site-main,
.author-template .site-main,
.paged .site-main {
  max-width: none;
  padding: 0;
}

.lp-header {
  background: var(--void);
  border-bottom: 1px solid var(--rule);
  padding: 14vh var(--space-lg) 7vh;
}
.lp-inner { max-width: var(--wide-width); margin: 0 auto; }
.lp-title {
  font-family: var(--font-display);
  font-size: clamp(36px, 7vw, 104px);
  line-height: 1;
  text-transform: uppercase;
  color: var(--text-strong);
  margin-top: var(--space-sm);
}
.lp-desc {
  max-width: 640px;
  margin-right: auto;
  margin-top: var(--space-md);
  font-size: var(--fs-lg);
  line-height: 1.5;
  color: var(--text-muted);
}
.lp-count {
  margin-top: var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  font-variant-numeric: var(--numerals);
}
.lp-link { color: var(--interface); }
.lp-link:hover { color: var(--interface-hover); }
.lp-author { display: flex; gap: var(--space-md); align-items: flex-start; }
.lp-avatar {
  width: 88px;
  height: 88px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  flex-shrink: 0;
}

.lp-feed {
  max-width: var(--wide-width);
  margin: 0 auto;
  padding: var(--space-2xl) var(--space-lg);
}

.lp-pagination {
  max-width: var(--wide-width);
  margin: 0 auto;
  padding: 0 var(--space-lg) var(--space-2xl);
  display: flex;
  align-items: center;
  gap: var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.lp-page-btn { color: var(--interface); }
.lp-page-btn:hover { color: var(--interface-hover); }
.lp-page-info { color: var(--text-faint); font-variant-numeric: var(--numerals); }

@media (max-width: 760px) {
  .lp-header { padding: 12vh var(--space-md) 6vh; }
  .lp-feed, .lp-pagination { padding-left: var(--space-md); padding-right: var(--space-md); }
}

/* ---- SEAMLESS PASS. The chrome the old theme still owned:
   nav, footer, subscribe band, plus cross-document transitions
   and the mobile hero. ---- */

/* cross-document view transitions: a soft crossfade between pages,
   the header holds still. Chrome and Edge 126+, Safari 26+.
   Browsers without support keep instant navigation. */
@view-transition { navigation: auto; }
.site-header { view-transition-name: btm-header; }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* nav speaks mono like every label on the site */
.site-nav a {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.site-nav a:hover { color: var(--interface-hover); }

/* Wide tables scroll inside their own box rather than taking the page
   with them. The table's own width rule is untouched on purpose: ten
   published posts render through .post-content and none of them should
   move. Built by home.js, so without JavaScript the result is exactly
   what shipped before. */
.pp-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  margin-bottom: 1.5rem;
}
.pp-scroll > table { margin-bottom: 0; }

/* ---- MOBILE NAV PANEL.

   Under 760px the nav leaves the header bar and becomes a panel that
   opens beneath it. The old header put the nav on a second full width
   row, where flex shrank each of the four items until the text wrapped
   inside it, so "The Metrics Shelf" broke over three ragged lines that
   aligned with nothing. Here every label gets one line at billboard
   scale, which is also what makes each one a 44px target.

   These rules must stay in this file. screen.css declares .site-nav a
   too, home.css loads after it, and a media query contributes no
   specificity, so the same rules in screen.css lost silently. ---- */

@media (max-width: 760px) {
  .site-nav {
    position: fixed;
    top: var(--hdr-h);
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 99;
    display: flex;
    flex-direction: column;
    padding: var(--space-lg) var(--space-md)
             calc(var(--space-lg) + env(safe-area-inset-bottom));
    background: var(--base);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Closed is the default state, so a browser that never runs the
       script gets a normal page rather than a panel over the top. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity var(--dur-base) var(--ease-standard),
                transform var(--dur-base) var(--ease-standard),
                visibility 0s linear var(--dur-base);
  }

  .site-nav.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity var(--dur-base) var(--ease-standard),
                transform var(--dur-base) var(--ease-standard),
                visibility 0s;
  }

  .site-nav ul {
    flex-direction: column;
    gap: 0;
    width: 100%;
  }

  .site-nav li { border-bottom: 1px solid var(--rule); }

  /* Scoped to the list. .site-nav also contains the language pair and
     Sign in, and an unscoped rule set those in Archivo Black too. */
  .site-nav ul a {
    display: block;
    padding: var(--space-sm) 0;
    font-family: var(--font-display);
    font-size: clamp(23px, 6.6vw, 34px);
    line-height: 1.25;
    letter-spacing: -0.01em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--text-strong);
  }

  .site-nav ul a:hover,
  .site-nav .nav-current a { color: var(--interface); }
}

@media (prefers-reduced-motion: reduce) {
  /* The panel still opens and closes, it just stops sliding.
     visibility keeps its zero duration either way. */
  .site-nav,
  .site-nav.is-open {
    transform: none;
    transition: none;
  }
}

/* ticker items are links now */
.hm-track a { color: var(--text-muted); transition: color var(--dur-fast) var(--ease-standard); }
.hm-track a:hover { color: var(--interface-hover); }
.hm-track a b {
  color: var(--data);
  font-weight: 700;
  text-shadow: 0 0 22px rgb(var(--data-blue-rgb) / 0.4);
}

/* subscribe band in the house language */
.subscribe-cta {
  background: var(--void);
  border-top: 1px solid var(--rule);
  padding: 12vh var(--space-lg);
}
.subscribe-cta-inner { max-width: var(--wide-width); margin: 0 auto; text-align: left; }
.subscribe-cta-title {
  font-family: var(--font-display);
  font-size: clamp(30px, 5vw, 64px);
  line-height: 1;
  text-transform: uppercase;
  color: var(--text-strong);
}
.subscribe-cta-desc {
  max-width: 560px;
  margin-top: var(--space-sm);
  font-size: var(--fs-base);
  color: var(--text-muted);
}
.subscribe-cta-btn {
  display: inline-block;
  margin-top: var(--space-md);
  background: var(--interface);
  color: var(--on-interface);
  font-weight: 600;
  font-size: var(--fs-xs);
  padding: 14px 28px;
  border-radius: 2px;
  transition: background var(--dur-base) var(--ease-standard);
}
.subscribe-cta-btn:hover { background: var(--interface-hover); }
.subscribe-cta-note {
  margin-top: var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ---- THEME NATIVE SIGNUP FORM ----------------------------------------
   Replaces the link to Ghost Portal that used to sit in this band. Portal
   renders in an iframe the theme cannot style, so the first screen a new
   member saw was Ghost's, on a pricing table. This is the same moment in
   our own type on our own tokens.

   Ghost's Portal script still drives the submit; it binds to the form's
   data attributes. What it does NOT do is supply any styling, so every
   state below is ours. Portal toggles `loading`, `success` and `error` on
   the FORM element and the descendant selectors here do the rest.
   See partials/subscribe-cta.hbs for the verified contract. ---- */
.sg { margin-top: var(--space-md); }

.sg-label {
  display: block;
  margin-bottom: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Wraps rather than shrinks. A button whose label is squeezed to two
   characters on a phone is worse than a button on its own line. */
.sg-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  max-width: 560px;
}

.sg-input {
  flex: 1 1 260px;
  min-width: 0;          /* without this the flex item refuses to shrink */
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  padding: 14px 16px;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  color: var(--text);
  transition: border-color var(--dur-base) var(--ease-standard);
}
.sg-input::placeholder { color: var(--text-faint); }
.sg-input:hover { border-color: var(--interface-line); }
.sg-input:focus-visible,
.sg-btn:focus-visible {
  outline: 3px solid var(--interface);
  outline-offset: 2px;
}

/* Same shape as .subscribe-cta-btn, which this replaced. */
.sg-btn {
  flex: 0 0 auto;
  border: 0;
  cursor: pointer;
  background: var(--interface);
  color: var(--on-interface);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-xs);
  padding: 14px 28px;
  border-radius: 2px;
  transition: background var(--dur-base) var(--ease-standard);
}
.sg-btn:hover { background: var(--interface-hover); }
/* Once the row has wrapped, a button sized to its label sits as a short stub
   under a full width field. It takes the whole line instead. */
@media (max-width: 600px) { .sg-btn { flex: 1 1 100%; } }
.sg .sg-btn[disabled],
.sg.loading .sg-btn { cursor: progress; }

/* Every state is hidden until Portal puts the matching class on the form.

   ON --surface, NOT ON THE BAND, AND THAT IS THE ACCESSIBLE PART.
   This band grounds on --void, which is a step darker than the --base the
   verdict colours were contrast checked against in tokens.css. Measured
   here: --ui-red on the band is 4.05:1 in the light theme, under the 4.5
   that normal size text needs. On --surface it is the 4.83 the token
   documents. So the message carries its own ground rather than the
   palette being forked for one band. */
.sg-state {
  display: none;
  max-width: 560px;
  margin-top: var(--space-xs);
  padding: 10px 14px;
  border-left: 2px solid currentColor;
  border-radius: 2px;
  background: var(--surface);
  font-size: var(--fs-sm);
}
.sg.loading  .sg-loading { display: block; color: var(--text-muted); }
.sg.success  .sg-success { display: block; color: var(--ui-green); }
.sg.error    .sg-error   { display: block; color: var(--ui-red); }

/* Portal's own catch branch adds `error` without clearing `loading`, so
   both messages would otherwise show at once. The error is the true one. */
.sg.loading.error .sg-loading { display: none; }

/* Once the link is sent, asking for the address again invites a second
   submit that does nothing useful. */
.sg.success .sg-field { display: none; }

/* footer in the house language */
.site-footer {
  background: var(--void);
  border-top: 1px solid var(--rule);
}
.footer-inner { max-width: var(--wide-width); margin: 0 auto; padding: var(--space-xl) var(--space-lg); }
.footer-mission {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--text-muted);
  max-width: 44ch;
}
.footer-cta {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.footer-bottom {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  color: var(--text-faint);
}

/* the mobile hero keeps its signature: the field sits centred
   behind the number instead of a sliver off the right edge */
@media (max-width: 760px) {
  .hm-field {
    right: 50%;
    transform: translate(50%, -58%);
    width: 130vw;
    height: 130vw;
    opacity: 0.4;
  }
}

/* ---- HEADER CONTROLS: language pair and theme switch ---- */

.header-right { display: flex; align-items: center; gap: var(--space-xs); flex: 0 0 auto; }

.hdr-lang {
  display: flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
}
.hdr-lang-opt { color: var(--text-faint); transition: color var(--dur-fast) var(--ease-standard); }
.hdr-lang-opt:hover { color: var(--interface-hover); }
.hdr-lang-opt.is-active { color: var(--interface); }
/* --text-faint, not --rule-strong. This is the "/" between EN and FR and it is
   TEXT, not a hairline: --rule-strong is white at 16% on dark and ink at 22% on
   light, which measured 1.55:1 and 1.63:1 and was the last failing cell on the
   home page in either theme. --text-faint is the quietest step that is actually
   a text colour, 4.66 dark and 4.95 light, and it still reads as a separator
   rather than as a third link. */
.hdr-lang-sep { color: var(--text-faint); }

.hdr-theme {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  color: var(--text-muted);
  background: none;
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 6px 10px;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.hdr-theme:hover { color: var(--interface-hover); border-color: var(--interface-quiet); }
.hdr-theme-ico { display: block; flex: 0 0 auto; }

@media (max-width: 760px) {
  /* The language pair moves into the nav panel, where there is room
     for it. The theme switch STAYS in the bar and drops to the icon
     alone: a display preference hidden behind a menu is a preference
     nobody finds. Both used to be display:none here, which is why the
     phone had neither. */
  .header-right > .hdr-lang { display: none; }

  .header-right { gap: var(--space-2xs); }

  .hdr-theme {
    padding: 0;
    width: 36px;
    height: 36px;
    flex: 0 0 auto;
    justify-content: center;
    border-color: transparent;
  }
  .hdr-theme-txt {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .hdr-theme-ico { width: 17px; height: 17px; }
}

/* ---- inside the mobile nav panel: language, sign in.
   Rendered a second time in the markup rather than moved by script,
   so it is correct before any JavaScript runs. Hidden at every width
   where the panel itself does not exist. ---- */

.nav-extra { display: none; }

@media (max-width: 760px) {
  .nav-extra {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    margin-top: auto;
    padding-top: var(--space-lg);
  }

  .nav-extra .hdr-lang { font-size: 13px; letter-spacing: 0.18em; }
  .nav-extra .hdr-lang-opt { padding: 10px 4px; }

  .nav-extra-link {
    font-family: var(--font-mono);
    font-size: 13px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 10px 0;
  }
  .nav-extra-link:hover { color: var(--interface-hover); }
}

/* ============================================================
   WORK WITH US, the commercial page.

   The figure is the offer made visible: one measured difference,
   two error bars, two verdicts. Pure CSS, two radio inputs and
   sibling selectors, the same mechanism as the Metrics Shelf
   filter. Both states exist in the markup and the selecting state
   is the CSS default, so a browser that supports nothing still
   renders a complete, readable figure.

   Every label is HTML rather than SVG text. The SVG scales with
   its container, so SVG text would land near 6px on a phone.
   ============================================================ */

.ww-band {
  padding: 12vh var(--space-lg);
  border-top: 1px solid var(--rule);
}
.ww-inner { max-width: var(--wide-width); margin: 0 auto; }

.ww-h2 {
  font-family: var(--font-display);
  font-size: clamp(28px, 5vw, 62px);
  line-height: 1.02;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--text-strong);
  margin-top: var(--space-sm);
}

.ww-lede {
  max-width: var(--content-width);
  margin-top: var(--space-md);
  font-size: var(--fs-lg);
  line-height: 1.5;
  color: var(--text-muted);
}

.ww-close {
  max-width: var(--content-width);
  margin-top: var(--space-lg);
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--text);
}

/* ---- the figure ---- */

.ww-fig {
  position: relative;
  margin-top: var(--space-xl);
  padding: var(--space-lg);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 3px;
}

/* Off screen rather than display:none, so the radios stay focusable
   and the control is usable from a keyboard. */
.ww-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.ww-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.ww-tab {
  flex: 1 1 260px;
  min-width: 0;
  cursor: pointer;
  padding: var(--space-sm);
  border: 1px solid var(--rule);
  border-radius: 2px;
  transition: border-color var(--dur-base) var(--ease-standard),
              background var(--dur-base) var(--ease-standard);
}
.ww-tab:hover { border-color: var(--interface-quiet); }

.ww-tab-k {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-faint);
  transition: color var(--dur-base) var(--ease-standard);
}
.ww-tab-q {
  display: block;
  margin-top: var(--space-3xs);
  font-size: var(--fs-sm);
  line-height: 1.4;
  color: var(--text-muted);
}

#ww-mode-select:checked ~ .ww-tabs .ww-tab[for="ww-mode-select"],
#ww-mode-track:checked ~ .ww-tabs .ww-tab[for="ww-mode-track"] {
  border-color: var(--interface);
  background: var(--interface-wash);
}
#ww-mode-select:checked ~ .ww-tabs .ww-tab[for="ww-mode-select"] .ww-tab-k,
#ww-mode-track:checked ~ .ww-tabs .ww-tab[for="ww-mode-track"] .ww-tab-k {
  color: var(--interface);
}
#ww-mode-select:checked ~ .ww-tabs .ww-tab[for="ww-mode-select"] .ww-tab-q,
#ww-mode-track:checked ~ .ww-tabs .ww-tab[for="ww-mode-track"] .ww-tab-q {
  color: var(--text);
}

.ww-radio:focus-visible ~ .ww-tabs .ww-tab[for="ww-mode-select"],
.ww-radio:focus-visible ~ .ww-tabs .ww-tab[for="ww-mode-track"] {
  outline: 3px solid var(--interface);
  outline-offset: 2px;
}

/* ---- plot ---- */

.ww-plotwrap { position: relative; margin-top: var(--space-lg); }

.ww-mark-lab {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  display: inline-block;
  white-space: nowrap;
  text-align: center;
  margin-bottom: var(--space-3xs);
}
.ww-fig-num {
  display: block;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--fs-lg);
  color: var(--data);
  font-variant-numeric: var(--numerals);
  text-shadow: 0 0 20px rgb(var(--data-blue-rgb) / 0.35);
}
.ww-fig-lab {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* preserveAspectRatio is none, so the box sets the height and the
   graphic stretches to it. Nothing in it is a shape that a stretch
   would distort. */
.ww-plot {
  display: block;
  width: 100%;
  height: 78px;
}

.ww-noise { fill: rgb(var(--fg-rgb) / 0.10); }
.ww-edge {
  stroke: var(--text-faint);
  stroke-width: 2;
  stroke-dasharray: 4 4;
  vector-effect: non-scaling-stroke;
}
.ww-axis {
  stroke: var(--rule-strong);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.ww-mark {
  stroke: var(--data);
  stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
}

.ww-scale {
  display: flex;
  justify-content: space-between;
  margin-top: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  color: var(--text-faint);
  font-variant-numeric: var(--numerals);
}

.ww-bandnote {
  margin-top: var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--text-muted);
}

.ww-verdict {
  margin-top: var(--space-md);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--text);
}

.ww-flag {
  display: inline-block;
  margin-right: var(--space-2xs);
  padding: 4px 10px;
  border: 1px solid currentColor;
  border-radius: 2px;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  vertical-align: 2px;
}
.ww-flag-no { color: var(--ui-red); }
.ww-flag-yes { color: var(--ui-green); }

.ww-fig-cap {
  margin-top: var(--space-lg);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}

/* ---- state switching. Selecting is the default, so the figure is
   complete before any interaction and without :checked support. ---- */

.ww-noise-track,
.ww-edge-track,
.ww-bandnote-track,
.ww-verdict-track { display: none; }

#ww-mode-track:checked ~ .ww-plotwrap .ww-noise-select,
#ww-mode-track:checked ~ .ww-plotwrap .ww-edge-select,
#ww-mode-track:checked ~ .ww-plotwrap .ww-bandnote-select,
#ww-mode-track:checked ~ .ww-verdict-select { display: none; }

#ww-mode-track:checked ~ .ww-plotwrap .ww-noise-track,
#ww-mode-track:checked ~ .ww-plotwrap .ww-edge-track { display: inline; }
#ww-mode-track:checked ~ .ww-plotwrap .ww-bandnote-track,
#ww-mode-track:checked ~ .ww-verdict-track { display: block; }

/* ---- closing call to action ---- */

.ww-cta .ww-lede { margin-bottom: var(--space-lg); }
.ww-btn {
  display: inline-block;
  background: var(--interface);
  color: var(--on-interface);
  font-weight: 600;
  font-size: var(--fs-xs);
  padding: 14px 28px;
  border-radius: 2px;
  transition: background var(--dur-base) var(--ease-standard);
}
.ww-btn:hover { background: var(--interface-hover); color: var(--on-interface); }

@media (max-width: 760px) {
  .ww-band { padding: 10vh var(--space-md); }
  .ww-fig { padding: var(--space-md); }
  .ww-tab { flex: 1 1 100%; }
}

/* ---- end of article: the same question, on your data.
   The only internal link from an article body to the commercial page.
   Quiet on purpose: it sits after the author card and before the
   subscribe band, and it must not read as an ad inside the research. ---- */

.pp-offer {
  border-top: 1px solid var(--rule);
  background: var(--surface);
}
.pp-offer-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: var(--space-xl) var(--space-lg);
}
.pp-offer-line {
  max-width: 60ch;
  margin-top: var(--space-xs);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--text-muted);
}
.pp-offer-link {
  display: inline-block;
  margin-top: var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--interface);
}
.pp-offer-link:hover { color: var(--interface-hover); }

@media (max-width: 760px) {
  .pp-offer-inner { padding: var(--space-lg) var(--space-md); }
}

/* ---- FORCED COLOURS, Windows High Contrast and similar.

   Both custom controls in this theme hide the real radio input and signal
   their state with colour, border-color and background-color. A forced
   colours mode overrides all three, so without this block every Metrics
   Shelf filter chip and both Work With Us tabs render identically with no
   visible selection at all, on the primary control of each page.

   outline survives forced colours where border and background do not, and
   the system keywords resolve to whatever the user actually chose. ---- */

@media (forced-colors: active) {
  .ms-rail input:checked + label,
  #ww-mode-select:checked ~ .ww-tabs .ww-tab[for="ww-mode-select"],
  #ww-mode-track:checked ~ .ww-tabs .ww-tab[for="ww-mode-track"] {
    outline: 3px solid SelectedItem;
    outline-offset: -3px;
  }

  .ms-rail input:focus-visible + label,
  .ww-radio:focus-visible ~ .ww-tabs .ww-tab[for="ww-mode-select"],
  .ww-radio:focus-visible ~ .ww-tabs .ww-tab[for="ww-mode-track"] {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }

  /* The noise band and the observed marker carry the whole argument of the
     figure and are fills, which forced colours flattens. Give them an edge. */
  .ww-noise { forced-color-adjust: none; }
  .ww-mark, .ww-edge { forced-color-adjust: none; }
}

/* ---- LIGHT THEME ADJUSTMENTS. Anything that assumes a dark
   ground: glows, grain, and the hero grid. Figures never invert. ---- */

[data-theme="light"] body::before { opacity: 0.03; }

[data-theme="light"] .hm-unit,
[data-theme="light"] .hm-num,
[data-theme="light"] .hm-panel .hm-k,
[data-theme="light"] .hm-track a b,
[data-theme="light"] .err-code { text-shadow: none; }

/* The dark plate that used to sit behind the logo in light mode is gone.
   It existed because the uploaded PNG was white type on transparent and
   vanished on paper. The theme now ships a primary lockup drawn in ink for
   exactly this ground, so the plate would only have shown as a black box. */

[data-theme="light"] .hm-field { filter: none; opacity: 0.75; }
[data-theme="light"] .hm-hero::before { opacity: 0.5; }

/* the figure frame keeps its dark paper in both themes, because the
   locked palette is exported against #090c11 */
[data-theme="light"] .pp-frame,
[data-theme="light"] .preview .hm-frame,
[data-theme="light"] .post-content .kg-image-card .kg-image {
  background: var(--fig-paper);
  border-color: rgb(var(--data-blue-rgb) / 0.35);
}

/* buttons keep a light-on-dark label in light mode */
[data-theme="light"] .hm-btn-primary,
[data-theme="light"] .sg-btn,
/* Removed: every filled button reads --on-interface now, which flips
   with the theme, so this one-off override is redundant. It was also
   the only button that had it, which is why the others failed. */

/* ---- ABOUT PAGE BANDS ---- */

.ab-band { padding: 12vh var(--space-lg); border-top: 1px solid var(--rule); }
.ab-band-void { background: var(--void); }
.ab-inner { max-width: var(--wide-width); margin: 0 auto; }
.ab-grid {
  display: grid;
  /* min() is load bearing. A bare minmax(260px, 1fr) track cannot shrink
     below 260px, so on a 320px phone the grid pushed the whole page
     sideways. With min() the track gives way instead. */
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: var(--space-lg);
  margin-top: var(--space-md);
}
.ab-grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.ab-item h3, .ab-person h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-lg);
  color: var(--text-strong);
}
.ab-item p, .ab-person p { margin-top: var(--space-2xs); font-size: var(--fs-sm); color: var(--text-muted); }
.ab-role {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--interface);
}
.ab-links { display: flex; gap: var(--space-sm); margin-top: var(--space-xs); }
.ab-links a {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--interface);
}
.ab-links a:hover { color: var(--interface-hover); }

.page-template .site-main { max-width: none; padding: 0; }

@media (max-width: 760px) {
  .ab-band { padding: 10vh var(--space-md); }
}

/* French surface note: the articles are English, said plainly */
.fr-note {
  max-width: 620px;
  margin: calc(-1 * var(--space-xs)) 0 var(--space-lg);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  color: var(--text-faint);
  border-left: 2px solid var(--interface-quiet);
  padding-left: var(--space-xs);
}

/* quiet sign-in beside the loud subscribe */
.hdr-signin {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-standard);
}
.hdr-signin:hover { color: var(--interface-hover); }
@media (max-width: 520px) { .hdr-signin { display: none; } }
