/*
 * trustedpartnernetwork.org - shared stylesheet.
 *
 * Organized as: reset, layout primitives, typography, shell (header/footer),
 * then reusable components. Page-specific rules live in the page's own
 * stylesheet under /css/, not here.
 *
 * Breakpoints, from the design handoff:
 *   1080px  nav collapses to hamburger
 *   1100px  3 -> 2 testimonial cards
 *   1000px  two-column sections stack
 *    960px  sidebar stacks
 *    900px  two-column grids -> one
 *    760px  2 -> 1 testimonial card
 *    640px  page padding 32px -> 20px, display type steps down
 *    560px  compact header, footer -> one column
 *    420px  assessor cards stack
 */

/* ------------------------------------------------------------------ reset */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Same-page anchors glide rather than jump. An instant jump gives no sense of
   which way the page moved or how far, so a reader following a menu item into a
   section on the page they are already on has to reorient from scratch.
   Overridden for prefers-reduced-motion further down, where the animation
   defences live. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font: 400 16px/1.6 var(--font-ui);
  color: var(--text-body);
  background: #fff;
}

img,
video {
  max-width: 100%;
}

/* Every <img> carries width/height attributes so the browser can reserve the
 * right box before the file loads. Without this rule the height attribute is
 * also used as a presentational height, so any image whose CSS sets only a
 * width renders stretched to the file's full pixel height. Any class rule that
 * sets a real height still wins on specificity. */
img {
  height: auto;
}

h1,
h2,
h3,
h4,
p,
figure,
blockquote {
  margin: 0;
}

ul,
ol {
  margin: 0;
  padding: 0;
}

a {
  color: var(--text-link);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

button {
  font: inherit;
  color: inherit;
}

input,
select,
textarea {
  font: inherit;
}

/* A visible focus ring on every surface. Selects override it: the double ring
 * reads as a heavy halo on a dropdown. */
:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 2px #fff,
    0 0 0 5px var(--primary);
  border-radius: 2px;
}

/* Form fields get a blue edge, not the halo.
 *
 * The shared ring is drawn 5px outside the box, so on a control that already
 * has a border it reads as the field abruptly growing a thick surround. Focus
 * recolors the edge the field already has instead. outline-offset is negative,
 * which draws the outline inside the box, so nothing shifts and nothing appears
 * to widen.
 *
 * This is the standard treatment for every form control on the site. New
 * fields inherit it; do not add a per-field focus style.
 *
 * Checkboxes, radios and button-type inputs are excluded: they are not edged
 * boxes, so an inset outline has nothing to sit on and the shared ring is
 * right for them. There are none on the site today.
 *
 * --primary on white measures 4.6:1, above the 3:1 WCAG 2.2 asks of a focus
 * indicator, and 2px clears its minimum thickness. */
input:not([type='checkbox'], [type='radio'], [type='submit'], [type='button']):focus-visible,
select:focus-visible,
textarea:focus-visible,
.field:focus-visible {
  box-shadow: none;
  border-color: var(--primary);
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The GTM <noscript> frame. Google's snippet carries this inline; it is a
   class here so the markup stays free of style attributes. It only ever
   renders when scripting is off, but the stylesheet still applies then. */
.gtm-noscript {
  display: none;
  visibility: hidden;
}

/* Skip link - first focusable element on every page. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 8px;
  z-index: 100;
  padding: 10px 16px;
  background: var(--primary);
  color: #fff;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
}

.skip-link:focus {
  top: 8px;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Material Icons are a ligature font: the element literally contains the text
 * "arrow_forward" until the font loads and folds it into one glyph. In a
 * fallback font that text measures ~100px against the glyph's 16px, so a
 * button holding one visibly jumps width on first paint.
 *
 * Two defences. The font is requested with `display=block` so the text is
 * never painted as words, and the box is pinned to exactly one glyph so the
 * unfolded ligature cannot affect layout at all. */
.material-icons,
.material-icons-outlined {
  display: inline-block;
  width: 1em;
  overflow: hidden;
  font-size: 24px;
  line-height: 1;
  vertical-align: middle;
  user-select: none;
}

/* Icon sizes.
 *
 * The base .material-icons box is 24px. Every other size on this site is one
 * of these five, so they are modifiers rather than 780-odd inline style
 * attributes repeating the same handful of values. --chevron is the drawer and
 * nav chevron, which is always 20px and always muted; it is one modifier
 * because the two always travel together. */
.material-icons--xs,
.material-icons-outlined--xs {
  font-size: 16px;
}

.material-icons--sm,
.material-icons-outlined--sm {
  font-size: 18px;
}

.material-icons--md,
.material-icons-outlined--md {
  font-size: 20px;
}

.material-icons--lg,
.material-icons-outlined--lg {
  font-size: 28px;
}

.material-icons--chevron {
  font-size: 20px;
  color: var(--grey-500);
}

/* The LinkedIn mark.
 *
 * Not a Material Icons ligature: that font carries no brand glyphs, which is
 * why this was the generic three-people `groups` symbol before. Inline SVG
 * taking currentColor, so the same markup draws navy on the header's light
 * bar and white on the footer's dark one.
 *
 * Sized in px rather than em because it sits beside 13px and 14px text in
 * three different places and should be the same size in all of them. */
.icon-li {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  fill: currentColor;
  vertical-align: middle;
}

.icon-li--sm {
  width: 18px;
  height: 18px;
}

/* ---------------------------------------------------------------- layout */

.wrap {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 32px;
}

.measure {
  max-width: var(--measure);
  margin: 0 auto;
}

/*
 * The section band. The ONLY thing on this site that sets vertical section
 * padding - page stylesheets must not, and check 14 fails the build if one
 * does.
 *
 * ONE value, and one narrow exception. Not a menu.
 *
 * The first version of this offered all five steps of the style guide's
 * spacing scale, which fixed the numbers without fixing anything else: the
 * site ran 17 sections at 44px, 38 at 64px and 19 at 80px with nothing
 * deciding which a section got. Tidier values, same arbitrariness, and every
 * page still read differently from the one before it.
 *
 * Every option here is a decision someone has to make correctly on every new
 * section, forever. That is the mechanism that produced the drift the first
 * time, when .section offered two of five steps and the other three got
 * written into page stylesheets instead. So there is a default and there is a
 * strip, and nothing else exists to be chosen wrongly.
 */
.section {
  padding: 64px 0;
}

/*
 * The one exception: a strip carrying controls or figures and no prose. A
 * filter bar or a stats row does not need a content band's air around it.
 */
.section--tight {
  padding: 24px 0;
}

/*
 * Narrow widths take one step down, so a band that is right on a desktop is
 * not wasteful on a phone. Set here rather than on the pages: every band used
 * to carry its own 640px override and they disagreed with each other.
 */
@media (max-width: 640px) {
  .section {
    padding: 44px 0;
  }
}

/*
 * The section header block: eyebrow, then heading, then intro.
 *
 * docs/style-guide.md fixes one stack for every section on this site - eyebrow,
 * heading, intro, then content - with 20px inside the header block and 24px
 * before the content, in four arrangements depending on which parts are
 * present.
 *
 * Nothing here expressed that, so 44 distinct section shapes grew instead, and
 * on more than one page two of them sit in the same file. Some sections wrap
 * the heading in a page-specific class, some in the generic .stack utility,
 * some in nothing at all. tools/audit-sections.mjs prints the spread.
 *
 * Intro copy is capped at the reading measure; a heading balances its own.
 */
.section__head {
  display: grid;
  gap: 20px;
  margin-bottom: 24px;
}

/*
 * Section sub-text fills its column. It is not capped at a reading measure,
 * and this is the rule that decides it for the whole site.
 *
 * It used to be capped at --measure. The heading and its rule run the full
 * width of the band, so a paragraph stopping 350px short of them reads as a
 * broken line rather than a considered measure: the last line ends up two
 * words long with a third of the column empty beside it. That got reported
 * four separate times, and each time it was patched on the one element in the
 * screenshot - .pull here, .body-lg there, .gs-phase__note in another
 * stylesheet - which fixed that instance and left the rest.
 *
 * The trade-off is real and worth stating: at 1176px these lines run to about
 * 140 characters, past the 45-75 that is comfortable and past the 80-100
 * usually treated as the ceiling. Section-head copy is one or two sentences,
 * so it is a subtitle being scanned rather than a passage being read, and
 * alignment with the heading matters more there than the measure does. Do not
 * copy this reasoning to body prose: that is what --measure is for.
 */

.section--grey {
  background: var(--grey-100);
}

.section--navy {
  background: var(--navy-800);
  color: rgba(255, 255, 255, 0.82);
}

/* TPN blue at full strength.
   Text is pure white here, not the 82% .section--navy can afford. White on
   --primary measures 4.60:1, which clears AA with nothing to spare, and 82%
   white drops it to 3.50:1. Every rule below exists because this band is much
   lighter than the navy one and things that read on navy stop reading on it. */
.section--blue {
  background: var(--primary);
  color: #fff;
}

.section--rule {
  border-bottom: 1px solid var(--border-subtle);
}

.stack {
  display: grid;
  align-content: start;
  gap: 20px;
}

.stack--tight {
  gap: 12px;
}

.stack--loose {
  gap: 24px;
}

@media (max-width: 640px) {
  .wrap {
    padding: 0 20px;
  }
}

/* ------------------------------------------------------------ typography */

.h1,
.h2,
.h3,
.eyebrow {
  font-family: var(--font-display);
  color: var(--text-heading);
}

.h1 {
  font-size: 56px;
  font-weight: 300;
  line-height: 1.05;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.h1--hero {
  font-size: 64px;
}

.h1--article {
  font-size: 42px;
  line-height: 1.14;
}

.h2 {
  font-size: 38px;
  font-weight: 300;
  letter-spacing: -0.01em;
  line-height: 1.15;
}

/*
 * A short rule under every section heading. 100x5.
 *
 * On the heading rather than in the markup, so it is one rule instead of 29
 * spans that can be forgotten on the thirtieth. It follows the text's
 * alignment where a heading is centred, because margin-inline:auto is set with
 * the centring rather than assumed here.
 */
.h2::after {
  content: '';
  display: block;
  width: 72px;
  height: 3px;
  margin-top: 16px;
  background: var(--primary);
}

/* On a dark or saturated band the blue rule is either invisible or the band's
   own color. White is the only value that reads on all of them. */
.on-dark .h2::after,
.section--blue .h2::after {
  background: #fff;
}

/* The opt-out. The rule marks the top of a block of content; a heading that
   sits inline beside its own button is a line of UI, not the head of anything,
   and the rule reads as a stray mark under it. */
.h2--norule::after {
  display: none;
}

.h3 {
  font-size: 19px;
  font-weight: 600;
  line-height: 1.3;
}

.eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.eyebrow--gold {
  color: var(--gold-500);
}

/* The one eyebrow that sits above a bled image rather than above type. The
   grid gap below it is the same 24px as everywhere else, but the Fix It mark
   has no leading of its own - its yellow plaque runs to the top edge of the
   PNG - so that 24px reads tighter than the same gap does above the gold
   tagline further down. The 20px puts the visible space back. */
.eyebrow--fixit {
  color: var(--fixit-yellow);
  padding-bottom: 20px;
}

/* An eyebrow that leads with an icon sets itself out as a row; the icon is
   always this size, so neither belongs in a style attribute on each page. */
.eyebrow--icon {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.eyebrow > .material-icons {
  font-size: 17px;
}

.lede {
  font-size: 19px;
  font-weight: 300;
  line-height: 1.65;
}

/* The gold line that closes a hero.
 *
 * One treatment and one position, because it had neither: on /getting-started/
 * it was a 17px sentence-case note, on /our-mission/ a 12px uppercase eyebrow, and
 * 17px put it below the 19px lede it was meant to land after. It is the last
 * thing in the hero now, after the title, the lede and any buttons, so it reads
 * as the closing thought rather than as a label for whatever follows.
 *
 * Distinct from .eyebrow--gold, which is the small uppercase category label
 * above an h1. Same color, different job. */
.hero-tagline {
  margin-top: 22px;
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 300;
  line-height: 1.3;
  color: var(--gold-500);
  text-wrap: balance;
}

@media (max-width: 640px) {
  .hero-tagline {
    margin-top: 18px;
    font-size: 21px;
  }
}

.body-lg {
  font-size: 17px;
  line-height: 1.7;
}

.body {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-secondary);
}

/* A small standalone link, e.g. the "All News" link beside a section heading. */
.link-sm {
  font-size: 14px;
  font-weight: 500;
}

.meta {
  font-size: 13px;
  color: var(--text-muted);
}

/* Must stay below .meta: same specificity, so source order decides. Sitting
   above it, this whole rule lost - the footer address block rendered
   --text-muted grey at 3.15:1 on the navy, failing AA, and the `color: inherit`
   that used to be here never applied at all.

   Full white rather than the footer's own 75%: the block sits directly under
   the logo as part of the brand lockup, and anything dimmer reads as disabled
   next to it. Looser leading because it is three short lines. */
.meta--footer {
  color: #fff;
  line-height: 1.7;
}

.on-dark,
.on-dark .h1,
.on-dark .h2,
.on-dark .h3 {
  color: #fff;
}

.on-dark .lede,
.on-dark .body-lg,
.on-dark .body {
  color: rgba(255, 255, 255, 0.86);
}

/* --------------------------------------------------- the blue band's ink */

/* These live here, after .on-dark, because they undo it and carry the same
   specificity. Moving them above it silently gives the band 86% white copy at
   3.84:1, which is the kind of regression nobody sees.

   Gold measures 1.96:1 on TPN blue against 6.90:1 on the navy, so the eyebrow
   that reads as an accent on one band is close to invisible on the other. */
.section--blue .lede,
.section--blue .body-lg,
.section--blue .body {
  color: #fff;
}

.section--blue .eyebrow,
.section--blue .eyebrow--gold {
  color: #fff;
}

/* Inline links. --text-link is #1565c0 against a #1976d2 band, 1.25:1, which is
   a link that is present in the DOM and absent on the screen. White carries the
   band's own 4.60:1, and the underline does the work the color used to, since
   there is no second color available here that clears AA. */
.section--blue a:not(.btn) {
  color: #fff;
  text-decoration: underline;
}

/* A --primary button on a --primary band is a button you cannot see. Inverted
   rather than left to each page, because this is never right anywhere. */
.section--blue .btn--primary {
  background: #fff;
  border-color: #fff;
  color: var(--primary);
}

.section--blue .btn--primary:hover {
  background: var(--grey-200);
  border-color: var(--grey-200);
  color: var(--primary-hover);
}

@media (max-width: 640px) {
  .h1 {
    font-size: 38px;
  }

  .h1--hero {
    font-size: 40px;
  }

  .h1--article {
    font-size: 30px;
  }

  .h2 {
    font-size: 28px;
  }

  .lede {
    font-size: 17px;
  }
}

/* -------------------------------------------------------------- buttons */

/* Both buttons in a pair share this geometry so they render the same height. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 13px 24px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  transition:
    background var(--dur-micro) ease,
    border-color var(--dur-micro) ease,
    color var(--dur-micro) ease;
}

.btn:hover {
  text-decoration: none;
}

.btn--primary {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

.btn--primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  color: #fff;
}

.btn--outline {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--navy-800);
}

/* --grey-100 is the same value as most of the surfaces these buttons sit on, so
   the old hover was a 3-point RGB shift on a tinted card and 26 on white:
   present in the stylesheet, invisible on screen. A darker fill plus a navy
   border gives two signals instead of half of one. */
.btn--outline:hover {
  background: var(--grey-200);
  border-color: var(--navy-800);
}

/* The campaign's logo standing in for the slide's heading, since it carries
   the name itself.
   This is the stacked mark with white ink, which the campaign ships for dark
   backgrounds - the "or RISK IT" line is white on transparent, so it reads on
   the hero navy with nothing behind it. The inline mark used elsewhere on the
   site is black ink and needs a light surface; on navy only its yellow
   "FIX IT" block would show. The suffix is the INK, not the background. */
.hero__fixit {
  /* justify-self, not display:inline-block. .hero__col is a grid, so a grid
     item stretches to its track whatever display it carries. */
  justify-self: start;

  /* Optical alignment. The strike through "RISK IT" overhangs the left of the
     mark's letterforms, so a box-aligned logo reads as indented against the
     eyebrow and copy beneath it. Nudged with position rather than margin so
     the grid track does not move with it. */
  position: relative;
  left: -20px;
}

.hero__fixit img {
  display: block;
  width: 245px;
  height: auto;
}

@media (max-width: 640px) {
  .hero__fixit img {
    width: 165px;
  }
}

/* Fix It or Risk It's own yellow, for the one slide that is about the
   campaign rather than about TPN. Deliberately not a TPN colour: the campaign
   is kept at arm's length so other organizations can co-sign it, and borrowing
   its palette for a link to it is the point.
   Navy text on the yellow measures 11.20:1, and the eyebrow in the same
   yellow on the hero navy is 12.50:1. */
.btn--fixit {
  background: var(--fixit-yellow);
  border-color: var(--fixit-yellow);
  color: var(--navy-800);
}

.btn--fixit:hover {
  background: #ffe94a;
  border-color: #ffe94a;
  color: var(--navy-800);
}

/* Get Started, wherever it appears.
   This was one fixed color for one destination, on the theory that the route
   into the program should be recognizable before the label is read. Page
   themes replaced that: every page now has an accent of its own, and a button
   holding a color no other element on the page uses reads as a stray rather
   than as a signal. It takes the page accent like every other primary action.
   Which variant to use is decided by the surface, not by emphasis: --start on
   a light band, --start-on-dark on a colored or navy one, where a saturated
   fill has too little separation from the ground behind it. */
.btn--start {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

.btn--start:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  color: #fff;
}

.btn--start-on-dark {
  background: #fff;
  border-color: #fff;
  color: var(--primary);
}

.btn--start-on-dark:hover {
  background: var(--grey-200);
  border-color: var(--grey-200);
  color: var(--primary-hover);
}

/* On a dark ground: a translucent white outline over a slight white fill, both
   coming up on hover.

   The fill was transparent, which was fine on the flat navy this was written
   for. It is not fine over film: with nothing behind the label the button had
   no shape of its own, and on a light frame it disappeared entirely. 0.12 is
   enough to give it a body without turning it into a solid. */
.btn--ghost {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.5);
  color: #fff;
}

.btn--ghost:hover {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.75);
  color: #fff;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* The word between two buttons that are alternatives rather than a sequence.
 *
 * Real text, not aria-hidden: "or" is what tells a reader the two actions are
 * a choice, and that is as true read aloud as it is on screen.
 *
 * color: inherit rather than a fixed value, because .btn-row appears on the
 * dark hero and on white bands, and an opacity on the inherited ink is the
 * only version that reads on both. align-self because .btn-row does not
 * centre its items, so a bare span would stretch to the row's height and sit
 * its text at the top. */
.btn-row__or {
  align-self: center;
  color: inherit;
  opacity: 0.75;
  font-size: 15px;
}

/* ---------------------------------------------------------------- cards */

.card {
  background: #fff;
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  padding: 28px;
  transition:
    box-shadow var(--dur-micro) ease,
    border-color var(--dur-micro) ease;
}

.card--link:hover {
  box-shadow: var(--shadow-card-hover);
  border-color: var(--primary);
  text-decoration: none;
}

.card__icon {
  font-size: 30px;
  color: var(--primary);
}

.card__title {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--navy-800);
}

.grid {
  display: grid;
  gap: 24px;
}

.grid--2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 32px;
}

.grid--auto {
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
}

@media (max-width: 900px) {
  .grid--2 {
    grid-template-columns: 1fr;
  }
}

/* ---------------------------------------------------------- breadcrumbs */

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  list-style: none;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.breadcrumb a {
  color: inherit;
}

.breadcrumb a:hover {
  color: var(--primary);
}

.breadcrumb li + li::before {
  content: '/';
  margin-right: 8px;
  color: var(--border-strong);
}

.on-dark .breadcrumb,
.on-dark .breadcrumb a {
  color: rgba(255, 255, 255, 0.7);
}

.on-dark .breadcrumb li + li::before {
  color: rgba(255, 255, 255, 0.4);
}

.on-dark .breadcrumb a:hover {
  color: #fff;
}

/* Anything a fragment link points at has to clear the sticky header, measured
   at 77px. Set once here so a new anchor target does not have to remember;
   pages needing more still override it.

   Exactly the header height, with nothing added. Every section on this site
   carries its own top padding - 44px on a .section - and that padding is the
   breathing room. The 20px cushion this used to add landed on top of it, so a
   fragment link stopped 63px short of its own heading with empty page in the
   gap. */
:root {
  /* The sticky header's own height, and the offset every anchor target has to
     clear. One number, because these two were separate and drifted: the offset
     stayed at the desktop 77px while the bar itself became 73px on tablet and
     65px on mobile, so a chevron left 12px of the previous section on screen
     above the one it had just scrolled to.

     Each value is the inner row plus the 1px rule under it. The media blocks
     below derive .site-header__inner's height from this, so the bar and the
     offset cannot disagree again. */
  --header-h: 77px;
}

@media (max-width: 1080px) {
  :root {
    --header-h: 73px;
  }
}

@media (max-width: 560px) {
  :root {
    --header-h: 65px;
  }
}

:target {
  scroll-margin-top: var(--header-h);
}

/* Same offset for the section jumps, which scroll to an element rather than to
   a fragment and so never match :target. */
main > section {
  scroll-margin-top: var(--header-h);
}

/* ------------------------------------------------------- section jump */

/* The chevron that moves to the next section. Built in site.js, which also
   decides which sections get one and adds .has-jump to them.
   It straddles the seam: centered on the section's bottom edge, half in this
   band and half in the next, and taken out of flow so it adds no height. In
   flow it cost 84px per section, which on a six-band page is 420px of page
   that exists only to hold five buttons. */
.has-jump {
  position: relative;
}

.section-jump {
  position: absolute;
  bottom: 0;
  left: 50%;
  z-index: 2;
  transform: translate(-50%, 50%);

  display: flex;
  align-items: center;
  justify-content: center;

  /* 44px square, the WCAG 2.2 target minimum, on a smaller icon. */
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--border-default);
  border-radius: 50%;

  /* Opaque, and one color on every seam. It sits on the join between two bands
     that are rarely the same color, so a transparent fill would be split down
     the middle by whatever is behind it. */
  background: #fff;
  color: var(--text-secondary);
  box-shadow: var(--shadow-card);
  cursor: pointer;
  transition:
    opacity var(--dur-micro) ease,
    background var(--dur-micro) ease,
    border-color var(--dur-micro) ease,
    color var(--dur-micro) ease;
}

/* Hosted by the section below the seam instead, because the one above clips.
   Same place on screen, hung off the host's top rather than the previous
   section's bottom. Also how the back-to-top button sits on the footer. */
.section-jump--below {
  top: 0;
  bottom: auto;
  transform: translate(-50%, -50%);
}

/* Same circle, turned over. The chevron glyph is symmetrical, so this is the
   up arrow without a second icon in the subsetted font. */
.section-jump--to-top .material-icons {
  transform: rotate(180deg);
}

.section-jump .material-icons {
  font-size: 24px;
}

.section-jump:hover {
  border-color: var(--navy-800);
  background: var(--grey-200);
  color: var(--navy-800);
}

/* Set by site.js on every jump except the next one down, so only one is ever on
   screen. Opacity rather than display, so scrolling back up fades it in rather
   than snapping it. pointer-events because an invisible button is still a
   button, and this one sits right under the header. */
.section-jump.is-hidden {
  opacity: 0;
  pointer-events: none;
}

/* ----------------------------------------------------------- page hero */

/*
 * Every interior page opens with this: a light ground carrying the words, cut
 * diagonally against a charcoal panel, with a pale wedge between them.
 *
 * Borrowed from the 2026 TPN STAR Report, which frames content with panels cut
 * on a constant diagonal. Two rules make it work, and both were defects first.
 *
 * ONE ANGLE, EVERYWHERE, AND IT IS A SKEW.
 *
 * A clip-path percentage resolves against its own element, so a shape sized to
 * each band comes out at a different angle on every page - these bands run
 * 261px to 575px tall, so the compact ones were more than twice as steep as the
 * tall ones. skewX is an absolute angle. It is identical at 200px and 800px, so
 * nothing has to be pinned to a fictional base height.
 *
 * The sliver is the single documented exception to one angle: -9deg against the
 * panel's -14deg, both from the SAME left and the SAME bottom-left origin, so
 * the two edges meet at the bottom and the wedge opens as it rises. The 5deg
 * delta IS the effect. A third angle anywhere reads as a mistake.
 *
 * THE GROUND IS THE SECTION, NOT THE PANEL.
 *
 * Below 1080px the panel is hidden and the band is simply light. Written the
 * other way round - charcoal section, light overlay - every heading and link
 * would need a colour swap at that breakpoint, which is where contrast
 * regressions come from.
 */
.page-hero {
  position: relative;
  overflow: hidden;

  /* --grey-50, the faintest step in the scale: 1.04:1 against pure white, which
     is about as close as a value can get and still be a value. It is NOT
     --grey-100, which the section below the hero uses - at 1.09:1 the two were
     near enough identical that the hero and that band ran together as one field
     with no seam, which is what the hairline below and this step together fix.

     Every ink on it clears AA: --text-muted 4.92:1 is the tightest, and that is
     the breadcrumb on every interior page. */
  background: var(--grey-50);

  /* The same hairline the sticky header carries, for the same reason: on a white
     ground the band has no edge of its own, so the hero and the section under it
     read as one field. --border-subtle is rgba(0,0,0,0.08), so it draws on the
     light side and disappears against the charcoal panel rather than striping
     across it. */
  border-bottom: 1px solid var(--border-subtle);

  /*
   * One angle for both edges of the diagonal, and the grey wedge between them
   * is tapered by a clip on the panel rather than by a second angle. See
   * .page-hero__panel for why.
   *
   * --wedge is the grey's width at the top, where it is widest. 56px matches
   * the widest the old two-angle wedge ever got - 0.091 x 640px on Our
   * Mission - so the band carries the same weight it always did, just at the
   * other end.
   */
  --sliver-angle: -14deg;
  --sliver: #dde3ec;
  --wedge: 56px;

  /*
   * Where the diagonal crosses the bottom edge, and the cap on the copy.
   *
   * Measured, not taken from the reference: the longest rendered line across
   * all 18 heroes is 500px, on Membership, Links & Resources, Shield Program
   * and Getting Started.
   *
   * --split has to clear the DRIFT as well as the copy, now that the panel is
   * anchored at the top: its leftmost point is the bottom crossing, which sits
   * height x tan(14deg) to the left of --split - 139px on the tallest hero. So
   * the split carries the column, the gutter and that drift.
   */
  --split: 65%;

  /*
   * Grows with the display instead of being one number.
   *
   * The room to the left of the diagonal is not constant: --split is a
   * percentage, so it advances at 0.65 per pixel of viewport while the centred
   * column's left edge advances at 0.5, and the gap opens as the screen gets
   * wider. A fixed cap therefore reads correctly at 1240 and leaves a third of
   * the light side empty at 2560.
   *
   * Measured, at six widths, as the largest cap still holding 45px of
   * clearance: 580 at 1240, 620 at 1440, 660 at 1600, 720 at 2000, 800 at 2560.
   * This line tracks a little under that line and stops at 800, which is about
   * --measure and as long as a line should get regardless of the room.
   */
  --col: clamp(580px, 360px + 16vw, 800px);

  /*
   * Pinned to the content box, not left as a bare percentage.
   *
   * A percentage advances at 0.46px per pixel of viewport while the centred
   * 1240px grid's left edge advances at 0.5, so the gap between them CLOSES as
   * the display widens - the safe text width shrinks exactly where it looks
   * like it should grow. At left:46% with a 500px column the two cross at about
   * 2200px and overlap beyond it. This audience runs 2560px and 3440px
   * displays.
   *
   * max(), not min(): min() picks the viewport value at precisely the widths
   * where it fails. Below 1240px the percentage wins; at and above it the calc
   * tracks the column and the safe width holds constant.
   */
  --split-x: max(var(--split), calc(50% - (620px - var(--col) - 32px)));
}

/* Light side only. On the charcoal it turns to mud and fights the glow. */
.page-hero__pattern {
  position: absolute;
  inset: 0;

  /*
   * A half-drop lattice of plus marks, drawn rather than fetched.
   *
   * This replaced the topo contour field, which was a 100KB generated SVG
   * tiled at 1200px. The tile here is about 300 bytes inline, so there is no
   * file and no request, and the geometry is numbers in this rule rather than
   * a seed in tools/gen-topo-pattern.mjs.
   *
   * 76x80 tile carrying two crosses, at the quarter and three-quarter points.
   * Two per tile is what staggers it: every other row lands half a tile over,
   * so the marks read as a diagonal weave rather than as a grid of columns.
   * Crosses are 14px arm to arm on a 1.25px stroke, which is the size and
   * weight in the reference.
   *
   * The tile is a fixed size, not a percentage, so the lattice lands
   * identically on every hero regardless of how tall the band is - the same
   * reason the diagonal is cut over a fixed 900px rather than a percentage of
   * its own element.
   *
   * Drawn as a MASK over a token colour, not as a background-image with the
   * colour baked into the data URI. A data URI cannot read var(), so baking it
   * would put a second copy of #1c2a42 in the stylesheet that no longer tracks
   * --navy-800. Masking keeps the SVG as pure geometry and lets the token
   * carry the colour. Rects rather than strokes, so the arms sit on whole
   * pixels instead of straddling them.
   */
  pointer-events: none;
}

/*
 * Everything that paints is inside the @supports, and that is the point.
 *
 * Unprefixed mask-image is Safari 15.4 and up. Below it the mask properties
 * are dropped but background-color is not, so the rule would degrade to a div
 * filled with --navy-800 at 0.22 - a flat navy wash across the whole hero,
 * which is far worse than no texture at all. Gating the colour on the same
 * feature that cuts it means an unsupporting browser gets an empty
 * transparent layer and a clean hero.
 *
 * -webkit-mask-image would reach those browsers with the real texture, but
 * stylelint's property-no-vendor-prefix rejects it and a decorative layer is
 * not worth a seventh entry in docs/css-lint-exceptions.md.
 */
@supports (mask-image: url('')) {
  .page-hero__pattern {
    background-color: var(--navy-800);
    mask-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2276%22%20height%3D%2280%22%3E%3Cg%20fill%3D%22%23000%22%3E%3Crect%20x%3D%2212%22%20y%3D%2219.375%22%20width%3D%2214%22%20height%3D%221.25%22%2F%3E%3Crect%20x%3D%2218.375%22%20y%3D%2213%22%20width%3D%221.25%22%20height%3D%2214%22%2F%3E%3Crect%20x%3D%2250%22%20y%3D%2259.375%22%20width%3D%2214%22%20height%3D%221.25%22%2F%3E%3Crect%20x%3D%2256.375%22%20y%3D%2253%22%20width%3D%221.25%22%20height%3D%2214%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E');
    mask-size: 76px 80px;
    mask-repeat: repeat;

    /* Texture, not pattern - the lattice has to be felt rather than read.
       0.05 of --navy-800 over --grey-50 is #eff1f4, a hair off the ground it
       sits on. At this strength the marks are a surface the words sit on
       rather than anything the eye stops at. */
    opacity: 0.05;
  }
}

/*
 * right:-420px because the skew slides the top edge right, which would
 * otherwise expose the ground at the top-right corner. The overhang has to beat
 * the drift - height x tan(14deg) - at the tallest band that will ever render.
 * 420px covers 1680px.
 */
/*
 * Anchored at the TOP, not the bottom.
 *
 * A skew pins whichever edge the origin sits on and lets the other one drift by
 * height x tan(14deg). Anchored at the bottom, the top crossing moved 67px
 * between the shortest hero on the site (369px, every legal page) and the
 * tallest (640px, Our Mission) - and the top crossing is the one directly under
 * the header, which is what the eye compares when you move from page to page.
 * It read as the diagonal jumping about. The bottom crossing sits at a
 * different screen position on every page anyway, so drift there is not
 * something anyone can see.
 *
 * top right bottom left. Written as the shorthand because stylelint asks for
 * it; the -420px is the overhang, not a negative margin on the left.
 */
.page-hero__sliver,
.page-hero__panel {
  position: absolute;
  inset: 0 -420px 0 var(--split-x);
  transform-origin: top left;
  pointer-events: none;
}

.page-hero__sliver {
  background: var(--sliver);
  transform: skewX(var(--sliver-angle));
}

/*
 * Painted over the sliver, so the grey wedge is whatever the panel leaves
 * uncovered.
 *
 * Same skew as the sliver, and the taper comes from the clip instead of a
 * second angle. The wedge is --wedge wide at the top and closes to nothing at
 * the bottom: wide under the header, gone at the fold.
 *
 * The clip is what makes that hold on every page. It was two different angles,
 * 14deg on the sliver against 9deg here, and a difference of angles opens by
 * height x tan(difference) - so the wedge was zero at the anchored top and
 * widest at the bottom, which is the wrong way round, and its width was a
 * function of hero height. Flipping the anchor to the bottom would have flipped
 * the taper but moved the top crossing 67px across the site, which is the
 * defect the top anchor exists to prevent. A clip is measured against the
 * element's own box, so the taper is proportional at 369px and at 640px while
 * the top crossing never moves.
 *
 * The color is the page's own accent, so the panel is the largest statement
 * of the theme the rest of the page then repeats in its buttons and rules. A
 * page with no theme class gets the blue from :root.
 */
.page-hero__panel {
  overflow: hidden;
  background: var(--primary);
  transform: skewX(var(--sliver-angle));
  clip-path: polygon(var(--wedge) 0, 100% 0, 100% 100%, 0 100%);
}

/*
 * Page themes.
 *
 * Set on <body>, so everything on the page is inside them. Each one rebinds
 * --primary and --primary-hover rather than introducing a parallel --accent
 * that components would have to opt into one at a time: every primary button,
 * icon, heading rule, focus ring and blue band on the site already reads
 * --primary, so rebinding it themes all of them at once and nothing can be
 * left behind by an incomplete sweep.
 *
 * --text-link takes the darker hover value. Link text has to clear 4.5:1 and
 * the base colors run as low as 4.60:1, which is too little margin to spend on
 * body copy.
 *
 * NOTHING ASSIGNS THESE RIGHT NOW. Every page ran a color of its own for a
 * while and the site reads better on one, so all fifteen dropped their class
 * and sit on the :root blue. The rules and the token pairs behind them stay
 * because the per-page look is a live question rather than a settled one, and
 * putting it back is a class on <body>. Do not delete them as dead code
 * without asking.
 */
.theme-rust {
  --primary: var(--accent-rust);
  --primary-hover: var(--accent-rust-hover);
  --text-link: var(--accent-rust-hover);
}

.theme-bronze {
  --primary: var(--accent-bronze);
  --primary-hover: var(--accent-bronze-hover);
  --text-link: var(--accent-bronze-hover);
}

.theme-pine {
  --primary: var(--accent-pine);
  --primary-hover: var(--accent-pine-hover);
  --text-link: var(--accent-pine-hover);
}

.theme-teal {
  --primary: var(--accent-teal);
  --primary-hover: var(--accent-teal-hover);
  --text-link: var(--accent-teal-hover);
}

.theme-steel {
  --primary: var(--accent-steel);
  --primary-hover: var(--accent-steel-hover);
  --text-link: var(--accent-steel-hover);
}

.theme-blue {
  --primary: var(--accent-blue);
  --primary-hover: var(--accent-blue-hover);
  --text-link: var(--accent-blue-hover);
}

.theme-violet {
  --primary: var(--accent-violet);
  --primary-hover: var(--accent-violet-hover);
  --text-link: var(--accent-violet-hover);
}

.theme-orchid {
  --primary: var(--accent-orchid);
  --primary-hover: var(--accent-orchid-hover);
  --text-link: var(--accent-orchid-hover);
}

/*
 * Anything inside a skewed box leans with it, so this undoes the panel's skew
 * for the glow and the watermark. The counter-skew has to be the exact
 * negative of the panel's angle or the mark tilts - and at 0.06 opacity that
 * is easy to ship without noticing, which is exactly what happened.
 *
 * It was a literal 9deg, correct back when the panel was skewed -9deg. When
 * both edges of the diagonal moved to one -14deg angle and the wedge became a
 * clip-path instead of a second angle, this was left behind: -14 against +9
 * is a residual -5.2deg, so the shield was sheared by tan(14)-tan(9) = 0.0909
 * of its own height. Measured on the composed matrix, not guessed.
 *
 * So it reads the angle rather than restating it. --sliver-angle is negative
 * and this is its negation, which means the two cannot drift apart again: move
 * the panel and the counter-skew follows in the same edit.
 */
.page-hero__panel-inner {
  position: absolute;
  inset: 0;

  /* After the shorthand, so it wins. Shifts the glow and the mark together,
     which keeps them in the same relationship to each other. */
  left: -60px;
  transform: skewX(calc(-1 * var(--sliver-angle)));
  transform-origin: top left;
}

/* Neutral, not the blue tint this carried on navy. */
.page-hero__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 100% at 30% 0%, rgba(255, 255, 255, 0.16) 0%, transparent 66%);
}

/*
 * One fixed placement on every page, specified by Ward: 540px tall, flush to
 * the left of .page-hero__panel-inner, 20px above the band's top edge.
 *
 * Fixed rather than sized to the band, which is the same call
 * handoff-hero-treatment.md made and for the same reason: the bands run 369px
 * to 608px at 1280, so anything band-relative renders the mark at a different
 * size on almost every page. One number means the shield is identical
 * everywhere and only its crop changes.
 *
 * The consequence is that the crop is not identical. 540px overhangs the
 * 369px bands - 9 of the 16 pages - by a long way at the bottom, and clears
 * the 608px ones. That is accepted: this is texture on the panel, not a logo
 * anyone is meant to read, so it may lose its point, its shoulders or its
 * left edge behind the diagonal depending on the page. Nothing downstream
 * should "fix" it back to fitting.
 *
 * No transform. The mark needs no centring now that top is a fixed offset,
 * and it never carried the skew correction - that is on
 * .page-hero__panel-inner, which still un-skews this element's parent.
 *
 * Only ever seen on desktop, so one desktop number is the whole story: the
 * rule at max-width 1079px hides .page-hero__panel, and this element is
 * inside it, so the mark does not render at all on tablet or phone.
 */
.page-hero__mark {
  position: absolute;
  left: 0;
  top: -20px;
  width: auto;
  height: 540px;
  max-width: none;
  opacity: 0.06;
  display: block;
}

/* A floor, not a height.
 *
 * The interior heroes ran anywhere from 306px to 608px depending on how much
 * copy each page put in one, so moving between the short ones and the tall
 * ones made the band appear to jump. 369px is the Assessors hero measured at
 * 1440px, which is the shortest hero that still looks deliberate; everything
 * below it is padded up to match and everything above it is left alone.
 *
 * Only where the slab is drawn. Below 1081px the hero is stacked text with no
 * panel behind it, and a floor there is just dead space. */
@media (min-width: 1081px) {
  .page-hero {
    min-height: 369px;
  }
}

.page-hero__inner {
  position: relative;
  display: grid;
  align-content: start;
  justify-items: start;
  gap: 20px;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 64px 32px 72px;

  /*
   * The copy cap. A grid track rather than a max-width on the children: page
   * stylesheets set their own max-width on the lede and load after this one, so
   * a rule on the children ties on specificity and loses on order. A track they
   * cannot exceed holds regardless - and an unset track is what runs a lede
   * under the diagonal.
   */
  grid-template-columns: minmax(0, var(--col));
}

/* The rule under the page title. Flat and square-ended: a 14deg cut on a 3px
   bar shears 0.7px and renders square anyway, and a bar tall enough to show the
   cut stops reading as an underline. The diagonal is panel geometry only. */
.page-hero .h1 {
  padding-bottom: 20px;
  background: linear-gradient(var(--primary) 0 0) left bottom / 72px 3px no-repeat;
}

/* Gold is not a text colour on this ground: --gold-500 is 2.10:1 on it. */
/*
 * The action row sits a touch further from the lede than the grid's own 20px
 * gap gives it, because a button reads as a separate move rather than the next
 * line of the paragraph.
 *
 * Here, on the shared hero, and not on the pages. Four page stylesheets had
 * written this same 6px privately - .about-hero__actions,
 * .assessors-hero__actions, .membership-hero__actions and
 * .shield-hero__actions, one each, all identical - so the fifth hero to get a
 * button simply did not get the nudge, and The Team came out 6px shorter than
 * About Us with otherwise identical contents. One rule cannot drift.
 */
.page-hero .btn-row {
  margin-top: 6px;
}

.page-hero .eyebrow--gold,
.page-hero .hero-tagline {
  color: var(--text-muted);
}

/* Same angle, later crossing. */
@media (max-width: 1239px) {
  .page-hero {
    --split: 68%;
    --col: 500px;
  }
}

/*
 * The drift does not shrink with the viewport, so below this it eats the
 * column. The panel goes rather than being restyled, and the band is simply its
 * own light ground - which is why no ink colour changes here.
 */
@media (max-width: 1079px) {
  .page-hero__sliver,
  .page-hero__panel {
    display: none;
  }

  .page-hero {
    --col: 620px;
  }
}

/* ------------------------------------------------------- hero watermark */

/*
 * The article header is the exception twice over. Its band is the one light
 * surface, so it takes the dark mark; and its inner column is --measure, much
 * narrower than the --page-max the other heroes use. Hanging the mark off that
 * column would park it mid-page, out of line with every other page. So it hangs
 * off the full-width band instead and this expression reconstructs where the
 * --page-max column's right edge would be, which is what the other heroes
 * resolve to at every width - including below --page-max, where the max() falls
 * back to the plain 32px gutter exactly as they do.
 */
.article__head::after {
  right: max(32px, calc((100% - var(--page-max)) / 2 + 32px));
  background-image: url('/img/logos/tpn-shield-mark-dark.svg');
  opacity: 0.05;
}

/* Over the gallery's photography the mark reads as noise unless it is fainter. */
.gallery-hero__inner::after {
  opacity: 0.05;
}

/*
 * One padding for every interior hero.
 *
 * There were three tiers - 64/72 on nine pages, 40/44 on eight and 64/68 on
 * one - so two heroes carrying the same elements in the same order came out
 * 52px apart for no reason a reader could see. The stated one, that Membership
 * runs straight into its benefits section, was true of the old flat band and is
 * not a property of the page.
 *
 * It matters more now than it did. Band height drives the diagonal's drift, so
 * every extra padding tier is another source of variation in the one dimension
 * that was just pinned.
 */
@media (max-width: 640px) {
  .page-hero__inner {
    padding: 56px 20px 60px;
  }
}

/* ---------------------------------------------------------------- chips */

.chip {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  line-height: 1;
  white-space: nowrap;
}

.chip--service {
  background: var(--chip-service);
  color: #fff;
}

.pill {
  display: inline-flex;
  align-items: center;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  background: var(--navy-800);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------- forms */

.field {
  height: 40px;
  padding: 0 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  font-size: 14px;
  color: var(--text-body);
  background: #fff;
}

.field--lg {
  height: 46px;
  font-size: 15px;
}

select.field {
  height: 38px;
  padding-right: 8px;
}

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

/*
 * The utility line lives inside the header now, above the nav and beside the
 * logo, so the two read as one block rather than two stacked bars.
 *
 * It collapses on scroll instead of scrolling away. It used to sit outside the
 * sticky header and simply leave, which is the same result; inside, the height
 * animates to nothing, which the nav row below it follows.
 */
.site-utility {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 24px;
  height: 44px;
  overflow: hidden;
  font-size: 13px;
}

/* The filled CTA is excluded rather than overridden: `.site-utility a` is more
 * specific than `.site-utility__cta`, so a plain descendant rule would win and
 * paint dark text on the blue button - and on hover, blue text on blue. */
.site-utility a:not(.site-utility__cta) {
  color: var(--text-secondary);
}

.site-utility a:not(.site-utility__cta):hover {
  color: var(--primary);
}

.site-utility__divider {
  width: 1px;
  height: 18px;
  background: var(--border-default);
}

.site-utility__cta,
.site-utility__cta:hover {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 16px;
  border-radius: var(--radius);
  background: var(--primary);
  color: #fff;
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
}

.site-utility__cta:hover {
  background: var(--primary-hover);
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: saturate(140%) blur(6px);
  border-bottom: 1px solid var(--border-subtle);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 20px 32px;
}

/* The utility line and the nav, stacked, with the logo beside both. */
.site-header__rows {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  justify-items: end;
}

/* A one-cell grid, not a flex row: the two lockups occupy the same cell and
   overlap, so the anchor's box is simply the larger of the two and follows them
   as they animate. Side by side they would each reserve width and the header
   would shuffle sideways mid-transition. */
/* The min-height is the 44px touch floor, not a layout number: the mark is
   44px tall at rest, so this changes nothing until the 560px rule steps the
   image down to 36px and takes the anchor with it. The image keeps its own
   height and the grid centres it, so the target grows and nothing moves. */
.site-header__logo {
  display: grid;
  align-items: center;
  justify-items: start;
  flex: 0 0 auto;
  min-height: 44px;
}

.site-header__logo img {
  grid-area: 1 / 1;
  height: 44px;
  width: auto;
  display: block;
}

/*
 * Two lockups, one shown at a time.
 *
 * The expanded header carries the "powered by Motion Picture Association"
 * version; collapsing swaps it for the plain mark. They are separate files
 * rather than one image because the MPA strip is not something a height change
 * can reveal.
 *
 * 88px, not 72. The two files are the same lockup - both wordmark blocks
 * measure 2.370 to a pixel - but the MPA file adds a 58px strip and a 38px gap
 * below it, so its wordmark is only 432 of its 528 rows. At 88px that wordmark
 * renders at exactly the 72px the plain mark used to, so the brand does not
 * appear to change size when the strip appears. It also still fits: the
 * expanded row is 120px tall.
 *
 * The plain mark is the default because it is the one every viewport below
 * 1081px uses, where the header has no expanded state to be in.
 *
 * Every selector here names .site-header__logo as well as the modifier, and it
 * has to. `.site-header__logo img` above is a class plus a type, (0,1,1), which
 * outranks a bare `.site-header__logo-mpa` at (0,1,0) - so the base 44px and
 * its display:block won both properties and the two lockups rendered on top of
 * each other at the same size. Two classes, (0,2,0), clears it.
 */
.site-header__logo .site-header__logo-mpa {
  display: none;
}

/*
 * Nothing animates until site.js has set the first state.
 *
 * The CSS default is the collapsed header, so on every load the utility line
 * was 0 tall and the brand 44px until the script ran a moment later - and with
 * transitions declared unconditionally, that correction played as an animation.
 * The header appeared to grow out of nothing on every page.
 *
 * site.js sets data-at-top first and data-ready on the next frame, so the
 * opening state is painted rather than transitioned to, and every state after
 * it still moves.
 */
.site-header[data-ready] .site-utility {
  transition:
    height var(--dur-fade) ease,
    opacity var(--dur-fade) ease;
}

.site-header[data-ready] .site-header__logo img {
  transition:
    height var(--dur-fade) ease,
    opacity var(--dur-fade) ease;
}

/*
 * Before the page is scrolled the utility line is showing and the brand is
 * large; once it is, the line collapses and the brand comes down. site.js sets
 * data-at-top='false' to collapse it.
 *
 * EXPANDED is the default, and which way round this goes is the whole
 * behaviour. The CSS state is what gets painted before any script has run, so
 * the default has to be the state a page most often opens in - and that is
 * scrolled to the top. Collapsed-by-default meant every single navigation
 * painted the small header and then corrected itself the moment site.js
 * executed, which is visible as a pop on every page change.
 *
 * The reverse case, a page opening already scrolled from a fragment link or a
 * restored position, still corrects - but it is rare, and it corrects before
 * data-ready arms the transitions, so it is painted rather than animated.
 *
 * Only above 1080px, which is where the utility line exists at all.
 */
.site-header[data-at-top='false'] .site-utility {
  height: 0;
  opacity: 0;
}

/*
 * A crossfade, not a swap. `display` cannot be transitioned, so toggling it
 * replaced what used to be a smooth 72-to-44 height animation with an instant
 * jump - and because the two lockups are different sizes the header changed
 * width at the same instant, which read as a hitch just as the logo grew back.
 *
 * Both stay displayed and animate instead: the MPA lockup scales 88 to 44 as it
 * fades out, the plain mark holds 44 and fades in, and the anchor is auto-sized
 * so its box follows them down rather than stepping.
 */
@media (min-width: 1081px) {
  .site-header__logo .site-header__logo-mpa {
    display: block;
    height: 88px;
    opacity: 1;
  }

  .site-header__logo .site-header__logo-plain {
    display: block;
    height: 44px;
    opacity: 0;
  }

  .site-header[data-at-top='false'] .site-header__logo .site-header__logo-mpa {
    height: 44px;
    opacity: 0;
  }

  .site-header[data-at-top='false'] .site-header__logo .site-header__logo-plain {
    opacity: 1;
  }
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 24px;
  height: 76px;
}

.site-nav__link {
  position: relative;
  padding: 26px 0 24px;
  font-size: 15px;
  font-weight: 500;
  color: var(--navy-800);
  white-space: nowrap;
}

.site-nav__link:hover {
  color: var(--primary);
  text-decoration: none;
}

.site-nav__link[aria-current='page'] {
  color: var(--primary);
}

/* The current-page rule. Drawn as a pseudo-element rather than a border-bottom
   so it sits under the words: the link's 24px of bottom padding exists to make
   the whole header band clickable, and a border hangs at the bottom of that,
   floating well clear of the text. */
.site-nav__link[aria-current='page']::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 18px;
  height: 2px;
  background: var(--primary);
}

/* About Us dropdown.
   Revealed on hover and on :focus-within. The reveal is triggered by focus
   landing on the group's own visible link - the items are visibility:hidden
   until then, so they are out of the tab order and cannot be tabbed to first.
   The link's 24px bottom padding runs to the header edge, so the panel at
   top:100% is contiguous with its trigger and the pointer can cross into it
   without passing over a gap (WCAG 1.4.13 Hoverable).
   Escape-to-dismiss is added by site.js; see initNavGroup. */
.site-nav__group {
  position: relative;
  display: flex;
  align-items: center;
}

/* The chevron marking a nav item that opens a submenu. Color is inherited
   rather than set, so it follows the link through hover and the current-page
   state instead of staying grey while the word beside it turns blue. */
.site-nav__link--group {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

.site-nav__chev {
  font-size: 18px;
  color: inherit;
}

/* Pulled up 20px from the bottom of the group. The link carries 24px of bottom
   padding purely to make the whole header band clickable, and hanging the
   flyout off the outside of that left it floating clear of the word that opens
   it. The overlap keeps the pointer inside :hover on the way down. */
.site-nav__menu {
  position: absolute;
  top: calc(100% - 20px);
  left: -14px;
  z-index: 30;
  min-width: 210px;
  padding: 6px 0;
  list-style: none;
  background: #fff;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card-hover);
  visibility: hidden;
  opacity: 0;
  transform: translateY(-4px);
  transition:
    opacity var(--dur-micro) ease,
    transform var(--dur-micro) ease,
    visibility 0s linear var(--dur-micro);
}

.site-nav__group:hover .site-nav__menu,
.site-nav__group:focus-within .site-nav__menu {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition-delay: 0s;
}

/* site.js sets this on Escape and clears it when the pointer or focus leaves. */
.site-nav__group[data-dismissed='true'] .site-nav__menu {
  visibility: hidden;
  opacity: 0;
}

.site-nav__menu a {
  display: block;
  padding: 9px 16px;
  font-size: 14.5px;
  font-weight: 500;
  color: var(--navy-800);
  white-space: nowrap;
}

.site-nav__menu a:hover {
  background: var(--grey-100);
  color: var(--primary);
  text-decoration: none;
}

.site-nav__menu a[aria-current='page'] {
  color: var(--primary);
}

/* Lives in the utility row, which is right-aligned, so this sits immediately
   left of the divider where the utility links used to end.

   That row collapses to height 0 on scroll along with the LinkedIn icon and
   the TPN+ button, so the search collapses with it. The burger row keeps its
   own search link below 1080px. */
.site-search {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 40px;
  overflow: hidden;
  transition: width var(--dur-micro) cubic-bezier(0.4, 0, 0.6, 1);
}

.site-search[data-open='true'] {
  width: 260px;
}

.site-search__toggle {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: 6px;
  border: none;
  background: none;
  color: var(--navy-800);
  cursor: pointer;
}

.site-search__input {
  flex: 1 1 auto;
  min-width: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-micro) ease;
}

.site-search[data-open='true'] .site-search__input {
  opacity: 1;
  pointer-events: auto;
}

/* `margin-left: auto` is what puts these on the right edge.
   .site-header__inner is a flex row of three: the logo, the rows column and
   this. On desktop the rows column carries `flex: 1 1 auto` and does the
   pushing, and this is hidden. Below 1080 the rows column is display:none, so
   there was nothing left to take up the slack and the search and menu buttons
   sat immediately beside the logo with 150px of empty header to their right. */
.site-burger {
  display: none;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}

.site-burger__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  background: none;
  color: var(--navy-800);
  cursor: pointer;
}

.site-burger__btn:hover {
  text-decoration: none;
}

.site-drawer {
  border-top: 1px solid var(--border-subtle);
  background: #fff;
  max-height: calc(100vh - 120px);
  overflow-y: auto;
}

.site-drawer[hidden] {
  display: none;
}

.site-drawer__inner {
  display: grid;
  gap: 20px;
  padding: 8px 20px 24px;
}

.site-drawer__nav {
  list-style: none;
  display: grid;
  gap: 1px;
}

/* Scoped to the top level: the About Us item nests a sub-list whose links are
   styled separately below. */
.site-drawer__nav > li > a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 52px;
  padding: 15px 4px;
  border-bottom: 1px solid var(--border-subtle);
  font-size: 17px;
  font-weight: 500;
  color: var(--navy-800);
}

.site-drawer__nav a:hover {
  color: var(--primary);
  text-decoration: none;
}

.site-drawer__nav a[aria-current='page'] {
  color: var(--primary);
}

/* In the drawer the sub-items are always shown - there is room to scroll, and a
   second tap target to open a submenu is worse than the two extra rows. */
.site-drawer__sub {
  list-style: none;
  border-bottom: 1px solid var(--border-subtle);
}

.site-drawer__sub a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 11px 4px 11px 20px;
  font-size: 15px;
  font-weight: 500;
  color: var(--text-secondary);
}

.site-drawer__sub li + li a {
  border-top: 1px solid var(--border-subtle);
}

.site-drawer__heading {
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.site-drawer__links {
  list-style: none;
  display: grid;
  gap: 2px;
}

.site-drawer__links a {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 13px 4px;
  font-size: 15px;
  color: var(--text-body);
}

.site-drawer__links a:hover {
  color: var(--primary);
  text-decoration: none;
}

.site-drawer__links .material-icons-outlined {
  font-size: 20px;
  color: var(--primary);
}

.site-drawer__cta {
  display: grid;
  gap: 12px;
}

.site-drawer__cta .btn {
  min-height: 52px;
}

@media (max-width: 1080px) {
  .site-nav,
  .site-utility {
    display: none;
  }

  .site-header__rows {
    display: none;
  }

  /* An explicit height, because with the rows column gone there is nothing left
     to set one. The bar collapsed to exactly the logo's 44px, so the mark ran
     edge to edge with no air above or below it. 72px gives it 14px either
     side. */
  .site-header__inner {
    height: calc(var(--header-h) - 1px);
  }

  .site-burger {
    display: flex;
  }
}

@media (max-width: 560px) {
  .site-header__inner {
    height: calc(var(--header-h) - 1px);
  }

  .site-header__logo img {
    height: 36px;
  }
}

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

.site-footer {
  background: var(--grey-900);
  color: rgba(255, 255, 255, 0.75);
}

.site-footer a {
  color: rgba(255, 255, 255, 0.75);
}

.site-footer a:hover {
  color: #fff;
}

.site-footer__grid {
  display: grid;
  grid-template-columns: 1.15fr 1fr 1.05fr 1.1fr;
  gap: 40px;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 72px 32px 40px;
}

.site-footer__brand {
  display: grid;
  align-content: start;
  gap: 20px;
}

.site-footer__brand img {
  width: 200px;
  display: block;
}

.site-footer__col {
  display: grid;
  align-content: start;
  gap: 16px;
}

.site-footer__heading {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #fff;
}

.site-footer__col ul {
  list-style: none;
  display: grid;
  gap: 12px;
  font-size: 14px;
}

/* 13px text puts the anchor's own box at 20.8px, well under the 44px touch
   floor. The target is extended with a pseudo-element rather than padding
   because the footer's vertical rhythm is set by its grid and padding here
   would push everything below it down on all 51 pages. Nothing is painted;
   inset stretches the hit area to 44.8px over 216px of clear space above and
   70px below, so it cannot reach a neighbouring control. */
.site-footer__social {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}

.site-footer__social::after {
  content: '';
  position: absolute;
  inset: -12px 0;
}

/* The support panel. A lifted tint on the footer's own ground rather than a
   different colour: it has to read as a panel without becoming a second
   surface at the bottom of every page. */
.site-footer__support {
  align-self: start;
  display: grid;
  gap: 12px;
  padding: 24px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.05);
}

.site-footer__support-head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.site-footer__support-head .material-icons-outlined {
  font-size: 22px;
  color: rgba(255, 255, 255, 0.75);
}

.site-footer__support-body {
  font-size: 14px;
  line-height: 1.7;
}

.site-footer__support-body a {
  text-decoration: underline;
}

/* Same treatment as .site-footer__social, and for the same reason: 22.4px of
   anchor inside a panel whose padding sets the footer's shape. It is the last
   element in the support panel with 40px of clear space above it. */
.site-footer__support-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 500;
  color: #fff;
}

.site-footer__support-link::after {
  content: '';
  position: absolute;
  inset: -11px 0;
}

@media (max-width: 900px) {
  .site-footer__grid {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 560px) {
  .site-footer__grid {
    grid-template-columns: 1fr;
    gap: 32px;
    padding: 48px 20px 32px;
  }
}

/* ------------------------------------------------------------ carousel */

/* Shared by the hero and the testimonial rail. The viewport clips; the track
 * translates. Keep overflow and horizontal padding on DIFFERENT elements -
 * overflow clips at the padding box, so combining them bleeds neighbouring
 * cards into the gutters. */
.carousel__viewport {
  overflow: hidden;
}

.carousel__track {
  display: flex;
  transition: transform var(--dur-slide) var(--ease-slide);
}

/*
 * Hands the horizontal axis to the drag handler in site.js while the browser
 * keeps vertical scrolling. Without it the page and the carousel both try to
 * claim the gesture, and a swipe either scrolls the page or has to be canceled
 * with preventDefault, which cannot be done from a passive listener.
 *
 * On the SURFACE - the clipping container createCarousel binds its handlers
 * to and flags with this attribute - not on the track. touch-action is read
 * from the ancestors of whatever the touch lands on, and mid-slide that can
 * be the container itself rather than a page: the outgoing page is inert
 * from the moment a move begins, so hit-testing skips it and falls through.
 * With pan-y only on the track, those touches had no restriction at all, the
 * browser took them as scrolls, and the swipe died with a pointercancel.
 *
 * Set by script, which is also why it is an attribute and not a class on the
 * markup: with JS off there is no drag handler, the testimonial viewport
 * scrolls natively with overflow-x:auto instead, and pan-y would block the
 * horizontal swipe that fallback depends on. No script, no restriction.
 */
[data-carousel-surface] {
  touch-action: pan-y;
}

/* Only while a drag is actually in progress, which site.js flags on the track.
   Set unconditionally it would make the quotes unselectable, and the text of a
   testimonial is worth being able to copy. */
.carousel__track[data-dragging] {
  user-select: none;
  cursor: grabbing;
}

.carousel__dots {
  display: flex;
  justify-content: center;
  gap: 12px;
}

/* The button is the hit target and paints nothing; ::before is the visible
 * dot. Keeping the two separate means a `background:` shorthand on a state
 * rule cannot reset the dot's geometry, and the pill is a plain block whose
 * border-radius always resolves cleanly - padding plus background-clip on the
 * button itself gave an elliptical inner radius, so the dots rendered as
 * squashed ovals and squares. */
/* 44px both ways. The height was already the touch floor and the width was
   24px, so the target measured 24x44 - half a target, on the one control that
   is nothing but a target. The visible dot is unchanged at 8px. */
.carousel__dot {
  display: grid;
  place-items: center;
  min-width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.carousel__dot::before {
  content: '';
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.2);
  transition:
    width var(--dur-micro) ease,
    background-color var(--dur-micro) ease;
}

.carousel__dot[aria-current='true']::before {
  width: 28px;
  background: var(--navy-800);
}

/* Dots sit on the navy hero: lighter inactive, gold and wider when active. */
.carousel__dots--on-dark .carousel__dot::before {
  background: rgba(255, 255, 255, 0.45);
}

.carousel__dots--on-dark .carousel__dot[aria-current='true']::before {
  width: 34px;
  background: var(--gold-500);
}

.carousel__arrow {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--border-default);
  border-radius: 50%;
  background: #fff;
  color: var(--navy-800);
  cursor: pointer;
  transition:
    background var(--dur-micro) ease,
    border-color var(--dur-micro) ease;
}

.carousel__arrow:hover {
  background: var(--grey-100);
  border-color: var(--navy-800);
}

/* --------------------------------------------------------- support bubble */

/*
 * Support, always reachable.
 *
 * It was a text link in the utility row, which collapses to nothing on scroll
 * - so the one thing a stuck visitor wants was only available at the top of
 * the page. Fixed to the corner instead.
 *
 * A pill with the label rather than a bare icon: an icon alone needs a hover
 * to explain itself, which is no use on a touch screen and no use to anyone
 * reading it for the first time. It collapses to the circle only where there
 * is genuinely no room for words.
 *
 * z-index is deliberately modest. The consent banner is fixed to the bottom
 * of the viewport too and belongs in front of this: consent is answered
 * first, and OneTrust sets its own very high stacking order to make sure of
 * it.
 */
.support-bubble {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 50;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 52px;
  padding: 0 22px 0 18px;
  border-radius: var(--radius-pill);

  /*
   * --sam, not --primary.
   *
   * --primary is remapped by the page theme classes, so a themed page would
   * recolour a control that is the same fixed thing in the same corner of every
   * page. --sam is the site's solid fuchsia fill - the one every Get Started
   * action already uses - and it is constant.
   */
  background: var(--sam);

  /* .site-footer a sets 75% white and beats a single class, so the label was
     rendering at 75% rather than the #fff this rule asked for. The bubble is
     inside the footer only because that is where apply-shell can propagate it;
     it is not footer text. */
  color: #fff;
  font-size: 15px;
  font-weight: 500;
  box-shadow: 0 6px 20px rgba(10, 16, 30, 0.28);
  transition:
    background var(--dur-micro) ease,
    box-shadow var(--dur-micro) ease;
}

.site-footer .support-bubble,
.support-bubble:hover {
  color: #fff;
}

.support-bubble:hover {
  background: var(--sam-hover);
  color: #fff;
  text-decoration: none;
  box-shadow: 0 8px 26px rgba(10, 16, 30, 0.34);
}

.support-bubble .material-icons {
  font-size: 22px;
}

/* Print has no fixed corner to sit in, and a floating button on paper is
   just a stray word. */
@media print {
  .support-bubble {
    display: none;
  }
}

/* No room for the words: the circle carries the icon on its own. */
@media (max-width: 560px) {
  .support-bubble {
    right: 16px;
    bottom: 16px;
    width: 52px;
    height: 52px;
    padding: 0;
    justify-content: center;
  }

  .support-bubble__label {
    display: none;
  }
}

/* ------------------------------------------------------------ pull line */

/* A single line of display type against a gold rule, introducing the section
   heading below it. It began as .gs-pull on Where to Start and is on four
   pages now, so it lives here rather than in one page's stylesheet.

   The rule is the line's left edge rather than a mark above it, so a wrapped
   second line still reads as part of the same statement. */
.pull {
  margin: 4px 0 8px;
  padding-left: 18px;
  border-left: 3px solid var(--gold-500);
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 300;
  line-height: 1.4;
  color: var(--text-heading);
  text-wrap: pretty;
}

/* ----------------------------------------------------------- data rows */

/* --------------------------------------------------------- press years */

.press-years {
  display: grid;
  gap: 12px;
}

.press-year {
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  background: #fff;
}

.press-year[hidden] {
  display: none;
}

.press-year__summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 22px;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  color: var(--navy-800);
  cursor: pointer;
  list-style: none;
}

/* Safari still paints the disclosure triangle without this. */
.press-year__summary::-webkit-details-marker {
  display: none;
}

/* Pushes the chevron to the far edge, so the count sits with the year it
   belongs to rather than drifting across the row. */
.press-year__count {
  margin-right: auto;
  min-width: 26px;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  background: var(--grey-100);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  text-align: center;
}

.press-year__chev {
  flex: 0 0 auto;
  font-size: 22px;
  color: var(--primary);
  transition: transform var(--dur-micro) ease;
}

.press-year[open] .press-year__chev {
  transform: rotate(180deg);
}

/* The rows already carry their own bottom hairline, so the last one would
   otherwise draw a second line just above the card's own edge. */
.press-year .row-list {
  padding: 0 22px 8px;
}

.press-year .row:last-child {
  border-bottom: none;
}

/* The Press index is this row. */
.row-list {
  list-style: none;
  display: grid;
}

.row {
  display: grid;
  grid-template-columns: 130px minmax(0, 1fr) auto;
  align-items: center;
  gap: 20px;
  padding: 18px 12px;
  border-bottom: 1px solid var(--border-subtle);
  transition: background var(--dur-micro) ease;
}

.row:hover {
  background: var(--grey-100);
}

.row__date {
  font-size: 13px;
  color: var(--text-muted);
}

/* 500, not 600. At 19px across a list this long the heavier weight read as a
   run of headings rather than as rows; the size already carries the hierarchy
   against the 13px date beside it. */
.row__title {
  font-size: 19px;
  font-weight: 500;
  color: var(--navy-800);
}

.row:hover .row__title {
  color: var(--primary);
}

@media (max-width: 760px) {
  .row {
    grid-template-columns: 1fr;
    gap: 8px;
  }
}

/* OneTrust binds this by class and opens its preference center. A button, not
 * a link: it opens a dialog rather than navigating, and with JS off (or the
 * SDK blocked) it simply does not appear as a broken destination. */
.site-footer__cookies {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: rgba(255, 255, 255, 0.75);
  cursor: pointer;
}

.site-footer__cookies:hover {
  color: #fff;
  text-decoration: underline;
}

/* ------------------------------------------------------- press filter */

/* Three toggles above the merged Press list. `aria-pressed` carries the state
   rather than a class, so the control is announced correctly and the styling
   reads off the same attribute the script sets. */
.press-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  max-width: var(--page-max);
  margin: 0 auto 28px;
  padding: 0 32px;
}

.press-filter__btn {
  min-height: 40px;
  padding: 8px 18px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: #fff;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-link);
  cursor: pointer;
  transition:
    background var(--dur-micro) ease,
    border-color var(--dur-micro) ease,
    color var(--dur-micro) ease;
}

.press-filter__btn:hover {
  border-color: var(--primary);
}

/* --primary with white text is 4.60:1, the same pairing every filled button on
   this site uses. */
.press-filter__btn[aria-pressed='true'] {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

@media (max-width: 640px) {
  .press-filter {
    padding: 0 20px;
  }
}
