@charset "UTF-8";
:root {
  --ds-color-brand-primary: #ea3700;
  --ds-color-brand-primary-hover: #e96007;
  --ds-color-brand-accent: #e96007;
  --ds-color-brand-accent-hover: #ff7a2e;
  --ds-color-fg-default: #b6b6b6;
  --ds-color-fg-heading: #f9fafa;
  --ds-color-fg-muted: #868686;
  --ds-color-fg-inverse: #0a0a0a;
  --ds-color-fg-on-accent: #f9fafa;
  --ds-color-bg-default: #131313;
  --ds-color-bg-subtle: #0a0a0a;
  --ds-color-bg-muted: #383838;
  --ds-color-bg-lift: #454545;
  --ds-color-bg-alt: #1e3a38;
  --ds-color-bg-alt-lift: #28504d;
  --ds-color-bg-subtle-lift: #1b1b1b;
  --ds-color-bg-inverse: #f9fafa;
  --ds-color-bg-overlay: rgba(10, 10, 10, 0.72);
  --ds-color-bg-surface-0: #0a0a0a;
  --ds-color-bg-surface-1: #101010;
  --ds-color-bg-surface-2: #161616;
  --ds-color-bg-surface-3: #1c1c1c;
  --ds-color-bg-surface-4: #222222;
  --ds-color-bg-mosaic-ground: #171717;
  --ds-color-bg-mosaic-veil: #202020;
  --ds-color-bg-mosaic-cap: #1a1a1a;
  --ds-color-bg-startgg-card: #181818;
  --ds-color-border-default: rgba(249, 250, 250, 0.13);
  --ds-color-border-strong: rgba(249, 250, 250, 0.26);
  --ds-color-border-hover: #e96007;
  --ds-color-border-focus: #e96007;
  --ds-color-border-focus-on-brand: #f9fafa;
  --ds-color-state-success: #e96007;
  --ds-color-state-warning: #e96007;
  --ds-color-state-danger: #ea3700;
  --ds-color-state-info: #1e3a38;
  --ds-color-channel-scrim: 10, 10, 10;
  --ds-color-channel-shadow: 0, 0, 0;
  --ds-color-channel-light: 255, 255, 255;
  --ds-color-channel-paper: 249, 250, 250;
  --ds-color-channel-key: 234, 55, 0;
  --ds-color-channel-accent: 233, 96, 7;
  --ds-color-channel-muted: 56, 56, 56;
  --ds-color-channel-surface: 22, 22, 22;
  --ds-color-channel-surface-3: 28, 28, 28;
  --ds-color-channel-alt: 30, 58, 56;
  --ds-color-channel-portrait: 16, 16, 17;
  --ds-color-channel-portrait-grade: 50, 78, 119;
  --ds-color-social-twitch: #9146ff;
  --ds-color-social-x: #f9fafa;
  --ds-color-btn-primary-text: #f9fafa;
  --ds-color-btn-primary-bg: #ea3700;
  --ds-color-btn-primary-border: #ea3700;
  --ds-color-btn-primary-hover-text: #f9fafa;
  --ds-color-btn-primary-hover-bg: #e96007;
  --ds-color-btn-primary-hover-border: #e96007;
  --ds-color-btn-secondary-text: #f9fafa;
  --ds-color-btn-secondary-bg: transparent;
  --ds-color-btn-secondary-border: #e96007;
  --ds-color-btn-secondary-hover-text: #f9fafa;
  --ds-color-btn-secondary-hover-bg: #e96007;
  --ds-color-btn-secondary-hover-border: #e96007;
  --ds-color-btn-on-brand-text: #f9fafa;
  --ds-color-btn-on-brand-bg: #0a0a0a;
  --ds-color-btn-on-brand-border: #0a0a0a;
  --ds-color-btn-on-brand-hover-text: #0a0a0a;
  --ds-color-btn-on-brand-hover-bg: #f9fafa;
  --ds-color-btn-on-brand-hover-border: #f9fafa;
  --ds-font-heading: TGS Perfect Condensed, Oswald, Roboto Condensed, Arial Narrow, Segoe UI Semibold, system-ui, sans-serif;
  --ds-font-body: Aloevera Display UGC, Segoe UI, system-ui, -apple-system, sans-serif;
  --ds-font-label: Aloevera Display UGC, Segoe UI, system-ui, -apple-system, sans-serif;
  --ds-size-h1: 80px;
  --ds-size-h2: 52px;
  --ds-size-h3: 27px;
  --ds-size-h4: 19px;
  --ds-size-h5: 17px;
  --ds-size-h6: 15px;
  --ds-size-lead: 19px;
  --ds-size-body: 16px;
  --ds-size-small: 14px;
  --ds-size-button: 17px;
  --ds-size-nav: 18px;
  --ds-weight-regular: 400;
  --ds-weight-medium: 500;
  --ds-weight-semibold: 600;
  --ds-weight-bold: 700;
  --ds-leading-tight: 1.03;
  --ds-leading-snug: 1.18;
  --ds-leading-normal: 1.5;
  --ds-leading-relaxed: 1.65;
  --ds-tracking-tight: -0.008em;
  --ds-tracking-normal: 0em;
  --ds-tracking-wide: 0.2em;
  --ds-layout-container-max: 1280px;
  --ds-layout-container-gutter: clamp(16px, 4vw, 54px);
  --ds-layout-section-space: clamp(52px, 7.5vw, 102px);
  --ds-layout-section-space-sm: clamp(40px, 5.3vw, 72px);
  --ds-layout-section-title-space: clamp(26px, 3.6vw, 44px);
  --ds-layout-grid-gap: 12px;
  --ds-layout-block-gap: 26px;
  --ds-control-height: 50px;
  --ds-control-height-slim: 40px;
  --ds-control-padding-x: 24px;
  --ds-control-border-width: 2px;
  --ds-radius-sm: 0;
  --ds-radius-md: 0;
  --ds-radius-lg: 0;
  --ds-radius-pill: 999px;
  --ds-shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.06);
  --ds-shadow-md: 0 4px 12px rgba(16, 24, 40, 0.08);
  --ds-shadow-lg: 0 16px 40px rgba(16, 24, 40, 0.12);
  --ds-motion-fast: 150ms;
  --ds-motion-base: 250ms;
  --ds-motion-slow: 400ms;
  --ds-motion-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ds-z-base: 1;
  --ds-z-dropdown: 100;
  --ds-z-sticky: 200;
  --ds-z-overlay: 300;
  --ds-z-modal: 400;
  --ds-z-toast: 500;
}

@media (max-width: 767.98px) {
  :root {
    --ds-size-h1: 38px;
    --ds-size-h2: 29px;
    --ds-size-h3: 20px;
    --ds-size-h4: 18px;
    --ds-size-h5: 16px;
    --ds-control-height: 46px;
    --ds-control-height-slim: 38px;
    --ds-control-padding-x: 18px;
  }
}
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--ds-font-body);
  font-size: var(--ds-size-body);
  font-weight: var(--ds-weight-regular);
  line-height: var(--ds-leading-normal);
  color: var(--ds-color-fg-default);
  background-color: var(--ds-color-bg-default);
}

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 0.5em;
  font-family: var(--ds-font-heading);
  font-weight: var(--ds-weight-semibold);
  line-height: var(--ds-leading-tight);
  color: var(--ds-color-fg-heading);
}

h1 {
  font-size: var(--ds-size-h1);
}

h2 {
  font-size: var(--ds-size-h2);
}

h3 {
  font-size: var(--ds-size-h3);
}

h4 {
  font-size: var(--ds-size-h4);
}

h5 {
  font-size: var(--ds-size-h5);
}

h6 {
  font-size: var(--ds-size-h6);
}

p {
  margin: 0 0 1em;
}

a {
  color: var(--ds-color-brand-accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--ds-motion-fast) var(--ds-motion-ease);
}
a:where(:hover) {
  color: var(--ds-color-brand-accent-hover);
}

img,
svg,
video {
  display: block;
  max-width: 100%;
  height: auto;
}

:focus-visible {
  outline: 2px solid var(--ds-color-border-focus);
  outline-offset: 2px;
}

.container {
  width: 100%;
  max-width: var(--ds-layout-container-max);
  margin-inline: auto;
  padding-inline: var(--ds-layout-container-gutter);
}

.section {
  padding-block: var(--ds-layout-section-space);
}
.section--sm {
  padding-block: var(--ds-layout-section-space-sm);
}

.section__head {
  margin-bottom: var(--ds-layout-section-title-space);
}

.stack > * + * {
  margin-top: var(--ds-layout-block-gap);
}

.grid {
  display: grid;
  gap: var(--ds-layout-grid-gap);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--ds-control-height);
  padding-inline: var(--ds-control-padding-x);
  border: var(--ds-control-border-width) solid transparent;
  border-radius: var(--ds-radius-md);
  font-family: var(--ds-font-heading);
  font-size: var(--ds-size-button);
  font-weight: var(--ds-weight-semibold);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--ds-motion-base) var(--ds-motion-ease), color var(--ds-motion-base) var(--ds-motion-ease), border-color var(--ds-motion-base) var(--ds-motion-ease);
}
.btn--slim {
  height: var(--ds-control-height-slim);
}
.btn--primary {
  color: var(--ds-color-btn-primary-text);
  background-color: var(--ds-color-btn-primary-bg);
  border-color: var(--ds-color-btn-primary-border);
}
.btn--primary:hover {
  color: var(--ds-color-btn-primary-hover-text);
  background-color: var(--ds-color-btn-primary-hover-bg);
  border-color: var(--ds-color-btn-primary-hover-border);
}
.btn--secondary {
  color: var(--ds-color-btn-secondary-text);
  background-color: var(--ds-color-btn-secondary-bg);
  border-color: var(--ds-color-btn-secondary-border);
}
.btn--secondary:hover {
  color: var(--ds-color-btn-secondary-hover-text);
  background-color: var(--ds-color-btn-secondary-hover-bg);
  border-color: var(--ds-color-btn-secondary-hover-border);
}

.card {
  padding: var(--ds-layout-block-gap);
  background-color: var(--ds-color-bg-default);
  border: 1px solid var(--ds-color-border-default);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-sm);
}

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

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}
body {
  margin: 0;
  background: var(--ds-color-bg-default);
  color: var(--ds-color-fg-heading);
  font-family: var(--ds-font-body);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* This one line is the whole of the design's link policy: a link takes its
   colour from whatever it sits in, and every hover colour on the site is
   scoped to the component that wants it - `.menu a:hover`, `.fcol a:hover`,
   `.gg-h .all:hover`, `.sec-h .row .more:hover`. The prototype has no global
   link hover at all, and neither may this.

   It replaces the base layer's `a{color:var(--ds-color-brand-accent)}`, and
   because that rule's hover half is written `a:where(:hover)` it replaces both
   states at once. Do not add an `a:hover` here to "restore" anything - a
   global hover at (0,1,1) outranks any component rule at (0,1,1) or less and
   silently repaints controls that have their own treatment. See _base.scss. */
a {
  color: inherit;
  text-decoration: none;
}

a:focus-visible, button:focus-visible {
  outline: 3px solid var(--ds-color-brand-accent);
  outline-offset: 3px;
}

/* THE RING IS DRAWN ON THE GROUND, NOT ON THE CONTROL. `outline-offset:3px` puts
   it 3px outside the box, so on a `brand-primary` flood the accent ring is
   measured against brand red - 1.22:1 - and keyboard focus inside those panels
   had no visible indicator at all. Not a weak one: none. White measures 3.98:1
   there against a 3:1 floor (WCAG 2.4.11). BUILD-DECISIONS §4.7.

   Here rather than in the two component partials because focus behaviour is one
   idea and splitting it is how a third flooded panel ships without it. `.sp.hot`
   is the split CTA on home, About and portfolio; `.ct.hot` is the press kit's
   closing panel, whose white underlined links had the same invisible ring. Both
   at (0,3,1) against the rule above at (0,1,1). */
.sp.hot a:focus-visible, .sp.hot button:focus-visible,
.ct.hot a:focus-visible, .ct.hot button:focus-visible {
  outline-color: var(--ds-color-border-focus-on-brand);
}

p {
  margin: 0;
}

img {
  max-width: 100%;
}

/* Headings: condensed, weighted - and never uppercase (client instruction) */
h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--ds-font-heading);
  font-weight: 600;
  line-height: 1.03;
  letter-spacing: -0.008em;
  text-wrap: balance;
  text-transform: none;
}

h1 {
  font-size: clamp(38px, 7vw, 80px);
}

h2 {
  font-size: clamp(29px, 4.4vw, 52px);
}

h3 {
  font-size: clamp(20px, 2.3vw, 27px);
}

h4 {
  font-size: 19px;
  line-height: 1.18;
}

.shell {
  max-width: var(--ds-layout-container-max);
  margin: 0 auto;
  padding-left: var(--ds-layout-container-gutter);
  padding-right: var(--ds-layout-container-gutter);
}

.eyebrow {
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
  font-weight: 700;
}

.lede {
  font-size: clamp(16.5px, 1.6vw, 19px);
  line-height: 1.62;
  color: var(--ds-color-fg-default);
  max-width: 58ch;
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--ds-font-heading);
  font-size: 17px;
  font-weight: 600;
  padding: 13px 24px;
  border: 2px solid transparent;
  background: var(--ds-color-brand-primary);
  color: var(--ds-color-fg-on-accent);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s, transform 0.12s;
}

.btn:hover {
  background: var(--ds-color-brand-accent);
  transform: translateY(-1px);
}

/* outlined CTA: orange rule, white label, fills solid on hover */
.btn.line {
  background: transparent;
  border-color: var(--ds-color-brand-accent);
  color: var(--ds-color-fg-heading);
}

.btn.line:hover {
  background: var(--ds-color-brand-accent);
  border-color: var(--ds-color-brand-accent);
  color: var(--ds-color-fg-heading);
}

.btn.alt {
  background: var(--ds-color-brand-accent);
  color: var(--ds-color-fg-on-accent);
}

.btn.alt:hover {
  background: var(--ds-color-brand-accent-hover);
}

.btn.sm {
  padding: 9px 16px;
  font-size: 15px;
}

@media (prefers-reduced-motion: reduce) {
  .btn:hover {
    transform: none;
  }
}
.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 6px 11px;
  background: rgba(var(--ds-color-channel-light), 0.07);
  color: var(--ds-color-fg-default);
}

.pill.aq {
  color: var(--ds-color-brand-accent);
  border-color: rgba(var(--ds-color-channel-accent), 0.45);
}

/* ---------- nav: solid black, nothing shows behind ---------- */
/* PURE black, not the brand near-black. The sticky bar sits over the hero
   photographs and the prototype is explicit that nothing shows behind it;
   #0a0a0a is 10/255 lighter and reads as a seam against a black slide. */
.nav {
  position: sticky;
  top: 0;
  z-index: 80;
  background: rgb(var(--ds-color-channel-shadow));
}

.nav .shell {
  display: flex;
  align-items: center;
  gap: clamp(14px, 3vw, 38px);
  height: 74px;
}

.logo {
  display: flex;
  align-items: center;
  gap: 11px;
  font-family: var(--ds-font-heading);
  font-size: 27px;
  font-weight: 600;
  white-space: nowrap;
}

.mark {
  width: auto;
  height: 38px;
  flex: none;
  display: block;
}

.logo--full {
  gap: 0;
  font-size: 0;
  line-height: 0;
}

.logo--full img {
  height: 58px;
  width: auto;
  display: block;
  max-width: 100%;
}

.menu {
  display: flex;
  gap: clamp(14px, 2.2vw, 30px);
}

.menu a {
  font-family: var(--ds-font-heading);
  font-size: 18px;
  font-weight: 600;
  color: var(--ds-color-fg-default);
  padding: 5px 0;
  border-bottom: 3px solid transparent;
  white-space: nowrap;
  transition: 0.15s;
}

.menu a:hover {
  color: var(--ds-color-fg-heading);
}

.menu a.on {
  color: var(--ds-color-fg-heading);
  border-color: var(--ds-color-brand-primary);
}

.nav-r {
  margin-left: auto;
  display: flex;
  gap: 10px;
  align-items: center;
}

/* ---------- the same bar, expressed in core's blocks ---------- */
/* Everything above is the prototype's own CSS, and it assumes the prototype's
   own markup: a bare <img> in an <a>, six bare <a>s in a div, two more in
   another. The build renders the same bar out of the image, site-title,
   navigation and button blocks, which nest the same things one level deeper
   and ship stylesheets of their own. These rules are the seam, and each one
   names what it is fighting - none is a design value, every design value is
   above. */
/* THE NAV DOES NOT STICK WHEN IT IS WRAPPED, AND NOTHING LOOKS WRONG.
   `position:sticky` moves an element within its PARENT's box. In the prototype
   `.nav` is a child of <body>, so it travels the whole page. A template part
   renders inside `<header class="wp-block-template-part">`, and that wrapper is
   74px tall - the nav has nowhere to travel, so it scrolls away like a static
   bar. Every screenshot of it is correct, at the top of every page, which is
   why this survived: the element diff compares the nav's own box and the nav's
   own box is right.
   Measured at 390px, scrolled to 900: prototype nav at viewport top 0, build at
   -900. The wrapper is what has to stick, and it must carry the z-index too, or
   the bands below paint over the bar instead of under it. */
header:has(> .nav) {
  position: sticky;
  top: 0;
  z-index: 80;
}

/* And nothing may sit between the bar and the page. WordPress spaces the
   document's top-level blocks with a MARGIN -
   `:root :where(.is-layout-flow) > *{margin-block-start:var(--ds-layout-block-gap)}` -
   so <main> begins 26px below the header and a strip of the page ground shows
   under a bar the design puts flush against the hero. Visible at 1440 as a
   dark band above the first slide; the section diff cannot see it, because it
   measures each band's own box and the gap is between two of them. */
header:has(> .nav) + * {
  margin-block-start: 0;
}

/* And the same 26px again between EVERY band, which is the visible one. The
   rule above only stops the gap under the nav; each `.sec` after the first is
   another top-level flow child, so WordPress put a block gap above all of them
   and 26px of page ground - rgb(19,19,19), lighter than most bands - showed as
   a bar across the full width at every section seam. It compounds down the
   page: #about started 52px low, #press 156px low.

   THE SECTION DIFF PASSED THE WHOLE TIME, and cannot ever catch this: it
   anchors on each band's own box and measures inside it, so a gap BETWEEN two
   boxes is in neither. It was reported from a screenshot. `tools/section-seams`
   is the shape of check that does catch it - compare each band's top against
   the previous band's bottom and assert the prototype's gap.

   The prototype stacks its sections flush and gives each one its own padding,
   which is why every band's HEIGHT already matched while every band's POSITION
   was wrong.

   THE BANDS ARE CHILDREN OF `.entry-content`, NOT OF `main`. Post content
   renders inside a wrapper of its own, so `main > .sec` matches nothing - and
   a rule written against it changes nothing while reading as entirely correct
   in the stylesheet and compiling and shipping without complaint. That was the
   first attempt at this fix and the seam measurement was byte-identical after
   it. Read the parent chain out of the browser; do not infer it from the
   template.

   `.entry-content:has(> .sec)` is (0,2,0) and beats the layout rule's (0,1,0)
   outright, the same trick as `footer:has(> .shell)` in _tiles.scss. */
.entry-content:has(> .sec) > * {
  margin-block-start: 0;
}

/* AND THE SAME THING ONE LEVEL OUT, for templates whose bands are children of
   `main` itself rather than of post content.

   The paragraph above says "`main > .sec` matches nothing" and that was true of
   every template on this build until `single.html` and `archive.html` were
   written: those two compose their bands in the template rather than pulling
   them from a page's `post_content`, so the wrapper `.entry-content` never
   exists and the rule above cannot reach them.

   IT REPRODUCED EXACTLY THE DEFECT ABOVE, measured rather than predicted:
   `#body` and `#more` both computed `margin-top:26px` on a rendered article
   while every band on `/news/` computed 0. Same 26px bar of page ground at every
   seam, same compounding offset, same reason no per-band diff could see it -
   found by reading the parent chain out of the browser, which is what that
   paragraph tells you to do.

   `main:has(> .sec)` is (0,1,1) - `:has()` takes its argument's specificity -
   against the layout rule's (0,0,1), so it wins outright. Scoped by `:has()`
   rather than written as a bare `main > *` for the same reason as the rule
   above: a `main` whose children are NOT bands still wants core's block gap. */
main:has(> .sec) > * {
  margin-block-start: 0;
}

/* The mark is a bare <img> in the prototype and an image BLOCK here, so
   `.mark` lands on the <figure> and the <img> inside it would render at its
   natural 200px. Size the image and let the figure be a box of no
   consequence. */
.logo .mark {
  margin: 0;
  line-height: 0;
}

.logo .mark img {
  height: 38px;
  width: auto;
  display: block;
}

/* The wordmark is the site title, so the type has to reach a <p> the design
   never sees. `.logo` sets it; this stops the paragraph interrupting it. */
.logo .wp-block-site-title {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
  font-family: inherit;
  line-height: inherit;
}

/* THE MARK AND THE WORDMARK ARE ONE LINK, which is what the prototype has and
   what the build had lost. `ugcza-homepage-brief.html` writes the whole thing
   as `<a href="#top" class="logo" aria-label="UGCZA - back to top">` with the
   <img> inside it; here `.logo` is a GROUP holding an image block and a
   site-title block, so only the wordmark was clickable and the emblem beside it
   was dead. `visual-diff.mjs` had been printing the evidence the whole time -
   `a.logo` in the prototype's element list against `div.logo` in the build's -
   and an element-name mismatch on a page full of block-vs-prototype tag
   differences is not a thing anyone reads as a defect.

   A STRETCHED LINK RATHER THAN A SECOND ANCHOR, and the reason is the
   accessibility tree. Giving the image block a `linkDestination` of its own
   makes the bar carry TWO adjacent links to the same URL, which is a
   well-known screen-reader annoyance and is not "one unit" in any sense that
   matters. This keeps exactly one link and grows its hit area to the group.

   It is also the only route that keeps the wordmark editable. The site title is
   one string the client changes in Settings - `parts/header.html` says so - and
   the alternatives all end with it typed into a template: `wp:group` takes no
   href, and there is no core block for a linked group.

   The emblem's `alt=""` stays empty and that is now load-bearing rather than
   incidental: the link's accessible name comes from the title text, so alt text
   on the image would append a second copy of the name to it.

   ONE MEASUREMENT NOTE. An absolutely-positioned ::after is out of flow, so
   `getBoundingClientRect()` on the <a> still returns the TEXT's box - the tap
   target the audit reports is the wordmark, not the area a finger can hit. The
   real target is the whole 38px-tall group. */
.nav .logo {
  position: relative;
}

.nav .logo .wp-block-site-title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* CORE OWNS THE MENU LINK'S COLOUR AND CANNOT BE OUTRANKED ON THE LINK.
   `wp-includes/blocks/navigation/style.css` carries
   `.wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content{color:inherit}`,
   which doubles its own class to reach (0,3,0) - `.menu a` is (0,1,1) and
   loses, so every item inherited the body's near-white and the prototype's
   #b6b6b6 never appeared. Set the colour on the ANCESTOR instead, which
   `inherit` resolves to, and the fight disappears.

   The current page is the same story from the other end: the prototype marks
   it with `class="on"`, core marks the <li> with `current-menu-item` and the
   link with `aria-current`. Colour from the item, underline from the link,
   where nothing competes for the border. */
.menu {
  color: var(--ds-color-fg-default);
}

.menu li:hover {
  color: var(--ds-color-fg-heading);
}

.menu .current-menu-item {
  color: var(--ds-color-fg-heading);
}

.menu a[aria-current=page] {
  border-color: var(--ds-color-brand-primary);
}

/* ---------- the mobile menu, which the prototype does not have ----------
   The design's answer below 880px was `.menu{display:none}` and nothing else.
   Overruled 2026-08-17; the reasoning, including why the outlined button is
   the control that yields, is BUILD-DECISIONS section 25.

   THE BREAKPOINT IS THE ONLY THING BEING FOUGHT HERE. Core's navigation block
   already ships the whole overlay - the dialog, the focus trap, Escape, the
   `aria-expanded` wiring - and switches it on below 600px:

     @media (min-width:600px){
       .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open)
         {display:block;position:relative;width:100%;z-index:auto}
       .wp-block-navigation__responsive-container-open:not(.always-shown)
         {display:none}
     }

   The design switches at 880px, so the 600-880 band is ours to put back. Both
   rules below restate core's own selector under `.nav .menu`, which takes them
   from (0,3,0) and (0,2,0) to (0,5,0) and (0,4,0) - won on specificity rather
   than on load order, because the block stylesheet's position in the cascade
   is core's to change. */
@media (min-width: 600px) and (max-width: 879.98px) {
  .nav .menu .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) {
    display: none;
    position: fixed;
    width: auto;
    z-index: auto;
  }
  .nav .menu .wp-block-navigation__responsive-container-open:not(.always-shown) {
    display: flex;
  }
}
/* THE OVERLAY IS WHITE UNTIL SOMETHING SAYS OTHERWISE. Core carries
   `.wp-block-navigation:not(.has-background) ... .is-menu-open ...{background-color:#fff}`
   and a matching `:not(.has-text-color)` rule for `color:#000`, both (0,5,0).
   `.menu.menu` doubles the class to clear them at (0,6,0) - the same move core
   makes on its own link colour, and the reason the comment above about
   `wp-block-navigation-item__content` exists. The alternative is setting the
   colours as block attributes, which puts two design values in a template
   instead of in the design system. */
.nav .menu.menu .wp-block-navigation__responsive-container.is-menu-open {
  background: rgb(var(--ds-color-channel-shadow));
  color: var(--ds-color-fg-heading);
}

/* The opener and the close control. 44px square, not core's bare 24px icon:
   the audit reports anything under 24 and this is the one new control on the
   site, so it is built at the comfortable size rather than the minimum.

   THIS RULE MUST NOT CARRY `display`, and getting that wrong put a hamburger
   in the DESKTOP bar. Core hides the opener with
   `@media (min-width:600px){...-open:not(.always-shown){display:none}}` at
   (0,2,0); a `display:flex` here is (0,3,0) and unconditional, so it won at
   every width and the 1440 bar - pixel-exact against the prototype - grew a
   fourth control. Core's own base rule already makes the opener a flex box;
   WHICH WIDTHS IT SHOWS AT is decided in the media block above and nowhere
   else. Only the close button needs a display of its own, because core gives
   it none. */
.nav .menu .wp-block-navigation__responsive-container-open,
.nav .menu .wp-block-navigation__responsive-container-close {
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  color: var(--ds-color-fg-heading);
}

.nav .menu .wp-block-navigation__responsive-container-close {
  display: flex;
}

.nav .menu .wp-block-navigation__responsive-container-open:hover,
.nav .menu .wp-block-navigation__responsive-container-close:hover {
  color: var(--ds-color-brand-primary);
}

/* Inside the overlay the items are the bar's own type, one per line, at the
   size the rest of the site gives a sub-heading. `.menu a`'s 3px underline is
   a bar affordance and reads as a stray rule stacked vertically, so the
   current page is marked with the accent colour and a left rule instead. */
.nav .menu .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
  gap: 2px;
}

.nav .menu .wp-block-navigation__responsive-container.is-menu-open a {
  font-size: 23px;
  padding: 11px 0 11px 16px;
  border-bottom: 0;
  border-left: 3px solid transparent;
  width: 100%;
}

.nav .menu .wp-block-navigation__responsive-container.is-menu-open a[aria-current=page] {
  border-left-color: var(--ds-color-brand-primary);
  color: var(--ds-color-fg-heading);
}

/* THE BAR HAS NO ROOM FOR A FOURTH CONTROL AT 390px. The outlined button is
   the one that goes: its destination is `/press-kit/`, which is also the
   menu's "Press" item, so below 880 it was the only thing in the bar
   duplicating something the overlay now offers. "Join Discord" stays - it is
   the primary call and is in no menu. Section 25.3, and the page-specific
   call still owed for sub-pages inherits this: whatever it becomes, it is
   desktop-only. */
@media (max-width: 879.98px) {
  .nav-r .wp-block-button.line {
    display: none;
  }
}
/* `.is-layout-flex > :is(*, div){margin:0}` is (0,1,1) and beats `.nav-r`'s
   own (0,1,0), so the auto margin that pushes the buttons to the right edge
   was being zeroed and the row sat wherever the shell's justification left
   it. */
.nav .shell > .nav-r {
  margin-left: auto;
}

/* The two buttons are `.nav-r`'s own flex children in the prototype; core's
   button block requires the `wp:buttons` wrapper, so here the row's gap and
   its refusal to wrap belong to that wrapper. Its default 26px gap wrapped the
   pair onto two lines at 390px and pushed both out of the 74px bar. */
.nav-r .wp-block-buttons {
  display: flex;
  flex-wrap: nowrap;
  gap: 10px;
  align-items: center;
}

/* ---------- hero: background slideshow, copy stays put ---------- */
/* PURE black, not the brand's #0a0a0a, and the channel exists for exactly this.
   It is only ever seen through a crossfade, where two half-transparent
   photographs let about a quarter of it through - so nothing on the page would
   ever have shown that it was 10/255 out. Measured against the prototype, which
   paints #000 here on purpose, for the same reason the nav does. */
.hero {
  position: relative;
  background: rgb(var(--ds-color-channel-shadow));
  overflow: hidden;
}

/* FULL-BLEED, and nothing here makes it so - `templates/front-page.html` does,
   by laying main and post content out as FLOW rather than constrained. Said
   here because it is invisible from this file and it was briefly wrong: a
   constrained wrapper capped every band at the 1280px content width, measured
   at 1280 against the prototype's 1440, with a strip of page showing down each
   side of the photographs and nothing anywhere reporting it. A `100vw` breakout
   sat here while that was true; it was removed once the template was fixed,
   because `100vw` includes the scrollbar and would leave the band a scrollbar's
   width too wide on the desktop browsers that still have one.
   The probe named 'hero stage' compares the used width, so putting the
   constraint back fails the comparison rather than shrinking the hero quietly. */
.hstage {
  position: relative;
  min-height: clamp(460px, 76vh, 700px);
  display: grid;
}

.hbg {
  grid-area: 1/1;
  position: relative;
  opacity: 0;
  transition: opacity 0.9s ease;
}

.hbg.on {
  opacity: 1;
}

/* The frame each of the 10 supplied photographs is mounted in. The frame is
   what zooms; the layer around it is what fades. One element cannot do both,
   because a transition and an animation on the same property fight. */
.hbg i {
  position: absolute;
  inset: 0;
  display: block;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  /* The RESTING state is the END of the zoom, and the run time matches the
     carousel dwell exactly (5500ms). When .on is removed the animated value and
     the resting value are therefore identical - mismatch them and the outgoing
     photo snaps back to its start scale in the middle of the crossfade. */
  transform: scale(1.07);
}

/* The prototype painted these as CSS backgrounds. They are <img> in the build
   so the browser can pick a width out of a srcset and so nine of the ten can be
   held off the critical path - neither is possible with a background-image,
   which is 620 KB and the LCP element. See PERFORMANCE-SPEC section 1.
   The frame around each picture is unchanged and still carries the zoom, so
   every rule below, INCLUDING the reduced-motion block, still addresses
   `.hbg i` and nothing about the motion moved. */
.hbg img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Slow drift while a photo holds the stage: 7% over 5.5s, ~1.3% a second.
   Deliberately below the threshold where you catch it moving - you only notice
   the frame was tighter than you thought.
   THIS DURATION IS ALSO THE CAROUSEL'S DWELL. hero.js reads it back off this
   very declaration rather than carrying a second copy of the number, so
   changing 5500ms here changes how long each photograph holds the stage, and
   the two can no longer be given different values by accident. */
.hbg.on i {
  animation: kenburns 5500ms linear forwards;
  will-change: transform;
}

@keyframes kenburns {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(1.07);
  }
}
/* The pointer is deliberately ignored: hovering the hero does not stop the
   carousel and does not stop the zoom, so the drift runs straight through.
   Keyboard focus still pauses both - that is the accessible half, and the zoom
   pauses with the carousel or the control reads as half-obeyed.

   :has(:focus-visible), NOT :focus-within, AND THE DIFFERENCE IS A REPORTED
   DEFECT. Clicking an arrow focuses the button it clicked, so :focus-within
   matched a mouse user immediately - the zoom froze mid-drift and the carousel
   stopped, for good, until they happened to click somewhere outside the hero.
   Using the arrows is the one thing most likely to make a visitor think the
   slideshow is broken, and it was the interaction that broke it.
   :focus-visible is the browser's own answer to "did they arrive by keyboard",
   so the accessible half is kept exactly and the pointer half stops firing.
   The prototype has the same defect; this is a deliberate divergence from it,
   recorded in TECH-DEBT section 9. hero.js carries the matching guard on its
   own half of the pause - the two must agree or the zoom and the advance
   disagree about whether they are paused. */
.hero:has(:focus-visible) .hbg.on i {
  animation-play-state: paused;
}

/* black gradient scrim - client: "so we can read clearly" */
.scrim {
  grid-area: 1/1;
  position: relative;
  z-index: 2;
  background: linear-gradient(90deg, rgba(var(--ds-color-channel-shadow), 0.94) 0%, rgba(var(--ds-color-channel-shadow), 0.84) 32%, rgba(var(--ds-color-channel-shadow), 0.44) 68%, rgba(var(--ds-color-channel-shadow), 0.2) 100%), linear-gradient(0deg, rgba(var(--ds-color-channel-shadow), 0.8) 0%, transparent 42%);
}

/* Keep .shell's 1280px + gutters; constrain the COPY, not the container,
   so the block stays left-aligned under the left-weighted scrim. */
.hbody {
  position: relative;
  z-index: 3;
  grid-area: 1/1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 19px;
  padding-top: clamp(40px, 6vw, 74px);
  padding-bottom: clamp(78px, 9vw, 104px);
}

.hbody > * {
  max-width: min(560px, 100%);
}

/* Core's flow layout puts a block-gap margin on every child of a group, through
   `:root :where(.is-layout-flow) > *`. That outranks the base layer's
   `p{margin:0}` and `h1{margin:0}`, which cannot beat it at any specificity
   they are allowed to have, so it arrives silently the moment this section is
   written as block markup: measured at 26px on the lede and on the scrim, and
   on nine of the ten photographs, which then sat 26px lower than the first one.
   Inside the hero that margin is never wanted - the stage stacks its layers in
   one grid cell and the copy is spaced by `gap`. Zeroed here, at equal
   specificity and later in the sheet, and deliberately ABOVE `.hcta`, which
   restates the 8px the design puts over the buttons and has to win. This is
   core's default leaking through the markup, not a design value. */
.hstage > *, .hbody > *, .htag > * {
  margin-block: 0;
}

.hbody h1 em {
  font-style: normal;
  color: var(--ds-color-brand-primary);
}

.htag {
  display: flex;
  gap: 9px;
  flex-wrap: wrap;
}

.hcta {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 8px;
}

/* THE FLEX ITEM HERE IS THE WRAPPER, NOT THE BUTTON, and the base layer pins it.
   Core's button block nests `a.wp-block-button__link` inside `div.wp-block-button`,
   and a className on the block lands on the WRAPPER - so `.btn` from the base
   layer styles the div, where `height: var(--ds-control-height)` (46px) applies.
   The link is the thing that carries the real control geometry - 13px padding,
   28.05px leading, 2px border, 58px - and `_wp-bridge.scss` gives it `height:auto`
   so that leading decides its size (PORT-27). Nothing does the same for the
   wrapper, so the wrapper stayed 46px with a 58px link overflowing it by 6px top
   and bottom.

   That reads as correct: the buttons LOOK right, because the link paints itself
   and overflow is not clipped. What it breaks is the column. `.hbody` centres its
   children between a 40px top and 78px bottom padding, so a `.hcta` measured 12px
   short moves the whole block - pills, headline, lede and buttons - 6px DOWN.
   Measured at 390px: htag at 181.6 against the prototype's 175.6, and the same
   6px at 1440 (134.3 against 130.3, where the wrapper collapses to 50px instead).
   Every line of hero copy was off by 6px at both widths, which is most of what
   the pixel diff was reporting on this band - 6.8% at 390.

   `height:auto` is the whole fix: the wrapper then takes its height from the line
   box the link sits in, which is the link's own 58px. Scoped to `.hcta` because
   this file owns `.hcta`; the same defect is in every other `.btn` on the site
   and the general fix belongs beside "the wrapper must paint nothing - it is a
   layout box" in `_wp-bridge.scss`. Reported rather than made there, because that
   file is shared with two other sessions working this page. */
.hcta .wp-block-button.btn {
  height: auto;
}

/* The slider controls share .shell with the nav, so the dots line up with the
   logo and the arrows line up with the nav CTAs - same max-width, same gutter,
   at every viewport. Padding lives on .shell, never restated here. */
.hnav {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  padding-bottom: 24px;
}

.hnav .shell {
  display: flex;
  align-items: center;
  gap: 14px;
}

.hdots {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.hdot {
  width: 26px;
  height: 4px;
  background: rgba(var(--ds-color-channel-light), 0.26);
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: background 0.2s;
}

.hdot:hover {
  background: rgba(var(--ds-color-channel-light), 0.55);
}

.hdot.on {
  background: var(--ds-color-brand-primary);
}

.harrows {
  margin-left: auto;
  display: flex;
  gap: 8px;
}

/* Outline only - a hairline rule, not a filled control. The one place the
   design keeps a 1px border, because the slider sits over photography and
   needs an edge to read against it.
   NO LONGER THE ONLY PLACE, AND NOT ONLY THE HERO'S ANY MORE. On 2026-08-14 the
   client asked for the footer's social buttons in "the same design style as the
   hero slider buttons", so `.fsoc .wp-block-social-link-anchor` in _tiles.scss
   now restates every value below - the 46px square, the .5 hairline, the
   near-white glyph and the hover wash. It could not be folded into one selector
   list here, for the specificity reason that file gives. CHANGE THE TWO
   TOGETHER: nothing measures them against each other, and a drift renders a
   working-looking page on both. */
.harrow {
  width: 46px;
  height: 46px;
  padding: 0;
  background: transparent;
  border: 1px solid rgba(var(--ds-color-channel-paper), 0.5);
  color: var(--ds-color-fg-heading);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.15s, background 0.15s;
}

.harrow:hover {
  border-color: var(--ds-color-fg-heading);
  background: rgba(var(--ds-color-channel-paper), 0.1);
}

.harrow svg {
  width: 13px;
  height: 13px;
  display: block;
}

/* ---------- start.gg strip: one band under the hero, ~150px total ---------- */
/* PURE black, not the brand's near-black, and the same call the nav makes for
   the same reason - nothing shows behind either of them. Folded onto `subtle`
   (#0a0a0a) during the port, which is 10/255 out and reads as a seam where the
   band meets the header. `channel-shadow` is the project's existing true-zero
   token; see the note beside it in the brand file. */
.gg {
  background: rgb(var(--ds-color-channel-shadow));
}

.gg .shell {
  padding-top: 15px;
  padding-bottom: 17px;
}

.gg-h {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}

.gg-h .l {
  font-family: var(--ds-font-label);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
  display: flex;
  align-items: center;
  gap: 7px;
}

/* studio tally light */
.gg-h .l i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ds-color-brand-primary);
  flex: none;
  animation: tally 2.4s ease-in-out infinite;
}

@keyframes tally {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(var(--ds-color-channel-key), 0.55);
    opacity: 1;
  }
  55% {
    box-shadow: 0 0 0 6px rgba(var(--ds-color-channel-key), 0);
    opacity: 0.5;
  }
}
@media (prefers-reduced-motion: reduce) {
  .gg-h .l i {
    animation: none;
  }
}
@keyframes ggp {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.2;
  }
}
@media (prefers-reduced-motion: reduce) {
  .gg-h .l i {
    animation: none;
  }
}
.gg-h .all {
  margin-left: auto;
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

.gg-h .all:hover {
  color: var(--ds-color-brand-accent);
}

/* THREE TILES ACROSS THE SAME BAND WIDTH, on client instruction 2026-08-14.
   The band keeps the shell it always had; what changes is that three tracks
   share it instead of as many 208px ones as fit, so a tile goes from ~208px to
   about a third of the shell. The prototype's six-card rail is the declared
   divergence - see UGCZA_STARTGG_CARDS in blocks-startgg.php.

   `auto-fit` is kept rather than a bare `repeat(3,1fr)` and that is the whole
   responsive story: the track floor is `min(300px,46%)`, so three thirds fit
   down to about a tablet and the browser lays out two, then one, below that.
   Three hard columns at 390px would be ~120px tiles, which is unusable, and a
   `repeat(3,1fr)` is exactly how that ships without anyone seeing it on a phone.

   `grid-auto-rows:0` IS GONE, AND ITS REMOVAL IS THE POINT. It clipped anything
   that wrapped, which was right when the band carried six cards and the tail was
   surplus - the whole season is on /tournaments/ anyway. With exactly three
   tiles a wrap loses a THIRD of the band, so at any width where three tracks do
   not fit the clip would silently drop a tournament the client asked to show.
   The tiles wrap and stay visible instead, which is why there is a row gap now. */
.gg-rail {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 46%), 1fr));
  column-gap: 14px;
  row-gap: 14px;
}

/* The card ground is its own role, not surface-2. Only a COMPLETED card ever
   shows it - the other two states override it - which is why folding it onto
   the nearest step survived a pass unnoticed. See the brand file. */
.gg-c {
  position: relative;
  overflow: hidden;
  background: var(--ds-color-bg-startgg-card);
  min-width: 0;
  padding: 12px 14px 13px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

/* Artwork sits behind the copy under a scrim - no extra height, and the
   client's "black gradient behind text so we can read clearly" still holds.

   The PICTURE inside this layer is an `<img loading="lazy">` rather than the
   layer's own background, because a CSS background has no lazy mechanism and
   six of these fired before the load event. The shared fill rule is at the top
   of _tiles.scss. `background-size`/`background-position` stay because they are
   still this layer's contract and the probe suite compares them against the
   prototype's.

   THE SCRIM STILL PAINTS OVER THE PICTURE. `::after` is the last child of its
   originating element, so with both at `position:absolute` and z-index auto the
   pseudo-element paints after the image. Nothing about the zoom, the greyscale
   or the dimming moved: they are on this layer and they carry the image with
   them. */
.gg-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
  transition: transform 0.55s ease-out, opacity 0.35s ease;
}

/* AN EVEN WASH ACROSS THE WHOLE TILE, not a 90deg ramp. Client instruction
   2026-08-14, and it is a fix rather than a preference: the ramp's stops were
   0%/58%/100% of a 208px card, so the mid stop sat 121px in and read as one
   wash. At 381px it sits 221px in, the ramp is nearly twice as long, and the eye
   resolves it as two zones - a flat dark panel butted against a photograph. The
   client reported the tiles as looking like "two images", which is exactly that.

   THE ALPHA IS MEASURED, AND THE FIRST VALUE WAS WRONG. Flat .8 was reasoned
   from the ramp's LIGHTEST stop (.64, on the right) when the number that governs
   is its darkest (.94, on the LEFT) - which is precisely where the date line
   sits. Driven against the real banners it put `.d` at 3.56 on the live tile and
   4.00 on completed, under the 4.5 floor: a change that reads as a small
   lightening of an area with no copy in it actually lightened the ground under
   the smallest text on the card.
   .92 is what passes with the banners now shipping, worst-case sampled from the
   LIGHTEST pixel anywhere in the tile rather than under the glyphs, so the
   figure holds wherever a future banner puts its bright area. */
.gg-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(var(--ds-color-channel-scrim), 0.92);
}

.gg-c > *:not(.gg-bg) {
  position: relative;
  z-index: 1;
}

.gg-c.past .gg-bg, .gg-c.gg-c--completed .gg-bg {
  filter: grayscale(1);
  opacity: 0.5;
}

.gg-c:hover .gg-bg {
  opacity: 0.85;
}

/* Hover-zoom, matching the tournaments page. The card is a link, so the
   keyboard gets the same move - focus-visible sits outside the (hover:hover)
   guard, or tab users on a touch device would never see it. */
@media (hover: hover) {
  .gg-c:hover .gg-bg {
    transform: scale(1.07);
  }
}
.gg-c:focus-visible .gg-bg {
  transform: scale(1.07);
  opacity: 0.85;
}

/* one line, truncate rather than wrap */
.gg-c .m, .gg-c .d, .gg-foot em {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.gg-c .n {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.gg-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: space-between;
  margin-top: 2px;
}

.gg-foot em {
  font-style: normal;
  font-family: var(--ds-font-label);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

.gg-c.up .gg-foot em, .gg-c.live .gg-foot em,
.gg-c.gg-c--upcoming .gg-foot em, .gg-c.gg-c--live .gg-foot em {
  color: var(--ds-color-fg-heading);
}

.gg-foot .go {
  margin-top: 0;
  padding-top: 0;
  border: 0;
  flex: none;
}

.gg-c:hover {
  background: var(--ds-color-bg-subtle);
}

.gg-c .d {
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  white-space: nowrap;
}

.gg-c .n {
  font-family: var(--ds-font-heading);
  font-size: 18px;
  font-weight: 600;
  line-height: 1.06;
  color: var(--ds-color-fg-heading);
}

.gg-c .m {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  color: var(--ds-color-fg-default);
  font-variant-numeric: tabular-nums;
}

.gg-c .go {
  margin-top: 3px;
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

/* Display priority: upcoming, then running, then past. The left edge encodes
   state - orange for what you can still enter, red for what is happening now,
   nothing for what is over. */
.gg-c.up, .gg-c.gg-c--upcoming {
  box-shadow: inset 3px 0 0 var(--ds-color-brand-accent);
  background: var(--ds-color-bg-subtle);
}

.gg-c.up .go, .gg-c.gg-c--upcoming .go {
  color: var(--ds-color-brand-accent);
}

/* The brand red (#ea3700) and orange (#e96007) are near-identical in a 3px
   sliver, so the edge alone cannot separate "running" from "upcoming".
   Running gets a warm wash off the edge as well - state readable at a glance,
   without inventing a colour the brand does not own. */
/* The live tile keeps a key-colour cast, evenly, for the same reason the wash
   above went flat. Its old ramp opened on 42% brand red, which at 381px was a
   wide coloured block on the left - the most "second image" of the three states,
   because it differed in HUE and not merely in darkness. Layered rather than
   ramped: the key tint sits under the same .8 scrim every other tile carries, so
   the state still reads as different without a seam and without the scrim floor
   above being weakened. */
.gg-c.live .gg-bg::after,
.gg-c.gg-c--live .gg-bg::after {
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-scrim), 0.9), rgba(var(--ds-color-channel-scrim), 0.9)), linear-gradient(0deg, rgba(var(--ds-color-channel-key), 0.28), rgba(var(--ds-color-channel-key), 0.28));
}

.gg-c.live, .gg-c.gg-c--live {
  box-shadow: inset 3px 0 0 var(--ds-color-brand-primary);
  background: var(--ds-color-bg-subtle);
}

.gg-c.live .go, .gg-c.gg-c--live .go {
  color: var(--ds-color-brand-primary);
}

.gg-c.past .n, .gg-c.gg-c--completed .n {
  color: var(--ds-color-fg-default);
}

.gg-c.past .go, .gg-c.gg-c--completed .go {
  color: var(--ds-color-fg-muted);
}

.gg-state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.gg-state i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.gg-c.live .gg-state, .gg-c.gg-c--live .gg-state {
  color: var(--ds-color-brand-primary);
}

.gg-c.live .gg-state i, .gg-c.gg-c--live .gg-state i {
  animation: ggp 2s ease-in-out infinite;
}

/* Intent, on top of state. State says when the tournament is; intent says what
   it wants from you, and the two are not the same axis - entries can close
   before a tournament starts, at which point an "upcoming" card wants you to
   watch, not to enter. So the key colour tracks state (orange upcoming, red
   running) while fill-vs-plate and arrow-vs-triangle carry intent. Colour is
   never the only signal, which is the same reason the running card has a wash
   as well as an edge.
   Register is the single filled control in the band - the strip's one thing to
   go and do, and it carries the brand's white-on-orange button label. Measured,
   that is 3.42:1, under the 4.5:1 small-text floor at 9.5px; --ink would give
   5.79:1. White is the client's call, taken deliberately - see TECH-DEBT. The
   watch chip is unaffected: white on its own dark plate is 18.8:1, immune to
   whatever artwork sits behind it, and it spends its key colour on the triangle
   and hairline, which are graphics and owe only 3:1 (measured 4.73:1). */
.gg-foot .go.cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 3px 8px;
  line-height: 1;
  --cta-key:var(--ds-color-brand-accent);
  --cta-edge:rgba(var(--ds-color-channel-accent),.62);
}

/* The tracking hangs a space off the last letter and doubles the gap. */
.gg-foot .go.cta .lbl {
  margin-right: -0.12em;
}

.cta-i {
  width: 11px;
  height: 9px;
  flex: none;
  display: block;
}

.i-play {
  order: -1;
  width: 9px;
  height: 9px;
  color: var(--cta-key);
}

.cta--reg .i-play, .cta--watch .i-go {
  display: none;
}

.gg-c.live .go.cta, .gg-c.gg-c--live .go.cta {
  --cta-key:var(--ds-color-brand-primary);
  --cta-edge:rgba(var(--ds-color-channel-key),.62);
}

/* Both set colour after .gg-c.up/.live .go, so a card that changes intent
   mid-session (see the countdown) is not left wearing the old label colour. */
.gg-c .go.cta--reg {
  background: var(--cta-key);
  color: var(--ds-color-fg-heading);
}

.gg-c .go.cta--watch {
  background: rgba(var(--ds-color-channel-scrim), 0.88);
  box-shadow: inset 0 0 0 1px var(--cta-edge);
  color: var(--ds-color-fg-heading);
}

.gg-c:hover .go.cta--watch, .gg-c:focus-visible .go.cta--watch {
  box-shadow: inset 0 0 0 1px var(--cta-key);
}

/* Status LEDs - a rack of lights across the strip, read left to right before
   any of the copy is. Three states, and hue is not what separates them: lit
   and pulsing (running), lit ring (coming up), unlit and recessed (done). That
   holds up under prefers-reduced-motion, where the pulse stops and solid-vs-
   ring still carries it, and for anyone who cannot tell the brand red from the
   brand orange in a 7px dot - which is the same reason the running card has a
   wash as well as a red edge.
   Positioned after .gg-c>*:not(.gg-bg), which would otherwise force it back to
   position:relative. */
.gg-c > .led {
  position: absolute;
  top: 11px;
  right: 12px;
  z-index: 2;
  width: 7px;
  height: 7px;
  border-radius: 50%;
}

/* Running: lit, with a steady bloom and the studio tally on top of it. */
.gg-c > .led--on {
  background: var(--ds-color-brand-primary);
  box-shadow: 0 0 6px 1px rgba(var(--ds-color-channel-key), 0.75), 0 0 0 0 rgba(var(--ds-color-channel-key), 0.55);
  animation: led 2.4s ease-in-out infinite;
}

@keyframes led {
  0%, 100% {
    box-shadow: 0 0 6px 1px rgba(var(--ds-color-channel-key), 0.75), 0 0 0 0 rgba(var(--ds-color-channel-key), 0.55);
  }
  55% {
    box-shadow: 0 0 6px 1px rgba(var(--ds-color-channel-key), 0.45), 0 0 0 5px rgba(var(--ds-color-channel-key), 0);
  }
}
/* Coming up: lit but hollow - the light is on, nothing is running through it. */
.gg-c > .led--due {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--ds-color-brand-accent), 0 0 5px 0 rgba(var(--ds-color-channel-accent), 0.45);
}

/* Done: unlit, and recessed rather than drawn - a dark lamp, not a grey dot. */
.gg-c > .led--off {
  background: rgba(var(--ds-color-channel-paper), 0.07);
  box-shadow: inset 0 0 0 1px rgba(var(--ds-color-channel-paper), 0.15);
}

/* The order matters: this has to sit after the rule it disables or it silently
   does nothing and the light keeps pulsing for people who asked it not to. */
@media (prefers-reduced-motion: reduce) {
  .gg-c > .led--on {
    animation: none;
  }
}
/* Keep the date clear of the lamp - it is a nowrap line that would otherwise
   ellipsis straight into it. */
.gg-c .d {
  padding-right: 15px;
}

/* No utility for this in the page yet: the lamp is the only visual, so the
   state needs a text equivalent inside the card link.
   .gg-c>*:not(.gg-bg) sets position:relative at (0,2,0) and outranks a bare
   .vh, which put the span back in flow - where a 1px box still collects the
   column's 5px gap and made every card 4px taller. Hence the child selector. */
.gg-c > .vh {
  position: absolute;
}

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

/* Two cards share the row under about 430px and the chip takes its width out
   of the countdown, which is the only thing in the footer that can shrink. The
   register arrow goes first: the solid fill already says "go and do this",
   where the watch triangle is the one mark that separates the two intents by
   shape rather than by colour, so it stays at every width. */
@media (max-width: 430px) {
  .gg-foot {
    gap: 6px;
  }
  .gg-foot .go.cta {
    padding: 3px 6px;
    gap: 4px;
  }
  .gg-foot .go.cta .lbl {
    letter-spacing: 0.06em;
    margin-right: -0.06em;
  }
  .cta--reg .i-go {
    display: none;
  }
}
/* ---------- sections ---------- */
/* `scroll-margin-top` IS PART OF THE BAND, not an afterthought on its heading.
   The nav is `position:sticky;top:0` and 74px tall, so an anchor the browser
   scrolls to lands underneath it. `_article.scss`, `_legal-page.scss` and
   `_info-hub-page.scss` each set 90px on their HEADINGS, with a comment naming
   the nav - the rule was written three times for headings and never once for a
   band, which left every section anchor on the site uncovered. Portfolio's
   `#reel` and `#events` lost 24px of eyebrow to it. BUILD-DECISIONS 25.4.

   THE STACK EXACTLY, not the stack plus clearance. Those three heading rules
   add 16px because a heading has no padding of its own; a band has 52-102px of
   it, and adding clearance on top opens a gap the design does not have. A page
   that stacks a second sticky bar under the nav overrides this: `_press-page.scss`
   does, for its 47px jump rail.

   TOURNAMENTS STACKS ONE TOO - `.filters`, measured stack 119px - AND HAS NO
   OVERRIDE ON PURPOSE. Nothing links to a section on that page today, so the
   rule would fix nothing and assert a number no render checks. If an anchor
   lands there, `tools/mobile-audit.mjs` reports it; that gate is the guard,
   not a speculative line here. */
.sec {
  padding: clamp(52px, 7.5vw, 102px) 0;
  scroll-margin-top: 74px;
}

/* THE #about -> #brands SEAM, RULED TIGHTER THAN THE PROTOTYPE.
   Measured at 1440 in both renders before changing anything, because both
   harnesses call this seam clean and both are right: the boxes touch, so
   section-seams.mjs reports 0/0, and each band's own height is exact, so
   visual-diff.mjs reports ok. What the eye reads as a gap is neither - it is
   226px of CLEAR SPACE made of #about's 102px bottom padding, #brands' 102px
   top padding and 22px of trailing space inside the stat strip. Identical in
   the prototype, so it was never a porting defect; it is the prototype's own
   rhythm, and the client asked for it closed on 2026-08-14.

   Both sides take the rhythm's FLOOR rather than its ceiling. 52px is not a
   new number - it is the value the clamp above already resolves to at 390 and
   below, so nothing is invented and THE 390 RENDER DOES NOT MOVE AT ALL. At
   1440 the clear space goes 226px -> 126px.

   Ids rather than a .pg-home scope, matching #partners, #portfolio and #press
   below: both are unique to the homepage - patterns/home-about.php and
   patterns/homepage.php are the only two files that emit them. Id (1,0,0)
   beats .sec (0,1,0), so this holds wherever the file lands in source order.

   A DELIBERATE DIVERGENCE, and the only seam on the site that carries one.
   visual-diff.mjs declares home:about and home:brands at 1440 for exactly this
   and will still report the real percentage; at 390 they must stay `ok`, and a
   divergence appearing there is a new finding, not this rule. */
#about {
  padding-bottom: 52px;
}

#brands {
  padding-top: 52px;
}

.sec.grey {
  background: var(--ds-color-bg-muted);
}

.sec.teal {
  background: var(--ds-color-bg-alt);
}

.sec.ink {
  background: var(--ds-color-bg-subtle);
}

/* Grounds for the two sections that had none of their own. Ids, so
   .sec.grey keeps its meaning and no markup moves. */
#partners {
  background: var(--ds-color-bg-surface-2);
}

/* ---------- Section wash (new) ----------
   One reusable layer; each section tunes it with custom properties. See the
   note by #about for why the veil is a separate layer from the photograph and
   why the bleed is structural. */
.swash {
  position: absolute;
  inset: -90px;
  z-index: 0;
  pointer-events: none;
  -webkit-mask-image: var(--w-mask);
  mask-image: var(--w-mask);
}

.swash::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--w-img) center/cover no-repeat;
  filter: blur(var(--w-blur)) saturate(0.18) brightness(var(--w-bright));
}

.swash::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(var(--ds-color-channel-muted), var(--w-veil));
}

/* --w-img is the ONE thing this layer does not declare. The photograph comes
   from the media library, on the element, so an editor can change what sits
   behind a band without a developer - `ugcza_section_wash_markup()` in
   mu-plugins/ugcza-content/blocks-playlists.php writes it, the same way
   .tile-art takes --tile-img. Everything below it was MEASURED against a
   specific source and stays here, so swapping the photograph cannot walk a
   band out of its tuning.
   The mask stops are pure black through the shadow channel rather than the ink
   surface: a mask reads alpha, so the two composite identically, but PORT-07
   reserves the channel for true black and a computed-style comparison sees the
   difference. */
#partners {
  position: relative;
  overflow: hidden;
}

#partners > .shell {
  position: relative;
  z-index: 3;
}

/* #partners HAS NO SWASH. It carries the property ground below instead, ruled
   2026-08-14 - BUILD-DECISIONS §19.1. Its blur/brightness/veil/mask tuning is
   deleted rather than commented out, and `'partners'` is out of
   `ugcza_wash_sections()`, so nothing emits a layer for this band to tune. */
/* ---------- Property ground (#partners) ----------
   Six event properties as full-height vertical blocks, cropped hard and
   covered by one flat scrim. Values in BUILD-DECISIONS §19.2, and every one of
   them was settled on a prototype rather than chosen here.

   THE COLUMN COUNT IS NOT A CONSTANT HERE. `--pg-count` comes from PHP, which
   caps it at six and excludes UGCZA Dojo - BUILD-DECISIONS §19.4 as amended.
   The fallback exists only so the layer degrades sanely if the property is ever
   missing; it is not the number the page renders, and lowering the cap needs no
   change in this file.

   THE SCRIM IS `::after` AND ABSOLUTELY POSITIONED, which is what keeps it out
   of the grid. A generated box inside a grid container becomes a grid item and
   would take a column of its own, leaving the last property unpainted and every
   block narrower than it should be - a defect that renders as a working-looking
   band, because six blocks and a scrim column still tile the section. Out of
   flow, it is not a grid item and covers the whole layer including the seams.

   NO GAP between the blocks, deliberately: the seam is the discontinuity
   between two crops, not a rule drawn between them. */
.pground {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: grid;
  grid-template-columns: repeat(var(--pg-count, 6), 1fr);
}

.pground::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(var(--ds-color-channel-scrim), var(--pg-scrim));
}

.pgb {
  position: relative;
  overflow: hidden;
}

/* `transform-origin` takes the SAME focal point as the image's own
   `object-position`, which `ugcza_tile_art()` writes inline. Zooming about the
   element's centre instead would walk the crop off whatever the editor aimed
   it at, and the further the zoom the worse the drift. */
.pgb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(var(--pg-zoom));
  transform-origin: var(--pg-pos, 50% 50%);
  filter: grayscale(1);
}

#partners {
  --pg-scrim:.87;
  --pg-zoom:1.4;
}

/* ---------- #portfolio: a ground, not a pool (client, 2026-08-14) ----------
   The band takes the Hype production frame as its BACKGROUND, ruled directly -
   BUILD-DECISIONS section 20. So the pool is gone: no mask, the picture covers
   the layer, and one vertical scrim does the darkening. The tuning that was
   here (blur 30, brightness .32, a .50 grey veil inside a radial mask at
   72% 64%) went with it, and `--w-bright` and `--w-veil` are no longer read for
   this band - the two pseudo-elements below name their own filter and their own
   background, so declaring either would be a value nothing consumes.

   THE CROP IS NOT A FRAMING PREFERENCE. `0% 50%/160%` exists to take "A Fight
   To The Top - An Esports Documentary", which is set into the bottom right of
   the source frame, outside the layer. At 160% the visible window is the left
   62.5% of the picture and the lockup begins at about 65%, so it clears - set
   type behind live type reads as a mistake, and cropping it is cheaper than
   asking for a clean plate. The enlargement is not a cost either way: the
   source is 1280 wide - the widest a YouTube frame comes - against a 2592px
   background box at 1440, so it is being scaled up regardless, and the 2px blur
   is there to settle that as much as to settle the picture.

   A GRADIENT RATHER THAN A FLAT VEIL, which is a deliberate exception to the
   rule that a flat scrim is safer where the client owns the copy length. It is
   arithmetic, not taste: the ground this replaced is #0a0a0a, and a flat veil
   dark enough to hold the section header near that leaves the picture reading
   at about a tenth of its range - which is the band as it was, with a
   photograph nobody can see. THE STOPS BELOW ARE ONE STEP DARKER THAN THE ONES
   THIS SHIPPED WITH, asked for by the client the same afternoon and taken from
   the treatment already prototyped as the darker alternative - BUILD-DECISIONS
   section 20.1. Measured on the built page with the copy hidden, the lightest
   pixel behind `.sec-h` is rgb(21,21,20) at 1440, where the lede holds 5.02:1
   (5.44:1 on flat ink, 4.84:1 at the first set of stops) and the eyebrow
   5.34:1. At 390 the band is 1964px tall, the header ends at 10.5% of it, and
   the ground there still measures #0a0a0a - the cost is desktop-only.

   THE 28% STOP IS THE HEADROOM AND IT IS THE THING TO WATCH. The scrim holds
   .94 flat down to 28% of the band and only ramps after that; `.sec-h` ends at
   25.0%, so there is about one more line of lede before the copy reaches the
   ramp. A longer paragraph eats it, and no gate here measures that - re-run the
   contrast reading if this band's copy is rewritten. */
#portfolio {
  position: relative;
  overflow: hidden;
}

#portfolio > .shell {
  position: relative;
  z-index: 3;
}

#portfolio .swash {
  --w-blur:2px;
  -webkit-mask-image: none;
  mask-image: none;
}

#portfolio .swash::before {
  background: var(--w-img) 0% 50%/160% no-repeat;
  filter: blur(var(--w-blur)) saturate(0.85);
}

#portfolio .swash::after {
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.97) 0%, rgba(var(--ds-color-channel-scrim), 0.955) 28%, rgba(var(--ds-color-channel-scrim), 0.84) 60%, rgba(var(--ds-color-channel-scrim), 0.84) 88%, rgb(var(--ds-color-channel-scrim)) 97%);
}

/* THE COVERAGE BAND IS A PHOTOGRAPH NOW, not a wash - BUILD-DECISIONS section
   22, on the client's instruction of 2026-08-14. It carried attachment 164, a
   320x160 crop at blur(66px) saturate(.18) brightness(.28) under a .70 grey
   veil inside a radial mask, over flat #1c1c1c. That renders as a faint haze
   and nothing else, so the ask was never "add a layer", it was "make the layer
   read". Hero slide 7 (attachment 132, the empty lit setup room) covers the
   layer at a 2px blur under a vertical scrim; the mask, the brightness and the
   grey veil are gone.

   THE QUIETEST PICTURE ON THE SITE, AND THAT IS THE POINT. This band is a wall
   of nine article screenshots, so the ground's job is depth rather than
   spectacle. The celebration frames all put their brightest region exactly
   where the section header sits. Slide 7 carries no set type, no faces and no
   bright centre, so it needs the least scrim and survives it best.

   THE VEIL IS THE BAND'S OWN GROUND, NOT THE SCRIM CHANNEL, and that is the
   whole reason `channel.surface-3` exists - see the note on it in
   brands/_ugcza.scss. With a dark source a black veil takes the band BELOW the
   #1c1c1c it replaced, which is the scrim-floor rule's failure mode inverted
   and is not covered by it.

   THE CLOSE IS AT 93%, NOT 97%, AND THE FOUR POINTS ARE NOT A TYPO. `.swash` is
   inset:-90px, so the LAYER is 180px taller than the band and the band occupies
   6.9% to 93.1% of it at 1440 - a stop written at 97% sits 45px BELOW the band's
   own bottom edge and never lands inside it. Measured, not reasoned: at 97% the
   last rows read #1a1a1c against the #1c1c1c they meet the black footer on; at
   93% they read #1c1c1c exactly, at both widths. The ratio moves with the band's
   height (6.9/93.1 at 1440, 3.4/96.6 at 390), so one percentage cannot close
   both - take the tighter, which closes early at 390 where early is harmless.
   #portfolio above still carries the 97% and is B-199, not this change.

   THE ORANGE IS THE CONSTRAINT, NOT THE HEADING. Measured on the rendered plate
   with the copy hidden: header plate #1f1f1f at 1440, where the eyebrow and the
   "All mentions" link hold 4.82:1 and "Coverage" 15.76:1. The orange crosses
   4.5:1 at a #242424 plate - eight levels of headroom - and the white tolerates
   #737373, so it never enters the arithmetic. Any retune of this band is a
   question about the orange. At 390 the header ends at 4.4% of a 2571px band,
   inside the flat part of the scrim, so both phone readings are unchanged at
   4.98:1 and the cost is desktop-only. */
#press {
  position: relative;
  overflow: hidden;
}

#press > .shell {
  position: relative;
  z-index: 3;
}

#press .swash {
  --w-blur:2px;
  -webkit-mask-image: none;
  mask-image: none;
}

#press .swash::before {
  filter: blur(var(--w-blur)) saturate(0.7);
}

#press .swash::after {
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-surface-3), 0.965) 0%, rgba(var(--ds-color-channel-surface-3), 0.96) 24%, rgba(var(--ds-color-channel-surface-3), 0.74) 58%, rgba(var(--ds-color-channel-surface-3), 0.74) 85%, rgb(var(--ds-color-channel-surface-3)) 93%);
}

#press {
  background: var(--ds-color-bg-surface-3);
}

.sec-h {
  margin-bottom: clamp(26px, 3.6vw, 44px);
  display: flex;
  flex-direction: column;
  gap: 13px;
}

/* The header owns its rhythm: a flex column on a 13px gap. Core's flow layout
   also writes `margin-block-start:var(--wp--style--block-gap)` onto every child
   of a group block, through `:root :where(.is-layout-flow) > *`, and on a flex
   container those margins ADD to the gap rather than collapsing into it - so
   every section header on the page ran 52px tall against the prototype's
   measured spacing (eyebrow, row, lede: two gaps at 26px). Equal specificity
   with the global rule (0,1,0), won on source order, because ugcza.css is
   enqueued after global-styles-inline-css. */
.sec-h > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

.sec-h .row {
  display: flex;
  align-items: flex-end;
  gap: 20px;
  flex-wrap: wrap;
}

.sec-h .row .more {
  margin-left: auto;
  font-family: var(--ds-font-heading);
  font-size: 17px;
  font-weight: 600;
  color: var(--ds-color-brand-accent);
}

.sec-h .row .more:hover {
  color: var(--ds-color-fg-heading);
}

/* about */
/* ---------- "Who we are": one grid, one 14px gutter ----------
   The stat row below runs on a 14px gap, so the copy and the reel run on the
   same one and the section reads as a single grid.

   align-items:center, not stretch: the copy is now much shorter than a 16:9
   reel (the watch call that used to fill that column has moved onto the
   video), so whichever side is shorter is optically centred against the
   other. That holds at every width without a second arrangement - the
   1120/1160 breakpoint dance the separate plate needed is gone with it. */
.about {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  align-items: center;
}

/* Core's flow layout writes `margin-block-start:var(--wp--style--block-gap)`
   onto every child of a group block, through `:root :where(.is-layout-flow)>*`.
   This grid owns its rhythm through the 14px gap above, and the inherited 26px
   does not collapse away on a grid item - it displaces the reel inside its own
   grid area, which at desktop widths drops it below the copy it is meant to sit
   beside. Same bridge, same reason, as `.split>*,.sp>*` in _tiles.scss: equal
   specificity (0,1,0) with the global rule, won on source order because
   ugcza.css is enqueued after global-styles-inline-css. */
.about > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

.about p {
  color: var(--ds-color-fg-default);
  line-height: 1.76;
}

.about p + p {
  margin-top: 16px;
}

.about strong {
  color: var(--ds-color-fg-heading);
  font-weight: 600;
}

/* Standfirst: the size step is what replaces the two paragraphs that came out.
   Capped at 20.8px, not 22 - 1.5vw of 1388px, the viewport where .shell stops
   widening. Past that the column is fixed, so type that kept growing just
   added lines. */
.about .lead {
  font-size: clamp(18.5px, 1.5vw, 20.8px);
  line-height: 1.6;
  color: var(--ds-color-fg-heading);
}

.about-txt {
  max-width: 68ch;
}

@media (min-width: 880px) {
  .about {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
  .about-txt {
    grid-column: 1/6;
    padding-right: clamp(8px, 1.6vw, 22px);
  }
  .about .reel {
    grid-column: 6/13;
  }
}
/* The watch call, now ON the video rather than in a plate beside it.

   The scrim is anchored to the text band, not spread over the whole frame,
   and that is a measured decision. Brand red is luminance .20, so it needs a
   ground under .0333 to clear its 3:1 large-text floor; rgba(10,10,10,a) over
   a pure-white poster frame only gets there at a >= .86, and at that strength
   a full-frame scrim erases the video. Held at .94 where the type actually
   sits and released by 78% up the frame, the picture survives and the floor
   still holds against the worst case the client could upload: red 4.24:1,
   white 16.9:1, the --tx-2 meta 8.7:1. Do not thin the bottom stops.
   (Red over a .55 scrim measures 1.06:1 - red and mid-grey are nearly the
   same luminance, so a half-strength scrim is not a weaker version of this,
   it is unreadable.)
   Same rule as the partner feature caption and the property tiles: imagery
   goes behind content under a scrim, never above it. */
.reel::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-scrim), 0.94) 0%, rgba(var(--ds-color-channel-scrim), 0.9) 26%, rgba(var(--ds-color-channel-scrim), 0.55) 52%, transparent 78%);
}

.watch {
  grid-row: 2;
  grid-column: 1;
  position: relative;
  z-index: 3;
  padding: clamp(16px, 2.4vw, 28px) clamp(18px, 2.6vw, 32px);
}

/* Bigger and heavier. Note the weight CANNOT come from font-weight: TGS
   Perfect Condensed is a single STATIC file declared 'font-weight:100 900',
   so every value in that range resolves to the same outlines and the browser
   never synthesises. 700 would render pixel-identical to 600. The stroke is
   the only honest way to add weight without shipping a second face; it is
   centred on the outline, so keep it under ~1.6% of the size or it starts
   closing the counters on a condensed face. */
.watch h3 {
  font-size: clamp(25px, 2.8vw, 40px);
  -webkit-text-stroke: 0.6px currentColor;
}

.watch em {
  font-style: normal;
  color: var(--ds-color-brand-primary);
}

.watch .m {
  display: block;
  margin-top: 9px;
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
}

/* The showreel carries its own call now - two rows rather than a centred
   flex box. Row 1 takes the slack and centres the play control in whatever
   height is left; row 2 is the copy. Rows, not absolute positioning, so the
   control can never end up underneath the type at a narrow width - which it
   did at 390px when the text block was ~113px of a 201px frame. */
.reel {
  position: relative;
  aspect-ratio: 16/9;
  background: var(--ds-color-bg-subtle);
  overflow: hidden;
  display: grid;
  grid-template-rows: 1fr auto;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

/* The reel is a <button> here and an <a> in the prototype, because in the theme
   it is a real control that swaps an iframe in rather than a link that opens
   YouTube (the prototype links out only because the artifact CSP blocks
   external frames). A button arrives with a user-agent border, padding, font
   and centred text that an anchor does not, so these four lines are not a
   design change - they are what makes the two renders identical. Nothing here
   is a value: every one of them is "whatever an anchor would have done".
   Same job as _wp-bridge.scss, kept beside the component that needs it. */
/* `width:100%` is in that list and is the one that is NOT obvious. A form
   control's `width:auto` is shrink-to-fit even when its `display` is a
   block-level box, so `display:grid` does not make a <button> fill its
   container the way it makes an <a> fill one. On the homepage the reel is a
   grid ITEM (`.about .reel{grid-column:6/13}`) and `justify-self:stretch`
   hid it; on the About page the reel sits in ordinary flow and the frame
   collapsed to the width of the text inside it - 241px of a 557px column at
   1440, 211px of 358px at 390 - with the 16:9 ratio faithfully preserved, so
   it looked like a deliberately small card rather than a bug. */
.reel {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: inherit;
  width: 100%;
}

/* The poster frame, and the veil over it.

   Layering follows .tile-art: ::before carries the picture, ::after the
   scrim, and the file is named by --reel-img on the element so swapping
   it is a markup change. In WordPress that attribute is the only line
   that moves - wp_get_attachment_image_url() on the poster attachment.

   The veil is NOT decoration. The frame YouTube picked as this video's
   thumbnail is a mid-video title card: a near-white band straight across
   the middle with the word CONTENT set into it. That band is almost
   exactly the pure-white worst case the scrim below was measured
   against, and it sits where the play disc lands. Held at .3 it
   composites to about #b3b2ae - the disc keeps its contrast, the band
   stops reading as a second headline competing with ours, and the still
   reads as footage. Measured, not guessed: see the note on ::after. */
.reel::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(var(--ds-color-channel-scrim), 0.3), rgba(var(--ds-color-channel-scrim), 0.3)), var(--reel-img) center/cover no-repeat, var(--ds-color-bg-subtle);
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.reel:hover::before, .reel:focus-visible::before {
  transform: scale(1.05);
}

.reel:hover .play, .reel:focus-visible .play {
  transform: scale(1.07);
}

.reel .play {
  transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.3, 1);
}

@media (prefers-reduced-motion: reduce) {
  .reel::before, .reel .play {
    transition: none;
  }
  .reel:hover::before, .reel:focus-visible::before,
  .reel:hover .play, .reel:focus-visible .play {
    transform: none;
  }
}
/* Centred in the frame, not in the space above the call (client,
   2026-08-12). Spanning both rows rather than absolute positioning keeps
   the grid as the only thing placing anything, so the disc still cannot
   drift out of the box.
   Scales down with the frame: at 390px the reel is only ~201px tall and a
   fixed 74px disc crowds the copy. */
.reel .play {
  grid-row: 1/3;
  grid-column: 1;
  align-self: center;
  justify-self: center;
  position: relative;
  z-index: 2;
  width: clamp(54px, 6vw, 74px);
  height: clamp(54px, 6vw, 74px);
  border-radius: 50%;
  background: var(--ds-color-brand-primary);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Below 480px the call fills so much of a small frame that a
   frame-centred disc lands ON the type - measured at 390px: copy starts
   47% up the frame, a centred 54px disc reaches 36.6%. Back to centring
   in the space above it, which is what row 1 does. Clear by 7.8px at
   500px, so 480 is the last width that holds. */
@media (max-width: 479px) {
  .reel .play {
    grid-row: 1;
    grid-column: 1;
  }
}
.reel .play::after {
  content: "";
  border-left: clamp(15px, 1.6vw, 20px) solid var(--ds-color-fg-on-accent);
  border-top: clamp(9px, 1vw, 12px) solid transparent;
  border-bottom: clamp(9px, 1vw, 12px) solid transparent;
  margin-left: clamp(4px, 0.5vw, 5px);
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 14px;
  margin-top: clamp(30px, 4vw, 46px);
}

.stat {
  background: var(--ds-color-bg-muted);
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sec.grey .stat {
  background: var(--ds-color-bg-subtle);
}

/* ---------- "Who we are": ambient ground, not a colour band ----------
   The section drops to the page's own #131313. The grey comes back not as a
   fill but as a filter over a blurred event photograph, masked to a pool of
   light behind the copy column. No new colour enters the palette.
   Scoped to #about on purpose: #press still uses .sec.grey. */
#about {
  position: relative;
  overflow: hidden;
  background: var(--ds-color-bg-surface-1);
}

/* Layer 1 - the photograph, blurred past recognition. It is only ever seen
   through a 41.4px blur, so the file is 320x160 at q40: 6 KB. Detail is wasted
   here; nothing but the tonal distribution survives.
   Blur was 46px until the client asked for 10% less. Softening it raises
   local contrast, so the wash keeps slightly brighter highlights: measured on
   the real render, the brightest 0.5% of pixels under the copy went .0344 ->
   .0356 and body copy 6.13:1 -> 6.05:1. Immaterial here, but re-measure if it
   is loosened much further - the guarantee below is what it eats into.
   inset:-90px is structural, not decorative - blur() samples beyond the
   element box, so without the bleed the wash fades out at its own edge. */
.about-bg {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  filter: blur(41.4px) saturate(0.18) brightness(0.38);
}

/* Layer 2 - the brand grey doing the filtering. Deliberately a separate layer
   and not a gradient inside .about-bg: filter applies to everything an element
   paints, so in there the brightness(.38) would darken the grey too, and the
   measured contrast assumes the grey lands ON TOP of the filtered photo.
   Measured: saturate(.18) brightness(.38) then rgba(56,56,56,.7) holds body
   copy at 6.05:1 over this photograph, and 4.80:1 over a pure white one - so
   the floor survives whatever the client uploads later, not just this image.
   Raising brightness or dropping the alpha breaks that guarantee. */
#about::before {
  content: "";
  position: absolute;
  z-index: 1;
  pointer-events: none;
  background: rgba(var(--ds-color-channel-muted), 0.7);
}

/* Identical geometry and mask on both, so the two washes coincide exactly.
   The mask is what keeps this a pool of light rather than a full-bleed
   photographic band - which would be the grey band again in a new coat. */
/* The opaque stop is pure black, not the brand near-black: a mask gradient is
   read as alpha, not as a colour, and the other two stops here are already
   channel-shadow. See the note on the override in _grounds.scss, which is the
   rule that actually paints. */
.about-bg, #about::before {
  inset: -90px;
  -webkit-mask-image: radial-gradient(66% 72% at 30% 46%, rgb(var(--ds-color-channel-shadow)) 0%, rgba(var(--ds-color-channel-shadow), 0.72) 40%, transparent 76%);
  mask-image: radial-gradient(66% 72% at 30% 46%, rgb(var(--ds-color-channel-shadow)) 0%, rgba(var(--ds-color-channel-shadow), 0.72) 40%, transparent 76%);
}

/* Desaturated fractal noise, tiled. Takes the dead flatness off a large dark
   fill and hides the banding a wide gradient shows on 8-bit displays. */
#about::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  opacity: 0.03;
}

#about > .shell {
  position: relative;
  z-index: 3;
}

/* Stat tiles: photography behind the figure. Same grid, same padding, same
   type - only the ground changes. The tile clips; the image drifts. */
#about .stat {
  background: var(--ds-color-bg-subtle);
  position: relative;
  overflow: hidden;
}

/* `em` is in this list because the tile carries an optional third line and an
   element left out of it sits at z-index auto, which is UNDERNEATH the scrim at
   z-index 1 - text that is stored, rendered, and invisible. Ported rather than
   invented: the press kit's own stat rule is
   `.stat>b,.stat>span,.stat>em{position:relative;z-index:2}` (prototype
   tilebg.py, STAT_CSS). The homepage's five tiles carry no sub-label, so this
   changes nothing that renders today. */
#about .stat > b, #about .stat > span, #about .stat > em {
  position: relative;
  z-index: 2;
}

/* The drifting layer. Its PICTURE is an `<img loading="lazy">` inside it, not
   this layer's background - five backgrounds cost 120 KB before the load event
   on a band nobody had scrolled to, and a CSS background cannot be lazy-loaded
   at all. The fill rule for that image is shared across all five tile families
   and lives at the top of _tiles.scss. `background-size`/`background-position`
   stay here: they are still this layer's contract, they paint nothing without
   an image, and the probe suite compares them against the prototype's. The
   drift and its reduced-motion guard never moved. */
.stat-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
  animation: statdrift 24s ease-in-out infinite alternate;
}

/* Scrim lives on the tile, NOT on the drifting image - carried on the image it
   would scale with it, so the protection under the label would drift too.
   The alphas are measured, not eyeballed. Against the brightest 0.5% of pixels
   in any of these crops: .84 puts the figure at 3.38:1 (large-text floor 3.0)
   and .93 puts the label at 4.86:1 (small-text floor 4.5). Below .82 the figure
   fails and below .90 the label does. Brand red is the binding constraint -
   #ea3700 has a luminance of 0.20, so against pure black it tops out at 5.04:1
   and needs a genuinely dark ground. Keep these floors if the photos change. */
#about .stat::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.84) 0%, rgba(var(--ds-color-channel-scrim), 0.86) 40%, rgba(var(--ds-color-channel-scrim), 0.93) 66%, rgba(var(--ds-color-channel-scrim), 0.93) 100%);
}

/* alternate, so it eases back out rather than cutting to scale 1. Negative
   delays start each tile mid-cycle - without them all five breathe in lockstep,
   which reads as a glitch rather than as life. */
@keyframes statdrift {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(1.14);
  }
}
/* Property tiles zoom harder and faster than the stat tiles: their art sits
   under a heavier scrim, so a 14%/26s drift was running but invisible. */
@keyframes propdrift {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(1.24);
  }
}
.stat:nth-child(2) .stat-bg {
  animation-delay: -5s;
}

.stat:nth-child(3) .stat-bg {
  animation-delay: -10s;
}

.stat:nth-child(4) .stat-bg {
  animation-delay: -15s;
}

.stat:nth-child(5) .stat-bg {
  animation-delay: -20s;
}

@media (prefers-reduced-motion: reduce) {
  .stat-bg {
    animation: none;
  }
}
.stat b {
  font-family: var(--ds-font-heading);
  font-size: clamp(32px, 4vw, 46px);
  font-weight: 600;
  color: var(--ds-color-brand-primary);
  line-height: 1;
}

.stat span {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 600;
}

/* The optional third line. Ported from the press kit, which is the only page
   whose tiles carry one - sentence case at 12.5px rather than the label's
   uppercase 10.5, so the two lines cannot be mistaken for each other. It takes
   --tx-2 there, one step brighter than the label's --tx-3, because it is
   ordinary reading size and the label is not. */
.stat em {
  font-style: normal;
  font-size: 12.5px;
  line-height: 1.4;
  margin-top: 3px;
  color: var(--ds-color-fg-default);
}

/* ---------- Brands: ambient ground, not a colour band ----------
   Was .sec.teal. Teal is a brand-bible colour (#1e3a38) and was being used
   exactly as the bible frames it - a dark surface, not an accent - but the
   client does not want a chromatic band here. Rather than tint or pattern
   over it, the ground drops to the page's own #131313 and the grey returns as
   a filter over a blurred event photograph, the same technique as #about.
   No new colour enters the palette.

   The filter stack and the .7 alpha are copied from #about deliberately and
   must stay in step with it: that pair is what was measured to hold body copy
   at 6.05:1 over this photograph and 4.80:1 over a pure white one. Change
   either and re-measure both sections.

   Only the mask moves - pooling right of centre instead of #about's 30% 46%,
   so two washes on one page do not read as the same asset twice, and wider
   because here the pool has to cover a full-width logo grid rather than a
   single copy column - the tonal step across that gutter is the only thing
   separating the wall.
   The pool sits at 54% and not centre-top for a measured reason: the
   .sec-h row is at the very top of the section and its 11px orange
   eyebrow is the tightest contrast here. Orange on the flat #131313
   ground is 5.43:1; under the pool it drops to about 4.3 and misses the
   4.5 floor. Keep the pool below the heading row. */
#brands {
  position: relative;
  overflow: hidden;
  background: var(--ds-color-bg-surface-4);
}

.wash-bg {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  filter: blur(41.4px) saturate(0.18) brightness(0.38);
}

#brands::before {
  content: "";
  position: absolute;
  z-index: 1;
  pointer-events: none;
  background: rgba(var(--ds-color-channel-muted), 0.7);
}

/* inset:-90px is structural - blur() samples beyond the element box, so
   without the bleed the wash fades out at its own edge. */
.wash-bg, #brands::before {
  inset: -90px;
  -webkit-mask-image: radial-gradient(88% 74% at 50% 54%, var(--ds-color-bg-subtle) 0%, rgba(var(--ds-color-channel-shadow), 0.78) 46%, transparent 84%);
  mask-image: radial-gradient(88% 74% at 50% 54%, var(--ds-color-bg-subtle) 0%, rgba(var(--ds-color-channel-shadow), 0.78) 46%, transparent 84%);
}

#brands > .shell {
  position: relative;
  z-index: 3;
}

/* brands - 3 columns per "PUT THIS INTO 2-3 Columns instead of 1" */
/* Nothing is drawn in the gutter and the grid has no background of its
   own - the washed section ground shows through, and that tonal step
   against the #111111 baked into the logo artwork is what separates the
   wall. Convention is borderless: surface tone and gap, no 1px rules.
   A grid background would also paint the empty cells at the end of a
   wrapped row as a solid block, which is why it is not used here. */
.brands {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 12px;
}

@media (max-width: 1120px) {
  .brands {
    grid-template-columns: repeat(4, 1fr);
  }
}
@media (max-width: 720px) {
  .brands {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (max-width: 440px) {
  .brands {
    grid-template-columns: repeat(2, 1fr);
  }
}
.brand {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--ds-color-bg-subtle);
  transition: transform 0.18s, filter 0.18s;
  filter: saturate(0.82);
}

.brand:hover {
  transform: translateY(-3px);
  filter: saturate(1);
}

.brand img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.brand.feat {
  grid-column: 1/span 2;
  grid-row: 1/span 2;
  aspect-ratio: auto;
  filter: saturate(1);
}

.brand.feat:hover {
  filter: saturate(1) brightness(1.06);
}

.feat-cap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-shadow), 0.94), rgba(var(--ds-color-channel-shadow), 0.6) 55%, transparent);
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
  line-height: 1.45;
}

.feat-cap em {
  font-style: normal;
  color: var(--ds-color-brand-accent);
  font-weight: 700;
}

@media (max-width: 440px) {
  .feat-cap {
    font-size: 9.5px;
    padding: 11px 12px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .brand:hover {
    transform: none;
  }
}
/* "What partners say" - the rule that sets this band's height.
   The prototype carries these two margins as an inline style on the h3
   (`margin-top:clamp(40px,5vw,62px);margin-bottom:20px`) rather than in its
   stylesheet, so porting the CSS could never pick them up and porting the
   markup dropped them: block markup has no place for a clamp(). Without them
   the band came out 50px short at 1440 and 28px at 390, which is exactly the
   two margins - 62 - 26 for the top, and 20 - 6 for the bottom, where the 6 is
   `.tcar`'s own margin winning the collapse once the h3 contributes nothing.
   The 26px is core's constrained-layout block gap, which is what fills the gap
   when nothing else claims it. Spacing is design, so it lives here rather than
   going back into the pattern as a style attribute. */
#brands > .shell > h3 {
  margin-top: clamp(40px, 5vw, 62px);
  margin-bottom: 20px;
}

/* testimonials */
.tcar {
  display: grid;
  margin-top: 6px;
}

.tslide {
  grid-area: 1/1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s ease, visibility 0s linear 0.45s;
  background: rgba(var(--ds-color-channel-shadow), 0.28);
  border-left: 4px solid var(--ds-color-brand-primary);
  padding: clamp(26px, 4vw, 44px);
  margin: 0;
}

.tslide.on {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.45s ease, visibility 0s;
}

.tslide p {
  font-size: clamp(17px, 2vw, 22px);
  line-height: 1.62;
  color: var(--ds-color-fg-heading);
}

.tslide .who {
  margin-top: 20px;
  display: flex;
  align-items: baseline;
  gap: 13px;
  flex-wrap: wrap;
}

.tslide .who b {
  font-family: var(--ds-font-heading);
  font-size: 22px;
  font-weight: 600;
  color: var(--ds-color-brand-accent);
}

.tslide .who span {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

/* Partner mark in the attribution line. The art arrives on a baked #121212
   ground, so it is keyed and cropped to its own ink - a square tile beside a
   name would read as a plate, not a logo. Heights match at 34px and widths
   run free, which is the only way a wide wordmark and a tall shield can share
   a line. align-self because .who aligns to baseline and an image has none. */
.who-mark {
  align-self: center;
  flex: none;
  height: 34px;
  width: auto;
  max-width: 92px;
  object-fit: contain;
}

/* Goliath's mark is a deep red on black: 1.38:1 against the About slide ground
   and 1.67:1 on the partners panel, so bare it reads as a smudge rather than a
   logo. A light chip puts it at 9.4:1 in the brand's own colours, which beats
   recolouring someone else's logo. Height drops so the lockup still measures 34. */
.who-mark--plate {
  box-sizing: content-box;
  height: 26px;
  padding: 4px 5px;
  background: var(--ds-color-bg-inverse);
}

.tctl {
  display: flex;
  gap: 8px;
  margin-top: 18px;
}

.tbtn {
  width: 38px;
  height: 38px;
  background: transparent;
  color: var(--ds-color-fg-default);
  cursor: pointer;
  font-family: var(--ds-font-label);
  font-size: 13px;
  transition: 0.15s;
  border: 1px solid rgba(var(--ds-color-channel-paper), 0.42);
}

.tbtn:hover {
  border-color: var(--ds-color-brand-accent);
  color: var(--ds-color-brand-accent);
}

.tbtn.on {
  background: var(--ds-color-brand-primary);
  border-color: var(--ds-color-brand-primary);
  color: var(--ds-color-fg-heading);
}

/* press */
.press {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(238px, 1fr));
  gap: 14px;
}

.pr {
  background: var(--ds-color-bg-lift);
  padding: 20px 18px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  transition: background 0.15s;
}

.pr:hover {
  background: var(--ds-color-bg-lift);
}

.pr span {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
}

.pr b {
  font-family: var(--ds-font-heading);
  font-size: 19px;
  font-weight: 600;
  line-height: 1.15;
}

.pr em {
  font-style: normal;
  font-size: 13.5px;
  color: var(--ds-color-fg-default);
  line-height: 1.5;
}

/* ---------------------------------------------------------------------
   THE ARTWORK LAYER'S PICTURE, for every tile family on the site.

   All five families - coverage tiles, playlist frames, event property
   tiles, stat tiles and start.gg cards - used to paint their photograph as
   the LAYER'S OWN background-image. That cost 587 KB on the homepage before
   the load event, none of it above the fold, because `background-image` has
   no lazy mechanism at all: the browser fetches it the moment the rule
   matches and there is no attribute, no API and no media query that defers
   it. PERFORMANCE-SPEC section 2 prescribes exactly this swap, and it is
   the only one of the four items in that document that had not been done.

   So the picture is an `<img loading="lazy">` and this is the rule that
   makes an element behave the way a background did. `object-fit:cover` with
   `object-position` is `background-size:cover` with `background-position`,
   term for term.

   THE IMAGE GOES INSIDE THE LAYER AND NEVER REPLACES IT. Every one of these
   layers carries measured motion - propdrift, statdrift, a hover zoom, a
   saturate, a greyscale - guarded by a `prefers-reduced-motion` block that
   has to sit after the rule it disables (PORT-05, which has failed three
   times on this project). Moving that onto the image would re-open all of
   it for nothing: the layer keeps its job and the image is a dumb fill.

   The layers keep their own `background-size`/`background-position` too.
   They paint nothing without an image, they are still the layer's contract,
   and the probe suite compares them against the prototype's - which is the
   same design expressed the way the prototype expressed it.

   `.csimg` is the sixth and the odd one out: it carries no motion at all and
   its picture is content rather than a backing, so it is the one that takes a
   real `alt`. It is here anyway because what this rule does - make an element
   behave the way a background did - is exactly what it needs, and a second
   copy of these five declarations in the portfolio component is how the two
   drift apart.
   --------------------------------------------------------------------- */
.tile-art > img, .pt-img > img, .pbgi > img, .stat-bg > img, .gg-bg > img, .csimg > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* ---------------------------------------------------------------------
   .tile-art - reusable artwork backing for any tile.

   API:   class="... tile-art"
          <img>                      the artwork, as the tile's own child.
                                     ITS PRESENCE IS THE SWITCH: the scrim is
                                     gated on it, so a tile with no picture
                                     paints no scrim either and degrades to a
                                     plain plate rather than to a darkened
                                     empty box. That used to be carried by a
                                     `--tile-img` custom property both layers
                                     named, so an absent property made both
                                     declarations invalid. Same behaviour,
                                     same single fact - and now the fact is
                                     the artwork itself, which cannot be a
                                     URL that 404s while the scrim still
                                     darkens the tile.
          --tile-pos : 50% 38%       optional focal point (default 50% 50%).
                                     Still a custom property, because a focal
                                     point is a VALUE and not a presence, and
                                     it belongs on the component rather than
                                     on the markup that fills it.

   ::after carries the scrim. The component knows nothing about the tile's
   own layout, so it composes with .pr here and with any future tile that
   wants a photograph behind it.
   --------------------------------------------------------------------- */
.tile-art {
  position: relative;
  overflow: hidden;
  background: var(--ds-color-bg-subtle);
}

.tile-art > img {
  z-index: 0;
  object-position: var(--tile-pos, 50% 50%);
  transform: scale(1);
  transition: transform 0.5s cubic-bezier(0.2, 0.72, 0.24, 1);
}

/* Scrim measured against the brightest 0.5% of each real crop, never by eye.
   The 10px orange meta sets the strength: #e96007 tops out at 6.13:1 on pure
   black, so it needs .93 under the text band to clear 4.5:1. Worst tile
   (GLITCHED) lands at 4.70:1; white heading 15.4:1, grey description 7.9:1.

   `:has(>img)` is the gate described in the API note above. It carries the
   `content`, so a tile with no artwork generates no ::after box at all - the
   hover rule below can then stay ungated, because there is nothing for it to
   restyle. */
.tile-art:has(> img)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  transition: background 0.25s;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.18) 0%, rgba(var(--ds-color-channel-scrim), 0.38) 30%, rgba(var(--ds-color-channel-scrim), 0.93) 50%, rgba(var(--ds-color-channel-scrim), 0.96) 100%);
}

/* This lifts the tile's own text over the scrim, and the picture is a child
   too - so it would lift that as well and paint the photograph over its own
   scrim. It does not, because it is (0,1,0) and `.tile-art>img` above is
   (0,1,1): the picture wins outright and stays at z-index 0, under the scrim.
   Source order is not what saves it here, specificity is. */
.tile-art > * {
  position: relative;
  z-index: 2;
}

/* Zoom the child, never the hover target itself. Hover lightens only the two
   upper stops - the text band is frozen, so hovering cannot walk the type
   out of contrast (4.56:1 worst case, still over the floor). */
.tile-art:hover > img, .tile-art:focus-visible > img {
  transform: scale(1.07);
}

.tile-art:hover::after, .tile-art:focus-visible::after {
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.06) 0%, rgba(var(--ds-color-channel-scrim), 0.26) 30%, rgba(var(--ds-color-channel-scrim), 0.93) 50%, rgba(var(--ds-color-channel-scrim), 0.96) 100%);
}

/* AFTER the rules it disables - media queries add no specificity, so with
   equal selectors source order is what decides. */
@media (prefers-reduced-motion: reduce) {
  .tile-art > img {
    transition: none;
  }
  .tile-art:hover > img, .tile-art:focus-visible > img {
    transform: none;
  }
}
/* .pr adopting the component: artwork needs room, so the tile grows and the
   text bottom-anchors into the strong end of the scrim. Add the class only to
   tiles that HAVE a picture - a coverage item with no artwork stays a plain
   .pr and keeps the flat grey card. */
.pr.tile-art {
  background: var(--ds-color-bg-subtle);
  min-height: 252px;
  justify-content: flex-end;
}

.pr.tile-art:hover {
  background: var(--ds-color-bg-subtle);
}

.pr.tile-art em {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* split CTA */
.split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
  gap: 14px;
}

.sp {
  padding: clamp(28px, 4vw, 46px);
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--ds-color-bg-subtle);
}

.sp.hot {
  background: var(--ds-color-brand-primary);
}

/* :not(.eyebrow) is a bridge, not a design change. The prototype's eyebrow is
   a <span>, so a bare `.sp p` could never reach it. Core's paragraph block can
   only emit <p>, and `.sp p` (0,1,1) outranks `.eyebrow` (0,1,0) - so without
   this the orange eyebrow in the ink panel silently renders body grey, and the
   42ch measure lands on a two-word label. The prototype's own <p> elements
   match either selector identically. */
.sp.hot p:not(.eyebrow) {
  color: rgba(var(--ds-color-channel-light), 0.92);
}

.sp.hot .eyebrow {
  color: rgba(var(--ds-color-channel-light), 0.88);
}

.sp p:not(.eyebrow) {
  color: var(--ds-color-fg-default);
  max-width: 42ch;
}

.sp .go {
  margin-top: auto;
  padding-top: 8px;
}

/* THE FLEX ITEM IS THE WRAPPER, NOT THE BUTTON, and the base layer pins it to
   `height:var(--ds-control-height)` (50px) because a className on a button
   block lands on `div.wp-block-button`. `_wp-bridge.scss` gives the LINK
   `height:auto` so its 13px padding, 2px border and 28.05px leading decide its
   size - 58px - and nothing did the same for the wrapper, so a 58px link sat
   inside a 50px box and overflowed it by 8px.

   That reads as correct, because the link paints itself and the overflow is not
   clipped: the buttons look right. What it breaks is the panel. `.go` takes its
   height from the wrapper, and `.sp` is a flex column, so both panels finished
   8px short - #partners measured 521px against the prototype's 529 at 1440 and
   658 against 682 at 390, entirely in the space under the two calls to action.

   Scoped to `.go` because this file owns `.go`. `.hcta` in _hero.scss carries
   the same one-line fix for the same reason, and the general form belongs
   beside "the wrapper must paint nothing - it is a layout box" in
   _wp-bridge.scss, which is shared with two other sessions working this page. */
.sp .go .wp-block-button.btn {
  height: auto;
}

/* Core's flow layout writes `margin-block-start:var(--wp--style--block-gap)`
   onto every child of a group block, through `:root :where(.is-layout-flow)>*`.
   Both of these own their rhythm - .split is a grid with a 14px gap and .sp a
   flex column with the same - so the inherited 26px would push the second panel
   a whole gutter below the first and open the panels' own copy out. Equal
   specificity (0,1,0) with the global rule, won on source order because
   ugcza.css is enqueued after global-styles-inline-css. */
.split > *, .sp > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

/* Core's button block nests the clickable element inside the wrapper, so a
   component class set on the block lands on the wrapper and never reaches the
   link. _wp-bridge.scss does this for .btn, .btn.line and .btn.sm; .alt is the
   split CTA's own variant - the orange call on the red panel - so its bridge
   sits beside the component that uses it. It wins on specificity (0,4,0
   against the base bridge's 0,3,0), not on order, because _wp-bridge loads
   last. */
/* Background only. The label stays the pure white the base bridge already
   paints - the prototype sets `.btn.alt{color:#ffffff}`, which is the same
   value, and naming --ds-color-fg-on-accent here re-folded it onto the brand
   near-white and showed up in the computed-style comparison as a 6/255 drift.
   PORT-08: a fold is a decision, and this one was not wanted. */
.wp-block-button.btn.alt > .wp-block-button__link {
  background: var(--ds-color-brand-accent);
}

.wp-block-button.btn.alt > .wp-block-button__link:hover {
  background: var(--ds-color-brand-accent-hover);
}

/* AND ON THE RED PANEL THE ACCENT HAS NOTHING LEFT. Orange on brand red is
   1.22:1 - not a weak button, an invisible one, with the white label doing all
   the separating - and hover made it worse at 1.60:1. So the button on a
   `brand-primary` flood takes the on-brand role: ink fill at 4.76:1 against the
   panel, white label at 18.93:1, lifting to a white fill at 3.98:1 with an ink
   label. RULED in BUILD-DECISIONS §4.7, and applied to the prototype in the same
   change so the two still agree - `probes/cta.mjs` compares this element's
   `backgroundColor` at zero tolerance and has no divergence table.

   SCOPED TO `.sp.hot`, deliberately. `.alt` is right where it stands today on
   the two grounds that are not red: portfolio's case-study band on teal, and the
   press kit's non-hot panel on `subtle-lift`. Editing `.alt` itself would have
   repainted both.

   THE LINK ONLY, never the wrapper - `_wp-bridge.scss` neutralises that div at
   (0,3,0) and the note there explains what a second painted box costs. These win
   at (0,5,0) against this file's own bridge above, on specificity rather than
   order.

   The type on this panel is NOT fixed by any of it: white at .92 is 3.71:1 and
   solid white only reaches 4.16:1, so the flood colour is the problem and that
   is a seventh brand value. TD-9. */
.sp.hot .wp-block-button.btn.alt > .wp-block-button__link {
  background: var(--ds-color-btn-on-brand-bg);
  color: var(--ds-color-btn-on-brand-text);
  border-color: var(--ds-color-btn-on-brand-border);
}

.sp.hot .wp-block-button.btn.alt > .wp-block-button__link:hover {
  background: var(--ds-color-btn-on-brand-hover-bg);
  color: var(--ds-color-btn-on-brand-hover-text);
  border-color: var(--ds-color-btn-on-brand-hover-border);
}

/* portfolio */
/* margin-block-start:0 because the band sits directly under `.sec-h`, whose own
   bottom margin is the measured gap. Core's constrained layout adds a block gap
   above every child of the shell, which put another 26px in on top of it. The
   same reason `.props` and `.props-h` name their own margins. */
.plists {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
  gap: 18px;
  margin-block-start: 0;
}

/* `subtle-lift`, not `surface-3`, and the two are one part in 255 apart - which
   is why this was wrong for as long as it was and no pixel diff could say so.
   Both prototypes set this card `#1b1b1b`; the build painted `#1c1c1c`, a step
   on the section-depth scale rather than a component surface, and
   `pixelmatch` at threshold .12 cannot see a single unit of grey. It took a
   computed-style probe to report rgb(27,27,27) against rgb(28,28,28) - the
   exact near-miss the `subtle-lift` token was added to stop being made silently.
   See its own note in `brands/_ugcza.scss`. Fixes the homepage's card too. */
.plist {
  background: var(--ds-color-bg-subtle-lift);
  display: flex;
  flex-direction: column;
  transition: background 0.2s;
}

.plist:hover, .plist:focus-visible {
  background: var(--ds-color-bg-surface-4);
}

.pthumb {
  aspect-ratio: 16/9;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  padding: 15px;
}

/* The playlist's lead-video thumbnail. Its own layer, because the zoom lives
   here and never on .plist itself - a transform on the hover target slides it
   out from under the pointer near an edge, which un-hovers it, which slides it
   back: a flicker loop. Same reason the event property tiles animate a child.

   The picture inside it is an `<img loading="lazy">` - see the shared fill at
   the top of this file. The zoom, the saturate and the reduced-motion block
   below all stay HERE, on the layer, so nothing about the motion moved. */
.pt-img {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  transform: scale(1);
  filter: saturate(0.5);
  transition: transform 0.42s cubic-bezier(0.2, 0.72, 0.24, 1), filter 0.42s ease;
}

.plist:hover .pt-img, .plist:focus-visible .pt-img {
  transform: scale(1.2);
  filter: saturate(1);
}

/* Grey overlay. Dark grey over the frame, then the foot darkens for the title.
   The grey is what makes three unrelated thumbnails read as one set.
   Hover lightens the overlay rather than moving the card - the site's standing
   hover rule.

   THE UPPER STOPS ARE `channel-surface` (22,22,22), NOT `channel-muted`
   (56,56,56), which is a change from the prototype and from what this shipped
   as: the client asked on 2026-08-14 for the card overlay to be dark grey
   rather than light grey - BUILD-DECISIONS section 21. Only the 58% and 100%
   stops moved, at both rest and hover; the 0% and 26% stops were already the
   scrim and the surface, so the foot is untouched by design.

   THE TITLE'S FLOOR DID NOT MOVE, and that is the reason the change was safe to
   make without re-tuning anything. `.pthumb` is `align-items:flex-end`, so the
   26px/600 title sits in the 0% stop's territory - rgba(10,10,10,.9) - and the
   stops that moved are above it. Measured lightest-pixel-behind-the-title, with
   the title hidden, on the three real cards: 10.67 / 10.29 / 16.25 at rest and
   9.32 / 8.49 / 15.63 on hover, against 10.67 / 10.13 / 16.25 and the same
   three hover figures before the change. It needs 3:1. The foot also holds
   against a pure-white frame, so it survives whatever the lead video becomes.

   NO GATE ON THIS PROJECT CAN SEE THIS CHANGE, which is worth knowing before
   trusting a green run over it. `pixelmatch` at threshold .12 does not count a
   21/255 shift in grey, so the band's pixel diff sat at 0.0% and 0.1% before
   and after, on both pages that carry these cards. The evidence is the sampled
   mean of each thumbnail's top third: 55.4 -> 34.5, 57.2 -> 36.5, 41.2 -> 20.3.
   Same blind spot the `subtle-lift` note above describes at 1/255, twenty-one
   times bigger and still invisible. */
.pthumb::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  transition: background 0.28s ease;
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-scrim), 0.9) 0%, rgba(var(--ds-color-channel-surface), 0.78) 26%, rgba(var(--ds-color-channel-surface), 0.66) 58%, rgba(var(--ds-color-channel-surface), 0.6) 100%);
}

.plist:hover .pthumb::before, .plist:focus-visible .pthumb::before {
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-scrim), 0.88) 0%, rgba(var(--ds-color-channel-surface), 0.66) 26%, rgba(var(--ds-color-channel-surface), 0.4) 58%, rgba(var(--ds-color-channel-surface), 0.3) 100%);
}

/* PURE white, not the brand's #f9fafa. Same call as the button labels in
   _wp-bridge.scss and for the same reason: the prototype paints this title
   #fff, the two are within a contrast rounding of each other over the measured
   overlay, and folding it onto the brand white showed up in the computed-style
   comparison as a 6/255 drift. The channel token is what PORT-07 reserves for
   true white. */
.pthumb b {
  position: relative;
  z-index: 2;
  font-family: var(--ds-font-heading);
  font-size: 26px;
  font-weight: 600;
  color: rgb(var(--ds-color-channel-light));
  text-shadow: 0 2px 14px rgba(var(--ds-color-channel-shadow), 0.75);
}

/* MUST sit after .plist:hover .pt-img above - media queries add no
   specificity, so with equal selectors source order is what decides. Placed
   before it (where the old .plist rule left it) this silently did nothing and
   the zoom still ran for people who asked it not to. Caught by Playwright
   newContext({reducedMotion:'reduce'}), not by reading the CSS. */
@media (prefers-reduced-motion: reduce) {
  .pt-img {
    transition: none;
  }
  .plist:hover .pt-img, .plist:focus-visible .pt-img {
    transform: none;
  }
}
.pbody {
  padding: 16px 17px 18px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  flex: 1;
}

.pbody p {
  font-size: 14.5px;
  color: var(--ds-color-fg-default);
  line-height: 1.55;
}

.pbody .n {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  margin-top: auto;
  padding-top: 12px;
}

/* The prototype carried this as `style="margin-top:clamp(36px,4.6vw,56px)"` on
   the heading itself. A length in block markup is a design value living under
   wp-content, which the token gate forbids, so it moves here and the pattern
   names it by class. It also has to beat core's flow-layout block gap on the
   same element, which it does at (0,1,0) plus source order. */
.props-h {
  margin-top: clamp(36px, 4.6vw, 56px);
}

.props {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(166px, 1fr));
  gap: 12px;
  margin-top: 22px;
}

/* Event property tiles: artwork behind, name over it - the site's standing rule
   that imagery goes under content, never above it. The art drifts like the stat
   tiles; the tile clips it. */
.prop {
  position: relative;
  overflow: hidden;
  background: var(--ds-color-bg-subtle);
  padding: 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 180px;
  justify-content: flex-end;
}

/* Two layers on purpose. .pbg takes the quick, strong hover zoom; .pbgi keeps
   the slow ambient drift. A transition and an animation on the same transform
   fight, so they get an element each and the scales compose.

   BOTH LAYERS SURVIVED THE MOVE TO AN `<img>`. It would look like a chance to
   collapse them - the image could carry one of the two - but it is the same
   trap in a new shape: an element can still hold only one of a transition and
   an animation on `transform`. The picture is a third, motionless layer inside
   .pbgi, and the two scales compose over it exactly as before. */
.prop .pbg {
  position: absolute;
  inset: 0;
  z-index: 0;
  transform: scale(1);
  transition: transform 0.42s cubic-bezier(0.2, 0.72, 0.24, 1);
}

.prop:hover .pbg {
  transform: scale(1.2);
}

.prop .pbgi {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  animation: propdrift 17s ease-in-out infinite alternate;
}

/* Scrim measured against the brightest 0.5% of these logos, which carry white
   and gold: .82 at the foot holds the category label at 5.96:1 and the name at
   11.6:1. The label had to move from --tx-3 to --tx-2 - #868686 cannot reach
   4.5:1 over this artwork at ANY usable scrim. */
.prop::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  transition: background 0.2s;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.34) 0%, rgba(var(--ds-color-channel-scrim), 0.52) 34%, rgba(var(--ds-color-channel-scrim), 0.8) 70%, rgba(var(--ds-color-channel-scrim), 0.8) 100%);
}

.prop > b, .prop > span {
  position: relative;
  z-index: 2;
}

.prop:nth-child(2) .pbgi {
  animation-delay: -5s;
}

.prop:nth-child(3) .pbgi {
  animation-delay: -9s;
}

.prop:nth-child(4) .pbgi {
  animation-delay: -13s;
}

.prop:nth-child(5) .pbgi {
  animation-delay: -17s;
}

.prop:nth-child(6) .pbgi {
  animation-delay: -21s;
}

/* Must sit AFTER .prop .pbg - equal specificity, so source order decides.
   Placed above it earlier and it silently did nothing. */
@media (prefers-reduced-motion: reduce) {
  .prop .pbgi {
    animation: none;
  }
  .prop .pbg {
    transition: none;
  }
  .prop:hover .pbg {
    transform: none;
  }
}
/* No transform on hover: moving the hover target itself makes it slide out
   from under the pointer near an edge, which un-hovers it, which moves it back
   - a visible flicker loop. Lighten the scrim instead, as .tr and .gg-c do. */
.prop:hover::after {
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.22) 0%, rgba(var(--ds-color-channel-scrim), 0.4) 34%, rgba(var(--ds-color-channel-scrim), 0.72) 70%, rgba(var(--ds-color-channel-scrim), 0.72) 100%);
}

/* The name is a LOCKUP, not a sentence: the prototype writes `Dragon
   Ninja<br>Laser Eyes` and `Rumble in<br>the Jungle` and leaves the four short
   names on one line. A post title cannot carry that break - `ugcza_render_properties()`
   escapes it, and it is presentation rather than the client's copy - so the
   measure does it instead. 6em is the measure the prototype's own breaks imply:
   wide enough that `Spring Showdown` (112px, the longest single-line name)
   stays whole, narrow enough that `Rumble in the Jungle` (132px) cannot. It has
   to be `balance` and not the greedy default, because greedy sets those two as
   `Dragon Ninja Laser / Eyes` and `Rumble in the / Jungle`; balancing puts the
   break where the prototype's <br> is, to the pixel, at 1440 and at 390.
   Verified against the prototype's own line boxes, not by eye: 79.5/68.5 and
   62.3/67.1 on both sides. */
.prop b {
  font-family: var(--ds-font-heading);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.06;
  max-width: 6em;
  text-wrap: balance;
}

.prop span {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
}

/* flagged / undecided blocks */
.flag {
  border: 2px dashed rgba(var(--ds-color-channel-accent), 0.42);
  padding: clamp(20px, 3vw, 32px);
  position: relative;
}

.flag .lab {
  position: absolute;
  top: -11px;
  left: 18px;
  background: var(--ds-color-bg-default);
  padding: 0 10px;
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
  font-weight: 700;
}

.sec.ink .flag .lab {
  background: var(--ds-color-bg-subtle);
}

.evs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(208px, 1fr));
  gap: 12px;
  margin-top: 16px;
}

.evc {
  background: var(--ds-color-bg-muted);
  padding: 15px 16px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.evc .d {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

.evc b {
  font-family: var(--ds-font-heading);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.05;
}

.evc .g {
  font-family: var(--ds-font-label);
  font-size: 11px;
  color: var(--ds-color-brand-accent);
}

.note {
  background: rgba(var(--ds-color-channel-accent), 0.07);
  border-left: 4px solid var(--ds-color-brand-accent);
  padding: 16px 19px;
  font-size: 14px;
  color: var(--ds-color-fg-default);
  line-height: 1.6;
}

.note b {
  color: var(--ds-color-brand-accent);
}

/* footer */
footer {
  background: rgb(var(--ds-color-channel-shadow));
  padding: clamp(44px, 6vw, 74px) 0 0;
  border-top: 3px solid var(--ds-color-brand-primary);
}

.fgrid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(auto-fit, minmax(148px, 1fr));
  gap: clamp(24px, 3.5vw, 44px);
  padding-bottom: 42px;
}

/* THE BRAND COLUMN COLLAPSES TO 14px BELOW ~880px, and the prototype does it
   too - this is a defect in the design, corrected in BOTH renders in the same
   change rather than declared as a divergence, which is the call BUILD-DECISIONS
   4.7 already made for the invisible CTA on the red panel. B-151.

   WHAT IT DOES. The track list above is `minmax(0, 1.5fr)` followed by
   `repeat(auto-fit, minmax(148px, 1fr))`. `auto-fit` packs in as many 148px
   link tracks as will fit, and the brand column's minimum is ZERO, so it is
   paid last out of whatever those tracks leave. Measured at 390 in both
   renders, identical to the pixel:

     tracks      14px 148px 148px
     brand col   14px wide
     paragraph   14px wide, 268.7px TALL   <- about one character per line
     social row  14px wide, 214.5px tall

   Nothing failed on it. Both renders compute the same tracks, so the pixel diff
   sat at 1.6% and every probe passed - the band was faithfully reproducing a
   broken layout, which is PORT-68's exact shape.

   WHY A BREAKPOINT AND NOT A MINIMUM. `minmax(268px, 1.5fr)` on the brand looks
   like the tidier fix and is wrong: a grid does not drop to fewer columns when a
   minimum cannot be met, it overflows. The track count has to be changed, and
   changing a track count needs a query.

   800px, AND THE NUMBER IS MEASURED RATHER THAN CHOSEN. `.logo--full img` sets
   `height:58px` with `max-width:100%`, so once the column is narrower than the
   lockup's natural 190px at that height the image is SQUASHED HORIZONTALLY, not
   scaled - measured 126.5x58 at a viewport of 700 against 190x58 at 780. The
   press kit page publishes "Please don't recolour, stretch or add effects to the
   mark" as a brand rule, and the footer was breaking it on all thirteen pages at
   every width below about 775. 800 is that crossover with a little headroom;
   780 leaves 1.7px and one font change would take it back.

   NOT 880, which was the first attempt because the design already breaks there
   for the nav. Measured, it is the wrong place: at 879 the brand column is
   238.8px and the row works exactly as drawn, so stacking there costs 229px of
   footer height - 737 against 508 - to fix nothing. Reusing an existing
   breakpoint is a good instinct and it lost to the measurement.

   Below it the brand takes a full row of its own and the link columns lay out
   beneath in whatever the width affords, which is 3 tracks at 799 and 2 at 390.
   That is SHORTER than the collapse it replaces, not taller: at 390 the footer
   goes 1175px to 939px, because the 14px column was forcing the paragraph and
   the social row into vertical strips. */
@media (max-width: 800px) {
  .fgrid {
    grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  }
  .fgrid .fbrand {
    grid-column: 1/-1;
  }
}
.fcol {
  display: flex;
  flex-direction: column;
  gap: 11px;
  min-width: 0;
}

.fcol .ttl {
  font-family: var(--ds-font-heading);
  font-size: 19px;
  font-weight: 600;
  margin-bottom: 3px;
}

.fcol a, .fcol .li {
  font-size: 14px;
  color: var(--ds-color-fg-default);
}

.fcol a:hover {
  color: var(--ds-color-brand-accent);
}

.fbrand p {
  color: var(--ds-color-fg-default);
  font-size: 14px;
  line-height: 1.6;
  max-width: 36ch;
  margin-top: 12px;
}

/* The social row: five monochrome icons, on the client's instruction of
   2026-08-13. The prototype has outlined WORD pills here and this is a
   deliberate divergence from it - TECH-DEBT section 16 carries the decision.

   Black and white was the whole of the request, so no brand colour is used in
   either state. That also keeps them legible against the black band without a
   chip behind them, which is what `is-style-logos-only` removes.

   THE BOX IS THE HERO SLIDER ARROW'S BOX, ON THE CLIENT'S INSTRUCTION OF
   2026-08-14: *"the same design style as the hero slider buttons"*. So every
   value below is `.harrow`'s in _hero.scss and none of them is a second
   opinion - 46px square, the .5 hairline rather than the .42 one, the near-
   white glyph in both states, and a hover that fills with a 10% paper wash and
   takes the rule up to solid white. THE TWO RULE SETS MUST BE CHANGED
   TOGETHER; there is no probe that would notice them drifting apart, because
   the treatment of what sits in this row is deliberately unprobed (the row is
   the prototype's, its contents are the client's - see tools/probes/chrome.mjs).
   It cannot be shared as one selector list instead: core paints each logo its
   brand colour at (0,3,0), so this row's colour has to be won at (0,4,0)
   below, which the hero's arrows have no reason to carry.

   The 1px hairline used to be the same .42 number as `.field` in
   _contact-page.scss and `.tbtn` in _brands.scss, and the comment in the first
   of those says the three should stay the same. That is now two, not three -
   this row has left the group for the slider's .5, which is the one place the
   design opens the hairline up because the control sits over photography. Do
   not close it back to .42 to make that sentence true again.

   Core's own rules are what these override, so each one is deliberate rather
   than defensive: `.wp-block-social-links` ships a list padding and per-item
   margins that would fight the 8px gap, and its anchors carry a fixed
   background and padding that only make sense for the coloured-chip style.
   `padding:0` is kept for that last reason and is belt-and-braces either way -
   the box is sized by `width`/`height` now, and the glyph is centred by the
   flex box rather than by a padding, so a core padding winning this one
   declaration would no longer move anything.

   THREE CLASSES, AND THE THIRD ONE IS THE BOTTOM EDGE OF THE BUTTON. Core ships
   `.wp-block-social-links .wp-social-link a,...a:hover{border-bottom:0}` -
   a link reset, aimed at themes that underline with a border - and it is
   (0,2,1). A `border` shorthand on `.fsoc .wp-block-social-link-anchor` is
   (0,2,0), so it lost the bottom edge alone and won the other three: these
   buttons rendered as three-sided boxes, open at the foot, from the day the row
   became icons. Naming the <li> as well is (0,3,0) and takes it back.

   IT WAS IN THE MEASUREMENTS THE WHOLE TIME AND WAS READ AS SOMETHING ELSE. The
   comment under this one recorded the box as "38x37" and put the missing pixel
   down to the SVG sitting on the text baseline. 37 is 1 + 9 + 18 + 9 + 0: nine
   above, nine below, one border at the top and none at the bottom. The baseline
   was a real defect and `display:block` was the right fix for it; it just was
   not this one, and a plausible cause for the whole discrepancy is what stopped
   anyone looking for a second. Found by diffing the four border colours
   separately against `.harrow` - `borderTopColor` matched and
   `borderBottomColor` came back solid white, which is `currentColor` showing
   through a border core had set to zero width. A probe on `border` or on
   `borderTopColor` alone reports this row as correct. */
.fsoc {
  display: flex;
  gap: 8px;
  margin-top: 16px;
  flex-wrap: wrap;
  padding-left: 0;
  list-style: none;
}

/* The `transition` is on the <li> because that is where the COLOUR changes: the
   service colour is set here and core forces the anchor to `color:currentColor`,
   so the anchor and its glyph inherit rather than declare. A `color` transition
   on the anchor would be transitioning an inherited value, which works, but puts
   the declaration one element away from the change it eases. `.15s` is the same
   step the border and the ground take on the same hover, so the three arrive
   together.
   It also replaces core's `transition:transform .1s ease` on this element,
   which now has no transform left to ease. Not gated on reduced motion, and
   deliberately: nothing here moves. A colour cross-fade is not the class of
   effect that setting exists to stop, and `.btn` has transitioned its colour
   ungated since the port. */
.fsoc .wp-block-social-link {
  margin: 0;
  padding: 0;
  background: none;
  transition: color 0.15s;
}

.fsoc .wp-social-link .wp-block-social-link-anchor {
  width: 46px;
  height: 46px;
  padding: 0;
  background: transparent;
  border: 1px solid rgba(var(--ds-color-channel-paper), 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.15s, background 0.15s;
}

/* AND CORE POPS THE BUTTON 10% ON HOVER, which the slider arrows do not do.
   `.wp-block-social-link:hover{transform:scale(1.1)}` at (0,1,0) took the 46px
   box to 50.6px under the pointer, so the row grew and the hairline thickened
   while the arrow it is now meant to match answers a hover on its border and
   its ground and stays exactly where it is. Removed rather than matched,
   because the design has no scale-on-hover anywhere.
   NOT ONLY A STYLE POINT: core gates the TRANSITION on
   `@media not (prefers-reduced-motion)` and leaves the `transform` itself
   ungated, so a reduced-motion visitor got the jump without the ease - the
   abrupt version of the motion the setting asks to be spared. This is the third
   time on this project that a reduced-motion guard has covered the wrapper and
   not the movement; see the note at the foot of this file. */
.fsoc .wp-social-link:hover {
  transform: none;
}

/* THE COLOUR HAS TO OUTRANK CORE'S BRAND PALETTE, and that is the whole
   difficulty here. `is-style-logos-only` does not mean "no colour" - it means
   "no chip", and core then paints each logo its brand colour with
   `.wp-block-social-links.is-style-logos-only .wp-social-link-twitch{color:#6440a4}`
   at (0,3,0). A sensible-looking `.fsoc .wp-block-social-link-anchor` is
   (0,2,0) and loses, so the row rendered in Twitch purple, YouTube red and
   Instagram magenta while the markup and the stylesheet both read as correct.
   It was caught by reading the COMPUTED colour off the anchor, not the rule.
   Naming the block and the style alongside our own class is (0,4,0) and wins
   outright rather than on load order.

   AND `:not(:hover)` IS HOW THE SERVICE COLOURS COME BACK. The client asked on
   2026-08-14 for each glyph to take its own service's colour on hover and stay
   monochrome at rest. Core already holds every one of those colours - the very
   rules described above - so the cheapest correct answer is not to write them
   again but to STOP WINNING for one state. Narrowing this rule to `:not(:hover)`
   means it does not match a hovered item at all, and the winning declaration
   falls back to core's `:where(...)` rule at (0,1,0). Three of the five services
   are then coloured by WordPress and by nothing of ours: measured on the live
   render against the ground they actually land on - the 10% wash over black,
   rgb(25,25,25) - YouTube `rgb(255,0,0)` 4.40:1, Instagram `rgb(240,0,117)`
   4.15:1, Discord `rgb(88,101,242)` 3.82:1, all clear of the 3:1 non-text floor.
   Against the bare band they read 5.25, 4.96 and 4.56, which is the flattering
   number and not the governing one: the wash arrives on the same hover as the
   colour. If a service rebrands, core carries the change.
   `:not(:hover)` costs one class of specificity, taking this to (0,5,0), which
   changes nothing it was already winning.
   The two exceptions are below, and they are exceptions because core's value is
   wrong for a dark band rather than because we prefer another - see `$social` in
   `brands/_ugcza.scss`, where each carries its measured ratio. */
.fsoc.wp-block-social-links.is-style-logos-only .wp-social-link:not(:hover) {
  color: var(--ds-color-fg-heading);
}

/* Twitch: core's #6440a4 is their old purple AND measures 2.36:1 on the hovered
   ground, under the floor. Their current #9146ff is 3.79:1 - the tightest of the
   five and the reason the wash is the ground worth measuring against.
   X: core's is #000000, which is 1.34:1 there - invisible rather than dim - and
   X's own guidance is a white mark on dark, so the paper white IS the mark. This
   button therefore does not change colour on hover, which is the correct
   rendering of that brand and not an omission.
   Both at (0,5,0), the same as the rest rule, and they do not collide with it -
   it matches `:not(:hover)` and these match `:hover`. */
.fsoc.wp-block-social-links.is-style-logos-only .wp-social-link-twitch:hover {
  color: var(--ds-color-social-twitch);
}

.fsoc.wp-block-social-links.is-style-logos-only .wp-social-link-x:hover {
  color: var(--ds-color-social-x);
}

/* `fill:currentColor` is the join: core sizes the glyph, the colour above
   drives it, so there is one place to change the treatment. `display:block`
   because an inline SVG sits on the text baseline and left the box 38x37.
   18px, NOT the arrow's 13px: the box is shared with `.harrow`, the glyph is
   not. 13px is the size of a chevron drawn to read as a chevron; a wordmark
   like the Discord or Twitch logo at that size stops being identifiable, which
   is the one thing this row exists to do. So the frame is the slider's and the
   glyph keeps the size the icon ruling gave it - 18px in a 46px box, which is
   14px of clear on each side.

   The <li> is named here for a second reason: core sizes the glyph twice, once
   at `.wp-block-social-links .wp-social-link svg{height:1em;width:1em}` and
   once at 1.25em for `logos-only`. The first of those is (0,2,1), which the
   two-class version of this selector TIED - so 18px against 24px was being
   decided by which stylesheet the page happened to load second. It resolved our
   way and measured correctly, which is the worst outcome of a tie. (0,3,1)
   wins it outright. */
.fsoc .wp-social-link .wp-block-social-link-anchor svg {
  display: block;
  width: 18px;
  height: 18px;
  fill: currentColor;
}

/* THE HOVER HALF RESTATES THE WHOLE BORDER, and `border-color` alone is not
   enough - which took a second measurement to see. Core's reset names the hover
   state as well as the rest state:
   `.wp-block-social-links .wp-social-link a,...a:hover{border-bottom:0}`, and
   that hover half is (0,3,1) - so it outranks the REST rule above at (0,3,0) for
   as long as the pointer is on the button. Setting only `border-color` here left
   the width to that fight and lost it: all four edges correct at rest, and the
   foot of the box opening again the moment it was hovered. `border` at (0,4,0)
   settles both edges and both states.
   Measured, because reading it was what got it wrong the first time:
   `borderBottomWidth` 0px under hover against the arrow's 1px, at 390 and at
   1440, with every other property already matching. */
.fsoc .wp-social-link .wp-block-social-link-anchor:hover {
  border: 1px solid var(--ds-color-fg-heading);
  background: rgba(var(--ds-color-channel-paper), 0.1);
}

.fbot {
  padding: 20px 0 32px;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

.fbot .r {
  margin-left: auto;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}

/* ---------- the same footer, expressed in core's blocks ---------- */
/* As in _chrome.scss: no design value lives here, only the seam between the
   prototype's markup and the blocks the build renders it with. */
/* The same 26px block gap that lands under the nav lands above the footer:
   the footer is the document's last top-level block, so WordPress puts
   `margin-block-start:var(--ds-layout-block-gap)` on it and 26px of page
   ground shows above a border the design draws hard against the band before
   it. `footer` on its own is (0,0,1) and loses to the layout rule's (0,1,0);
   naming the shell inside it makes this (0,1,1) and it does not. */
footer:has(> .shell) {
  margin-block-start: 0;
}

/* The lockup is the site logo, and core sizes it by WIDTH:
   `.wp-block-site-logo.is-default-size img{width:120px}` is (0,2,1), the same
   as `.logo--full img`, and a tie is decided by source order - which is a
   coin toss to rest a logo on. Name the column and win it outright: the mark
   was rendering 120x37 where the design says 190x58. */
footer .fbrand .logo--full img {
  height: 58px;
  width: auto;
}

/* WordPress spaces a group's children with a MARGIN, not with a gap:
   `:root :where(.is-layout-flow) > *{margin-block-start:var(--ds-layout-block-gap)}`
   lands on every footer link at (0,1,0) and added 26px to each of the nine in
   the Explore column - 234px of column that is not in the design. `.fcol` is
   spaced by its own 11px flex gap and by nothing else.

   The links only, not `.fcol > *`: the brand column's two other children carry
   a lead of their own - `.fbrand p` at 12px and `.fsoc` at 16px - and a
   child-combinator rule sitting later in this file is (0,1,0) too, so it would
   silently win against `.fsoc` and close the gap over the social row. */
.fcol > .li {
  margin-block-start: 0;
}

/* The same 26px again, one level up: the bottom row is the shell's second
   child, so it took a block gap the design does not have. The two rows are
   spaced by `.fgrid`'s 42px foot and `.fbot`'s own 20px head. */
footer .fbot {
  margin-block-start: 0;
}

/* The social row needed a `.fbrand .fsoc` override here while it was built as
   one paragraph of word pills: `.fbrand p` is (0,1,1), outranks `.fsoc`, and
   handed the row a 36ch measure, the paragraph's 14px and its tighter leading,
   which took a pixel off every pill. The 2026-08-13 ruling made the row core's
   social-links block - a <ul>, which `.fbrand p` cannot match - so the override
   is gone rather than left standing as a no-op nobody dares delete. If this row
   ever goes back to being a paragraph, it comes back with it. */
/* THE PROTOTYPE'S `.menu{display:none}` USED TO BE THE FIRST LINE HERE, and it
   is gone rather than commented out. Two reasons, and the second is a bug it
   caused.

   It was overruled on 2026-08-17: the design's answer below 880px was to hide
   the links and offer nothing, and the header now carries core's overlay
   instead. BUILD-DECISIONS section 25.

   It also never did what it looks like it does. At (0,1,0) it loses to global
   styles' `body .is-layout-flex{display:flex}` at (0,1,1), so on the navigation
   block it never applied at all - `_chrome.scss` used to carry a second,
   stronger copy that was doing the actual hiding. What this one DID do, once
   the overlay landed, was hide the whole `.menu` element at EXACTLY 880px:
   `max-width:880px` is inclusive and the overlay's band stops at 879.98px, so
   for that one pixel the bar had neither an inline menu nor a hamburger. A rule
   with no effect for a year is still live, and it found the one width nobody
   tests. */
@media (max-width: 880px) {
  .hbody {
    max-width: none;
  }
  .scrim {
    background: linear-gradient(0deg, rgba(var(--ds-color-channel-shadow), 0.95) 0%, rgba(var(--ds-color-channel-shadow), 0.72) 46%, rgba(var(--ds-color-channel-shadow), 0.5) 100%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .hbg, .tslide, .tslide.on {
    transition: none;
  }
  /* A slow full-bleed zoom is exactly the motion this setting exists to stop.
     Kill the animation and the resting scale with it, so the photo sits at its
     native framing rather than a permanent 7% crop-in. */
  .hbg i {
    transform: none;
  }
  .hbg.on i {
    animation: none;
  }
  .gg-bg {
    transition: none;
  }
  .gg-c:hover .gg-bg, .gg-c:focus-visible .gg-bg {
    transform: none;
  }
}
.arw {
  width: 0.72em;
  height: 0.72em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
}

/* The same arrow, for the "more" links that CANNOT carry the SVG.
   `.arw` above is a class for markup, and the dynamic bands emit it happily
   through `ugcza_arrow()`. The section-heading links are pattern markup - a
   `wp:paragraph` - and a paragraph cannot hold an inline <svg>: core's
   `createFromElement()` in wp-includes/js/dist/rich-text.js keeps an empty
   element only as `{type, attributes}`, so the <path> child is dropped on the
   editor's round-trip and the block then fails validation against what was
   saved. Putting the glyph in markup there would work until the first time the
   client retyped the label, and then fail silently.
   The arrow is decorative - the prototype marks its SVG aria-hidden - so a
   pseudo-element loses nothing a screen reader wanted, and the label stays
   rich text the client can edit.
   Masked rather than drawn, so `currentColor` still decides the colour and the
   hover state follows `.more:hover` exactly as the inline SVG's
   `stroke="currentColor"` does. The stroke inside the data URI is opaque black
   because a mask reads alpha, not colour - the same reason the pool masks in
   _grounds.scss stop on pure black rather than on a surface token.
   The path, viewBox and 1.7 stroke are the prototype's own, character for
   character. `contain` reproduces the inline SVG's default xMidYMid meet,
   which centres a 12x10 viewBox in a square box.
   SCOPED TO THE BANDS THAT HAVE BEEN MEASURED against the prototype, one line
   per band as each is taken. `#portfolio` joined `#brands` and `#press` here
   rather than getting a second copy of the glyph in _sections.scss: its `.more`
   was 44.4px wide against the prototype's 62.4, so "All work" sat 18px right of
   where it belongs, which is exactly the .72em glyph plus its .34em clearance.
   Still not page-wide: `#about` and the About page's own `.more` are in bands
   other sessions are working in.
   `#press-coverage` is the About page's press band, measured 2026-08-13: its
   `.more` was 67.7px against the prototype's 85.7, and 18.0px is the .72em
   glyph plus its .34em clearance to the tenth of a pixel. */
/* The Portfolio page takes it by PAGE rather than by band, and it is the first
   to do so. Both of its `.more` links - "Our YouTube" in `#work` and "All
   results" in `#events` - are the same component measured the same way, and a
   page whose every section header has been compared against the prototype is a
   safer unit than two more ids in a list that has to be extended each time.
   `.pg-portfolio` is the body class `ugcza_page_body_class()` emits from the
   slug, so this cannot leak onto another page's `#events`. */
/* The press kit joins it by page for the same reason, 2026-08-14. It carries
   exactly one `.more`, over the brand asset list, and it is the only one on this
   list whose label and href come from a BLOCK rather than from a typed
   paragraph - the archive URL is a setting. That changes nothing here, because
   `ugcza_render_press_download()` emits the same `<p class="more"><a>` shape
   precisely so this rule keeps reaching it. */
/* THE ARTICLE PAGE'S `#more` BAND JOINS THIS LIST WITHOUT HAVING BEEN MEASURED,
   which is a first and is stated rather than slipped in. Every other selector
   above was added after its band was compared against the prototype; there is no
   prototype for an article page (BUILD-DECISIONS 23.2), so the justification is
   the opposite direction - `ugcza_render_article_more()` emits the same
   `<p class="more"><a>` shape inside a `.sec-h .row` that five measured bands
   use, so leaving it off this list would be the deviation. `.pg-news #more`
   rather than a bare `#more`: the id is generic enough that a future page could
   claim it, and the body class cannot leak because only a single post and a post
   archive wear it. */
.pg-news #more .sec-h .row .more a::after,
#brands .sec-h .row .more a::after,
#press .sec-h .row .more a::after,
#press-coverage .sec-h .row .more a::after,
.pg-portfolio .sec-h .row .more a::after,
.pg-press-kit .sec-h .row .more a::after,
#portfolio .sec-h .row .more a::after {
  content: "";
  width: 0.72em;
  height: 0.72em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
  background: currentColor;
  --ds-arw-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M7 1l4 4-4 4M11 5H1' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask: var(--ds-arw-glyph) center/contain no-repeat;
  mask: var(--ds-arw-glyph) center/contain no-repeat;
}

/* ---------- Pools pulled below the header rows (new) ----------
   Appended rather than edited: #about .about-bg beats the original
   .about-bg on specificity and the repeated ::before wins on order.
   Each pool's vertical radius is solved so the falloff reaches
   transparent above ~30% of the layer, which is where the .sec-h row
   sits. The eyebrow then lands on unwashed ground, where it measures
   5.56 / 4.98 / 4.77 on --surface-1 / -3 / -4. */
/* The opaque stop is PURE BLACK, not the brand near-black. The port folded
   `#000` onto --ds-color-bg-subtle (#0a0a0a) because they look the same, and in
   a mask they are not a colour at all - the gradient is read as alpha, and the
   two other stops in this same gradient are already channel-shadow. Folding one
   stop of a three-stop gradient onto a surface role left the mask describing
   itself in two different vocabularies, and the computed-style comparison
   reported it as `rgb(0,0,0)` against `rgb(10,10,10)`. PORT-07 and PORT-08. */
#about .about-bg, #about::before {
  -webkit-mask-image: radial-gradient(66% 44% at 30% 66%, rgb(var(--ds-color-channel-shadow)) 0%, rgba(var(--ds-color-channel-shadow), 0.72) 40%, transparent 88%);
  mask-image: radial-gradient(66% 44% at 30% 66%, rgb(var(--ds-color-channel-shadow)) 0%, rgba(var(--ds-color-channel-shadow), 0.72) 40%, transparent 88%);
}

#brands .wash-bg, #brands::before {
  -webkit-mask-image: radial-gradient(88% 40% at 50% 66%, var(--ds-color-bg-subtle) 0%, rgba(var(--ds-color-channel-shadow), 0.78) 46%, transparent 86%);
  mask-image: radial-gradient(88% 40% at 50% 66%, var(--ds-color-bg-subtle) 0%, rgba(var(--ds-color-channel-shadow), 0.78) 46%, transparent 86%);
}

/* == tile grounds: filled by tilebg.py == */
/* Testimonial panels: a ground rather than a flat plate. The quote is large
   type but the attribution under it is not, so the scrim is sized for the
   attribution. */
.tslide {
  position: relative;
  overflow: hidden;
}

.tslide > * {
  position: relative;
  z-index: 2;
}

.ts-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
}

.tslide::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(160deg, rgba(var(--ds-color-channel-scrim), 0.88) 0%, rgba(var(--ds-color-channel-scrim), 0.74) 100%);
}

/* Brands band: a real ground, not a wash.
   This section was .sec.teal (#1e3a38), dropped 2026-08-12 because the client
   did not want a flat chromatic band, then carried a blurred-photo wash over
   #222222. It now takes a teal-MAPPED ground from "Overlayed backgrounds/" -
   the colour relationship the client knows, without the flat chroma, and an
   actual picture rather than a filter stack.
   The wash machinery is switched off here rather than deleted, because #about
   still uses its own .about-bg implementation and the two were kept in step.
   The scrim is heaviest at the very top: the .sec-h row sits in the first ~10%
   of the band and its 11px orange eyebrow is the tightest contrast on the page
   - orange on flat #131313 is 5.43:1, and under the old pool it fell to ~4.3
   and missed the floor. Keep the scrim off nothing above that row. */
#brands {
  background: var(--ds-color-bg-surface-4) url(ASSET) center/cover no-repeat;
}

#brands .wash-bg {
  display: none;
}

#brands::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.94) 0%, rgba(var(--ds-color-channel-scrim), 0.8) 18%, rgba(var(--ds-color-channel-scrim), 0.58) 46%, rgba(var(--ds-color-channel-scrim), 0.58) 100%);
  -webkit-mask-image: none;
  mask-image: none;
}

#brands {
  --veil: var(--ds-color-bg-mosaic-veil);
  --veil-a: .96;
  --mos-bright: .92;
  --mos-sat: .6;
  --cap: var(--ds-color-bg-mosaic-cap);
  --cap-solid: 200px;
  --cap-fade: 340px;
  background: var(--ds-color-bg-mosaic-ground);
  position: relative;
  overflow: hidden;
}

#brands::before {
  content: none;
}

#brands .logo-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: grid;
  overflow: hidden;
  align-content: start;
  grid-template-columns: repeat(var(--mos-cols), 1fr);
  filter: saturate(var(--mos-sat)) brightness(var(--mos-bright));
}

#brands .logo-bg i {
  aspect-ratio: 1;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}

#brands .logo-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--veil);
  opacity: var(--veil-a);
}

#brands .logo-cap {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 2;
  pointer-events: none;
  height: var(--cap-fade);
  background: linear-gradient(180deg, var(--cap) 0, var(--cap) var(--cap-solid), transparent 100%);
}

#brands > .shell {
  position: relative;
  z-index: 3;
}

#brands .brands {
  position: relative;
  z-index: 3;
}

#brands {
  --mos-cols: 18;
}

@media (max-width: 1279px) {
  #brands {
    --mos-cols: 15;
  }
}
@media (max-width: 899px) {
  #brands {
    --mos-cols: 9;
  }
}
@media (max-width: 519px) {
  #brands {
    --mos-cols: 6;
  }
}
.wp-block-button.btn > .wp-block-button__link,
.wp-block-button.btn.is-style-outline > .wp-block-button__link {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--ds-font-heading);
  font-size: var(--ds-size-button);
  font-weight: var(--ds-weight-semibold);
  line-height: var(--ds-leading-relaxed);
  padding: 13px 24px;
  height: auto;
  border-radius: var(--ds-radius-md);
  border: var(--ds-control-border-width) solid transparent;
  background: var(--ds-color-btn-primary-bg);
  color: rgb(var(--ds-color-channel-light));
  text-decoration: none;
  transition: background var(--ds-motion-fast) var(--ds-motion-ease), color var(--ds-motion-fast) var(--ds-motion-ease), border-color var(--ds-motion-fast) var(--ds-motion-ease), transform 0.12s var(--ds-motion-ease);
}

.wp-block-button.btn > .wp-block-button__link:hover,
.wp-block-button.btn.is-style-outline > .wp-block-button__link:hover {
  background: var(--ds-color-btn-primary-hover-bg);
}

.wp-block-button.btn.line > .wp-block-button__link,
.wp-block-button.btn.line.is-style-outline > .wp-block-button__link {
  background: transparent;
  border-color: var(--ds-color-btn-secondary-border);
  color: var(--ds-color-fg-heading);
}

.wp-block-button.btn.line > .wp-block-button__link:hover,
.wp-block-button.btn.line.is-style-outline > .wp-block-button__link:hover {
  background: var(--ds-color-btn-secondary-hover-bg);
  border-color: var(--ds-color-btn-secondary-hover-border);
  color: var(--ds-color-btn-secondary-hover-text);
}

.wp-block-button.btn.sm > .wp-block-button__link {
  padding: 9px 16px;
  font-size: 15px;
}

.wp-block-button.btn,
.wp-block-button.btn:hover {
  background: none;
  border: 0;
  padding: 0;
  height: auto;
}

.pg-about .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-about .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-about h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-about h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-about .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* The testimonial panel is the same step down, and it is the only component on
   this page that ALSO renders on the homepage, so these four cannot go in
   _brands.scss without moving the homepage with them. Prototype values, both
   files diffed rather than guessed: `.tslide` 26->24 padding and .28->.3
   ground, `.who` 20->18, and `.tcar` loses the homepage's 6px lead-in because
   `.sec-h`'s own margin-bottom is the gap on this page. */
.pg-about .tcar {
  margin-top: 0;
}

.pg-about .tslide {
  background: rgba(var(--ds-color-channel-shadow), 0.3);
  padding: clamp(24px, 4vw, 42px);
}

.pg-about .tslide .who {
  margin-top: 18px;
}

/* ---------- page head ----------
   Every page in this design opens with this band rather than a post title.
   Pure black ground with two radial pools over it and a left-weighted scrim
   pulling the type side back down - the h1 sits on the dark end, the colour
   lives on the right where nothing is read. */
.phead {
  position: relative;
  background: rgb(var(--ds-color-channel-shadow));
  overflow: hidden;
  padding: clamp(46px, 7vw, 92px) 0 clamp(40px, 6vw, 76px);
}

.phead::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(62% 90% at 78% 20%, rgba(var(--ds-color-channel-key), 0.42), transparent 62%), radial-gradient(50% 80% at 10% 100%, rgba(var(--ds-color-channel-alt), 0.85), transparent 62%);
}

.phead::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(var(--ds-color-channel-shadow), 0.92) 0%, rgba(var(--ds-color-channel-shadow), 0.7) 46%, rgba(var(--ds-color-channel-shadow), 0.3) 100%);
}

.phead .shell {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.crumb {
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

.crumb a:hover {
  color: var(--ds-color-brand-accent);
}

/* ---------- story ----------
   Two columns that fold to one below ~660px: the copy on the left, the
   showreel and the press-kit call on the right. `auto-fit` rather than a
   fixed pair, so the fold is driven by the 300px minimum the copy needs
   rather than by a breakpoint invented here. */
.story {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(26px, 4vw, 58px);
  align-items: start;
}

.story p {
  color: var(--ds-color-fg-default);
  line-height: 1.78;
}

.story strong {
  color: var(--ds-color-fg-heading);
  font-weight: 600;
}

/* The drop cap is the only place on the site where type is set from the
   heading face at a multiple of its own size, so it is expressed in `em` and
   follows whatever the paragraph resolves to. */
.drop::first-letter {
  font-family: var(--ds-font-heading);
  font-size: 3.4em;
  float: left;
  line-height: 0.82;
  padding: 4px 12px 0 0;
  color: var(--ds-color-brand-primary);
}

/* The press-kit call under the showreel. The prototype writes this as an
   inline `margin-top:26px` on the wrapper, which is a value in markup; the
   class carries it here instead. It has to outrank the `.story>*>*` block-gap
   reset at the foot of this file, which it does on SPECIFICITY - (0,2,0)
   against that rule's (0,1,0) - and not on source order, so moving either one
   does not silently drop the gap. `margin-top` and the reset's
   `margin-block-start` are the same computed property in this writing mode and
   do compete. */
.story .go {
  margin-top: 26px;
}

/* ---------- the showreel, in the About page's own dress ----------
   `.reel` IS A SHARED SELECTOR WITH TWO DIFFERENT VALUES, which is the same
   class of finding as the five scale values at the head of this file. The
   homepage prototype carries the two-row treatment - a `.watch` call set into
   the bottom of the frame under a tall scrim, disc clamped to the frame - and
   `_about.scss` is the port of that. THIS page's prototype carries the older
   arrangement and still does: one 80px disc centred in the whole frame, a
   shallow scrim foot, and a `.cap` line bottom-left. Both files were compared
   directly rather than assumed, as the header of this file requires.

   An earlier revision took the homepage's treatment here on the grounds that
   there is one `.reel` in the design system and the two-row version replaced
   it on 2026-08-12. That reasoning does not survive the second half of the
   sentence: the replacement was made ON THE HOMEPAGE, this page's prototype
   was regenerated after it (13:38 on 2026-08-13) and still carries the centred
   disc, and `_portfolio-page.scss` had already forked the same three rules for
   the same reason. So the divergence is the design, not drift.

   The block markup follows: `wp:ugcza/about-reel {"call":"caption"}` emits a
   `.cap` where the homepage emits a `.watch`, because the copy differs too and
   CSS cannot rewrite a string. */
.pg-about .reel {
  display: grid;
  grid-template-rows: none;
  place-items: center;
}

/* A shallow foot rather than the homepage's tall ramp: this frame carries one
   11px line at the very bottom, not a 40px headline halfway up it, so the
   scrim only has to hold `fg-default` over the last few rows and the still
   gets the rest of the box back. The stops are the prototype's own. */
.pg-about .reel::after {
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-scrim), 0.92) 0%, rgba(var(--ds-color-channel-scrim), 0.88) 16%, rgba(var(--ds-color-channel-scrim), 0.45) 28%, transparent 46%);
}

/* Fixed 80px, not the homepage's clamp, and centred in the WHOLE frame rather
   than in row 1 of two. `grid-row:auto` undoes both the `1/3` span and the
   `@media (max-width:479px)` override in _about.scss - a media query adds no
   specificity, so this (0,3,0) selector wins at every width, which is what is
   wanted here because there is no second row for a disc to land on. */
.pg-about .reel .play {
  grid-row: auto;
  grid-column: auto;
  width: 80px;
  height: 80px;
}

/* PURE white, not the brand's #f9fafa: the prototype paints this triangle
   `#fff` and the two are 6/255 apart - small enough to read as a fold and
   large enough for a computed-style comparison to report it (PORT-08). */
.pg-about .reel .play::after {
  border-left: 22px solid rgb(var(--ds-color-channel-light));
  border-top: 13px solid transparent;
  border-bottom: 13px solid transparent;
  margin-left: 6px;
}

/* The caption, where the homepage's reel carries its watch call. Absolutely
   positioned rather than placed in the grid, because the disc is centred in the
   whole frame and there is no second row for this to sit in. */
.pg-about .reel .cap {
  position: absolute;
  z-index: 3;
  left: 16px;
  bottom: 14px;
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
}

/* ---------- founders ----------
   A portrait card: a 4:5 plate carrying the photograph, a gradient foot over
   it, and the name block beneath. The photograph arrives on the element as
   `--pi` because it comes from the media library; the gradient does NOT,
   because it is identical on every card and is design rather than content.
   In the prototype it was inlined as `--pg` on all three cards at once, which
   is the same value written three times in markup. */
.founders {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  gap: 18px;
}

.fdr {
  background: var(--ds-color-bg-subtle);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.fdr .por {
  aspect-ratio: 4/5;
  position: relative;
  background: var(--ds-color-bg-lift);
  background-image: var(--pi);
  background-size: cover;
  background-position: 50% 50%;
}

/* Transparent to 54%, then down to .6. Kept off the photograph itself so a
   portrait that is already dark at the foot does not double up. */
.fdr .por::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-portrait), 0) 54%, rgba(var(--ds-color-channel-portrait), 0.6));
}

.fdr .por span {
  position: absolute;
  z-index: 2;
  left: 0;
  bottom: 0;
  right: 0;
  padding: 16px 17px;
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-shadow), 0.9), transparent);
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
}

.fdr .b {
  padding: 18px 19px 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.fdr .b h4 {
  font-size: 24px;
}

.fdr .b .role {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
}

.fdr .b p {
  font-size: 14.5px;
  color: var(--ds-color-fg-default);
  line-height: 1.55;
}

/* ---------- mission ---------- */
.mission {
  border-left: 5px solid var(--ds-color-brand-primary);
  padding: clamp(20px, 3vw, 34px) 0 clamp(20px, 3vw, 34px) clamp(22px, 3vw, 38px);
}

.mission p {
  font-size: clamp(19px, 2.4vw, 28px);
  line-height: 1.48;
  color: var(--ds-color-fg-heading);
  font-family: var(--ds-font-heading);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.taglines {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: 14px;
  margin-top: clamp(26px, 4vw, 42px);
}

.tg {
  background: var(--ds-color-bg-subtle);
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

/* `.kind` and `.line` beside the prototype's own `span` and `b`: core's
   paragraph block cannot emit a bare `<b>` or `<span>`, and the copy has to
   stay editable in wp-admin, so the class is what carries the treatment and
   the element is whichever of the two rendered it. Same bridge as
   `.sp p:not(.eyebrow)` in _tiles.scss - no value changes, only the selector. */
.tg span, .tg .kind {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
}

.tg b, .tg .line {
  font-family: var(--ds-font-heading);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ds-color-fg-heading);
}

.tg.pick {
  background: var(--ds-color-brand-primary);
}

.tg.pick span, .tg.pick .kind {
  color: rgba(var(--ds-color-channel-light), 0.85);
}

/* PURE white, not the brand's #f9fafa. The prototype paints this line #fff on
   the red plate and the two are 6/255 apart, which a computed-style comparison
   reads and reports - the same fold `_wp-bridge.scss` had to undo on button
   labels. The contrast is unchanged either way. */
.tg.pick b, .tg.pick .line {
  color: rgb(var(--ds-color-channel-light));
}

/* ---------- values ----------
   `min-height` rather than an equal-height hack: the four cards carry
   different amounts of copy and the grid already stretches them, so this only
   sets the floor for the shortest. */
.vals {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(238px, 1fr));
  gap: 14px;
}

.val {
  background: var(--ds-color-bg-alt-lift);
  padding: 24px 21px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-height: 150px;
}

.val .num {
  font-family: var(--ds-font-label);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--ds-color-brand-accent);
}

.val h4 {
  font-size: 21px;
  line-height: 1.14;
}

/* `:not(.num)` because the number renders as a paragraph here and as a span in
   the prototype, and it must not pick up the body copy's size or colour. */
.val p:not(.num) {
  font-size: 14px;
  color: rgba(var(--ds-color-channel-light), 0.72);
  line-height: 1.5;
}

/* ---------- ambitions ----------
   A card carries optional artwork under a veil, on the client's instruction of
   2026-08-14 (BUILD-DECISIONS 4.8). `.art` is a SEPARATE treatment rather than
   a variable filled in: an ambition with no featured image renders exactly the
   flat card that shipped before, because painting the veil over the flat ground
   would darken every card that has no picture to justify it.

   THE VEIL IS MEASURED, AND THE FLOOR IS THE GROUND IT REPLACES. `.when` is
   `fg-muted` #868686 on `bg-muted` #383838, which is 3.22:1 - the prototype's
   own pairing, ported verbatim and already under 4.5:1 before any of this.
   Artwork must not make it worse, so the veil holds the composite no lighter
   than #383838 in the WORST case, which is a pure-white region of a photograph:
   255 - 245a <= 56 gives a >= .812. At .82 the type is exactly as legible over
   the brightest part of any picture as it is over the flat card today, and a
   normal dark frame lands between #0e0e0e and #363636 - a ground with texture
   in it rather than a picture, which is what a 135px card carrying two lines of
   type can actually hold. Lifting it means lifting `.when` off `fg-muted` in
   the same change, not on its own.

   FLAT RATHER THAN A GRADIENT, deliberately, and this is the part that looks
   like a missed opportunity. The type sits at the TOP of the card and the goal
   runs to three lines on the longest of the five, so the text zone is anywhere
   between 46% and 79% of the card depending on what the client types. A
   gradient lightening toward the foot would pass on today's copy and drop under
   the floor above the day somebody writes a longer goal - a defect that appears
   when the CONTENT changes rather than when the CSS does, which is the kind no
   gate here is watching for.

   `--ai` is the file and arrives on the element from the media library; the
   gradient does not, because it is identical on every card. Same split as
   `.fdr .por` above. */
.amb {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
}

.am {
  background: var(--ds-color-bg-muted);
  border-top: 3px solid var(--ds-color-brand-primary);
  padding: 20px 18px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  background-size: cover;
  background-position: 50% 50%;
}

/* Two background layers in one declaration, not a scrim pseudo-element. A
   pseudo-element over the image has to be held under the type by a stacking
   context, and this page already carries the note about an element left out of
   one painting UNDERNEATH the scrim it was supposed to sit above. As layers the
   gradient is over the picture and both are under the text with no z-index
   anywhere - the construction `.reel` already uses in _about.scss. */
.am.art {
  background-image: linear-gradient(rgba(var(--ds-color-channel-scrim), 0.82), rgba(var(--ds-color-channel-scrim), 0.82)), var(--ai);
}

.am b, .am .goal {
  font-family: var(--ds-font-heading);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--ds-color-fg-heading);
}

.am span, .am .when {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

/* ---------- voice ---------- */
.voice {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 18px;
}

.vc {
  background: var(--ds-color-bg-subtle);
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.vc h4 {
  color: var(--ds-color-brand-accent);
}

.vc ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.vc li {
  font-size: 14.5px;
  color: var(--ds-color-fg-default);
  padding-left: 16px;
  position: relative;
}

/* A 7px rule rather than a bullet glyph: neither licensed face carries a
   disc that sits on this baseline, and a list-style marker cannot be
   positioned. */
.vc li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 7px;
  height: 1px;
  background: var(--ds-color-brand-primary);
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` onto every
   child of a group block through `:root :where(.is-layout-flow) > *`. On a
   flex or grid container those margins ADD to the gap instead of collapsing
   into it, so every band above would run its own measured gap plus 26px. The
   selector is (0,1,0), equal to core's, and wins on source order because
   ugcza.css is enqueued after global-styles-inline-css. Exactly the fix
   `.sec-h>*` already carries in _sections.scss. */
.phead .shell > *,
.story > *, .story > * > *,
.vals > *, .val > *,
.amb > *, .am > *,
.voice > *, .vc > *,
.taglines > *, .tg > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

/* And the bands themselves. Each sits directly under `.sec-h`, whose own
   margin-bottom is the measured gap; core's block gap on top of it would add a
   second one. Same fix `.plists` already carries in _tiles.scss. `.taglines` is
   not in the list because it declares its own margin-top, which outranks core's
   rule on specificity (0,1,0 against 0,0,1). */
.story, .founders, .mission, .vals, .amb, .voice {
  margin-block-start: 0;
}

/* `.press` and `.tcar` render on the homepage too, so they are scoped rather
   than added to the line above. THE HOMEPAGE HIDES THIS ONE: block gap is a
   flat 26px and the homepage's `.sec-h` margin-bottom is `clamp(26px,3.6vw,44px)`,
   so the larger of the two collapsing margins is always the right one there. On
   this page `.sec-h` starts at 24px, block gap wins below ~765px, and both
   bands sat 2px low at 390 and correct at 1440 - which reads as a mobile-only
   defect rather than as the shared rule it is. */
.pg-about .press, .pg-about .tcar {
  margin-block-start: 0;
}

/* After the reset above, and higher specificity than it, so the paragraph
   rhythm inside the story survives. */
.story p + p {
  margin-top: 17px;
}

/* ---------- the page head ------------------------------------------------- */
/* THE BAND ITSELF IS NOT DEFINED HERE. `.phead`, its two wash layers and
   `.crumb` are shared by every inner page and land in `_about-page.scss`, which
   is where the first of them arrived. Only two things are added to that base:
   the type sizes, which every inner-page prototype sets identically and which
   differ from the homepage's, and this page's own deltas further down.
   Duplicating the base here would silently repaint the About page's wash from
   this file, because two identical selectors are decided by source order. */
/* The inner pages run a smaller h1 and a larger lede than the homepage. That is
   a real difference in the design rather than drift: the homepage's h1 is the
   only one on the site with nothing above it. Scoped to `.phead`, so the
   homepage keeps its own. */
.phead h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.phead .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* The live-sync label, with the studio tally light beside it. */
.src {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
}

/* `tally` is declared once, in _startgg.scss, and shared - the strip's tally
   and this one are the same light and must pulse together. */
.src i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ds-color-brand-primary);
  flex: none;
  animation: tally 2.4s ease-in-out infinite;
}

/* AFTER the rule it disables, or it silently does nothing: media queries add no
   specificity and with equal selectors source order decides. This has failed
   three times on this project. */
@media (prefers-reduced-motion: reduce) {
  .src i {
    animation: none;
  }
}
/* This page's own head: a tighter stack than the other inner pages, and its
   wash sits higher and hotter. Compound selectors throughout, so these still
   win once a second page defines the base above. */
.phead.phead--tournaments {
  padding: clamp(44px, 6.5vw, 84px) 0 clamp(34px, 5vw, 60px);
}

.phead.phead--tournaments .shell {
  gap: 15px;
}

.phead.phead--tournaments::before {
  background: radial-gradient(60% 92% at 84% 14%, rgba(var(--ds-color-channel-key), 0.44), transparent 62%), radial-gradient(52% 80% at 4% 100%, rgba(var(--ds-color-channel-alt), 0.9), transparent 62%);
}

.phead.phead--tournaments::after {
  background: linear-gradient(90deg, rgba(var(--ds-color-channel-shadow), 0.93) 0%, rgba(var(--ds-color-channel-shadow), 0.72) 46%, rgba(var(--ds-color-channel-shadow), 0.3) 100%);
}

.phead.phead--tournaments .lede {
  max-width: 62ch;
}

/* ---------- core block bridge --------------------------------------------- */
/* Core's flow layout writes `margin-block-start:var(--wp--style--block-gap)`
   onto every child of the page's `main`, which is 26px between bands that the
   design stacks flush - the head, the season figures, the sticky filter bar and
   the rows are one continuous column in the prototype, and each band pays for
   its own space with padding. Nothing in the markup shows you the gap; it comes
   from a rule the theme never wrote. Same fix `.sec-h>*` and `.about>*` already
   carry, applied at page level. */
.season, .filters, .sec.sec--rows {
  margin-block-start: 0;
}

/* ---------- the season band ----------------------------------------------- */
/* Five figures, every one of them computed from the tournament records at
   render. Nothing here is typed in, so the band cannot disagree with the rows
   under it. */
.season {
  background: var(--ds-color-bg-alt);
}

.season .shell {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px;
}

.st {
  background: var(--ds-color-bg-alt-lift);
  padding: 20px 18px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

/* PURE white, not the brand's #f9fafa, and the same call the button labels make:
   these sit on the teal lift where the brand white reads a touch cream. */
.st b {
  font-family: var(--ds-font-heading);
  font-size: clamp(28px, 3.6vw, 42px);
  font-weight: 600;
  color: rgb(var(--ds-color-channel-light));
  line-height: 1;
}

.st span {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(var(--ds-color-channel-light), 0.6);
  font-weight: 600;
}

/* ---------- the filter bar ------------------------------------------------ */
/* Sticks under the nav, which is 74px tall - the one place on the site where a
   component has to know another component's height. */
.filters {
  position: sticky;
  top: 74px;
  z-index: 60;
  background: var(--ds-color-bg-subtle);
}

.filters .shell {
  display: flex;
  align-items: center;
  gap: 0;
  overflow-x: auto;
}

.fbtn {
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  font-family: var(--ds-font-label);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  padding: 15px 18px 13px;
  white-space: nowrap;
  transition: 0.15s;
}

.fbtn:hover {
  color: var(--ds-color-fg-heading);
}

.fbtn.on {
  color: var(--ds-color-fg-heading);
  border-color: var(--ds-color-brand-primary);
}

.fbtn em {
  font-style: normal;
  color: var(--ds-color-fg-muted);
  margin-left: 6px;
  font-size: 10px;
}

.fbtn.on em {
  color: var(--ds-color-brand-accent);
}

/* ---------- the rows ------------------------------------------------------ */
/* A tighter section rhythm than the rest of the site: clamp(46px,6vw,84px)
   against the shared clamp(52px,7.5vw,102px). The prototype sets it per page
   and this one is 18px shorter at desktop, which is a difference worth keeping
   rather than folding - the page opens on a sticky filter bar, so the band
   under it starts closer. */
.sec.sec--rows {
  padding: clamp(46px, 6vw, 84px) 0;
}

.list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.tr {
  position: relative;
  overflow: hidden;
  background: var(--ds-color-bg-subtle);
  display: block;
}

/* Artwork behind the row under a scrim - no height cost. Opacity transitions
   alongside the scale so the whole hover resolves as one move; brightening
   instantly while growing over half a second reads as two separate events. */
.tr-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center 40%;
  transition: transform 0.55s ease-out, opacity 0.35s ease;
}

.tr-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(var(--ds-color-channel-scrim), 0.95) 0%, rgba(var(--ds-color-channel-scrim), 0.88) 45%, rgba(var(--ds-color-channel-scrim), 0.74) 100%);
}

.tr > * {
  position: relative;
  z-index: 1;
}

.tr.past .tr-bg, .tr.tr--completed .tr-bg {
  filter: grayscale(1);
  opacity: 0.42;
}

.tr:hover .tr-bg {
  opacity: 0.9;
}

/* Hover-zoom, the same 7% as the homepage hero but eased rather than linear -
   this is a response to the pointer, not a drift, so it wants to arrive and
   settle. Guarded on (hover:hover): rows expand on tap, and a touch device
   would leave the artwork stuck mid-zoom afterwards. The row already clips. */
@media (hover: hover) {
  .tr:hover .tr-bg {
    transform: scale(1.07);
  }
}
@media (prefers-reduced-motion: reduce) {
  .tr-bg {
    transition: none;
  }
  .tr:hover .tr-bg {
    transform: none;
  }
}
.tr.up, .tr.tr--upcoming {
  box-shadow: inset 3px 0 0 var(--ds-color-brand-accent);
}

.tr.live, .tr.tr--live {
  box-shadow: inset 3px 0 0 var(--ds-color-brand-primary);
}

/* The wash. Red at .34 over the left third of the artwork, which is what stops
   "running" and "coming up" being told apart by a 3px sliver of near-identical
   warm. */
.tr.live .tr-bg::after,
.tr.tr--live .tr-bg::after {
  background: linear-gradient(90deg, rgba(var(--ds-color-channel-key), 0.34) 0%, rgba(var(--ds-color-channel-scrim), 0.9) 45%, rgba(var(--ds-color-channel-scrim), 0.76) 100%);
}

.tr.past .ev, .tr.tr--completed .ev {
  color: var(--ds-color-fg-default);
}

/* Truncate rather than wrap: one long tournament name cannot be allowed to
   stretch a row, and a meta line that wraps takes the grid with it. */
.tr-meta, .tr-when .dt {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.tr-main h3 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.tr-cd {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ds-color-fg-heading);
}

.tr-win {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The prototype lifts the row ground to #151515 on hover. Folded onto
   `surface-2` (#161616), one part in 255 lighter: the row is under artwork at
   90% opacity by then, no type contrast floor moves, and there is no other
   #151515 anywhere in the design to give a role to. PORT-08 - a fold is a
   decision, and this is the deliberate kind. */
.tr:hover {
  background: var(--ds-color-bg-surface-2);
}

.tr.hide {
  display: none;
}

.tr-top {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr) auto;
  gap: clamp(12px, 2.4vw, 28px);
  padding: 20px 22px;
  align-items: start;
}

.tr-when {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.tr-when .dt {
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ds-color-fg-default);
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}

.tr-when .tm {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

.tr-main {
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-width: 0;
}

.tr-main h3 {
  line-height: 1.05;
}

.tr-meta {
  font-family: var(--ds-font-label);
  font-size: 11.5px;
  color: var(--ds-color-fg-muted);
  letter-spacing: 0.04em;
}

.tr-meta b {
  color: var(--ds-color-fg-default);
  font-weight: 600;
}

.games {
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
}

.g {
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
  padding: 3px 7px;
  white-space: nowrap;
}

/* The figure column. `text-align:right` here is the alignment the type forces -
   see the header of this file - and it is why the column collapses to the left
   only when it stops being a column at all, under 820px. */
.tr-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  text-align: right;
}

.tr-num {
  font-family: var(--ds-font-heading);
  font-size: 34px;
  font-weight: 600;
  line-height: 1;
  color: var(--ds-color-fg-heading);
}

.tr-num span {
  display: block;
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 400;
  margin-top: 4px;
}

.chip {
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 5px 10px;
  background: rgba(var(--ds-color-channel-light), 0.09);
  white-space: nowrap;
}

.chip.up, .chip.chip--upcoming {
  color: var(--ds-color-brand-accent);
}

.chip.live, .chip.chip--live {
  color: var(--ds-color-brand-primary);
}

.chip.done, .chip.chip--completed {
  color: var(--ds-color-fg-muted);
}

.tr-links {
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.tr-links a {
  background: transparent;
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  padding: 6px 10px;
  transition: 0.15s;
  border: 1px solid rgba(var(--ds-color-channel-paper), 0.42);
}

.tr-links a:hover {
  color: var(--ds-color-brand-accent);
  border-color: var(--ds-color-brand-accent);
}

/* The one filled control on a row, and it carries the brand's white-on-red
   button label - PURE white, like every other button on the site. */
.tr-links a.hot {
  color: rgb(var(--ds-color-channel-light));
  background: var(--ds-color-brand-primary);
  border-color: var(--ds-color-brand-primary);
}

.tr-links a.hot:hover {
  background: var(--ds-color-brand-accent);
  border-color: var(--ds-color-brand-accent);
  color: rgb(var(--ds-color-channel-light));
}

/* ---------- standings ----------------------------------------------------- */
/* Closed by default and opened by the row's own button, because four open
   tables is a page nobody can scan. */
.stand {
  padding: 0 22px 22px;
  display: none;
}

.tr.show .stand {
  display: block;
}

.stand-h {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
  padding: 16px 0 12px;
}

.tw {
  overflow-x: auto;
}

.res {
  width: 100%;
  min-width: 460px;
  border-collapse: collapse;
  font-family: var(--ds-font-label);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}

.res th {
  text-align: left;
  font-size: 9.5px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
  padding: 0 14px 9px 0;
  white-space: nowrap;
}

.res td {
  padding: 10px 14px 10px 0;
  color: var(--ds-color-fg-default);
  white-space: nowrap;
}

.res .pl {
  width: 44px;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
}

.res .pn {
  color: var(--ds-color-fg-heading);
  font-weight: 600;
  font-size: 13px;
}

/* The winner's line, in the brand red. It is the one row anybody looks for. */
.res tr.p1 .pl, .res tr.p1 .pn {
  color: var(--ds-color-brand-primary);
}

.toggle {
  background: transparent;
  cursor: pointer;
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  padding: 6px 10px;
  transition: 0.15s;
  border: 1px solid rgba(var(--ds-color-channel-paper), 0.42);
}

.toggle:hover {
  color: var(--ds-color-brand-accent);
  border-color: var(--ds-color-brand-accent);
}

/* Inline SVG, not U+25BE. Neither licensed face carries a triangle, so a
   literal caret falls back to a system font inside a button - which reads as a
   slightly wrong glyph rather than as an error. Same measurement that put the
   strip's arrow in SVG. */
.tgl-i {
  width: 0.62em;
  height: 0.62em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
  transition: transform 0.18s ease;
}

.tr.show .toggle .tgl-i {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .tgl-i {
    transition: none;
  }
}
/* ---------- the empty state ----------------------------------------------- */
.empty {
  padding: 38px 22px;
  text-align: center;
  font-family: var(--ds-font-label);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  display: none;
}

.empty.on {
  display: block;
}

@media (max-width: 820px) {
  .tr-top {
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .tr-side {
    align-items: flex-start;
    text-align: left;
  }
  .tr-links {
    justify-content: flex-start;
  }
}
/* ---------- figure tables ------------------------------------------------- */
/* THE ALIGNMENT RULES, and they load last on purpose - they are the correction
   the page ships, not the port's first draft. Seed and Record are figures and
   go right; Pl is a figure with a suffix and goes right; Player and Team are
   words and stay left. `table-layout:fixed` is what stops a long team name
   pulling the numeric columns out from under their headings. */
.res th:nth-child(n+4), .res td:nth-child(n+4) {
  text-align: right;
}

.res {
  table-layout: fixed;
}

.res th, .res td {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.res .pl {
  width: 72px;
  text-align: right;
  padding-right: 20px;
}

.res th:nth-child(3), .res td:nth-child(3) {
  width: 26%;
}

.res th:nth-child(4), .res td:nth-child(4) {
  width: 84px;
}

.res th:nth-child(5), .res td:nth-child(5) {
  width: 96px;
}

/* ===========================================================================
   SHARED SUB-PAGE CHROME - see note 2. Not portfolio-specific.
   =========================================================================== */
/* The page header band. Identical in the About, Partners, Portfolio, Contact,
   News and Accademy prototypes, down to the clamps.
   PURE black, not the brand near-black, for the same reason `.nav` is: this band
   butts against the nav and #0a0a0a would read as a seam across the top of every
   sub-page. */
.phead {
  position: relative;
  background: rgb(var(--ds-color-channel-shadow));
  overflow: hidden;
  padding: clamp(46px, 7vw, 92px) 0 clamp(40px, 6vw, 76px);
}

/* The copy column owns its rhythm: a flex column on a 16px gap. Core's flow
   layout also writes `margin-block-start:var(--wp--style--block-gap)` onto every
   child of a group block, through `:root :where(.is-layout-flow) > *`, and on a
   flex container those margins ADD to the gap rather than collapsing into it.
   Same bridge, same reason, as `.sec-h > *` in _sections.scss. */
.phead .shell > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

/* The breadcrumb stays at --tx-3 on every sub-page. The documented rule that
   #868686 cannot hold 4.5:1 over artwork was measured against event logos under
   a light scrim; here the ground is L=.0044 and it measures 5.30:1. Re-measure
   if the scrim over the backdrop is ever weakened. */
.crumb {
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

.crumb a:hover {
  color: var(--ds-color-brand-accent);
}

/* ===========================================================================
   THE PORTFOLIO PAGE
   =========================================================================== */
/* THE BAND SEAMS ARE NOT FIXED HERE, and the rule that used to be was inert.
   `pg-portfolio` is a BODY class - `ugcza_page_body_class()` emits it from the
   page slug - so `.pg-portfolio>*` selects `.wp-site-blocks` and never a band.
   The bands are children of `.entry-content`, two levels further in, and
   `_chrome.scss` already zeroes core's flow gap on them site-wide with
   `.entry-content:has(> .sec) > *`. This file has never been live until now, so
   the wrong selector had never been exercised; it is recorded rather than
   quietly deleted because the same mistake is one page slug away at any time.
   See the `ugcza-rendered-dom-chain` note and PORT-40.

   For the bands' own contents. `margin-block-end` is deliberately NOT reset
   here: `.sec-h` carries a measured bottom margin and this selector outranks
   it. */
.pg-portfolio .shell > * {
  margin-block-start: 0;
}

/* ---------- the sub-page type scale (see note 2) ---------- */
/* One step down from the homepage's, and consistently: `.sec` 50/7vw/96 against
   52/7.5vw/102, h1 74 against 80, h2 48 against 52, the section header's gap 42
   against 44. Six sub-page prototypes agree; the homepage is the outlier. */
.pg-portfolio .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-portfolio h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-portfolio h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-portfolio .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

/* 62ch rather than the homepage's 58: this page's lede is the longest on the
   site and the header column is full width rather than a hero's half. */
.pg-portfolio .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 62ch;
}

/* ---------- the page header, portfolio only ---------- */
/* THE BACKDROP IS THE NEWEST UPLOAD ACROSS THE THREE PLAYLISTS, so this header
   re-dresses itself every time the client adds a video - no image to export,
   nothing to brief. `ugcza_render_portfolio_header_bg()` supplies the file and
   nothing else; everything measured is here.
   PORTFOLIO ONLY. `.phead` is shared chrome across twelve sub-pages, so this
   rule and its markup are deliberately not in the others.

   Blurred to 32px: enough shape survives to read as a photograph, not enough to
   identify the frame. That is the property to preserve if the number is ever
   tuned - this is an unattended feed, and whatever the client uploads next lands
   behind the page title without anyone vetting it. The source is 960px wide
   BECAUSE the blur is 32px; at 60px nothing above ~500px was visible and the
   file was 560px. The two track each other, so change them together.

   Contrast does NOT depend on the blur: the copy sits on the .phead::after
   scrim, and every value from 24px to 60px measures within half a point.
   Dial it freely on taste; re-measure only if the SCRIM moves.

   inset:-88px is structural, not decorative - blur() samples beyond the element
   box, so without the bleed the wash fades out at its own edges. `.phead`
   already clips. */
.pg-portfolio .phead-bg {
  position: absolute;
  inset: -88px;
  z-index: 0;
  pointer-events: none;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  filter: blur(32px) saturate(0.72) brightness(0.5);
}

/* THE Z-INDEXES ARE LOAD-BEARING, not tidiness. A ::before with `z-index:auto`
   paints BEFORE the element's own children, so the backdrop above would cover
   the orange spotlight entirely. Stack: backdrop 0, spotlight 1, scrim 2,
   copy 3. */
.pg-portfolio .phead::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: radial-gradient(60% 90% at 80% 18%, rgba(var(--ds-color-channel-accent), 0.44), transparent 62%), radial-gradient(52% 80% at 6% 100%, rgba(var(--ds-color-channel-alt), 0.9), transparent 62%);
}

/* Scrim strengthened from .92/.70/.30 across, which is what the other sub-pages
   carry: it now has a photograph under it rather than flat black, and it is what
   holds the copy at its contrast floor. Measured on the shipped render, against
   the brightest 0.5% of the copy column: h1 19.30:1, lede 9.52:1, breadcrumb
   5.30:1, eyebrow orange 5.64:1. DO NOT WEAKEN IT without re-measuring all four.
   Pure black through the shadow channel, not the brand near-black: the gradient
   has to reach the header's own ground and #0a0a0a would leave an edge. */
.pg-portfolio .phead::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(90deg, rgba(var(--ds-color-channel-shadow), 0.94) 0%, rgba(var(--ds-color-channel-shadow), 0.82) 46%, rgba(var(--ds-color-channel-shadow), 0.55) 100%);
}

.pg-portfolio .phead .shell {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* The two calls under the lede. A length in block markup is a design value
   living under wp-content, which the token gate forbids, so the row is a class
   and its geometry is here - the same move `.props-h` and `.hcta` already make. */
.pg-portfolio .phead-cta {
  display: flex;
  gap: 11px;
  flex-wrap: wrap;
  margin-top: 8px;
}

/* ---------- playlist cards: the portfolio's dress ---------- */
/* The shared component does the work; these four are what the portfolio
   prototype changes and says it changes. The card is wider here (268 against
   258) because there are three across a full-width shell rather than three in a
   band, and the title is 28px against 26 - the floor is 3:1 either way, since
   both are large text. */
.pg-portfolio .plists {
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
}

.pg-portfolio .pthumb {
  padding: 16px;
}

.pg-portfolio .pthumb b {
  font-size: 28px;
  line-height: 1.02;
  text-shadow: 0 2px 16px rgba(var(--ds-color-channel-shadow), 0.8);
}

.pg-portfolio .pbody {
  padding: 17px 18px 19px;
}

/* THE BADGE IS THE ONE THING THIS PAGE ADDS. It carries the real video count,
   pulled from the playlist feed, where the homepage card prints the plain
   "YouTube playlist" label in its foot. Above the frame's grey overlay
   (z-index 1) or it disappears under it. */
.pg-portfolio .pthumb .cnt {
  position: absolute;
  z-index: 2;
  right: 12px;
  top: 12px;
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: rgba(var(--ds-color-channel-shadow), 0.75);
  padding: 5px 8px;
  color: var(--ds-color-fg-default);
}

/* The foot becomes a two-part row - source on the left, the fact that it keeps
   itself up to date on the right - because the count has moved to the badge. */
.pg-portfolio .pbody .n {
  padding-top: 13px;
  display: flex;
  justify-content: space-between;
  gap: 10px;
}

.pg-portfolio .pbody .n em {
  font-style: normal;
  color: var(--ds-color-brand-accent);
}

/* ---------- event property tiles ---------- */
/* EXPLICIT COLUMN COUNTS, NOT THE HOMEPAGE'S auto-fit minmax(166px). Same
   treatment, different arithmetic: there are SEVEN tiles here against the
   homepage's six, and auto-fit resolves to six columns at most desktop widths,
   which strands the seventh alone beside five empty cells. These counts divide
   seven without an orphan row wider than a third of the grid.
   The widest step takes the count from the block rather than a literal 7, so an
   eighth property - allowed by decision, client 2026-08-13 - lands in the same
   row instead of starting a second one. `--prop-count` is written by
   `ugcza_render_properties()`; the fallback is what the prototype shows.
   `margin-top:0` because this grid sits directly under `.sec-h`, whose measured
   bottom margin is the gap. The homepage's copy sits under a heading of its own
   and carries 22px for it. */
.pg-portfolio .props {
  margin-top: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 620px) {
  .pg-portfolio .props {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 900px) {
  .pg-portfolio .props {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
@media (min-width: 1320px) {
  .pg-portfolio .props {
    grid-template-columns: repeat(var(--prop-count, 7), minmax(0, 1fr));
  }
}
/* THE ONE RULE HERE THAT IS NOT SCOPED TO THIS PAGE, and it is here to prevent a
   regression rather than to style anything. Publishing CREW gives the tile set
   seven members, and every band that shows them draws from the same post type -
   so the homepage's auto-fit grid would strand the seventh tile the moment this
   page's content landed. `:has()` means the rule engages only once there IS a
   seventh tile: with six the selector does not match and the homepage's grid is
   byte-identical to what it was. */
@media (min-width: 1320px) {
  .props:has(> :nth-child(7)) {
    grid-template-columns: repeat(var(--prop-count, 7), minmax(0, 1fr));
  }
}
/* Negative delays start each tile mid-cycle - without them the tiles breathe in
   lockstep, which reads as a glitch rather than as life. The design system
   carries two through six on a 4s ladder; these continue it for the seventh and
   for an eighth, rather than restating the prototype's own 3s ladder, which
   would fork a shared component to no visible end. */
.prop:nth-child(7) .pbgi {
  animation-delay: -25s;
}

.prop:nth-child(8) .pbgi {
  animation-delay: -29s;
}

/* ---------- the events table ---------- */
/* Scrolls rather than shrinks. A five-column table of dates and figures has a
   width below which it stops being readable, and `min-width` plus this is what
   keeps a narrow screen scrolling one element instead of the page. */
.pg-portfolio .tw {
  overflow-x: auto;
}

/* `table-layout:fixed` with the three widths below is the same contract the
   standings table uses: the event name is the only elastic column, so it is the
   one that ellipsises when the rest have had their share. */
/* `line-height:normal`, EXPLICITLY, AND IT IS NOT A TIDY-UP. The prototype sets no
   line-height on this table at all and its cells compute to `normal`; the built
   page's inherit the body's 1.65, which made every row 7px taller and the whole
   table 39px - right content, right widths, right padding, wrong box.
   The cause is not in either stylesheet. THE PROTOTYPE FILES CARRY NO DOCTYPE -
   they open on `<title>` - so a browser renders them in QUIRKS MODE, where
   tables do not inherit font properties from the body. WordPress emits
   `<!DOCTYPE html>`, so the build is in standards mode and the table inherits.
   Two renders of identical CSS, differing on a rule neither file contains.
   Reproducing the design therefore means saying `normal` out loud, which lets
   each face's own metrics decide the leading exactly as they do over there -
   19px TGS Perfect Condensed and 10px Aloevera do not resolve to the same
   ratio, so a number here would have to be two numbers and both would be wrong
   the moment a face changed.
   ON THE TABLE, NOT ON `th, td`, so the cells INHERIT it - which is the same
   thing the prototype's own cells are doing, and it means the table element
   itself compares equal too rather than being the one box left carrying 1.65.
   EVERY OTHER TABLE ON THIS SITE HAS THE SAME PROBLEM WAITING - the standings
   table on Tournaments, and anything the press kit grows. See PORT-56. */
.pg-portfolio table {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  font-size: 14.5px;
  table-layout: fixed;
  line-height: normal;
}

.pg-portfolio th {
  text-align: left;
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
  padding: 13px 15px;
  background: var(--ds-color-bg-subtle);
  white-space: nowrap;
}

.pg-portfolio td {
  padding: 14px 15px;
  color: var(--ds-color-fg-default);
  vertical-align: middle;
}

.pg-portfolio tr:last-child td {
  border-bottom: 0;
}

.pg-portfolio tr:hover td {
  background: rgba(var(--ds-color-channel-light), 0.03);
}

.pg-portfolio td.ev {
  font-family: var(--ds-font-heading);
  font-size: 19px;
  font-weight: 600;
  color: var(--ds-color-fg-heading);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* RIGHT-ALIGNED, and that is a typographic requirement rather than a taste.
   Neither licensed face carries tabular figures and neither exposes a `tnum`
   feature, so a column of counts cannot align on its digits - right-aligning
   puts the ragged edge on the left, where the eye does not track it. The
   `font-variant-numeric` below is the prototype's and is a no-op on these faces;
   it is kept so the two renders compare identically. */
.pg-portfolio td.num {
  text-align: right;
  font-family: var(--ds-font-label);
  font-variant-numeric: tabular-nums;
  color: var(--ds-color-brand-accent);
  font-weight: 600;
  white-space: nowrap;
}

.pg-portfolio td.dt {
  font-family: var(--ds-font-label);
  font-size: 12px;
  color: var(--ds-color-fg-muted);
  white-space: nowrap;
}

.pg-portfolio .gt {
  display: inline-block;
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
  padding: 3px 7px;
  margin: 2px 3px 2px 0;
  white-space: nowrap;
}

.pg-portfolio td.dt, .pg-portfolio th:nth-child(2) {
  width: 190px;
}

.pg-portfolio th:nth-child(3), .pg-portfolio td:nth-child(3) {
  width: 150px;
}

.pg-portfolio table th:last-child, .pg-portfolio table td:last-child {
  width: 110px;
  text-align: right;
}

/* ---------- the case study ---------- */
.pg-portfolio .cs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(24px, 3.6vw, 50px);
  align-items: start;
}

.pg-portfolio .cs > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

/* The plate the event photography goes into. The picture is the tournament's
   own, layered in as an `<img>` by the shared rule in `_tiles.scss`; until
   there is one the plate is the brand's two warms crossed at 155deg, which
   reads as a designed ground rather than as a missing image - the same call the
   coverage tiles make.

   THE GRADIENT STAYS UNDER THE PICTURE rather than being switched off by it.
   It is what shows while a lazily-loaded image is still in flight and behind
   anything transparent, so the plate is never briefly empty and never has to
   pick between two grounds. The stack is ground 0, picture 1, caption 2 - and
   the picture needs its `z-index` said out loud: `::before` and `<img>` are
   both positioned at `auto`, so they would paint in tree order, which is an
   ordering that a later edit to the markup could silently reverse. */
.pg-portfolio .csimg {
  position: relative;
  aspect-ratio: 4/3;
  background: var(--ds-color-bg-lift);
  overflow: hidden;
}

.pg-portfolio .csimg::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(155deg, rgba(var(--ds-color-channel-key), 0.6), rgba(var(--ds-color-channel-alt), 0.95));
}

.pg-portfolio .csimg > img {
  z-index: 1;
  transform: scale(1);
  transition: transform 0.5s cubic-bezier(0.2, 0.72, 0.24, 1);
}

/* The overlay, and it is NOT decoration alone - it is what keeps the caption
   readable now that the ground is a photograph instead of a flat gradient.

   MEASURED, NOT CHOSEN, per the project's scrim rule: the floor is the ground
   the scrim replaced, which is a harder bar than 4.5:1 and the right one, since
   the plate must not get worse for having gained a picture. The caption on the
   old two-warm gradient measures 6.77:1 at its worst pixel. On the bare
   photograph it collapses to 1.52:1 - the bottom left of this crop holds a lit
   desk - so the pair below was swept until it cleared the floor, landing 7.05:1.

   TWO STOPS BECAUSE THERE ARE TWO JOBS. The first is a bottom-weighted neutral
   that restores the caption band; at .80 over the lowest third it is still
   LIGHTER than `.tile-art`, which runs to .93 at 50% and .96 at 100% for the
   same reason. The second is the brand's own 155deg diagonal, the decoration,
   at the strength where it reads as a treatment rather than as mud - .50 was
   measured too, and flattened the photograph's colour without buying contrast
   the foot was not already providing.

   `:has(>img)` is the gate, the same one `.tile-art` uses: a plate with no
   photograph generates no ::after box at all, so the empty state stays the
   designed gradient ground rather than a darkened empty box.

   It sits at z-index 1 alongside the picture and above it regardless, because
   `::after` is by definition the last child and nothing an editor does to the
   markup can reorder it. The caption stays at 2 and is untouched. */
.pg-portfolio .csimg:has(> img)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-shadow), 0.8) 0%, rgba(var(--ds-color-channel-shadow), 0) 34%), linear-gradient(155deg, rgba(var(--ds-color-channel-key), 0.21), rgba(var(--ds-color-channel-alt), 0.38));
}

/* Zoom the picture, never the plate. `.csimg` carries `overflow:hidden`
   already, so the scale is clipped by the frame it is drawn in.

   `:hover` ALONE, with no `:focus-visible`, and that is deliberate rather than
   an omission. The plate is a plain `div` - unlike the coverage tiles and
   playlist cards, which are links and are focusable, so their zoom has a
   keyboard equivalent to honour. Adding one here would style a state this
   element can never enter. */
.pg-portfolio .csimg:hover > img {
  transform: scale(1.07);
}

/* AFTER the rules it disables. Media queries add no specificity, so with equal
   selectors source order is the only thing deciding - PORT-05, which has failed
   three times on this project. `.csimg` is also added to the MOTION list in
   `tools/check-reduced-motion.mjs`; that list is explicit, so motion on a new
   selector is unverified until it is named there. */
@media (prefers-reduced-motion: reduce) {
  .pg-portfolio .csimg > img {
    transition: none;
  }
  .pg-portfolio .csimg:hover > img {
    transform: none;
  }
}
.pg-portfolio .csimg span {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 16px 17px;
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-shadow), 0.9), transparent);
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
}

.pg-portfolio .csgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 14px;
  margin-top: 20px;
}

.pg-portfolio .csgrid > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

.pg-portfolio .csn {
  background: var(--ds-color-bg-subtle);
  padding: 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.pg-portfolio .csn > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

.pg-portfolio .csn b {
  font-family: var(--ds-font-heading);
  font-size: 30px;
  font-weight: 600;
  color: var(--ds-color-brand-primary);
  line-height: 1;
}

.pg-portfolio .csn span {
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 600;
}

/* The five-part capture checklist. Every past event gets the same template, so a
   gap is obvious rather than absent. */
.pg-portfolio .cslist {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.pg-portfolio .cslist li {
  background: var(--ds-color-bg-subtle);
  padding: 13px 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 14px;
  color: var(--ds-color-fg-default);
}

.pg-portfolio .cslist li b {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  font-weight: 700;
  margin-left: auto;
  white-space: nowrap;
}

.pg-portfolio .cslist li b.yes {
  color: var(--ds-color-brand-accent);
}

.pg-portfolio .cslist li b.no {
  color: var(--ds-color-fg-muted);
}

/* The case-study copy sits on the teal band, where --tx-2 is too dark to hold
   its floor: white at .82 measures 9.6:1 on #1e3a38 where #b6b6b6 manages 4.1.
   The prototype carries this as an inline style on each paragraph; a colour in
   block markup is exactly what the token gate forbids, so it is a class. */
.pg-portfolio .cscopy {
  color: rgba(var(--ds-color-channel-light), 0.82);
  line-height: 1.75;
}

.pg-portfolio .cscopy + .cscopy {
  margin-top: 16px;
}

.pg-portfolio .csh {
  margin-top: 26px;
  color: var(--ds-color-brand-accent);
}

.pg-portfolio .csbtns {
  display: flex;
  gap: 11px;
  flex-wrap: wrap;
  margin-top: 22px;
}

/* ---------- the showreel ---------- */
/* SAME POSTER AND SAME VEIL AS THE HOMEPAGE, and a different scrim on purpose.
   This box carries one line of 11px meta rather than the homepage's headline, so
   the scrim is a shallow foot rather than that page's tall ramp: it holds
   --tx-2 over the worst row of the real frame and gives the picture back the
   rest of the box. The frame is also far bigger here - full shell width against
   seven columns of twelve - which is why the play control is a fixed 82px rather
   than the homepage's clamp. */
.pg-portfolio .reel::after {
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-scrim), 0.92) 0%, rgba(var(--ds-color-channel-scrim), 0.88) 16%, rgba(var(--ds-color-channel-scrim), 0.45) 28%, transparent 46%);
}

.pg-portfolio .reel .play {
  width: 82px;
  height: 82px;
}

/* PURE white, not the brand's #f9fafa, which is what the shared rule paints.
   The prototype sets this triangle `#fff` and the two are 6/255 apart - small
   enough to look like a fold and large enough for a computed-style comparison to
   report it, which is exactly the case PORT-08 says to decide rather than tidy.
   The channel token is what PORT-07 reserves for true white. */
.pg-portfolio .reel .play::after {
  border-left: 22px solid rgb(var(--ds-color-channel-light));
  border-top: 13px solid transparent;
  border-bottom: 13px solid transparent;
  margin-left: 6px;
}

/* The caption, where the homepage's reel carries its watch call. Absolutely
   positioned rather than placed in the grid, because the disc is centred in the
   whole frame here and there is no second row for this to sit in. */
.pg-portfolio .reel .cap {
  position: absolute;
  z-index: 3;
  left: 17px;
  bottom: 15px;
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
}

/* ---------- motion ---------- */
/* MUST SIT AFTER EVERY RULE IT DISABLES. Media queries add no specificity, so
   with equal selectors source order is what decides, and a reduce block placed
   above the rules it turns off silently does nothing while the page keeps
   animating for people who asked it not to. That has failed three times on this
   project and no visual check catches it - verify with Playwright's
   newContext({ reducedMotion: 'reduce' }), never by reading the CSS.
   The shared components disable themselves in their own partials; what is left
   here is the seventh and eighth property tiles' drift, which this file is the
   one that starts. */
@media (prefers-reduced-motion: reduce) {
  .prop:nth-child(7) .pbgi, .prop:nth-child(8) .pbgi {
    animation: none;
  }
}
/* ---------- the page's scale ----------
   See the header. These five are the whole of the difference between this page
   and the design system's defaults, and they are the prototype's own numbers. */
.pg-press-kit .sec {
  padding: clamp(48px, 6.6vw, 92px) 0;
}

.pg-press-kit .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-press-kit h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-press-kit h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-press-kit .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 62ch;
}

/* The lede on the teal band. `fg-default` is drawn for the ink grounds and goes
   flat over teal; the prototype writes `rgba(255,255,255,.75)` inline on that one
   paragraph, which is a colour living in markup. It is the rule rather than the
   markup that carries it here, and it is the pure-white channel because that is
   what the prototype names - the brand near-white is 6/255 away and a
   computed-style comparison reports the fold (PORT-08). */
.pg-press-kit .sec.teal .lede {
  color: rgba(var(--ds-color-channel-light), 0.75);
}

/* ---------- the page header ----------
   The band itself is `_about-page.scss`'s, unscoped, and stays that way - every
   page opens with one. What differs here is the two pools over it: this page
   throws the red higher and wider (92% tall at 16% down, against About's 90% at
   20%) and takes the teal harder into the bottom-left corner. Pseudo-elements,
   so no computed-style probe can reach them - the pixel diff is the only thing
   that can see this rule, which is why it is written from the prototype's own
   values rather than approximated from About's. */
.pg-press-kit .phead::before {
  background: radial-gradient(60% 92% at 82% 16%, rgba(var(--ds-color-channel-key), 0.46), transparent 62%), radial-gradient(52% 80% at 4% 100%, rgba(var(--ds-color-channel-alt), 0.9), transparent 62%);
}

.pg-press-kit .phead::after {
  background: linear-gradient(90deg, rgba(var(--ds-color-channel-shadow), 0.93) 0%, rgba(var(--ds-color-channel-shadow), 0.72) 46%, rgba(var(--ds-color-channel-shadow), 0.3) 100%);
}

/* The two calls under the lede. The prototype writes this as an inline
   `style="display:flex;gap:11px;flex-wrap:wrap;margin-top:8px"` on the wrapper,
   which is four design values living in markup - the token gate forbids that
   under wp-content, so the class carries them and the pattern names the class. */
.pact {
  display: flex;
  gap: 11px;
  flex-wrap: wrap;
}

/* THE 8px LEAD HAS TO OUTRANK THE BLOCK-GAP RESET, and it did not at first.
   `_about-page.scss` zeroes `.phead .shell>*` at (0,2,0) to strip core's flow
   margin off every child of the page header - correct, and it also killed a
   plain `.pact{margin-top:8px}` at (0,1,0). The band came out EIGHT PIXELS
   SHORT at both widths, which then reported as `starts -8.00px` on all eleven
   bands below it and made the page look like it had a systemic offset rather
   than one wrong rule. Naming the child beats the reset at (0,3,0). */
.phead .shell > .pact {
  margin-block-start: 8px;
}

/* THE GAP HAS TO OUTRANK CORE'S OWN FLEX LAYOUT, which is the whole difficulty.
   `.pact` lands on a `wp:buttons` block, and core ships
   `.wp-block-buttons.is-layout-flex{gap:var(--wp--style--block-gap)}` at (0,2,0)
   - so a sensible-looking `.pact{gap:11px}` at (0,1,0) loses outright and the
   two calls sit a 26px block gap apart while the stylesheet reads as correct.
   Naming the block and its layout class alongside ours is (0,3,0) and wins on
   specificity rather than on load order, which is the same call `.fsoc` makes in
   _tiles.scss against core's social-link palette. */
.pact.wp-block-buttons.is-layout-flex {
  gap: 11px;
}

/* Core's button block nests the clickable element inside a wrapper, and the base
   layer pins that wrapper to `--ds-control-height`. `_wp-bridge.scss` gives the
   LINK `height:auto` so its own padding, border and leading decide its size;
   without the same for the wrapper a 58px link sits in a 50px box. The identical
   one-line fix `.sp .go` and `.hcta` already carry, for the identical reason. */
.pact .wp-block-button.btn {
  height: auto;
}

/* ---------- the jump bar ----------
   A second sticky rail under the nav, so the nine bands of a long reference page
   stay one press away wherever you are in it.

   `top` IS THE NAV'S OWN HEIGHT and the two are a pair: the nav is 74px and
   sticks at 0, this sticks directly under it. The prototype restates 74px inside
   its own `@media (max-width:880px)` block, which changes nothing - the nav does
   not resize there, only `.menu` is hidden - so that rule is not ported.

   `overflow-x:auto` with `min-width:max-content` on the row is what makes nine
   labels survive a 390px viewport: the rail scrolls sideways rather than
   wrapping to three lines and pushing the page down. */
.jump {
  position: sticky;
  top: 74px;
  z-index: 60;
  background: var(--ds-color-bg-subtle);
  overflow-x: auto;
}

/* AND EVERY DESTINATION THIS RAIL POINTS AT CLEARS BOTH BARS. `_sections.scss`
   gives `.sec` a 74px `scroll-margin-top` for the nav alone; this rail is 47px
   more, stuck directly under it, so all nine sections landed 73px of content
   behind the pair. Tapping "Brand assets" put the reader in the middle of a
   sentence with the eyebrow, the heading and the download button above the
   fold. Measured at 390, and the number is the stack, not a guess: nav 74 +
   rail 47. BUILD-DECISIONS 25.4. */
.pg-press-kit .sec {
  scroll-margin-top: 121px;
}

.jump .shell {
  display: flex;
  gap: 0;
  min-width: max-content;
}

.jump a {
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  padding: 14px 18px 13px;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  font-weight: 700;
}

.jump a:hover {
  color: var(--ds-color-fg-heading);
  border-color: var(--ds-color-brand-primary);
}

/* ---------- the facts table ---------- */
.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}

.fact {
  background: var(--ds-color-bg-subtle);
  padding: 19px 20px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.fact span {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
}

/* The BODY face at 16.5px, not the heading face: these are values to be read and
   copied, some of them long enough to wrap, and the condensed heading face is
   drawn for short lockups. */
.fact b {
  font-size: 16.5px;
  font-weight: 600;
  color: var(--ds-color-fg-heading);
  font-family: var(--ds-font-body);
  line-height: 1.4;
}

.fact b a {
  color: var(--ds-color-brand-accent);
}

/* ---------- the stat row, in this page's dress ----------
   The component is `_about.scss`'s and the artwork layer, the drift and the
   reduced-motion guard all stay there. Three things differ and all three are the
   prototype's own:

   1. THE GROUND IS `alt-lift`, because this band sits on the teal section rather
      than on the homepage's ink one. A tile carrying `subtle` here would read as
      a hole in the band.
   2. THE FIGURE IS WHITE, not brand red. Red on teal is the one pairing the
      palette does not hold: #ea3700 has luminance .20 and tops out at 5.04:1 on
      pure black, so over a scrimmed photograph on a teal plate it has nothing
      left. The prototype paints `#fff` here and that is the channel token, not
      the brand near-white - the two are 6/255 apart and a computed-style
      comparison reports the fold (PORT-08).
   3. THE SCRIM IS HEAVIER: .89/.91/.95/.95 against the homepage's
      .84/.86/.93/.93. Measured, not chosen - the label sits at 10.5px over
      photography and the homepage's floor was set against a red figure that is
      not here.

   The sub-label goes to `fg-default` for the reason `_about.scss` states at its
   own `.stat em`: `fg-muted` (#868686) at 10.5px cannot clear 4.5:1 over
   anything lighter than about #1a1a1a, so the ground was never the fixable
   part. */
.pg-press-kit .stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 14px;
  margin-top: 0;
}

.pg-press-kit .stat {
  background: var(--ds-color-bg-alt-lift);
  padding: 24px 20px;
  gap: 5px;
  position: relative;
  overflow: hidden;
}

.pg-press-kit .stat > b, .pg-press-kit .stat > span, .pg-press-kit .stat > em {
  position: relative;
  z-index: 2;
}

.pg-press-kit .stat b {
  font-size: clamp(34px, 4.4vw, 50px);
  color: rgb(var(--ds-color-channel-light));
}

.pg-press-kit .stat > span, .pg-press-kit .stat > em {
  color: var(--ds-color-fg-default);
}

.pg-press-kit .stat::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.89) 0%, rgba(var(--ds-color-channel-scrim), 0.91) 40%, rgba(var(--ds-color-channel-scrim), 0.95) 66%, rgba(var(--ds-color-channel-scrim), 0.95) 100%);
}

/* A sixth tile, continuing the 5s ladder `_about.scss` runs to five. Without it
   the sixth breathes in lockstep with the first, which reads as a glitch. */
.pg-press-kit .stat:nth-child(6) .stat-bg {
  animation-delay: -25s;
}

/* ---------- the boilerplate ---------- */
.boiler {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.bl {
  background: var(--ds-color-bg-subtle);
  padding: 22px 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 11px;
}

.bl .top {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

.bl .top b {
  font-family: var(--ds-font-heading);
  font-size: 21px;
  font-weight: 600;
}

.bl .top span {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
}

/* The copy control takes the hero slider arrows' treatment - outline only, a
   hairline rule rather than a filled button - because it is a utility beside the
   text and not a call to action. `channel-paper` rather than `channel-light`:
   the prototype rules it in the brand's near-white, and this hairline sits on a
   flat plate rather than over photography, which is the case `_ugcza.scss`
   reserves pure white for. */
.bl .top .copy {
  margin-left: auto;
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ds-color-fg-heading);
  padding: 9px 14px;
  background: transparent;
  border: 1px solid rgba(var(--ds-color-channel-paper), 0.5);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}

.bl .top .copy:hover {
  border-color: var(--ds-color-fg-heading);
  background: rgba(var(--ds-color-channel-paper), 0.1);
}

.bl p {
  color: var(--ds-color-fg-default);
  line-height: 1.72;
  font-size: 15.5px;
}

/* The long entry runs to two paragraphs. `wpautop()` emits them as siblings with
   no margin of their own, and the prototype spaces the second with an inline
   `margin-top:12px` - a design value in markup, so it lives here instead. The
   `.bl` flex gap is 11px and governs the gap under `.top`, which is why this is
   a margin on the paragraph rather than a change to that gap. */
.bl p + p {
  margin-top: 12px;
}

/* ---------- the asset checklist ---------- */
.assets {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.as {
  background: var(--ds-color-bg-subtle);
  padding: 16px 20px;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  transition: background 0.15s;
}

.as:hover {
  background: var(--ds-color-bg-muted);
}

/* The format chip. `flex:none` so a four-letter format does not squash it, and a
   fixed square so the column reads as a column down nine rows of varying height. */
.as .ic {
  width: 44px;
  height: 44px;
  flex: none;
  background: var(--ds-color-bg-lift);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--ds-color-fg-muted);
}

.as .ic.on {
  background: var(--ds-color-brand-primary);
  color: rgb(var(--ds-color-channel-light));
}

.as .t {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 180px;
  flex: 1;
}

.as .t b {
  font-family: var(--ds-font-heading);
  font-size: 19px;
  font-weight: 600;
}

.as .t span {
  font-family: var(--ds-font-label);
  font-size: 11px;
  color: var(--ds-color-fg-muted);
  letter-spacing: 0.04em;
}

.as .st {
  font-family: var(--ds-font-label);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 5px 10px;
  background: rgba(var(--ds-color-channel-light), 0.09);
  white-space: nowrap;
}

/* Ready is quiet and pending is loud, which is the right way round: three of the
   nine are waiting on the client and the page's job is to say so plainly. */
.as .st.ready {
  color: var(--ds-color-fg-default);
}

.as .st.need {
  color: var(--ds-color-brand-primary);
}

/* A row becomes a link the day a file is attached to it - see
   `ugcza_render_press_assets()`. The anchor must not pick up a link colour or an
   underline: the row's own type is already coloured by the three rules above. */
.as[href] {
  color: inherit;
  text-decoration: none;
}

/* ---------- palette and type specimens ----------
   THE SIX CHIPS ARE PAINTED FROM THE DESIGN SYSTEM'S OWN TOKENS, never from the
   hex the caption beside them quotes. That is the whole point of the band: if
   the swatch and the site ever disagree, the swatch is wrong and this way it
   cannot be. It also keeps six colour literals out of pattern markup, which the
   token gate forbids under wp-content. The captions are writing and live in the
   pattern; they are the brand bible's own hex and CMYK builds. */
.sw {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px;
}

.swc {
  background: var(--ds-color-bg-subtle);
}

.swc i {
  display: block;
  height: 74px;
}

.swc.c-red i {
  background: var(--ds-color-brand-primary);
}

.swc.c-orange i {
  background: var(--ds-color-brand-accent);
}

.swc.c-grey i {
  background: var(--ds-color-bg-muted);
}

.swc.c-teal i {
  background: var(--ds-color-bg-alt);
}

.swc.c-black i {
  background: var(--ds-color-bg-subtle);
}

.swc.c-white i {
  background: var(--ds-color-bg-inverse);
}

.swc b {
  display: block;
  font-family: var(--ds-font-heading);
  font-size: 17px;
  font-weight: 600;
  padding: 11px 13px 2px;
}

/* `u` carries no underline here - it is the prototype's element for the spec
   line and the browser default would read as a link. */
.swc u {
  display: block;
  font-family: var(--ds-font-label);
  font-size: 11px;
  text-decoration: none;
  color: var(--ds-color-fg-muted);
  padding: 0 13px 13px;
}

.types {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 18px;
  margin-top: 20px;
}

.ty {
  background: var(--ds-color-bg-subtle);
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ty span {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
  font-weight: 700;
}

/* The specimen is set IN THE FACE IT DESCRIBES, which is the only honest way to
   show a typeface. 34px is large enough to read the drawing of it. */
.ty .spec {
  font-family: var(--ds-font-heading);
  font-size: 34px;
  font-weight: 600;
  line-height: 1.05;
}

.ty .spec.b {
  font-family: var(--ds-font-body);
  font-size: 17px;
  font-weight: 400;
  color: var(--ds-color-fg-default);
  line-height: 1.6;
}

.ty em {
  font-style: normal;
  font-family: var(--ds-font-label);
  font-size: 12px;
  color: var(--ds-color-brand-accent);
}

/* ---------- spokespeople ----------
   A square plate rather than the About page's 4:5 - this band is three cards
   across a full-width grid where that one is a portrait wall, and a square holds
   a video still as readily as a face. `.pp` is the third component to want a
   card surface on the ink band; `subtle-lift` is the token that was missing. */
.people {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 18px;
}

.pp {
  background: var(--ds-color-bg-subtle-lift);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.pp .por {
  aspect-ratio: 1/1;
  position: relative;
  background: var(--ds-color-bg-lift);
  background-image: var(--pi);
  background-size: cover;
  background-position: 50% 50%;
}

/* THE WASH IS THE PLACEHOLDER, NOT A TREATMENT OVER THE PHOTOGRAPH, and `:has`
   is what tells them apart. The prototype inlines a different flat gradient on
   each of the three cards, at full cover, because none of them had a picture -
   painted over a real portrait it would hide it completely. So the gradient is
   gated on the pending caption being present, which is the same switch
   `.tile-art:has(>img)::after` uses one file over: the presence of the content
   decides, and it cannot be set to a state the markup contradicts.
   The three gradients themselves are design rather than content - the prototype
   writes them three times in markup - so they are here, addressed positionally. */
.pp .por:has(> span)::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(160deg, rgba(var(--ds-color-channel-key), 0.55), rgba(var(--ds-color-channel-portrait), 0.95));
}

.pp:nth-child(2) .por:has(> span)::before {
  background: linear-gradient(160deg, rgba(var(--ds-color-channel-alt), 0.9), rgba(var(--ds-color-channel-portrait), 0.95));
}

.pp:nth-child(3) .por:has(> span)::before {
  background: linear-gradient(160deg, rgba(var(--ds-color-channel-accent), 0.26), rgba(var(--ds-color-channel-alt), 0.95));
}

.pp .por span {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 14px 15px;
  background: linear-gradient(0deg, rgba(var(--ds-color-channel-shadow), 0.9), transparent);
  font-family: var(--ds-font-label);
  font-size: 9.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

/* THE THIRD PLATE IS A VIDEO STILL AMONG PHOTOGRAPHS, and it is graded here so
   it joins the row rather than interrupting it. Measured on the centre square
   each plate actually renders, the two portraits average 48/60/77 and 19/29/39
   - blue running 1.61 and 2.02 times red - and the still averages 89/89/86,
   which is 0.97. So the still is NOT warm, it is dead neutral, and it only
   reads warm because both its neighbours are cool. Grading it is a tint job,
   not a desaturation.

   `color` IS THE MODE, not `overlay` or an alpha wash: it takes hue and
   saturation from this layer and luminosity from the photograph, which is what
   a grade is. An alpha wash would flatten the still toward one colour instead.

   THIS VALUE IS NOT THE MEASURED CAST, AND THE DIFFERENCE IS THE DECISION.
   The founder shoot's own cast is hsl(215, 23%, 25%); this is hsl(215, 41%,
   33%) at .66 strength, which lands blue:red at 1.30 against Junayd's 1.61,
   p95 at 204 against 139 and p50 at 66 against 48. It harmonises rather than
   matches, ON PURPOSE. Matching needs the framing moved off the burned-in
   title card - about a fifth of this plate is above L200, and a near-white
   pixel cannot hold a cast at any tint - and the only framings that clear it
   are zooms past `cover` that sample a ~280px strip of a 1280x720 source. That
   was weighed and refused: the plate keeps its resolution and its composition,
   and the card is allowed to read as footage rather than as a third face.

   DO NOT REACH FOR BRIGHTNESS TO CLOSE THE REMAINING GAP. Filters run AFTER
   the blend and scale all three channels together, so brightness leaves the
   ratio untouched - measured invariant from 1.0 down to 0.45. Contrast does
   move it, and only by flattening it back toward neutral, which is why the
   figures here are mild. The gap closes by changing the frame or the frame's
   source, not by another filter.

   Gated on the pending caption being ABSENT, the same switch the placeholder
   gradient above uses and for the same reason: over the pending wash this
   would grade a colour that was already chosen. `isolation` is redundant while
   a filter is present, since a filter makes its own stacking context, and
   stays so that removing the filter cannot leak the blend onto the card. */
.pp .por--reel:not(:has(> span)) {
  isolation: isolate;
  filter: contrast(0.84) brightness(0.91);
}

.pp .por--reel:not(:has(> span))::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgb(var(--ds-color-channel-portrait-grade));
  mix-blend-mode: color;
  opacity: 0.66;
}

.pp .b {
  padding: 16px 17px 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pp .b h4 {
  font-size: 22px;
}

.pp .b h4 a:hover {
  color: var(--ds-color-brand-accent);
}

.pp .b .role {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
}

.pp .b p {
  font-size: 14px;
  color: var(--ds-color-fg-default);
  line-height: 1.5;
}

/* ---------- coverage, as the complete record ----------
   Not `.press`/`.pr`, which is the marketing grid on the homepage and About.
   Same records, different columns: this one prints the outlet, the FULL
   published headline and the date, and carries no excerpt - see
   `ugcza_render_coverage_list()` for why that is two components rather than one
   in two dresses. The artwork backing is the shared `.tile-art`, so the scrim,
   the hover zoom and the reduced-motion guard are all `_tiles.scss`'s. */
.cov {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(238px, 1fr));
  gap: 14px;
}

.cv {
  background: var(--ds-color-bg-muted);
  padding: 20px 18px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  transition: background 0.15s;
}

.cv:hover {
  background: var(--ds-color-bg-lift);
}

.cv .out {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
  font-weight: 700;
}

.cv .hd {
  font-family: var(--ds-font-heading);
  font-size: 19px;
  font-weight: 600;
  line-height: 1.15;
}

.cv .dt {
  font-family: var(--ds-font-label);
  font-size: 11px;
  color: var(--ds-color-fg-muted);
  white-space: nowrap;
}

/* A tile carrying artwork grows to hold it and bottom-anchors its type into the
   strong end of the scrim. The ground goes back to ink so the picture is not
   sitting on grey at its edges. */
.cv.tile-art {
  background: var(--ds-color-bg-subtle);
  min-height: 252px;
  justify-content: flex-end;
}

.cv.tile-art:hover {
  background: var(--ds-color-bg-subtle);
}

/* `fg-muted` cannot reach 4.5:1 over artwork at any usable scrim - the same
   failure the event property labels hit. Over a picture the date steps up. */
.cv.tile-art .dt {
  color: var(--ds-color-fg-default);
}

/* ---------- partner quotes, stacked ---------- */
.quotes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  gap: 18px;
}

/* THE 16/40 MARGIN IS THE USER-AGENT DEFAULT AND IT IS DELIBERATE THAT IT
   SURVIVES. The prototype resets `p` and `h1`-`h4` to `margin:0` and never
   touches `blockquote`, so every browser's own `margin:1em 40px` stays on these
   three cards - measured, not assumed: 16px top and bottom, and the card 298.7px
   wide inside a 378.7px grid track at 1440.

   That is almost certainly an oversight in the prototype rather than a design
   intent - no other card grid on the site is inset - but rule 1 is that the
   build matches the prototype and that exceptions are raised and agreed rather
   than taken. Resetting it to `margin:0` cost the band 82px at 1440 and 221px at
   390, because narrower cards wrap more text: it read as 27% and 39% of the band
   differing, which looks like a broken component and was one declaration.

   Written out rather than left to the browser, so it cannot vary between
   engines. Raised as a backlog row; one line to change when it is answered. */
.qt {
  background: var(--ds-color-bg-subtle);
  border-left: 4px solid var(--ds-color-brand-primary);
  padding: 22px 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 16px 40px;
}

.qt p {
  font-size: 15.5px;
  line-height: 1.62;
  color: var(--ds-color-fg-default);
}

/* `margin-top:auto` is what lines the three attributions up along one baseline
   when the quotes run to different lengths. */
.qt .who {
  display: flex;
  align-items: baseline;
  gap: 11px;
  flex-wrap: wrap;
  margin-top: auto;
}

.qt .who b {
  font-family: var(--ds-font-heading);
  font-size: 20px;
  font-weight: 600;
  color: var(--ds-color-brand-accent);
}

.qt .who span {
  font-family: var(--ds-font-label);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

/* ---------- event properties, at seven ----------
   EXPLICIT COLUMN COUNTS, NOT `_tiles.scss`'s auto-fit minmax(166px). Same
   treatment, different arithmetic: there are seven tiles here against the
   homepage's six, and auto-fit resolves to six columns at most desktop widths,
   which strands the seventh alone beside five empty cells. These counts divide
   seven without an orphan row wider than a third of the grid.
   The widest step takes the count from the block rather than a literal seven, so
   the eighth property BUILD-DECISIONS section 3.2 allows for lands in the same
   row instead of starting a second one. `--prop-count` is written by
   `ugcza_render_properties()` and the fallback is what the prototype draws.
   `_portfolio-page.scss` carries the identical block for the identical reason
   and is not yet compiled; when it is, the two should become one modifier rather
   than three copies. Recorded as a backlog row rather than done here, because
   that file is another session's. */
.pg-press-kit .props {
  margin-top: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 620px) {
  .pg-press-kit .props {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 900px) {
  .pg-press-kit .props {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
@media (min-width: 1320px) {
  .pg-press-kit .props {
    grid-template-columns: repeat(var(--prop-count, 7), minmax(0, 1fr));
  }
}
/* THE SEVENTH TILE'S DRIFT DELAY IS NOT HERE, and its absence is deliberate.
   `_portfolio-page.scss` already carries `.prop:nth-child(7) .pbgi` unscoped,
   continuing the design system's 4s ladder, and that file entered the build on
   2026-08-14 (PORT-50). A scoped copy of the same value would be two places to
   change one ladder. If that rule ever moves, this page loses its stagger and
   the seventh tile breathes in lockstep with the third - which is visible, and
   is what this comment is for.
   The heading over the grid. The prototype writes both of these inline on the
   `<h3>`, which is two design values in markup. `_tiles.scss` already owns
   `.props-h` at the homepage's clamp; this page's is a step wider and carries
   its own gap under it, so it is scoped rather than folded. */
.pg-press-kit .props-h {
  margin-top: clamp(38px, 5vw, 58px);
  margin-bottom: 18px;
}

/* ---------- the showreel, in this page's dress ----------
   THE THIRD FORK OF THE SAME THREE RULES, and saying so is the point. The
   homepage's `.reel` sets a two-row "Press play." call into a tall scrim with
   the disc clamped to the frame; About and this page both carry the older
   arrangement - one 80px disc centred in the whole frame and an 11px caption
   bottom-left. `_about-page.scss` forked it, `_portfolio-page.scss` forked it
   again, and this is the third. That is a component wanting a modifier
   (`.reel--centred`) rather than three page scopes, and it is a backlog row
   rather than a change made here: two of those three files belong to work in
   flight in another session, and a shared refactor landed from outside is the
   race the record protocol exists to avoid.
   The caption's offsets are this prototype's own - 17/15 against About's 16/14. */
.pg-press-kit .reel {
  display: grid;
  grid-template-rows: none;
  place-items: center;
}

.pg-press-kit .reel .play {
  grid-row: auto;
  grid-column: auto;
  width: 80px;
  height: 80px;
}

.pg-press-kit .reel .cap {
  position: absolute;
  z-index: 3;
  left: 17px;
  bottom: 15px;
  font-family: var(--ds-font-label);
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-default);
}

/* ---------- the two closing panels ---------- */
.contact {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
}

.ct {
  background: var(--ds-color-bg-subtle-lift);
  padding: clamp(26px, 4vw, 42px);
  display: flex;
  flex-direction: column;
  gap: 13px;
}

.ct.hot {
  background: var(--ds-color-brand-primary);
}

/* `:not(.eyebrow)` is a bridge, not a design change, and it is the same one
   `.sp` carries in _tiles.scss. The prototype's eyebrow is a `<span>`, so a bare
   `.ct p` could never reach it; core's paragraph block can only emit `<p>`, and
   `.ct p` at (0,1,1) outranks `.eyebrow` at (0,1,0) - so without this the orange
   eyebrow renders body grey and the 42ch measure lands on a two-word label. */
.ct p:not(.eyebrow), .ct .li {
  color: var(--ds-color-fg-default);
  max-width: 42ch;
}

.ct.hot p:not(.eyebrow), .ct.hot .li {
  color: rgba(var(--ds-color-channel-light), 0.92);
}

.ct.hot .eyebrow {
  color: rgba(var(--ds-color-channel-light), 0.88);
}

/* The prototype writes this lead-in as an inline `margin-top:6px` on the address
   block - a design value in markup. It has to beat the block-gap reset at the
   foot of this file, which it does on specificity, (0,2,0) against that rule's
   (0,1,0), and not on source order. */
.ct .li {
  font-size: 15px;
  margin-top: 6px;
}

.ct .li a {
  color: var(--ds-color-brand-accent);
}

/* Underlined on the red plate and nowhere else: the accent orange that carries
   these links elsewhere has nothing left over brand red, so the underline is
   what marks them as links rather than a colour change. */
.ct.hot .li a {
  color: rgb(var(--ds-color-channel-light));
  text-decoration: underline;
}

.ct .go {
  margin-top: auto;
  padding-top: 8px;
}

.ct .go .wp-block-button.btn {
  height: auto;
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` onto every child
   of a group block through `:root :where(.is-layout-flow) > *`. On a flex or
   grid container those margins ADD to the gap instead of collapsing into it, so
   every container above would run its own measured gap plus 26px. The selectors
   below are (0,1,0), equal to core's, and win on source order because ugcza.css
   is enqueued after global-styles-inline-css - the same fix `.sec-h>*` carries
   in _sections.scss and `.split>*,.sp>*` in _tiles.scss.

   EVERY NEW CONTAINER ON THIS PAGE IS IN THIS LIST. One left out is 26px of band
   that no probe measures and that the pixel diff reports as a height difference
   with no obvious cause - which is exactly how the About page's nine bands went
   wrong at once.

   `.quotes>*` IS DELIBERATELY NOT IN IT, and this is the other half of the same
   lesson: a reset is not free. `.qt` declares `margin:16px 40px` because the
   prototype inherits the user-agent's blockquote margin (see that rule), and
   `.quotes>*` is (0,1,0), ties with it, and wins from the foot of the file -
   zeroing the block axis while leaving the inline axis alone. The cards came out
   the right size in the right grid with 32px missing from the band, which read
   as 14% of it differing. `.quotes` is a plain `<div>` emitted by
   `ugcza_render_quote_cards()` and carries no `is-layout-*` class, so core never
   writes a block gap onto its children and the reset had nothing to reset. The
   rule for the rest of this list: a container needs a line here only if it is a
   CORE layout group. The others are defensive and cost nothing, but a defensive
   reset over a declared margin costs 32px. */
.jump .shell > *,
.facts > *, .fact > *,
.boiler > *, .bl > *, .bl .top > *,
.assets > *, .as > *, .as .t > *,
.sw > *, .swc > *, .types > *, .ty > *,
.people > *, .pp > *, .pp .b > *,
.cov > *, .cv > *,
.qt > *, .qt .who > *,
.contact > *, .ct > *,
.pact > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

/* And the bands themselves. Each sits directly under `.sec-h`, whose own
   margin-bottom is the measured gap; core's block gap on top of it would add a
   second one. `.props` and `.stats` are not in this list because both declare
   their own margin-top above, which outranks core's rule on specificity. */
.facts, .boiler, .assets, .sw, .people, .cov, .quotes, .contact {
  margin-block-start: 0;
}

/* The jump bar itself. It is a top-level child of the content wrapper, so it
   takes the block gap that `.entry-content:has(> .sec) > *` zeroes for sections
   but not for a div.

   THE PAGE HEADER'S CHILDREN ARE NOT IN THIS LIST, and leaving them out is the
   fix rather than an omission. `_about-page.scss` already zeroes
   `.phead .shell>*` at (0,2,0), so a copy here was redundant - and it was worse
   than redundant: scoped it became (0,3,0), which TIED with
   `.phead .shell>.pact{margin-block-start:8px}` and won on source order, since
   this block sits at the foot of the file. The 8px lead under the lede
   disappeared, the band came out 8px short at both widths, and eleven bands
   below it reported `starts -8.00px` - which reads as a systemic page offset
   rather than as one redundant selector. */
.pg-press-kit .jump {
  margin-block-start: 0;
}

/* ---------- the page's scale ----------
   See the header. These five are the whole of the difference between this page
   and the design system's defaults. They are the prototype's own numbers, and
   they are About's numbers, and those two facts were checked separately. */
.pg-contact .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-contact .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-contact h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-contact h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-contact .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* ---------- the enquiry form ----------
   THE ONE PLACE THIS DESIGN KEEPS A HAIRLINE, which is the prototype's own
   comment above these rules and worth carrying over: `_ugcza.scss` states that
   separation is by surface tone and gap, and reserves 1px rules for focus rings
   and control outlines. A text input is the case that reservation was written
   for - a field has to advertise where it starts and ends before it is touched,
   and a tone step alone does not do that at 15px.

   `.form` is a generic-looking name and it is the prototype's. It is safe
   unscoped because it only ever matches an element that carries `class="form"`,
   which no core block emits - core's own search and comment forms carry
   `wp-block-search__*` and `comment-form`. */
.form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}

/* `min-width:0` is what stops a long placeholder or a wide select from blowing
   the grid track past its 1fr share: a flex item's automatic minimum size is
   its content, not zero. */
.field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-width: 0;
}

.field.wide {
  grid-column: 1/-1;
}

.field label {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

/* The ground is PURE black at .34, not the brand near-black, and not a surface
   token. It is a well cut into whatever band the form sits on, so it has to go
   darker than the ground beneath it wherever that ground lands - a flat token
   would read as a plate on the ink band and as a hole on the grey one. The
   hairline is the brand near-white at .42, which is the identical rule `.tbtn`
   draws in _brands.scss; the two are the same hairline and should stay the same
   number. `.fsoc` was the third until 2026-08-14, when the client moved that row
   onto the hero slider arrow's treatment and its hairline opened to .5 with it -
   do not pull this one along to match. Measured anchors: the prototype
   writes rgba(0,0,0,.34) and rgba(249,250,250,.42), and #f9fafa is the brand
   white, so `channel-paper` is the triplet and not `channel-light` (PORT-08). */
.field input, .field select, .field textarea {
  font-family: var(--ds-font-body);
  font-size: 15px;
  color: var(--ds-color-fg-heading);
  background: rgba(var(--ds-color-channel-shadow), 0.34);
  border: 1px solid rgba(var(--ds-color-channel-paper), 0.42);
  padding: 12px 14px;
  width: 100%;
  transition: border-color 0.15s, background 0.15s;
}

/* `resize:vertical` rather than the browser default `both`: a textarea dragged
   wider escapes its grid track and takes the layout with it. */
.field textarea {
  min-height: 132px;
  resize: vertical;
}

.field input:hover, .field select:hover, .field textarea:hover {
  border-color: rgba(var(--ds-color-channel-paper), 0.7);
}

/* Focus removes the outline and answers it with the accent rule plus a deeper
   well, which is the prototype's treatment. It is a real focus indicator and
   not a bare `outline:none`: the border goes from a .42 white hairline to solid
   accent orange, which is both a colour change and a contrast change. */
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--ds-color-brand-accent);
  background: rgba(var(--ds-color-channel-shadow), 0.5);
}

/* SCOPED TO `.field`, AND THAT IS THE PROTOTYPE'S OWN RULE RATHER THAN A
   TIGHTENING OF IT. There are two `.hint` spans on the page: "optional" inside
   the organisation field's label, which this reaches, and "Or just email us"
   beside the submit button in `.form-go`, which it does not - so that second
   one renders at body size in the prototype too, not at 12.5px muted. Left
   exactly as drawn: the pixel diff compares against the prototype's render, so
   widening this to a bare `.hint` would introduce a difference rather than fix
   one. It is a question for the client's designer and a backlog row, not a
   change made silently here. */
.field .hint {
  font-size: 12.5px;
  color: var(--ds-color-fg-muted);
}

.consent {
  grid-column: 1/-1;
  display: flex;
  gap: 11px;
  align-items: flex-start;
  font-size: 13.5px;
  color: var(--ds-color-fg-default);
  line-height: 1.55;
}

/* `flex:none` so the box does not shrink against two lines of consent text, and
   `margin-top:2px` to sit it on the first line's cap height rather than its
   box top. `accent-color` is brand RED here and not the accent orange, which
   reads backwards until you look at the page: this is the only checkbox on the
   site and red is the brand's own affirmative mark. The prototype names it. */
.consent input {
  width: 17px;
  height: 17px;
  flex: none;
  margin-top: 2px;
  accent-color: var(--ds-color-brand-primary);
}

.form-go {
  grid-column: 1/-1;
  display: flex;
  gap: 13px;
  align-items: center;
  flex-wrap: wrap;
}

/* THE SUBMIT IS THE ONLY REAL `<button class="btn">` ON THIS SITE, and it is the
   one control the three existing `height:auto` fixes do not reach - they are all
   written against `.wp-block-button.btn`, which is core's wrapper, and this is
   not a block button because a block button renders an `<a>` and an `<a>` cannot
   submit a form.

   `_base.scss` sets `.btn{height:var(--ds-control-height)}`, and that token STEPS
   at 768px: 50px above, 46px below (`$control-mobile`, "a touch target is not a
   fluid quantity"). The prototype's `.btn` sets no height at all, so its button
   is content-sized - 13+13 padding, 2+2 border, and a `<button>`'s UA
   `line-height:normal` rather than the body's 1.65, which lands it at 50.4px.

   So the two agreed at 1440 BY COINCIDENCE, 50 against 50, and disagreed by 4px
   at 390 where the token steps and the prototype does not. That 4px was the last
   thing wrong with this page: `#enquiry` read 0.1% at 1440 and 0.9% with a -4px
   height at 390, and the three bands below it inherited the offset. A control
   that matches at one width because two unrelated numbers happen to be equal is
   the exact hazard PORT-48 is about.

   `height:auto` hands the size back to padding, border and leading, which is what
   the prototype does. Same one-line fix, same reason, as `.pact`, `.sp .go` and
   `.hcta` - see PORT-27 and PORT-32. Scoped, because a site-wide `.btn{height:
   auto}` would undo the design system's deliberate touch-target step everywhere
   else.

   AND THE LEADING WITH IT, "both, or neither works", which is PORT-27 word for
   word. `height:auto` alone took the button to 47px, three short at BOTH widths
   this time - a new number, not the old one, which is the tell that a second
   cause was underneath the first. `_base.scss` sets `.btn{line-height:1}` and
   that is 17px here; the prototype's `<button>` sets no line-height and takes
   the UA's form-control `normal`, about 20px.

   `normal`, NOT the body's 1.65 that `_wp-bridge.scss` restates for the core
   button link. The two really are different: an `<a class="btn">` inherits the
   body's leading and measures 58.05px in the prototype, and a `<button
   class="btn">` does not inherit at all because the UA resets form controls, so
   it measures 50.40px. Both numbers are in this page's prototype, on the same
   screen - the four `.go` anchors at 58.05 and this submit at 50. Restating
   1.65 here would fix the arithmetic and draw the wrong control. */
/* AND THE LABEL COLOUR, for the same reason and from the same blind spot.
   `_wp-bridge.scss` line 58 paints core's button link
   `rgb(var(--ds-color-channel-light))` - PURE white, not the brand's #f9fafa -
   with a comment saying the prototype paints button labels #fff and that folding
   the two showed up in the visual comparison as a 6/255 drift. Every button on
   the site takes that, because every other button on the site is a core button
   block. This one is not, so it kept `.btn{color:var(--ds-color-fg-on-accent)}`
   from `_controls.scss` and was the one control still carrying the fold -
   `proto rgb(255,255,255) / built rgb(249,250,250)`, found by the probe suite
   and by nothing else, because 6/255 is far under the pixel diff's threshold
   (PORT-52) and a band can read 0.1% with it present.
   Fixed HERE rather than by changing `--ds-color-fg-on-accent`, which would
   repaint every button on every page and put five signed-off pages back in the
   queue for a difference only this one has. PORT-08: a fold is a decision. */
.pg-contact .form-go .btn {
  height: auto;
  line-height: normal;
  color: rgb(var(--ds-color-channel-light));
}

/* ---------- the states the prototype could not draw ----------
   The three blocks below have no prototype counterpart, and that is not a
   divergence: the prototype is a static preview whose own `.note` says "in this
   preview the form doesn't send". The build's form does send, stores a record
   and validates - see `contact.php` - so it has states a preview never had to
   express. AT REST NONE OF THEM RENDER: `.fmsg` and `.err` need markup the
   server only emits after a submit, `.field.bad` needs a class only validation
   adds, and `.hp` is out of flow by definition. So none of them can move a
   pixel in `visual-diff.mjs`, which is what makes it legitimate to add them to
   a file that otherwise ports one-for-one.

   THE BANNER IS `.fmsg`, NOT `.note`. It wants `.note`'s exact proportions - a
   4px left rule, 16px 19px of padding, 14px text on a .07 wash of its own hue -
   and it must not have `.note`'s NAME, because `visual-diff.mjs` fails the
   whole page at 100% on any PROTOTYPE_ONLY match and `.note` is on that list.
   Same drawing, different name, and the name is the load-bearing part.

   Orange for success and red for failure is the palette's own vocabulary: the
   brand bible carries no green, and `_ugcza.scss` maps `state.success` onto the
   accent for exactly this reason. `.ok` and `.bad` therefore differ only in
   hue, so the two read as one object in two states rather than two objects. */
/* `scroll-margin-top` IS THE WHOLE POINT OF THE BANNER WORKING. The handler
   redirects to `#contact-result` and the banner focuses itself, so the browser
   scrolls it to viewport top - underneath the 74px sticky nav. Measured at 390:
   the banner is 122px tall and the bar covered the top 74px of it, which is the
   entire "Thank you - your message is with us." heading. What a visitor saw
   after sending a message was a clipped half-sentence above an empty form.

   90px, matching `_article.scss` and `_legal-page.scss`, and NOT `.sec`'s bare
   74px: this is not a band, it has 16px of padding rather than 52, so it needs
   the same 16px of clearance a heading does.

   NO GATE THAT WALKS LINKS COULD HAVE FOUND THIS. Nothing on the page has an
   href pointing at `#contact-result` - it is reached by a redirect. It was
   found by submitting the form on a phone. BUILD-DECISIONS 25.4. */
.fmsg {
  border-left: 4px solid transparent;
  padding: 16px 19px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ds-color-fg-default);
  margin: 0 0 14px;
  scroll-margin-top: 90px;
}

.fmsg.ok {
  border-left-color: var(--ds-color-brand-accent);
  background: rgba(var(--ds-color-channel-accent), 0.07);
}

.fmsg.bad {
  border-left-color: var(--ds-color-brand-primary);
  background: rgba(var(--ds-color-channel-key), 0.07);
}

/* The banner leads with a bold sentence and follows it with a plain one -
   `ugcza_contact_banner()` emits `<b>heading</b> detail` - which is `.note`'s
   own shape, and `.note b` takes the rule's colour there for the same reason it
   does here: the bold half IS the answer and the rest is explanation. Two rules
   rather than one on `.fmsg b`, because the hue has to follow the state. */
.fmsg.ok b {
  color: var(--ds-color-brand-accent);
}

.fmsg.bad b {
  color: var(--ds-color-brand-primary);
}

/* The banner is drawn as a sibling ABOVE the form, which is why it carries its
   own bottom margin. If it is ever moved inside the form instead it has to span
   the grid or it opens a half-width track beside the first field, and its
   margin has to go or it doubles the 14px gap. Both are covered here so the
   placement decision cannot silently break the layout. */
.form .fmsg {
  grid-column: 1/-1;
  margin-bottom: 0;
}

/* The per-field error, at `.hint`'s scale so the two occupy the same slot under
   a control. No margin: `.field` is a flex column with a 7px gap, which is the
   same gap that already sits under the label. */
.field .err {
  font-size: 12.5px;
  color: var(--ds-color-brand-primary);
  line-height: 1.5;
}

/* THE `:not(:focus)` IS THE WHOLE POINT OF THIS RULE and is not decoration.
   Written plainly, `.field.bad input` is (0,2,1) - the same weight as both
   `.field input:hover` and `.field input:focus` - so which colour an invalid
   field shows would be decided by source order alone, and the answer would be
   wrong at one end whichever order was chosen. Excluding focus lifts it to
   (0,3,1), so it beats hover unconditionally (an invalid field stays red under
   the pointer) and yields to focus unconditionally (a field being corrected
   still shows it has the caret). Order-independent, which is what stops the
   next edit to this file changing behaviour by moving a block. */
.field.bad input:not(:focus),
.field.bad select:not(:focus),
.field.bad textarea:not(:focus) {
  border-color: var(--ds-color-brand-primary);
}

/* The honeypot wrapper. Positioned off-canvas rather than hidden, and both
   halves of that are deliberate:
     - NOT `display:none` and not `visibility:hidden`. A bot that parses CSS
       skips fields it can see are hidden, which defeats the trap; a bot that
       fills every input it finds walks into it.
     - NOT the `.screen-reader-text` idiom either, even though the geometry is
       close. That idiom exists to hide something FROM SIGHT but keep it for
       assistive technology - the exact opposite of what is wanted, because this
       field carries `aria-hidden` and a screen-reader user who filled it would
       be scored as spam. Off-canvas leaves it in the accessibility tree only if
       the markup puts it there, and the markup deliberately does not.
   `overflow:hidden` and the 1px box keep it from extending the scrollable area
   at the left edge, which is the one way an off-canvas element can be felt. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- contact detail rows ----------
   `232px` rather than the form's 260px: these hold an address or a phone
   number, not a labelled control, and the narrower minimum keeps four rows on
   one line at container width instead of orphaning the fourth. Prototype
   values, both numbers its own. */
.deets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: 14px;
}

.deet {
  background: var(--ds-color-bg-subtle);
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.deet .k {
  font-family: var(--ds-font-label);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ds-color-brand-accent);
}

/* The link and the plain value take the SAME treatment, which is the point of
   the pair: whether a row is a mailto, a tel or a street address is a property
   of the record, not a change of type. `word-break:break-word` because an email
   address is one unbreakable token and a long one would otherwise overflow a
   232px track rather than wrap. */
.deet a, .deet .v {
  font-family: var(--ds-font-heading);
  font-size: 19px;
  font-weight: 600;
  color: var(--ds-color-fg-heading);
  line-height: 1.2;
  word-break: break-word;
}

.deet a:hover {
  color: var(--ds-color-brand-accent);
}

.deet .sub {
  font-size: 13px;
  color: var(--ds-color-fg-muted);
  line-height: 1.5;
}

/* ---------- the four panel buttons carry an arrow ----------
   THIS PAGE'S ONE DIFFERENCE FROM THE HOMEPAGE'S IDENTICAL-LOOKING PANEL, and
   it was measured rather than inherited. `.split`/`.sp`/`.go` are `_tiles.scss`'s
   and shared, so the easy assumption is that the shared component already
   covers the button - it does not, and the difference is not visible in the
   CSS at all. It is in the markup.

   Grepped against both prototypes rather than eyeballed: `ugcza-contact.html`
   carries four `class="arw"` and ALL FOUR sit inside
   `<div class="go"><a class="btn ...">Label<svg class="arw">` - one in the
   `.sp.hot` panel and three in plain `.sp` panels. `ugcza-homepage-brief.html`
   carries ten, and not one of them is in a `.sp .go` button: its two panel
   buttons, "Download press kit" and "Join our community", are label-only. So
   `patterns/home-cta.php` is right to have no arrow, this page needs one, and
   inheriting the shared component would have shipped four buttons about 18px
   short - the same .72em glyph plus .34em clearance that `_tiles.scss` records
   measuring twice on `.more` links.

   THE SCOPE IS LOAD-BEARING. The homepage's other four `.go` elements are the
   start.gg cards' `<span class="go">Results` inside `.gg-foot`, and those DO
   carry the glyph in markup already. A rule keyed on `.go` alone would double
   the arrow there. `.pg-contact .sp .go` cannot reach them.

   A PSEUDO-ELEMENT, NOT MARKUP, for the reason `_tiles.scss` sets out at length
   above its own `.more` rule: core's `createFromElement()` in
   wp-includes/js/dist/rich-text.js keeps an empty element only as
   `{type, attributes}`, so an inline `<svg>`'s `<path>` child is dropped on the
   editor's round-trip and the block then fails validation the first time the
   client retypes the label. The glyph, the viewBox, the 1.7 stroke and the mask
   treatment are that block's, character for character - one arrow, one data
   URI, `currentColor` deciding the colour so hover follows the button.

   The pseudo goes on the LINK, not on the block wrapper: `_wp-bridge.scss`
   states that a component class set on a button block lands on the wrapper
   while the treatment belongs to `.wp-block-button__link`, and that link is the
   `inline-flex` box with the 9px gap. `margin-left:.34em` is kept ON TOP of
   that gap, exactly as `.arw` and the `.more` rule both do: the prototype's
   spacing between label and glyph is the 9px flex gap PLUS 5.78px of margin at
   17px, not one or the other, and dropping either closes it by a third. */
.pg-contact .sp .go .wp-block-button.btn > .wp-block-button__link::after {
  content: "";
  width: 0.72em;
  height: 0.72em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
  background: currentColor;
  --ds-arw-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M7 1l4 4-4 4M11 5H1' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask: var(--ds-arw-glyph) center/contain no-repeat;
  mask: var(--ds-arw-glyph) center/contain no-repeat;
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` onto every child
   of a group block through `:root :where(.is-layout-flow) > *`. On a flex or
   grid container those margins ADD to the gap instead of collapsing into it, so
   every container above would run its own measured gap plus 26px. The selectors
   below are (0,1,0), equal to core's, and win on source order because ugcza.css
   is enqueued after global-styles-inline-css - the same fix `.sec-h>*` carries
   in _sections.scss and `.split>*,.sp>*` in _tiles.scss.

   EVERY NEW CONTAINER ON THIS PAGE IS IN THIS LIST. One left out is 26px of
   band that no probe measures and that the pixel diff reports as a height
   difference with no obvious cause - which is exactly how the About page's nine
   bands went wrong at once. `.field` is here twice over: it is a flex column
   whose label, control and hint would otherwise stack 26px apart instead of
   7px, which is the single most visible way this page could go wrong. */
/* `.consent > *` IS DELIBERATELY NOT IN THIS LIST, and leaving it in cost 1.08px
   of band. The consent row is a flex container whose checkbox keeps the UA's own
   `margin: 3px 3px 3px 4px`, which the prototype overrides on the TOP edge only -
   `margin-top:2px` - so the box occupies 2 + 17 + 3 = 22px and the row is 22px
   tall rather than the 20.92px its text needs. Zeroing `margin-block-end` here
   takes that 3px away and the row measured 20.92 against the prototype's 22.00.
   Nothing needed the reset: core's block gap only reaches direct children of an
   element carrying `is-layout-flow` or `is-layout-constrained`, and `.consent` is
   a plain `<label>` emitted by `ugcza_contact_consent()` inside a plain `<form>` -
   neither carries a layout class, so core never had a rule here to beat. */
.form > *, .field > *, .form-go > *,
.deets > *, .deet > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

/* AND THE BAND CONTENT ITSELF, STRUCTURALLY RATHER THAN BY NAME. This started as
   `.form,.deets{margin-block-start:0}` and `.split` was left out of it, which is
   the omission the paragraph above warns about happening in the same file that
   warns about it. What it cost is worth writing down, because the shape recurs:

   `.sec-h` sets `margin-bottom:clamp(24px,3.4vw,42px)` and core sets
   `margin-block-start:26px` on the next child. Adjacent flow margins collapse to
   the LARGER, so at 1440 the design's 42 wins and the band is exact, and at 390
   the design's 24 loses to core's 26 and the band is 2px tall. `#doors` read
   0.0% at 1440 and 4.5% at 390 with every single element inside it measuring
   identical to 0.01px - the whole difference was one collapsed margin at the top
   of the band, pushing everything below it 2px out of sub-pixel phase.

   A BAND THAT PASSES AT ONE WIDTH AND FAILS AT THE OTHER IS THIS, USUALLY. The
   clamp crosses core's fixed 26px somewhere in the middle of the range, so the
   defect exists only below that crossover - here at 765px, where 3.4vw = 26.
   PORT-48 is the standing instruction never to accept it as noise.

   Structural, so it cannot be left incomplete: every direct child of a `.shell`
   on this page, whatever a later band puts there. (0,4,0) against core's (0,1,0),
   so it wins on specificity rather than on the enqueue order the list above
   relies on. `.sec-h`'s own margin-BOTTOM is untouched and stays the gap. */
.pg-contact .sec > .shell > * {
  margin-block-start: 0;
}

/* THE FORM'S BOTTOM MARGIN IS A QUIRKS-MODE UA DEFAULT, and reproducing it is a
   decision rather than an oversight either way, so here is the measurement.
   NO PROTOTYPE PAGE DECLARES A DOCTYPE - checked all four of the built ones -
   so every one of them renders in BackCompat, where Chrome's `quirks.css` gives
   `form{margin-bottom:1em}` and standards mode gives none. Measured directly:

     no doctype      compatMode=BackCompat   form margin-bottom=16px
     <!doctype html> compatMode=CSS1Compat   form margin-bottom=0px
     the built page  compatMode=CSS1Compat

   That 16px is the whole of the `#enquiry` band's height difference once the
   1.08px above is accounted for, and it is invisible in the prototype as drawn
   because the `.note` under the form carries `margin-top:22px` and the two
   collapse to 22. Strip the note - which the build must, and which the harness
   does - and the 16px is the last thing in the band.
   It is reproduced because the client reviewed the prototype AS RENDERED, and
   written as `1em` rather than `16px` because that is literally the UA rule
   being restated, not a spacing value this design chose. Scoped to this page so
   the next form on the site does not silently inherit a quirks-mode default.
   The build cannot simply drop its own doctype: WordPress emits it and standards
   mode is not optional. See PORTING-PLAYBOOK PORT-63. */
.pg-contact .form {
  margin-block-end: 1em;
}

/* The page header's own children, as on every other page partial. */
.pg-contact .phead .shell > * {
  margin-block-start: 0;
}

/* ---------- the page's scale ----------
   THE DESIGN SYSTEM SHIPS THE HOMEPAGE'S SCALE, and every sub-page steps down
   from it. Leaving these out is not a subtle difference: it cost 18px on EVERY
   band of this page on the first run - `worked-with` 914 against 896 - and
   because a band inherits the offset of everything above it, the last section
   started 70px low and the whole page read 11.4% differing pixels with no single
   band obviously at fault. That is the signature: a UNIFORM height error on
   every `.sec` and an offset that compounds down the page.

   The numbers are this prototype's own, read out of its `<style>` block, and
   they are also About's and Contact's - two facts checked separately:

     node tools/prototype-style-diff.mjs ugcza-about.html ugcza-partners.html \
       ".sec,.sec-h,h1,h2,h3,h4,.lede,.phead,.crumb,.split,.sp,.eyebrow,.btn,.shell,.vals,.val,.who,.go,.arw"
       -> shared 53, differ 0

   `h3` and `h4` are deliberately NOT in the list. The prototype sets them to
   clamp(20px,2.3vw,27px) and 19px/1.18, which is what `_primitives.scss` already
   ships, so a line here would restate a value rather than change one - and this
   page has an `<h3>` in the red panel that would then have two definitions.

   WHY THESE FIVE ARE DUPLICATED rather than written `.pg-about, .pg-contact,
   .pg-partners`: each page states its own numbers because the prototypes are
   standalone HTML files that drift independently. Three pages agreeing today is
   a fact about today, not a rule. `_contact-page.scss` carries the full argument.
   The duplication is real and tracked as B-123. */
.pg-partners .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-partners .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-partners h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-partners h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-partners .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* ---------- the stacked partner quotes ----------
   THE ONE COMPONENT THIS PAGE BRINGS THAT THE BUILD DID NOT HAVE. Everywhere
   else on the site a testimonial is a `.tslide` in a crossfade carousel running
   the ABRIDGED copy; here they are the quotes as supplied and they stack.

   The prototype says why, and it is a layout fact rather than a preference: a
   carousel sizes its cell to the longest slide, and rAge runs five paragraphs
   against Mirage's four lines, so the two short ones would each sit on half a
   screen of dead space. It is also the reason the testimonial CPT carries two
   text fields rather than one body - see `ugcza_testimonial_text()`.

   .45 rather than the carousel's .3, and the prototype measured it: on the
   `grey` band the 10.5px uppercase role label reads 4.09:1 at .3 and 4.54:1 at
   .45, so the lighter scrim misses the 4.5 floor. The panels sit RECESSED
   against the lifted ground (31 against 56), which is the opposite of every
   other card on the site and is deliberate. */
.tq {
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 2vw, 20px);
}

.tqc {
  background: rgba(var(--ds-color-channel-shadow), 0.45);
  border-left: 4px solid var(--ds-color-brand-primary);
  padding: clamp(24px, 4vw, 42px);
  margin: 0;
}

/* The first paragraph carries the display size and the rest step down, so a
   five-paragraph quote stays readable rather than shouting for its whole
   length. `:first-of-type` and not `:first-child`: the build's `<p>` elements
   are emitted by `ugcza_render_partner_quotes()` and are the only children, but
   an editor who adds anything above them in the block editor must not silently
   move the display size onto it. */
.tqc p {
  font-size: 16.5px;
  line-height: 1.68;
  color: var(--ds-color-fg-default);
  margin: 0 0 15px;
  max-width: 76ch;
}

.tqc p:first-of-type {
  font-size: clamp(17px, 2vw, 22px);
  line-height: 1.62;
  color: var(--ds-color-fg-heading);
}

.tqc p:last-of-type {
  margin-bottom: 0;
}

/* The attribution line. NOT `.tslide .who`, which is 20px of margin and no
   rule: the stacked panel is taller and carries a hairline above the mark, and
   the two live side by side on this site - the homepage and About render the
   carousel, this page renders these.
   The hairline is the brand near-white at .16, so `channel-paper` and not
   `channel-light`: 249,250,250 against 255,255,255 is the 6/255 fold that a
   computed-style comparison reads and reports (PORT-08). */
.tqc .who {
  margin-top: 24px;
  padding-top: 19px;
  border-top: 1px solid rgba(var(--ds-color-channel-paper), 0.16);
  display: flex;
  align-items: baseline;
  gap: 13px;
  flex-wrap: wrap;
}

.tqc .who b {
  font-family: var(--ds-font-heading);
  font-size: 22px;
  font-weight: 600;
  color: var(--ds-color-brand-accent);
}

.tqc .who span {
  font-family: var(--ds-font-label);
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ds-color-fg-muted);
}

/* ---------- the figures, which are the homepage's tiles on a different ground
   The five records are the SAME five the homepage's `#about` band reads - same
   figures, same labels, same order, verified against the prototype rather than
   assumed - so the block takes no `scope` and the tiles need no new content.
   What differs is the treatment, and it differs in three measurable ways that
   are all scoped here because `#about` and `.pg-press-kit` already own their
   own versions of the same three rules:

   1. THE TILE GROUND. `.stat{background:var(--grey)}` unscoped in the prototype,
      which the build's own `.stats` rule in _about.scss already gives on a plain
      `.sec`. Nothing owed - stated because the homepage's `#about .stat` paints
      `bg-subtle` instead and reading that rule is how you would conclude this
      page owes one.
   2. THE SCRIM IS HEAVIER HERE: .89/.91/.95/.95 against the homepage's
      .84/.86/.93/.93. Measured in the prototype, not chosen, and the direction
      makes sense - these tiles sit on #383838 where the homepage's sit on
      #0a0a0a, so the same photograph has less ground helping it.
   3. THE LABEL IS ONE STEP BRIGHTER. `--tx-2` here against the homepage's
      `--tx-3`, which is the same lift the press kit takes and for the same
      reason its own comment records: #868686 at 10.5px cannot clear 4.5:1 over
      anything lighter than about #1a1a1a, so the ground was never the fixable
      part. On this page it is #383838, which is very much lighter.

   `em` is in the z-index list for the reason _about.scss records: a tile may
   carry an optional third line, and an element left out of it sits at z-index
   auto, which is UNDERNEATH the scrim at z-index 1 - text that is stored,
   rendered and invisible. No tile on this page has one today. */
.pg-partners .stat {
  position: relative;
  overflow: hidden;
}

.pg-partners .stat > b, .pg-partners .stat > span, .pg-partners .stat > em {
  position: relative;
  z-index: 2;
}

.pg-partners .stat > span, .pg-partners .stat > em {
  color: var(--ds-color-fg-default);
}

.pg-partners .stat::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.89) 0%, rgba(var(--ds-color-channel-scrim), 0.91) 40%, rgba(var(--ds-color-channel-scrim), 0.95) 66%, rgba(var(--ds-color-channel-scrim), 0.95) 100%);
}

/* ---------- both panel buttons carry an arrow ----------
   THE SAME DIFFERENCE THE CONTACT PAGE HAS FROM THE HOMEPAGE, and it was grepped
   in the prototypes rather than inherited from that finding. `ugcza-partners.html`
   carries three `class="arw"`: one in the featured Acer card's caption, which
   `ugcza_render_brands()` emits in markup already, and one in EACH of this
   band's two `.go` buttons - "Read the full story" in the plain `.sp` and "Start
   a conversation" in the `.sp.hot`. `ugcza-homepage-brief.html` carries ten and
   not one of them is in a `.sp .go`, which is why `patterns/home-cta.php` is
   right to have none and why this rule cannot lose its page scope: the homepage's
   two panel buttons are label-only, and a rule keyed on `.sp .go` alone would put
   an arrow on both of them.

   A PSEUDO-ELEMENT, NOT MARKUP, and the reason is core rather than taste:
   `createFromElement()` in wp-includes/js/dist/rich-text.js keeps an empty
   element only as `{type, attributes}`, so an inline `<svg>`'s `<path>` child is
   dropped on the editor's round-trip and the block fails validation the first
   time the client retypes the label. The glyph, the viewBox, the 1.7 stroke and
   the mask are `_tiles.scss`'s `.arw`, character for character, with
   `currentColor` so hover follows the button. `margin-left:.34em` sits ON TOP of
   the link's own 9px flex gap, which is what the prototype measures.

   THE SECOND CONSUMER OF A RULE THAT IS NOW WRITTEN TWICE. `_contact-page.scss`
   has the identical declaration under `.pg-contact`, and the two want to fold
   into one multi-scope selector in `_tiles.scss` beside the `.more` arrow, which
   already lists six page scopes for exactly this reason. Not done here because
   that file is being edited by another session as this lands, and a duplicated
   data URI is a smaller problem than a raced stylesheet. B-140. */
.pg-partners .sp .go .wp-block-button.btn > .wp-block-button__link::after {
  content: "";
  width: 0.72em;
  height: 0.72em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
  background: currentColor;
  --ds-arw-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M7 1l4 4-4 4M11 5H1' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask: var(--ds-arw-glyph) center/contain no-repeat;
  mask: var(--ds-arw-glyph) center/contain no-repeat;
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` onto every child
   of a group block through `:root :where(.is-layout-flow) > *`. Structural
   rather than a list of names, for the reason `_contact-page.scss` sets out at
   length: a named list is one omission away from 26px of band that no probe
   measures, and `.split` was left out of that page's list in the same file that
   warns about it. (0,3,0) against core's (0,1,0), so it wins on specificity
   rather than on enqueue order.

   `.sec-h`'s own margin-BOTTOM is untouched and stays the gap between a
   section's head and its content. */
.pg-partners .sec > .shell > * {
  margin-block-start: 0;
}

/* AND THE ONE CHILD THAT MUST GET ITS MARGIN BACK. `.stats` is the only band
   content on this page that declares a top margin of its own - clamp(30px,4vw,
   46px), in _about.scss, ported from the prototype - and the structural rule
   above outranks it (0,3,0 against 0,1,0), so without this line the figures
   band would lose 30px at 390 and 46px at 1440.

   It is NOT the same value as core's block gap and the two are not
   interchangeable: adjacent flow margins collapse to the LARGER, so the
   prototype's own gap here is max(sec-h bottom, stats top) = 30 at 390 and 46 at
   1440, and core's flat 26 would be swallowed by neither. Measured on the built
   page: `.stats` computes margin-top 46px at 1440 and 30px at 390.

   IT WINS ON SPECIFICITY, NOT ON SOURCE ORDER - (0,4,0) against the reset's
   (0,3,0) - so it stays correct wherever it sits in this file. Written after the
   reset because that is the order a reader needs, not because the cascade needs
   it. `_about-page.scss` reaches the same outcome by leaving `.taglines` OUT of
   a named list; this page's list is structural, so the exception has to be
   written rather than omitted. */
.pg-partners .sec > .shell > .stats {
  margin-block-start: clamp(30px, 4vw, 46px);
}

/* ---------- the pages' scale ----------
   The five lines every sub-page partial carries, at these prototypes' own
   numbers - which are the contact page's numbers, and those two facts were
   checked separately with the tool quoted in the header. A partial without
   them inherits the HOMEPAGE's scale from the design system and every band on
   the page comes out ~18px tall with a compounding document-top offset, which
   is PORT-83 and cost the About page nine bands at once.

   Scoped by the body class `ugcza_page_body_class()` emits from the page slug,
   never by page id: the id differs between this install, staging and
   production, so an id-keyed stylesheet stops applying on the host and reads as
   a deployment fault.

   `.sec-h` AND `h2` ARE STATED THOUGH NO CURRENT BAND USES THEM. These pages
   are a header and one section of prose - there is no section head and no h2
   anywhere in the three patterns. They are here because the scale is the PAGE's
   and a partial that states it in part is a partial that forks the moment
   somebody adds a band: the next h2 on this page would silently take the
   homepage's `clamp(28px,4.4vw,52px)`. Nothing probes them, because a probe on
   an absent element cannot pass. */
.pg-privacy .sec, .pg-terms .sec, .pg-cookies .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-privacy .sec-h, .pg-terms .sec-h, .pg-cookies .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-privacy h1, .pg-terms h1, .pg-cookies h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-privacy h2, .pg-terms h2, .pg-cookies h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-privacy .lede, .pg-terms .lede, .pg-cookies .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* ---------- the jump list ----------
   A grid rather than a row, so eight or nine section names wrap into as many
   210px columns as the shell affords instead of scrolling sideways. `auto-fit`
   means the fold is driven by what a label needs rather than by a breakpoint
   invented here - at 390 that is one column, at 1440 it is five.

   THE ANCHORS ARE THE GRID ITEMS, WHICH IS WHY THE PATTERNS USE `core/html`.
   The 7px padding and the hairline below belong to the `<a>`, so the rule has
   to span the whole track: wrap an anchor in core's paragraph block and the
   track holds a `<p>` while the border underlines only the width of the words.
   No core block emits a bare anchor, which is the same reason
   `page-press-kit.php` renders its `.jump` rail as raw HTML.

   THE HAIRLINE IS PURE WHITE AT .09, NOT THE BRAND NEAR-WHITE. The prototype
   writes `rgba(255,255,255,.09)`, and #f9fafa at that alpha reads warm against
   the ink ground - the same distinction `_ugcza.scss` records when it derives
   `channel-light` from pure white rather than folding it onto `channel-paper`
   (PORT-08). It is also a different rule from the .42 hairline the contact
   form's controls carry: this one separates list items, that one advertises
   where a field starts.

   `--ds-font-label` is the prototype's `--data` face. The two are the same
   family as `--body` in this brand, and the token name is what carries the
   intent, so a future label face changes this list and not the prose. */
.toc {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px 22px;
}

.toc a {
  font-family: var(--ds-font-label);
  font-size: 12.5px;
  color: var(--ds-color-fg-default);
  padding: 7px 0;
  border-bottom: 1px solid rgba(var(--ds-color-channel-light), 0.09);
}

.toc a:hover {
  color: var(--ds-color-brand-accent);
}

/* The list's own two margins are set in the core block bridge at the foot of
   this file, because both have to outrank a core rule to survive. */
/* ---------- the notice itself ----------
   74ch, and the measure is the whole typographic argument of the page: this is
   the only band on the site that is continuous reading rather than panels and
   tiles, so it is capped narrower than the 1172px shell around it. It is a
   `ch` because the cap has to follow the body face's own width, not a pixel
   count that goes wrong when the face changes.

   THE COLUMN IS LEFT-ALIGNED AND CORE WOULD HAVE CENTRED IT. `is-layout-
   constrained` emits `margin-left:auto !important;margin-right:auto !important`
   onto every child, which is a no-op for a full-width child and moves a
   max-width one to the middle - about 286px right at 1440 here. That is why the
   three patterns give the section's `.shell` FLOW layout rather than the
   constrained layout `page-contact.php` uses: that page has no max-width child,
   so the difference never showed there. `!important` means no rule in this file
   could have won it back (PORT-22's shape, from the layout engine rather than
   from a preset). */
.legal {
  max-width: 74ch;
}

/* `clamp(21px,2.4vw,27px)` is NOT the design system's `h3`, which is
   `clamp(20px,2.3vw,27px)` - one pixel at the floor and .1vw through the middle.
   Both are in the prototype, the base rule and this override, so the difference
   is the design's and restating it is the port rather than a correction.
   `scroll-margin-top:90px` is what stops the sticky 74px nav covering a heading
   the jump list just moved to; the extra 16px is the prototype's clearance. */
.legal h3 {
  font-size: clamp(21px, 2.4vw, 27px);
  margin-top: clamp(34px, 4vw, 52px);
  scroll-margin-top: 90px;
}

/* The first heading opens the band, so it takes the section's padding as its
   space rather than adding to it. This also stops the margin collapsing out of
   `.legal` and pushing the whole column down, which is what it would do
   otherwise - `.legal` has no padding or border of its own. */
.legal h3:first-child {
  margin-top: 0;
}

/* 1.75 leading rather than the body's 1.65: the one place on the site with
   paragraphs long enough for the extra line spacing to be worth the height. */
.legal p {
  color: var(--ds-color-fg-default);
  line-height: 1.75;
  margin-top: 13px;
}

.legal ul {
  margin: 13px 0 0;
  padding-left: 19px;
  color: var(--ds-color-fg-default);
  line-height: 1.75;
}

/* AN ADDRESS IN THIS COLUMN IS ONE UNBREAKABLE TOKEN.
   `POPIAComplaints@inforegulator.org.za` is 287px wide with nowhere to wrap,
   and inside a list item indented 19px it pushed `/privacy/` to 322px at a
   320px viewport - the whole page scrolling sideways for one word.

   `_contact-page.scss` ruled this for `.deet a` when the contact page was
   built, in the same words and for the same reason. Carried across rather than
   re-derived; `word-break:break-word` to match that declaration exactly, so
   the two places that solve this problem solve it identically.

   CLEAN AT 390 AND AT EVERY WIDTH THE PIXEL DIFF MEASURES. Overflow lives in
   the last 70px, which is why `tools/mobile-audit.mjs` sweeps 320 separately.
   BUILD-DECISIONS 25.5. */
.legal a {
  word-break: break-word;
}

.legal li {
  margin-top: 7px;
}

/* The marker is the only accent in the column, and `::marker` is the property
   that carries it - colouring the `<li>` instead would take the text with it. */
.legal li::marker {
  color: var(--ds-color-brand-accent);
}

/* `strong` steps UP to the heading white from the body's muted grey, which is
   what makes the cookie categories read as a list of names rather than four
   sentences. Weight 600 rather than the UA's 700, because the body face's bold
   is heavier than this design uses anywhere else. */
.legal strong {
  color: var(--ds-color-fg-heading);
  font-weight: 600;
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` - a FIXED 26px -
   onto every child of a group block through `:root :where(.is-layout-flow) > *`,
   and `margin-block-end:0` alongside it. Both have to be dealt with here, and
   the two halves need different weights.

   STRUCTURAL, NOT A LIST OF CONTAINER NAMES. `.pg-x .sec>.shell>*` is (0,3,0)
   against core's (0,1,0) and reaches every direct child of the band's shell,
   whatever a later edit puts there. The list version is how the same collision
   got into `_contact-page.scss` in the same file that warns about it, and what
   it costs is a band that is EXACT AT ONE WIDTH AND 2px OUT AT THE OTHER with
   every child measuring identical to 0.01px: `.toc`'s own margin is a clamp,
   core's is a constant, adjacent flow margins collapse to the larger, and a
   clamp crosses a constant somewhere in the middle of the range. PORT-64. */
.pg-privacy .sec > .shell > *,
.pg-terms .sec > .shell > *,
.pg-cookies .sec > .shell > * {
  margin-block-start: 0;
}

/* AND THE JUMP LIST'S OWN TWO MARGINS PUT BACK, at (0,4,0) so they beat the
   reset above rather than depending on where in this file they sit.

   The 26px top is the PROTOTYPE'S INLINE STYLE - `<div class="toc"
   style="margin-top:26px">` - moved into the component layer where it belongs.
   An inline style is invisible to a CSS port because it is not in the
   stylesheet, and inexpressible in a markup port because a length in block
   markup is a raw value the token gate forbids under `wp-content`: it falls
   through both routes silently and the band inherits core's own gap instead
   (PORT-30). That it happens to equal core's 26px block gap is a COINCIDENCE
   and is exactly why it is stated rather than left to fall through - the two
   numbers have nothing to do with each other and either can move.

   It survives the harness's edit, which is the reason it matters. The
   scaffolding `.note.warn` above the list is stripped from the prototype render
   by `visual-diff.mjs`, so this margin becomes the first thing in the band on
   both sides; `.shell` has no padding-top, so it collapses out through the
   shell and lands inside `.sec`'s padding in both renders alike.

   The bottom is the gap to the notice - the prototype's
   `clamp(30px,4vw,46px)` - restated here rather than left on `.toc` because
   core's `margin-block-end:0` is (0,1,0), the same weight a bare `.toc` rule
   would carry, and a tie decided by enqueue order is a tie waiting to be
   lost. */
.pg-privacy .sec > .shell > .toc,
.pg-terms .sec > .shell > .toc,
.pg-cookies .sec > .shell > .toc {
  margin-block-start: 26px;
  margin-block-end: clamp(30px, 4vw, 46px);
}

/* THE NOTICE'S OWN CHILDREN, AND THIS ONE IS DELIBERATELY UNSCOPED AND WEAK.
   `.legal>*` is (0,1,0) - a universal selector contributes nothing - so it ties
   with core's rule and wins on source order (ugcza.css is enqueued after
   global-styles-inline-css), while LOSING to every one of this file's own
   `.legal h3`, `.legal p` and `.legal ul` rules at (0,1,1). That is the exact
   behaviour wanted: any child type nobody has written a rule for falls back to
   the prototype's zero instead of taking a 26px gap, and the three types that
   do have rules keep their measured 13px and clamp.

   A SCOPED VERSION OF THIS LINE WOULD BREAK THE PAGE. `.pg-privacy .legal>*` is
   (0,2,0), which beats `.legal p` at (0,1,1) on class count and would zero
   every paragraph's 13px lead-in - the notice would set solid. The weaker
   selector is the correct one here, which is the opposite of the rule above it,
   and the difference is that this one has to lose to something. */
.legal > * {
  margin-block-start: 0;
}

/* ---------- the page's scale ----------
   See the header. These five are the whole of the difference between this page
   and the design system's defaults, and they are the prototype's own numbers.
   `h3` and `h4` are NOT here because the two sheets already agree on them. */
.pg-news .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-news .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-news h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-news h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-news .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* ---------- the one value the prototype wrote inline ----------
   PORT-30: an inline style is invisible to a CSS port - it is not in the
   stylesheet - and inexpressible in a markup port, because a clamp has no
   block-supports form. So it falls through both routes silently and the band
   inherits nothing. `ugcza-news.html` carries exactly two `style=` attributes
   that are not artwork, and this is the structural one:

     <div class="sec-h" style="margin-top:clamp(30px,4vw,46px)">

   THIS IS A JUDGEMENT CALL AND IT IS MADE IN THE OPEN, because the space it
   holds belongs to an element the build does not have. In the prototype the
   band opens with a `.note` and this margin separates that note from the
   section head. Strip the note - which the build must, and which
   `visual-diff.mjs` does to the prototype before measuring it - and the margin
   is left standing as the first thing inside the band.

   Reproduced, on two pieces of evidence rather than on the gate alone:

     1. The margin is declared on `.sec-h`, an element the build keeps, and the
        SAME FILE shows the designer's other habit one band further down -
        `<div class="note" style="margin-top:22px">` puts the spacing on the
        note when the spacing belongs to the note. Here it is not on the note.
     2. Measured in the stripped prototype render rather than argued from the
        CSS: dropping it leaves band 1 at 527.66px against 573.66px at 1440 and
        889.28 against 919.28 at 390 - 46px and 30px out, where the gate is 8.

   If it is ever decided that the gap died with the note, this rule is the one
   line to delete and the band will report -46/-30 for that reason and no other.

   THE SPECIFICITY IS THE POINT OF THE ID. The structural bridge at the foot of
   this file zeroes `margin-block-start` on every direct child of a `.shell` at
   (0,3,0); `.pg-news #outline .sec-h` is (1,2,0) and outranks it, so the two
   rules cannot be reordered into each other's way. */
.pg-news #outline .sec-h {
  margin-block-start: clamp(30px, 4vw, 46px);
}

/* ---------- the two panel buttons carry an arrow ----------
   Grepped against the prototype rather than eyeballed: `ugcza-news.html` carries
   exactly two `class="arw"`, one in the `.sp` panel and one in the `.sp.hot`
   panel, both inside `<div class="go"><a class="btn ...">Label<svg class="arw">`.
   The shared `.split`/`.sp`/`.go` component in `_tiles.scss` does NOT supply it
   - the difference between this page's panel and the homepage's is in the
   markup, not in the CSS - so inheriting the component alone would ship two
   buttons about 18px short, which is the same .72em glyph plus .34em clearance
   `_tiles.scss` records measuring twice on `.more`.

   A PSEUDO-ELEMENT, NOT MARKUP, for the reason `_tiles.scss` sets out above its
   own `.more` rule: core's `createFromElement()` in
   wp-includes/js/dist/rich-text.js keeps an empty element only as
   `{type, attributes}`, so an inline `<svg>`'s `<path>` child is dropped on the
   editor's round-trip and the block then fails validation the first time the
   client retypes the label. The glyph, the viewBox, the 1.7 stroke and the mask
   treatment are that block's, character for character - one arrow, one data
   URI, `currentColor` deciding the colour so hover follows the button.

   THE SCOPE IS LOAD-BEARING, exactly as `_contact-page.scss` records: a rule
   keyed on `.go` alone would double the arrow on the homepage's start.gg cards,
   whose `<span class="go">Results` already carries the glyph in markup.
   `.pg-news .sp .go` cannot reach them, and this page has no other `.go`.

   The pseudo goes on the LINK, not on the block wrapper: `_wp-bridge.scss`
   states that a component class set on a button block lands on the wrapper
   while the treatment belongs to `.wp-block-button__link`, and that link is the
   `inline-flex` box with the 9px gap. `margin-left:.34em` is kept ON TOP of that
   gap, exactly as `.arw` and the `.more` rule both do: the prototype's spacing
   between label and glyph is the 9px flex gap PLUS 5.78px of margin at 17px, not
   one or the other, and dropping either closes it by a third. */
.pg-news .sp .go .wp-block-button.btn > .wp-block-button__link::after {
  content: "";
  width: 0.72em;
  height: 0.72em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
  background: currentColor;
  --ds-arw-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M7 1l4 4-4 4M11 5H1' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask: var(--ds-arw-glyph) center/contain no-repeat;
  mask: var(--ds-arw-glyph) center/contain no-repeat;
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` - a FIXED 26px -
   onto every child of a group block through `:root :where(.is-layout-flow) > *`.
   On a flex or grid container those margins ADD to the gap instead of collapsing
   into it, and on a flow child they collapse with the design's own margin to the
   LARGER of the two, which is where this bites.

   STRUCTURAL RATHER THAN A LIST OF CONTAINER NAMES, and that choice is PORT-64.
   The list version cannot be checked for completeness: `_contact-page.scss`
   carries a comment reading "EVERY NEW CONTAINER ON THIS PAGE IS IN THIS LIST"
   directly above a list that `.split` was missing from. `.sec-h` sets
   `margin-bottom:clamp(24px,3.4vw,42px)` and core sets 26px on the next child,
   so above the width where the clamp crosses 26 the design wins and the band is
   exact, and below it core wins and the band is 2px tall - the crossover for
   this clamp is 26 / 0.034 = 765px, which is why such a band reads 0.0% at 1440
   and several percent at 390 with every child measuring identical to 0.01px.

   (0,3,0) against core's (0,1,0), so it wins on specificity rather than on the
   enqueue order. Every direct child of a `.shell` on this page, whatever a later
   band puts there - `.sec-h`, `.vals`, `.press` and `.split` today. `.sec-h`'s
   own margin-BOTTOM is untouched and stays the gap.

   The containers INSIDE those children are already covered unscoped and are
   deliberately not restated: `.sec-h>*` in _sections.scss, `.vals>*` and
   `.val>*` in _about-page.scss, `.split>*` and `.sp>*` in _tiles.scss. `.pr`'s
   three children need nothing at all - the tile is emitted whole by
   `ugcza_render_news_index()` inside a plain `<a>`, so no element in it carries
   a layout class and core never had a rule there to beat. */
.pg-news .sec > .shell > * {
  margin-block-start: 0;
}

/* The page header's own children. `_about-page.scss` already states this
   unscoped, so this line is insurance rather than necessity - it removes a
   cross-file dependency on a rule that lives in a partial named after a
   different page, and it is what every other page partial does. */
.pg-news .phead .shell > * {
  margin-block-start: 0;
}

/* ---------- photographic ground on the four outline cards ----------
   B-228, client instruction 2026-08-15: monochrome photographs from the event
   gallery behind the four `#outline` cards, graded onto the teal.

   SCOPED TO `.pg-news #outline`, AND THAT IS LOAD-BEARING RATHER THAN TIDY.
   `.val` is shared by three pages and two record types - PORT-58 states it:
   About renders it from `ugcza_value`, Partners from `ugcza_inclusion`, and
   News is the only one that is pattern markup. An unscoped `.val::before` would
   put a photograph behind every value on About and every inclusion on Partners,
   on bands that are signed off at 0.0%, and the pixel diff for THIS page would
   still be green. The `#outline` half narrows it again so a future `.vals` band
   elsewhere on News is unaffected.

   `mix-blend-mode:multiply` IS THE WHOLE CONTRAST ARGUMENT, and it is a proof
   rather than a measurement. The scrim floor rule is that a photographic layer
   may never leave the ground LIGHTER than the flat colour it replaced - the
   type's contrast was set against that flat colour, so anything lighter takes
   contrast away, and the answer is never to re-derive an alpha per photograph.
   Multiply cannot go lighter: the result is `ground x art / 255` per channel,
   which is <= ground for every possible input, and equals it exactly where the
   art is pure white. `opacity` then interpolates between that result and the
   untouched ground, so it stays on the safe side of the floor at every value.

   The consequence is the property `blocks-about.php` prizes about the section
   wash, in its words: the treatment stays in CSS and is not baked into the file,
   so the floor holds over whatever is put here next rather than over these four
   crops alone. A pure white photograph lands exactly on the teal and every
   darker one lands below it. Nothing has to be re-measured when the pictures
   change, which is the failure mode a per-image alpha guarantees.

   MEASURED ON THE BUILT PAGE, not argued from the model - the type is hidden so
   the ground under it is sampled directly, and the worst (lightest) pixel in
   each of the four cards is taken, because that is the pixel that decides the
   floor:

                    flat #28504d    worst pixel, all four cards
     h4 heading        8.56:1              8.56:1
     body copy         5.35:1              5.35:1
     .num accent       2.62:1              2.62:1

   IDENTICAL, to the last measurable step, on every card. That is the multiply
   floor doing exactly what it promises rather than approximately: the lightest
   pixel a photograph can produce IS the ground, reached where the art is white,
   and every other pixel is darker than it. The first draft of this comment
   predicted three improved ratios and all three were wrong - the improvement is
   not in the floor, it is in everything above it. Median luminance across the
   four cards lands at 0.031 to 0.033 against the ground's 0.067, so the typical
   pixel is about twice as dark as the flat card and the type reads BETTER over
   the artwork than without it, while never reading worse.

   THE ACCENT IS BELOW 4.5:1 IN BOTH COLUMNS AND THIS CHANGE DOES NOT MOVE IT.
   `.num` is the 11px letterspaced label the design has always drawn in orange on
   this teal card, so 2.62:1 is what the card has always been; it is not
   introduced here and it is not improved here either. B-229 tracks it, and it
   belongs to the card rather than to the photograph - which is why it is stated
   here rather than quietly absorbed into a change that had nothing to do with
   it.

   `isolation:isolate` CONFINES THE BLEND TO THE CARD. Without it the multiply
   composites against everything painted below in the stacking context - the
   band's own ground and whatever sits under that - so the card would darken the
   page rather than itself, and the effect would change depending on what band
   preceded it. It is one word and it is the difference between a treatment and
   an accident.

   THE GRADE IS `grayscale(1)` FIRST, then a flattened tone curve. The client
   asked for monochrome and the multiply supplies the teal, so any colour left in
   the file fights the ground - `contrast(.42)` pulls the range toward the middle
   so the darkest parts do not crush to black under the multiply, and
   `brightness(1.5)` lifts that compressed range back up so the card still reads
   as teal rather than as a dark plate. Neither is fixing a cast, which is the
   one thing this pair cannot do. */
.pg-news #outline .val {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.pg-news #outline .val-art::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: var(--ds-outline-art);
  background-size: cover;
  background-position: center;
  filter: grayscale(1) contrast(0.42) brightness(1.5);
  mix-blend-mode: multiply;
  opacity: 0.62;
  z-index: -1;
}

/* NO `position:relative` ON THE CHILDREN, AND THAT ABSENCE IS LOAD-BEARING.
   This block used to lift `.val>*` to `z-index:1` over an artwork layer at 0.
   It worked, and it silently broke the whole-card link added below: an
   absolutely positioned `::after` resolves against its nearest POSITIONED
   ancestor, so with the `h4` relative the overlay covered the heading text
   instead of the card, and only the words were clickable. The card looked
   correct and a click landed nowhere - found by driving a real click at the
   card's bottom corner rather than by reading the CSS.

   The artwork sits at `z-index:-1` instead. A negative-z-index child paints
   ABOVE its parent's own background and BELOW the parent's in-flow content, so
   the teal ground, then the photograph, then the type is the natural order and
   nothing has to be lifted at all. `isolation:isolate` on `.val` keeps the -1
   inside the card rather than letting it fall behind the band, and the multiply
   still blends against the card's own background because that is what the
   isolated stacking context contains. */
/* One class per card, naming the CATEGORY rather than the position, so the four
   can be reordered in the pattern without the pictures following the slots. The
   URL is relative to the built stylesheet at `assets/css/ugcza.css`. */
/* ---------- each card opens its category archive ----------
   B-234. The four category archives rendered correctly and NOTHING ON THE SITE
   POINTED AT ANY OF THEM - `archive.html` was reachable only by typing a URL.
   These four cards are the route in from the index; the article head's category
   eyebrow is the route in from an article.

   THE HEADING CARRIES THE LINK AND THE `::after` CARRIES THE CARD. The whole
   card is clickable, but the anchor's TEXT is "Event recaps" rather than an
   empty box, so the accessible name is the category name and the link is
   announced as one item rather than as a bare region. Wrapping the card's three
   children in an `<a>` would have been the other way to do it and is not
   available here: a group block cannot be an anchor, and nesting the number, the
   heading and the paragraph inside one would put block markup inside a link that
   the editor then has to round-trip.

   THE OVERLAY RESOLVES AGAINST `.val`, AND THAT IS THE FRAGILE PART. An
   absolutely positioned `::after` uses its nearest POSITIONED ancestor, so this
   only covers the card while nothing between it and `.val` is positioned. The
   first version of this shipped with `.val>*{position:relative}` still in the
   block above and the overlay covered the heading text alone - the card looked
   right and a click on it did nothing. If a rule ever gives the `h4` or its
   ancestors a position, this breaks the same way and just as quietly.
   `check-article-page.mjs` now drives a click at the card's bottom corner,
   which is the only thing that catches it.

   THE KNOWN COST IS TEXT SELECTION. An overlay across the card means the copy
   underneath cannot be selected with a mouse. That is the accepted trade for a
   whole-card target and it is why the overlay is not used on the article tiles,
   which are already anchors in their own right.

   FOCUS RINGS THE CARD, NOT THE WORDS. `:focus-visible` on an inline anchor
   would draw a box around the heading text alone, which is a poor signal for a
   target this size; the ring goes on the `::after` so it traces what actually
   responds to Enter. `outline-offset` is negative because `.val` is
   `overflow:hidden` for the artwork and a positive offset would be clipped. */
.pg-news #outline .val h4 a {
  color: inherit;
  text-decoration: none;
}

.pg-news #outline .val h4 a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
}

.pg-news #outline .val:hover h4 a,
.pg-news #outline .val h4 a:focus-visible {
  color: var(--ds-color-brand-accent);
}

.pg-news #outline .val h4 a:focus-visible::after {
  outline: 2px solid var(--ds-color-brand-accent);
  outline-offset: -3px;
}

.pg-news #outline .val-art--recaps {
  --ds-outline-art:url("../img/outline/recaps.webp");
}

.pg-news #outline .val-art--features {
  --ds-outline-art:url("../img/outline/features.webp");
}

.pg-news #outline .val-art--production {
  --ds-outline-art:url("../img/outline/production.webp");
}

.pg-news #outline .val-art--announcements {
  --ds-outline-art:url("../img/outline/announcements.webp");
}

/* ---------- the page's scale ----------
   See the header. These five are the whole of the difference between this page
   and the design system's defaults, which ship the HOMEPAGE's numbers. They are
   the prototype's own, and they are About's and Contact's, and those facts were
   checked separately. Prototype values beside each: `.sec` is
   clamp(52px,7.5vw,102px) by default, `.sec-h` clamp(26px,3.6vw,44px), h1
   clamp(38px,7vw,80px), h2 clamp(29px,4.4vw,52px), `.lede`
   clamp(16.5px,1.6vw,19px)/1.62/58ch - every one of them a step ABOVE what this
   page asks for, which is why a missing scale block reads as "uniformly, very
   slightly too big" rather than as a fault. */
.pg-info-hub .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-info-hub .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-info-hub h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-info-hub h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-info-hub .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* ---------- the contents list ----------
   `auto-fit` at a 210px floor, so six links run as one row of six at 1440 and
   reflow to one column at 390 with no media query - which is why this grid is
   safe on a prototype page whose stylesheet was cut down (PORT-81 is about the
   grids that are NOT).

   THE TWO GAP VALUES ARE DIFFERENT ON PURPOSE and both are the prototype's: 10px
   between rows, 22px between columns. The rows are separated by each link's own
   hairline as well, so they need less air than two columns of running text do.

   `.toc` IS ONE LETTER FROM `.todo`, which is on `visual-diff.mjs`'s
   PROTOTYPE_ONLY list and fails the page at 100% if the build matches it. The
   selector is an exact class match, so `.toc` cannot match `.todo` and vice
   versa - but a future rule written as `[class^="to"]` would, and this is the
   page where somebody might. */
.toc {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px 22px;
  margin-bottom: clamp(30px, 4vw, 46px);
}

/* The hairline is PURE white at .09, not the brand near-white: it is a rule over
   a `bg-muted` band rather than a control's own edge, and at 9% the two are 0.5
   of one part in 255 apart while `channel-light` says what the prototype says.
   The prototype writes rgba(255,255,255,.09) here and rgba(249,250,250,.42) on
   its form controls, so the distinction is the designer's and is kept (PORT-08).
   `padding:7px 0` with no left padding, so the link's text starts on the shell's
   own gutter and the rules below the six links form one continuous baseline. */
.toc a {
  font-family: var(--ds-font-label);
  font-size: 12.5px;
  color: var(--ds-color-fg-default);
  padding: 7px 0;
  border-bottom: 1px solid rgba(var(--ds-color-channel-light), 0.09);
}

.toc a:hover {
  color: var(--ds-color-brand-accent);
}

/* ---------- the reference sections ----------
   `74ch` is a reading measure and it is wider than the `.lede`'s 60ch on
   purpose: a lede is a short standfirst set large, and this is several hundred
   words of reference text a player scans for one clause.

   `scroll-margin-top:90px` is what stops the sticky nav covering a heading
   somebody has just followed a contents link to. The bar is 74px, so 90 leaves
   16px of clearance. It only works because the scroll target is the `<h3>`
   itself - `ugcza_render_info_sections()` puts the id on the heading and not on
   a wrapper, which is also what keeps the id where the record can own it. */
.legal {
  max-width: 74ch;
}

/* The heading steps DOWN from the design system's h3 at the small end and meets
   it at the large - clamp(21px,2.4vw,27px) against clamp(20px,2.3vw,27px) - so
   it has to be restated rather than inherited. The prototype's own numbers. */
.legal h3 {
  font-size: clamp(21px, 2.4vw, 27px);
  margin-top: clamp(34px, 4vw, 52px);
  scroll-margin-top: 90px;
}

/* The first heading sits directly under `.toc`'s own bottom margin, so its
   top margin would double the gap. `:first-child` rather than `:first-of-type`,
   which is the prototype's, and correct here because the block emits the
   heading first with nothing before it. */
.legal h3:first-child {
  margin-top: 0;
}

.legal p {
  color: var(--ds-color-fg-default);
  line-height: 1.75;
  margin-top: 13px;
}

/* `padding-left:19px` rather than the UA's 40px, which is the one place on this
   page a UA default is being overridden rather than inherited - so it is the
   one place quirks mode could have differed, and it cannot, because both
   documents take this value. `document.compatMode` is BackCompat in the
   prototype and CSS1Compat in the build on every page of this build (PORT-56,
   PORT-63); this page carries no `<table>` and no `<form>`, which are the two
   elements where that difference is known to bite. */
.legal ul {
  margin: 13px 0 0;
  padding-left: 19px;
  color: var(--ds-color-fg-default);
  line-height: 1.75;
}

.legal li {
  margin-top: 7px;
}

/* `::marker` takes the accent while the text stays `fg-default`. It is the only
   list on the site drawn with real markers - `.vc li` in _about-page.scss uses a
   7px rule instead, because neither licensed face carries a disc that sits on
   that baseline. Here the marker is the browser's, so the face never enters it. */
.legal li::marker {
  color: var(--ds-color-brand-accent);
}

.legal strong {
  color: var(--ds-color-fg-heading);
  font-weight: 600;
}

/* ---------- both panel buttons carry an arrow ----------
   MEASURED RATHER THAN INHERITED, the same way `_contact-page.scss` records it.
   `.split`/`.sp`/`.go` are `_tiles.scss`'s and shared, so the easy assumption is
   that the shared component already covers the button - it does not, and the
   difference is not visible in the CSS at all. It is in the markup.

   Grepped against the prototypes rather than eyeballed: `ugcza-info-hub.html`
   carries exactly two `class="arw"` and BOTH sit inside
   `<div class="go"><a class="btn ...">Label<svg class="arw">` - one in the plain
   `.sp` panel and one in the `.sp.hot` one. `ugcza-homepage-brief.html` carries
   ten and not one of them is in a `.sp .go` button, so `patterns/home-cta.php`
   is right to have no arrow and this page needs one. Inheriting the shared
   component would have shipped two buttons about 18px short - the .72em glyph
   plus its .34em clearance, which `_tiles.scss` records measuring twice on
   `.more` links.

   A PSEUDO-ELEMENT, NOT MARKUP, for the reason `_tiles.scss` sets out above its
   own `.more` rule: core's `createFromElement()` in
   wp-includes/js/dist/rich-text.js keeps an empty element only as
   `{type, attributes}`, so an inline `<svg>`'s `<path>` child is dropped on the
   editor's round-trip and the block then fails validation the first time the
   client retypes the label. The glyph, the viewBox, the 1.7 stroke and the mask
   treatment are that block's, character for character - one arrow, one data
   URI, `currentColor` deciding the colour so hover follows the button.

   The pseudo goes on the LINK, not on the block wrapper: `_wp-bridge.scss`
   states that a component class set on a button block lands on the wrapper
   while the treatment belongs to `.wp-block-button__link`, and that link is the
   `inline-flex` box with the 9px gap. `margin-left:.34em` is kept ON TOP of
   that gap, exactly as `.arw` and the `.more` rule both do: the prototype's
   spacing between label and glyph is the 9px flex gap PLUS 5.78px of margin at
   17px, not one or the other, and dropping either closes it by a third.

   THE SCOPE IS LOAD-BEARING. The homepage's other `.go` elements are the
   start.gg cards' `<span class="go">Results` inside `.gg-foot`, and those DO
   carry the glyph in markup already. A rule keyed on `.go` alone would double
   the arrow there. `.pg-info-hub .sp .go` cannot reach them. */
.pg-info-hub .sp .go .wp-block-button.btn > .wp-block-button__link::after {
  content: "";
  width: 0.72em;
  height: 0.72em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
  background: currentColor;
  --ds-arw-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M7 1l4 4-4 4M11 5H1' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask: var(--ds-arw-glyph) center/contain no-repeat;
  mask: var(--ds-arw-glyph) center/contain no-repeat;
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` onto every child
   of a group block through `:root :where(.is-layout-*) > *`. `.shell` is a
   constrained group here, so every band's content takes a fixed 26px it did not
   ask for.

   STRUCTURAL RATHER THAN A LIST OF CONTAINER NAMES, which is PORT-64's whole
   point: `.pg-info-hub .sec>.shell>*` at (0,4,0) cannot be left incomplete,
   where a list can - and the list version is how the Contact page shipped two
   bands 2px tall, in a file that carried a comment reading "EVERY NEW CONTAINER
   ON THIS PAGE IS IN THIS LIST" with `.split` not in it.

   WHY IT MATTERS AT ONE WIDTH ONLY, which is the part that makes it hard to
   see: `.sec-h` sets `margin-bottom:clamp(24px,3.4vw,42px)` and core sets a flat
   26px on the next child. Adjacent flow margins collapse to the LARGER, so above
   the width where the clamp crosses 26 the design wins and the band is exact,
   and below it core wins and the band is 2px tall. This clamp's `vw` term equals
   26px at 26 / 0.034 = 765px, so 1440 is clean and 390 is not. A band that is
   0.0% at one width and several percent at the other with every child measuring
   identical is this, and PORT-48 is the standing instruction never to file it as
   noise. */
.pg-info-hub .sec > .shell > * {
  margin-block-start: 0;
}

/* AND THE TWO THE PROTOTYPE SETS INLINE, restated at the same (0,4,0) and after
   the reset so they win on source order.

   THESE ARE PORT-30 EXACTLY and they are the one thing on this page that BOTH
   kinds of port drop silently. `ugcza-info-hub.html` carries
   `<div class="split" style="margin-top:22px">` and
   `<div class="toc" style="margin-top:26px">` - inline styles, so a CSS port
   never sees them (they are not in the stylesheet) and a markup port cannot
   express them (a group block has no margin-top support that emits a bare px).
   They fall through both routes and the band inherits core's 26px instead: 4px
   wrong on `.split` and 0px wrong on `.toc` above 765px, which is exactly small
   enough to be attributed to something else.

   Written as `margin-block-start` rather than `margin-top` so the property being
   contested is the same one core writes - a physical/logical pair cascades
   together, but naming the same property keeps the winner obvious to a reader.
   `.toc`'s 26px still COLLAPSES with `.sec-h`'s clamp above it, to the larger of
   the two, exactly as it does in the prototype. */
.pg-info-hub .sec > .shell > .split {
  margin-block-start: 22px;
}

.pg-info-hub .sec > .shell > .toc {
  margin-block-start: 26px;
}

/* The page header's own children, as on every other page partial. */
.pg-info-hub .phead .shell > * {
  margin-block-start: 0;
}

/* ---------- the page's scale ----------
   See the header. These five are the whole of the difference between this page
   and the design system's defaults, which are the HOMEPAGE's. Leaving them out
   is not subtle: PORT-83 measures it at 18px on every band, compounding down the
   page, with no single band obviously at fault. They are the prototype's own
   numbers, and they are About's, Contact's and Partners' numbers, and those
   facts were checked separately.

   `h3` and `h4` are deliberately NOT in the list. The prototype sets them to
   clamp(20px,2.3vw,27px) and 19px/1.18, which is what `_primitives.scss` already
   ships, so a line here would restate a value rather than change one - and this
   page has an `<h3>` in the red panel and four `<h4>` in the routes that would
   then each have two definitions. */
.pg-accademy .sec {
  padding: clamp(50px, 7vw, 96px) 0;
}

.pg-accademy .sec-h {
  margin-bottom: clamp(24px, 3.4vw, 42px);
}

.pg-accademy h1 {
  font-size: clamp(38px, 6.6vw, 74px);
}

.pg-accademy h2 {
  font-size: clamp(28px, 4.2vw, 48px);
}

.pg-accademy .lede {
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.6;
  max-width: 60ch;
}

/* ---------- the figures, in this page's dress ----------
   The component is `_about.scss`'s and the artwork layer, the drift and the
   reduced-motion guard all stay there. This page's prototype had NO `.stat`
   rules of its own until `tilebg.py` supplied them, and that file records why:
   the generated-pages builder never emitted any, so the tiles rendered as
   unstyled divs and the grid and type were copied in from the Partners page.
   That is why the three overrides below are Partners' three, character for
   character, rather than a set derived here:

     node tools/prototype-style-diff.mjs ugcza-partners.html ugcza-accademy.html \
       ".stats,.stat,.stat-bg"
       -> shared 14, differ 3

   and all three "differences" are whitespace inside a declaration block, except
   `.stat span`, where Partners writes `color:var(--tx-3)` and this page omits it.
   Both pages then override it to `--tx-2` on the child selector below, so the two
   compute identically - which is the only thing that matters, and is why this
   file states the override rather than the base.

   1. THE TILE GROUND IS OWED NOTHING. `.stat{background:var(--grey)}` unscoped in
      the prototype, and `_about.scss`'s own unscoped `.stat` already paints
      `bg-muted` on a plain `.sec`. Stated because the homepage's `#about .stat`
      paints `bg-subtle` instead, and reading that rule is how you would conclude
      this page owes one.
   2. THE SCRIM IS HEAVIER THAN THE HOMEPAGE'S: .89/.91/.95/.95 against
      .84/.86/.93/.93. Measured in `tilebg.py` against the brightest 0.5% of
      pixels in these crops, not chosen, and the direction makes sense - these
      tiles sit on #383838 where the homepage's sit on #0a0a0a, so the same
      photograph has less ground helping it. Brand red is the binding constraint
      either way: #ea3700 has luminance .20 and tops out at 5.04:1 even on pure
      black.
   3. THE LABEL IS ONE STEP BRIGHTER, `--tx-2` against the homepage's `--tx-3`,
      which is the same lift the press kit and Partners both take. #868686 at
      10.5px cannot clear 4.5:1 over anything lighter than about #1a1a1a, so the
      ground was never the fixable part, and here it is #383838.

   `em` is in the z-index list for the reason `_about.scss` records: a tile may
   carry an optional third line, and an element left out of it sits at z-index
   auto, which is UNDERNEATH the scrim at z-index 1 - text that is stored,
   rendered and invisible. No tile on this page has one, and the block will emit
   one the moment somebody types a sub-label into wp-admin. */
.pg-accademy .stat {
  position: relative;
  overflow: hidden;
}

.pg-accademy .stat > b, .pg-accademy .stat > span, .pg-accademy .stat > em {
  position: relative;
  z-index: 2;
}

.pg-accademy .stat > span, .pg-accademy .stat > em {
  color: var(--ds-color-fg-default);
}

.pg-accademy .stat::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(var(--ds-color-channel-scrim), 0.89) 0%, rgba(var(--ds-color-channel-scrim), 0.91) 40%, rgba(var(--ds-color-channel-scrim), 0.95) 66%, rgba(var(--ds-color-channel-scrim), 0.95) 100%);
}

/* ---------- every panel button carries an arrow ----------
   THE SAME DIFFERENCE THE CONTACT AND PARTNERS PAGES HAVE FROM THE HOMEPAGE, and
   it was grepped in this prototype rather than inherited from those findings.
   `ugcza-accademy.html` carries FIVE `class="arw"` and all five sit inside
   `<div class="go"><a class="btn ...">Label<svg class="arw">` - one per panel
   across all four bands, in `.btn`, `.btn.line` and `.btn.alt` alike.
   `ugcza-homepage-brief.html` carries ten and not one of them is in a `.sp .go`,
   which is why `patterns/home-cta.php` is right to have none and why this rule
   cannot lose its page scope: a rule keyed on `.sp .go` alone would put an arrow
   on the homepage's two label-only panel buttons.

   THE SCOPE IS LOAD-BEARING FOR A SECOND REASON. The homepage's other four `.go`
   elements are the start.gg cards' `<span class="go">Results` inside `.gg-foot`,
   and those DO carry the glyph in markup already, so an unscoped rule would
   double the arrow there.

   A PSEUDO-ELEMENT, NOT MARKUP, and the reason is core rather than taste:
   `createFromElement()` in wp-includes/js/dist/rich-text.js keeps an empty
   element only as `{type, attributes}`, so an inline `<svg>`'s `<path>` child is
   dropped on the editor's round-trip and the block fails validation the first
   time the client retypes the label. The glyph, the viewBox, the 1.7 stroke and
   the mask are `_tiles.scss`'s `.arw`, character for character, with
   `currentColor` so hover follows the button.

   `margin-left:.34em` sits ON TOP of the link's own 9px flex gap, not instead of
   it: the prototype's spacing between label and glyph is the gap PLUS 5.78px of
   margin at 17px, and dropping either closes it by a third. `_tiles.scss` records
   measuring exactly that on the `.more` links twice.

   THE THIRD CONSUMER OF A RULE NOW WRITTEN THREE TIMES. `_contact-page.scss` and
   `_partners-page.scss` carry the identical declaration under their own scopes,
   and the three want to fold into one multi-scope selector in `_tiles.scss`
   beside the `.more` arrow, which already lists six page scopes for exactly this
   reason. Not done here because that file is shared with three sessions building
   three other pages as this lands, and a duplicated data URI is a smaller problem
   than a raced stylesheet. B-140 is the row. */
.pg-accademy .sp .go .wp-block-button.btn > .wp-block-button__link::after {
  content: "";
  width: 0.72em;
  height: 0.72em;
  display: inline-block;
  flex: none;
  vertical-align: -0.02em;
  margin-left: 0.34em;
  background: currentColor;
  --ds-arw-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M7 1l4 4-4 4M11 5H1' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask: var(--ds-arw-glyph) center/contain no-repeat;
  mask: var(--ds-arw-glyph) center/contain no-repeat;
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` - a FIXED 26px -
   onto every child of a group block through `:root :where(.is-layout-flow) > *`.
   Structural rather than a list of names, for the reason `_contact-page.scss`
   sets out at length: a named list is one omission away from 26px of band that no
   probe measures, and `.split` was left out of that page's list in the same file
   that warns about it. (0,3,0) against core's (0,1,0), so it wins on specificity
   rather than on enqueue order.

   THIS PAGE IS THE EXACT SHAPE PORT-64 IS ABOUT. `.sec-h` sets
   `margin-bottom:clamp(24px,3.4vw,42px)` and adjacent flow margins collapse to
   the LARGER of the two, so above the width where the clamp crosses 26px the
   design wins and the band is exact, and below it core wins and the band is 2px
   tall. The crossover is 26 / 0.034 = 765px, which is why a page can read 0.0% at
   1440 and several percent at 390 with every element inside it identical to
   0.01px. All four bands here open with a `.sec-h`, so all four would do it.

   `.sec-h`'s own margin-BOTTOM is untouched and stays the gap between a section's
   head and its content. */
.pg-accademy .sec > .shell > * {
  margin-block-start: 0;
}

/* AND THE ONE CHILD THAT MUST GET ITS MARGIN BACK. `.stats` is the only band
   content on this page that declares a top margin of its own -
   clamp(30px,4vw,46px), in _about.scss, ported from the prototype - and the
   structural rule above outranks it (0,3,0 against 0,1,0), so without this line
   the figures would sit 30px high at 390 and 46px high at 1440.

   It is NOT core's block gap by another name and the two are not
   interchangeable. `.stats` follows `.split` here rather than `.sec-h`, so the
   collapse is max(0, 30..46) and the prototype's own gap is the clamp outright -
   core's flat 26 would be swallowed at 1440 and would win at 390, which is a 4px
   difference at one width only and is PORT-64 again from the other side.

   IT WINS ON SPECIFICITY, NOT ON SOURCE ORDER - (0,4,0) against the reset's
   (0,3,0) - so it stays correct wherever it sits in this file. `_partners-page.scss`
   carries the identical line for the identical reason, which is the second
   instance and is what makes it a property of the `.stats` band rather than of
   that page. */
.pg-accademy .sec > .shell > .stats {
  margin-block-start: clamp(30px, 4vw, 46px);
}

/* The page header's own children, as on every other page partial. `.phead` is
   not a `.sec`, so the structural rule above cannot reach it. */
.pg-accademy .phead .shell > * {
  margin-block-start: 0;
}

/* AND THE GRID AND FLEX CONTAINERS INSIDE THE BANDS. On a flow container core's
   margins collapse; on a flex or grid container they ADD to the gap instead, so
   every one of these would run its own measured gap PLUS 26px. `.split>*` and
   `.sp>*` are already zeroed in `_tiles.scss` and `.vals>*`/`.val>*` in
   `_about-page.scss`, so what is left is the figures band - `.stats` is a grid
   with a 14px gap and `.stat` a flex column with a 4px one, and the four tiles
   are emitted by `ugcza_render_about_stats()` inside a `.shell` that carries
   `is-layout-constrained`. */
.pg-accademy .stats > *, .pg-accademy .stat > * {
  margin-block-start: 0;
  margin-block-end: 0;
}

/* ---------- the featured image ----------
   Core's `wp:post-featured-image` emits `<figure class="wp-block-post-featured-image
   artfig"><img>`, and the figure is what needs the gap - `_primitives.scss`
   already caps the image at `max-width:100%` and core's own stylesheet sizes it.
   `display:block` on the img kills the inline-baseline gap under it, which would
   otherwise put 4px of figure below a picture that looks like it ends at its own
   edge.

   The gap to the prose below is the design's own band rhythm rather than a new
   number: `clamp(24px,3.4vw,42px)` is `.sec-h`'s margin-bottom on this page, which
   is what every other band on the site puts between a head and its content. */
.artfig {
  margin: 0 0 clamp(24px, 3.4vw, 42px);
}

.artfig img {
  display: block;
  width: 100%;
  height: auto;
}

/* ---------- the prose ----------
   74ch, and the measure is the whole typographic argument, exactly as
   `_legal-page.scss` states it: this and the legal notices are the only bands on
   the site that are continuous reading rather than panels and tiles, so the
   column is capped far narrower than the 1172px shell around it. A `ch` rather
   than a pixel count so the cap follows the body face's own width.

   THE COLUMN IS LEFT-ALIGNED AND CORE WOULD HAVE CENTRED IT. `single.html` gives
   the band's `.shell` FLOW layout, not constrained, for the reason
   `_legal-page.scss` records: `is-layout-constrained` emits
   `margin-left:auto !important;margin-right:auto !important` onto every child,
   which is a no-op for a full-width child and moves a max-width one to the
   middle - about 286px right at 1440. No rule in this file could win that back. */
.article {
  max-width: 74ch;
}

/* `h2` IS THE SUBHEADING AN EDITOR ACTUALLY TYPES, and it cannot take the page's
   own `h2` - that is `clamp(28px,4.2vw,48px)`, a band heading, and it would set a
   subheading inside a 74ch column at half the column's width per line. It takes
   `.legal h3`'s `clamp(21px,2.4vw,27px)` instead, which is the design's ONLY
   measured in-prose heading size and is why that number is here rather than one
   invented for this file. `h3` steps down to the design system's own `h4`.

   `scroll-margin-top:90px` is what stops the sticky 74px nav covering a heading
   an in-page link just moved to; the extra 16px is the clearance `.legal h3`
   already uses. */
.article h2 {
  font-size: clamp(21px, 2.4vw, 27px);
  margin-top: clamp(34px, 4vw, 52px);
  scroll-margin-top: 90px;
}

.article h3 {
  font-size: 19px;
  line-height: 1.18;
  margin-top: clamp(26px, 3vw, 38px);
  scroll-margin-top: 90px;
}

/* The first block opens the band, so it takes the section's padding as its space
   rather than adding to it. This also stops the margin collapsing out of
   `.article` and pushing the whole column down, which is what it would do
   otherwise - `.article` has no padding or border of its own. */
.article > :first-child {
  margin-top: 0;
}

/* 1.75 leading rather than the body's 1.65, for the reason `.legal p` gives: the
   only paragraphs on the site long enough for the extra line spacing to earn its
   height. */
.article p {
  color: var(--ds-color-fg-default);
  line-height: 1.75;
  margin-top: 13px;
}

.article ul, .article ol {
  margin: 13px 0 0;
  padding-left: 19px;
  color: var(--ds-color-fg-default);
  line-height: 1.75;
}

.article li {
  margin-top: 7px;
}

/* The marker is the only accent in the column, and `::marker` is the property
   that carries it - colouring the `<li>` would take the text with it. */
.article li::marker {
  color: var(--ds-color-brand-accent);
}

/* `strong` steps UP to the heading white from the body's muted grey, at 600
   rather than the UA's 700 because the body face's bold is heavier than this
   design uses anywhere else. */
.article strong {
  color: var(--ds-color-fg-heading);
  font-weight: 600;
}

/* A quotation is the one prose element the legal pages never carry, so it has no
   measured form. It is composed from two things that are already the design's:
   the accent rule is the same colour and the same 1px hairline weight as
   `.pr span` and `_legal-page.scss`'s list separator, and the indent is the list
   indent. Deliberately NOT a type-size change - a pull quote that also resizes is
   a second decision, and this one is a blockquote in an editor. */
.article blockquote {
  margin: clamp(26px, 3vw, 38px) 0 0;
  padding-left: 19px;
  border-left: 2px solid var(--ds-color-brand-accent);
  color: var(--ds-color-fg-heading);
}

.article blockquote p {
  color: inherit;
}

/* ---------- the archive's pagination ----------
   THE DESIGN HAS NO PAGINATION COMPONENT AND THIS IS NOT A PORT. No prototype
   page paginates anything, so the rules below are composed from the type and
   colour tokens the rest of the design already uses - `.pr span`'s label face and
   tracking, `.toc a`'s hairline treatment. Said plainly because a reader who
   finds these in a file full of measured numbers should know which kind they are.

   IT DOES NOT RENDER TODAY. Nine articles across four categories against a
   `posts_per_page` of ten means `max_num_pages` is 1 on every archive the site
   currently has, and `paginate_links()` returns null there. The first time this
   is seen is after the client has been writing for a year, which is exactly when
   nobody will remember it was never looked at. */
/* Kept as the component's own statement of the gap; the rule that actually wins
   is the scoped pair at the foot of this file. */
.pager {
  margin-top: clamp(30px, 4vw, 46px);
}

.pager ul {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.pager a, .pager span {
  display: inline-block;
  font-family: var(--ds-font-label);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  padding: 9px 14px;
  color: var(--ds-color-fg-default);
  border: 1px solid rgba(var(--ds-color-channel-light), 0.09);
}

.pager a:hover {
  color: var(--ds-color-fg-heading);
  border-color: var(--ds-color-brand-accent);
}

/* The current page is not a link, so it gets the accent as a statement of place
   rather than as an affordance. Core emits it as `<span class="page-numbers current">`. */
.pager .current {
  color: var(--ds-color-fg-on-accent);
  background: var(--ds-color-brand-accent);
  border-color: var(--ds-color-brand-accent);
}

/* ---------- the way back off an archive ----------
   B-237. An archive is the one page on this site a reader can reach with no
   route onward: the four `#outline` cards and every article's category eyebrow
   lead INTO it, its tiles lead to articles, and the only exit was the crumb -
   12.5px of text at the top, above a grid the reader has just scrolled past.

   THE SAME `clamp` AS `.pager`, because this sits directly under whichever of
   the two is last and the gap above it should not change depending on whether
   the archive happens to paginate. With nine articles across four categories
   `paginate_links()` returns null on every current archive, so today this
   follows the grid and tomorrow it follows the pager, at the same distance.

   The button itself needs nothing here: `.btn.line` is the design's secondary
   action, styled in `_base.scss` on the class rather than on a wrapper, so a
   bare anchor in a render callback takes it whole.

   THE MARGIN HAS TO OUTRANK THE BLOCK BRIDGE, and the first version of this did
   not. `_news-page.scss` carries `.pg-news .sec>.shell>*{margin-block-start:0}`
   at (0,3,0) - the structural reset PORT-64 argues for - and the archive wears
   `pg-news`, so a bare `.archive-back{margin-top:...}` at (0,1,0) lost outright
   and the button rendered FLUSH against the grid. Measured, not guessed: the
   link's top and the grid's bottom were both 735, exactly equal, which is what
   sent the gate's "below the grid" row red on a page that looked almost right.

   `.pager` HAD THE SAME BUG AND NOBODY HAD SEEN IT, because
   `paginate_links()` returns null on every archive this site currently has, so
   the element never renders. It is fixed here rather than left for the day the
   client has written enough to paginate - which is precisely when nobody will
   remember this rule exists. Same cause, same weight, one line each. */
.pg-news .sec > .shell > .archive-back {
  margin-block-start: clamp(30px, 4vw, 46px);
}

.pg-news .sec > .shell > .pager {
  margin-block-start: clamp(30px, 4vw, 46px);
}

/* ---------- core block bridge ----------
   Core writes `margin-block-start:var(--wp--style--block-gap)` - a FIXED 26px -
   onto every child of a flow container through `:root :where(.is-layout-flow)>*`.

   THE BAND-LEVEL RESET IS ALREADY DONE AND IS DELIBERATELY NOT RESTATED HERE.
   `_news-page.scss` carries `.pg-news .sec>.shell>*` and `.pg-news .phead .shell>*`
   at (0,3,0), and these two pages wear `pg-news`, so `.artfig`, `.article`,
   `.sec-h`, `.press` and `.pager` are all covered by rules that already exist. A
   second copy here would be a second place to make the same edit, and the first
   person to change one and not the other would get a band that is exact at one
   width and 2px out at the other (PORT-64).

   WHAT IS *NOT* COVERED IS INSIDE `.article`, because `wp:post-content` is its
   own flow container: the paragraphs are children of `.entry-content`, not of
   `.shell`. This line is UNSCOPED AND WEAK ON PURPOSE, exactly as `.legal>*` is.
   `.article>*` is (0,1,0) - a universal selector contributes nothing - so it ties
   with core and wins on source order, while LOSING to `.article p`, `.article h2`
   and `.article ul` at (0,1,1). Any block type nobody has written a rule for
   falls back to zero instead of taking a 26px gap; the ones that have rules keep
   their measured numbers.

   A SCOPED VERSION OF THIS LINE WOULD BREAK THE PAGE. `.pg-news .article>*` is
   (0,2,0), which beats `.article p` at (0,1,1) on class count and would zero
   every paragraph's 13px lead-in - the article would set solid. The weaker
   selector is the correct one here, which is the opposite of the rule directly
   above it in `_news-page.scss`, and the difference is that this one has to lose
   to something. */
.article > * {
  margin-block-start: 0;
}

@font-face {
  font-family: "TGS Perfect Condensed";
  src: url("../fonts/tgs-perfect-condensed.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Aloevera Display UGC";
  src: url("../fonts/aloevera-display-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Aloevera Display UGC";
  src: url("../fonts/aloevera-display-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Aloevera Display UGC";
  src: url("../fonts/aloevera-display-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
