/* Phase 4a - About / News / Contact / Single templates.
   Everything here is new layout only - colours, type, .section, .wrap,
   .section__head, .eyebrow, .card, .btn and .tag all come from site.css
   unmodified. This file is enqueued only on those templates, never on
   front-page.php, template-services.php or template-knowledge.php, so
   nothing here can affect a page that already shipped. */

/* Screen-reader-only label. Copied from services.css's .u-sr rather than
   loading that whole stylesheet here for one utility class. */
.u-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── header over the dark masthead ──────────────────────
   Lifted from css/services.css, which is the treatment the client approved for
   Services and the Knowledge Hub: the bar is transparent over the page's dark
   masthead and flips to navy (`is-dark`, in css/hero-spider.css) once the
   masthead's last pixel goes under it. About, News and Contact were left on
   site.css's default instead - a white translucent strip carrying
   logo-for-header.svg, whose wordmark is white, so the lockup all but vanished
   and the bar read as a light band pasted over a dark masthead.

   Scoped to the pages that actually open on a dark plane - every template in
   this family, single.php included as of 2026-09-19 (it used to open on a
   plain light .section with no pagehead, which is what left it on the solid
   light bar with an unreadable white-on-white wordmark). The flip boundary is
   measured in js/pages.js.

   `:not(.is-dark)` and not a bare `.nav`, which is how css/services.css can
   write it: this selector is three classes deep, so without the exclusion it
   would outrank `.nav.is-dark` in css/hero-spider.css and the bar would stay
   transparent past the masthead instead of going navy. */
body:has(main > .pagehead:first-child) .nav:not(.is-dark) {
  background: transparent;
  backdrop-filter: none;
  border-bottom-color: transparent;

  --nav-ink: #ffffff;
  --nav-ink-2: rgba(255, 255, 255, 0.84);
  --nav-ink-3: rgba(255, 255, 255, 0.62);
  --nav-line: rgba(255, 255, 255, 0.26);
  --nav-accent: #ffffff;

  /* css/hero-spider.css gives every bar without `is-over-hero` a blue lift,
     drawn for a white plane. There is no plane here until the flip, so it
     hangs in the masthead as a halo. It returns with `is-dark`, which brings
     a black one. */
  box-shadow: none;
}

/* ...but only while nothing is moving. Transparent is right for the masthead
   at rest: the bar reads as a set of links floating on one plane. It stops
   being right on the first flick of the wheel, because what climbs under the
   bar is the masthead's own eyebrow and h1 - so the wordmark ends up sitting
   on the page title and the reader cannot tell which of the two they are
   looking at. Reported by the client 2026-09-11 against every page but the
   homepage.

   The homepage solved this a fortnight earlier with `is-scrolled` (see the
   note in css/hero-spider.css), and this is that answer brought across, same
   navy and same blur, so the two arrive at the same bar. js/pages.js now
   writes the class; it used to be read only on the homepage.

   No media query, where the homepage's version is capped at 1080px: the hero
   keeps an empty band at the top of the art, so a wide homepage bar has
   nothing to collide with. A masthead carries its title directly under the
   bar at every width, and the collision is as plain at 1280 as at 390.

   One class deeper than the rule above so it wins on the same file, and still
   `:not(.is-dark)` so the flip past the masthead takes over cleanly. */
body:has(main > .pagehead:first-child) .nav:not(.is-dark).is-scrolled {
  background: color-mix(in srgb, var(--blue-900) 92%, transparent);
  backdrop-filter: blur(14px);
  border-bottom-color: rgba(255, 255, 255, 0.12);
}

/* Navy on navy is not a button, and the ground is dark on both sides of the
   flip - so the primary cannot go back to blue in between. */
body:has(main > .pagehead:first-child) .nav:not(.is-dark) .btn--primary {
  background: #ffffff;
  color: var(--blue-900);
}
body:has(main > .pagehead:first-child) .nav:not(.is-dark) .btn--primary:hover {
  background: var(--blue-100);
}

/* ── plain dark masthead ──────────────────────────────────────────────
   Services and Knowledge Hub each earned a bespoke masthead (a canvas map,
   a radar). About, News and Contact do not carry that kind of content, so
   this is just .section--dark with a taller top pad and an h1 in place of
   the usual h2 - everything else (eyebrow colour, lead colour) is the
   section head's own rule, inherited as-is. */
/* The bar is fixed, so the first section of every page in this family has to
   clear it itself - services.css's .svm does the same.

   It clears the bar at its TALLEST, not at rest. --nav-h is the resting height,
   and the reader arriving at the top of the page never sees that: `is-at-top`
   grows the lockup by 32px on desktop and 14px on a phone, and this rule used
   to hope the clamp below absorbed it. On a short viewport the clamp sits on
   its floor, so most of what it was supposed to be spending on air went on the
   growth instead and the lockup came down on the eyebrow. --nav-h-top is the
   grown height, published beside --nav-h by js/pages.js; max() rather than a
   plain swap so a browser that never fires the observer still gets the resting
   height, and the fallbacks stand in for both before any of it runs.

   The floors went up with it (2.2 to 2.8, 3.4 to 4.2) on the client's
   instruction 2026-09-02 that the menu was sitting on the masthead type. The
   two changes are separate: one stops the overlap, the other is the air.

   The plain-section rule is the base case for a page whose first section is
   not a pagehead; every template in this family now opens on one, single.php
   included as of 2026-09-19, matching the static build's news-article.php
   (masthead added there 2026-09-02). The pagehead rule follows so it wins
   where both match. */
:root { --nav-clear: max(var(--nav-h-top, 9.5rem), var(--nav-h, 3.4rem)); }
main > .section:first-child { padding-top: calc(var(--nav-clear) + clamp(2.8rem, 5vh, 3.4rem)); }
.pagehead { padding-block: clamp(3.4rem, 9vh, 6rem) clamp(2.2rem, 6vh, 3.6rem); }
main > .pagehead:first-child { padding-top: calc(var(--nav-clear) + clamp(4.2rem, 9vh, 6rem)); }
.pagehead h1 { font-size: clamp(2.1rem, 4vw, 3.1rem); margin-block: 0.6rem 1rem; color: #fff; }
.pagehead__lead + .pagehead__lead { margin-top: 0.9rem; }

/* ── bulleted content lists ───────────────────────────────────────────
   Used for the ecosystem / knowledge / role lists on About. A square
   marker rather than a numeral: none of these are steps in a sequence. */
.info-list { list-style: none; margin: 1.4rem 0 0; padding: 0; display: grid; gap: 0.85rem; max-width: 68ch; }
.info-list li { position: relative; padding-left: 1.5rem; color: var(--ink-2); line-height: 1.55; }
.info-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--blue-600);
}
.section--dark .info-list li { color: rgba(255, 255, 255, 0.78); }
.section--dark .info-list li::before { background: var(--gold-300); }

/* ── mission / vision: the two statements ─────────────────────────────
   Redesigned 2026-09-04 on the client's note that the section read as
   boring, and moved with it from the foot of the page to directly under
   the masthead. It was two paragraph plates with a photo on the lid; it
   is now a diptych - the work the Center does for the region now, and the
   position that work is meant to reach - either side of a connector.

   Motion. Everything is spent off `--p`, the 0-1 js/scrub.js writes to
   .mv-grid, so the entrance is a function of scroll position and runs
   backwards on the way back up like every other figure on this page. The
   plates read it through `--lp`, which is the lag shape [data-lag] gives a
   row restated for two known children: the second plate starts 12% into
   the travel, so the pair arrives as a sequence rather than as one flat
   block. Nothing here has a duration except the hover states and the two
   ambient loops, and all of those stop under prefers-reduced-motion.

   The moving parts, in the order a reader meets them:
     the wash     two soft radials drifting behind the section
     the wipe     each photo opens downward from its top edge (clip-path)
     the sheen    a specular band crosses the photo once as the plate lands
     the numeral  01 and 02 rise into the photo's bottom edge
     the rule     a line draws under each label
     the spine    a hairline draws out from the centre of the gap, with a
                  gold node that keeps pulsing after it has landed
   ──────────────────────────────────────────────────────────────────── */

.mv { position: relative; overflow: hidden; }
/* The band the plates sit on. On a pseudo-element rather than on the
   section's own background, which .section owns, and inset well past the
   edges so the drift below never walks a hard edge into view. */
.mv::before {
  content: '';
  position: absolute;
  inset: -25% -12%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(38% 42% at 22% 28%,
      color-mix(in srgb, var(--c-azure) 14%, transparent), transparent 70%),
    radial-gradient(34% 40% at 80% 70%,
      color-mix(in srgb, var(--c-gold) 12%, transparent), transparent 70%);
  animation: mv-drift 26s ease-in-out infinite alternate;
}
.mv > .wrap { position: relative; z-index: 1; }
@keyframes mv-drift {
  from { transform: translate3d(-2.4%, -1.6%, 0) scale(1); }
  to   { transform: translate3d(2.4%, 1.6%, 0) scale(1.07); }
}

.mv-grid {
  position: relative;
  display: grid;
  gap: 2.4rem;
  grid-template-columns: 1fr;
  margin-top: 2.4rem;
}
@media (min-width: 820px) {
  .mv-grid { grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); }
}

/* The connector. It spans the whole grid and draws from the centre out,
   but the plates are opaque and sit above it, so the only length a reader
   ever sees is the one between them - the join. The node is a sibling and
   not a child: the spine is scaled, and a scaled parent would squash the
   diamond and its glow with it. */
.mv-grid__spine {
  position: absolute;
  z-index: 0;
  display: block;
  pointer-events: none;
  transform-origin: center;
}
@media (min-width: 820px) {
  .mv-grid__spine {
    inset-inline: 0;
    top: 50%;
    height: 1px;
    background: linear-gradient(90deg, transparent,
      var(--gold-500) 20%, var(--gold-500) 80%, transparent);
    transform: scaleX(var(--p, 1));
  }
}
@media (max-width: 819.98px) {
  .mv-grid__spine {
    inset-block: 0;
    left: 50%;
    width: 1px;
    margin-left: -0.5px;
    background: linear-gradient(180deg, transparent,
      var(--gold-500) 22%, var(--gold-500) 78%, transparent);
    transform: scaleY(var(--p, 1));
  }
}
.mv-grid__node {
  position: absolute;
  z-index: 0;
  left: 50%;
  top: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 2px;
  background: var(--gold-500);
  rotate: 45deg;
  opacity: var(--p, 1);
  animation: mv-node 3.6s ease-in-out infinite;
}
@keyframes mv-node {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--c-gold) 50%, transparent); }
  60%      { box-shadow: 0 0 0 10px color-mix(in srgb, var(--c-gold) 0%, transparent); }
}

.mv-card {
  position: relative;
  z-index: 1;
  overflow: hidden;
  /* A column, so the two plates can be the same height without the shorter
     statement leaving a hole under itself: the paragraph's `margin: auto`
     below spends whatever height it did not need above and below it. */
  display: flex;
  flex-direction: column;
  padding: 0 1.7rem 2.1rem;
  border-radius: var(--corner-box);
  background: var(--surface);
  border: 1px solid var(--line);
  text-align: center;
  box-shadow: 0 22px 50px -34px rgba(20, 30, 38, 0.4);
  opacity: var(--lp, 1);
  transition: border-color 0.35s var(--ease-out), transform 0.35s var(--ease-out),
              box-shadow 0.35s var(--ease-out);
}
/* The two plates close on the join from their own sides. `translate` and
   `transform` are separate properties on purpose: the arrival is scrubbed
   here and the hover lift below is a transition, and they compose instead
   of overwriting one another. */
.mv-card--now {
  --lp: clamp(0, calc(var(--p, 1) / 0.88), 1);
  translate: calc((1 - var(--lp, 1)) * -22px) 0;
}
.mv-card--next {
  --lp: clamp(0, calc((var(--p, 1) - 0.12) / 0.88), 1);
  translate: calc((1 - var(--lp, 1)) * 22px) 0;
}
/* The rule across the lid, .card::before's geometry in this section's gold. */
.mv-card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 2;
  height: 2px;
  background: var(--gold-500);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.45s var(--ease-out);
}
.mv-card:hover::before { transform: scaleX(1); }
.mv-card:hover {
  border-color: var(--blue-300);
  transform: translateY(-4px);
  box-shadow: 0 26px 56px -28px rgba(20, 30, 38, 0.42);
}

/* The photo, bled to the plate's padding the way site.css bleeds
   .card__photo - the inline numbers are that padding doubled, so if the
   padding above changes these change with it. It wipes open downward as
   the plate arrives, and the crop settles the last of a push-in behind
   the wipe rather than sitting still under it. */
.mv-card__frame {
  display: block;
  position: relative;
  overflow: hidden;
  width: calc(100% + 3.4rem);
  /* the global `img { max-width: 100% }` would clamp the bleed straight back */
  max-width: none;
  margin: 0 -1.7rem 1.2rem;
  aspect-ratio: 16 / 9;
  border-bottom: 1px solid var(--line-soft);
  /* the plate's own top corner, so the photo cannot square it off */
  border-radius: var(--r-box) 0 0 0;
  clip-path: inset(0 0 calc((1 - var(--lp, 1)) * 100%) 0);
}
.mv-card__photo {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center 42%;
  filter: saturate(calc(0.5 + var(--lp, 1) * 0.5));
  scale: calc(1.07 - var(--lp, 1) * 0.07);
  transition: transform 0.5s var(--ease-out);
}
.mv-card:hover .mv-card__photo { transform: scale(1.05); }
/* A scrim at the photo's foot, so the numeral that straddles the edge has
   something to sit on whatever the crop underneath is doing. */
.mv-card__frame::after {
  content: '';
  position: absolute;
  inset: 34% 0 0;
  background: linear-gradient(180deg, transparent,
    color-mix(in srgb, var(--c-navy) 78%, transparent));
}
/* One specular pass across the photo, spent over the plate's own arrival:
   it enters from the left as the wipe opens and is gone by the time the
   plate has landed. The opacity overshoots 1 deliberately - it holds full
   strength through the first half of the travel and then fades. */
.mv-card__sheen {
  position: absolute;
  top: -30%;
  bottom: -30%;
  width: 40%;
  left: calc(var(--lp, 1) * 170% - 60%);
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  transform: skewX(-14deg);
  opacity: calc((1 - var(--lp, 1)) * 1.6);
  pointer-events: none;
  z-index: 2;
}

/* 01 and 02, outlined rather than filled, standing on the photo's bottom
   edge over the scrim - which is why the stroke is --gold-300 and not the
   light ground's --gold-700. The fill is a faint gold as well as the
   stroke, so a browser without text-stroke shows a numeral rather than
   nothing. It is inside the frame, so the wipe passing over it is the
   last thing to arrive.

   `line-height: 1` and no descenders, so the box is the digits: the
   distance below is what the number sits off the edge. */
.mv-card__index {
  position: absolute;
  inset: auto 0 0.55rem 0;
  z-index: 1;
  font-size: clamp(2.2rem, 4.6vw, 3.1rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--c-gold) 26%, transparent);
  -webkit-text-stroke: 1px var(--gold-300);
  opacity: var(--lp, 1);
  translate: 0 calc((1 - var(--lp, 1)) * 14px);
}

.mv-card h3 {
  position: relative;
  /* shrink-wrapped rather than full width, so the rule under it is the
     length of the label - in a flex column that is `align-self`, because a
     flex item's `display: inline-block` is blockified away */
  align-self: center;
  margin: 0 0 0.95rem;
  padding-bottom: 0.55rem;
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-700);
  opacity: var(--lp, 1);
}
.mv-card h3::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 2px;
  background: var(--blue-600);
  transform: scaleX(var(--lp, 1));
}
.mv-card > p {
  margin: auto;
  max-width: 46ch;
  color: var(--ink-2);
  font-size: 1.05rem;
  line-height: 1.65;
  opacity: var(--lp, 1);
  translate: 0 calc((1 - var(--lp, 1)) * 10px);
}

/* Reduced motion: the diptych keeps its shape, its scrim and its hover
   colours, and loses every part of the arrival. The scrub already parks
   `--p` at 1 under this preference, but a stale 0 reaching the wipe or the
   sheen would read as broken rather than as still, so both are stated. */
@media (prefers-reduced-motion: reduce) {
  .mv::before,
  .mv-grid__node { animation: none; }
  .mv-card,
  .mv-card__index,
  .mv-card h3,
  .mv-card > p { opacity: 1; translate: none; }
  .mv-card__frame { clip-path: none; }
  .mv-card__photo { filter: none; scale: 1; }
  .mv-card__sheen { display: none; }
  .mv-grid__spine,
  .mv-card h3::after { transform: none; }
  .mv-card:hover { transform: none; }
  .mv-card:hover .mv-card__photo { transform: none; }
}

/* ── contact details + form-slot placeholder ──────────────────────────
   Two plates side by side once there is room: the org's own details on
   the left (sourced from the options page, never duplicated here as
   literal text) and the enquiry-form placeholder on the right. */
.contact-grid { display: grid; gap: 1.6rem; grid-template-columns: 1fr; margin-top: 0.4rem; align-items: start; }
@media (min-width: 900px) { .contact-grid { grid-template-columns: 1fr 1.15fr; } }
.contact-card { padding: 1.8rem 1.7rem; border: 1px solid var(--line); border-radius: var(--corner-box); background: var(--surface); }
.contact-card h3 { font-size: 1.05rem; margin: 0 0 1.1rem; }
.contact-card ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.9rem; }
.contact-card li { display: flex; gap: 0.65rem; align-items: flex-start; color: var(--ink-2); line-height: 1.5; }
.contact-card__icon { width: 20px; height: 20px; flex: none; margin-top: 0.15em; color: var(--blue-600); }
.contact-card a { color: var(--blue-700); text-decoration: none; }
.contact-card a:hover { text-decoration: underline; }

.formslot {
  padding: 2rem 1.8rem;
  border: 1px dashed var(--line);
  border-radius: var(--corner-box);
  background: var(--surface-2);
  text-align: center;
  display: grid;
  gap: 0.7rem;
  justify-items: center;
}
.formslot__tag {
  display: inline-block;
  padding: 0.28rem 0.75rem;
  border-radius: var(--corner-chip);
  background: var(--gold-300);
  color: var(--c-navy);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.formslot p { color: var(--ink-2); margin: 0; max-width: 42ch; }

/* ── news index card grid ─────────────────────────────────────────────
   The cards on home.php are the homepage's .pub, grid and all (css/site.css),
   so nothing about their look lives here any more. The bespoke .news-grid and
   .newscard rules that used to stand in this place were removed on 2026-09-02
   on the client's instruction that the two should be one design. What is left
   further down is the filter's own choreography, which is this page's alone. */

/* ── single article ───────────────────────────────────────────────────*/
.article { max-width: 72ch; margin-inline: auto; }
.article__meta { display: flex; gap: 0.9rem; align-items: center; color: var(--ink-3); font-size: 0.9rem; margin: 0 0 1.3rem; flex-wrap: wrap; }
/* `height: auto` is load-bearing, not tidiness. single.php prints the image
   with the_post_thumbnail(), which always emits width and height attributes,
   and an HTML height attribute is a presentational hint the UA applies as a
   used height - so without this every article photo is squashed or stretched
   to whatever height the source file happened to have. The three-ring frame
   below is the client's instruction of 2026-09-02. */
.article__photo {
  width: 100%;
  height: auto;
  border-radius: var(--corner-box);
  border: 1px solid var(--line);
  box-shadow:
    0 0 0 5px var(--blue-50),
    0 0 0 6px var(--line-soft),
    0 24px 48px -34px color-mix(in srgb, var(--c-navy) 40%, transparent);
  margin: 0 0 2.4rem;
}
.article__body { color: var(--ink-2); font-size: 1.05rem; line-height: 1.75; display: grid; gap: 1.1rem; }
.article__body p { margin: 0; }

/* single.php runs the_content() into this box, so an editor can put anything
   the block editor offers inside it - and the box is `display: grid` with no
   list padding, which left a heading unstyled and a bullet list with its
   markers outside the measure. These are the elements editorial actually
   reaches for; the grid gap sets the rhythm, so the margins here are only the
   extra a heading needs over a paragraph. */
.article__body h2 {
  margin: 1rem 0 0;
  font-size: 1.4rem;
  color: var(--ink);
}
.article__body h3 {
  margin: 0.4rem 0 0;
  font-size: 1.1rem;
  color: var(--ink);
}
.article__body ul {
  margin: 0;
  padding-inline-start: 1.4rem;
  display: grid;
  gap: 0.4rem;
}
.article__body li { padding-inline-start: 0.2rem; }
.article__body a { color: var(--c-blue); }

/* The Cookie Policy's cookie register - the one table in the legal copy. It
   scrolls inside its own box rather than widening the 72ch measure, and the
   wrapper carries role="group" in the markup so the scroll box is reachable
   from the keyboard. `text-align: start` rather than left, so the Arabic page
   needs no flip in rtl.css. */
.legal-table { overflow-x: auto; }
.legal-table table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.legal-table th, .legal-table td {
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--line);
  text-align: start;
  vertical-align: top;
}
.legal-table th {
  background: var(--surface-2);
  color: var(--ink);
  font-weight: 600;
}

.article__back { margin-top: 2.6rem; }

/* ── the masthead's heading and second lede ───────────────────────────────────
   BUG FIX, 2026-09-01. Two faults in one component, both above 900px only,
   which is why the masthead looked fine on a phone.

   1. site.css places `.section__head--wide h2`. Every masthead uses an h1, so
      the heading was never placed and auto-flowed into the NARROW 1fr column,
      leaving the wide 1.35fr column holding nothing but the eyebrow. site.css's
      own comment says what --wide is for: "heading in a 1.35fr column with the
      lede beside its baseline". Placing h1 the way h2 is placed restores the
      arrangement the component was written for.

   2. site.css places EVERY non-eyebrow paragraph in ONE cell:
        .section__head--wide > p:not(.eyebrow) { grid-column: 2; grid-row: 2 }
      Right for one lede beside a heading. The About masthead has TWO, so both
      landed in column 2 / row 2 and rendered ON TOP of each other, illegibly.
      .pagehead__lead + .pagehead__lead's margin cannot separate them, because
      grid placement, not flow, positions them.

   Row 3 is explicit and deliberate. `grid-row: auto` does NOT work here, and the
   reason is worth writing down: auto-placement fills the EARLIEST free cell, and
   column 2 row 1 is free (the eyebrow only occupies column 1), so the second
   lede jumped ABOVE the first instead of below it - measured, not guessed. The
   eyebrow is pinned to row 1 for the same reason, so nothing can be dragged into
   an implicit row later. A THIRD lede would need a row of its own; ACF gives
   exactly two, and the PHP build is written to match.

   Fixed here rather than in site.css because site.css is shipped and every
   section head on every page depends on those two rules. Scoped to .pagehead, so
   only the mastheads change: About (both faults) and news, contact and the
   listing form (fault 1 only, heading moves into the wide column).
   The rules this works around are at site.css:4256-4257 in this theme. The same
   block is in the PHP-only build's php/css/pages.css: the two builds share this
   component, so a change to one belongs in the other. */
@media (min-width: 900px) {
  .pagehead .section__head--wide .eyebrow {
    grid-row: 1;
  }
  .pagehead .section__head--wide h1 {
    grid-column: 1;
    grid-row: 2;
  }
  .pagehead .section__head--wide > .pagehead__lead + .pagehead__lead {
    grid-row: 3;
  }
}

/* ── the masthead, centred ────────────────────────────────────────────────────
   Set by the client 2026-09-02: the section heads on these pages read as one
   centred block, not as a title in one column with its description in another.
   --center already stacks the asymmetric head back into a single column, but
   the placement above still applies underneath it, and in a one-column grid two
   ledes both pinned to row 3 land in the same cell - the exact overlap the
   2026-09-01 fix was written to remove. So each part gets a row of its own here.
   Same specificity as the rules above, later in the file, which is what decides
   it. */
@media (min-width: 900px) {
  .pagehead .section__head--center h1 {
    grid-column: 1;
    grid-row: 2;
  }
  .pagehead .section__head--center > .pagehead__lead {
    grid-column: 1;
    grid-row: 3;
  }
  .pagehead .section__head--center > .pagehead__lead + .pagehead__lead {
    grid-row: 4;
  }
  /* The back link, on the login, portal and listing-form mastheads. Third
     fault of the same family, found 2026-09-09: site.css:4493 pins EVERY
     non-eyebrow paragraph in this head to `grid-row: 3`, and .pagehead__back
     is a paragraph, so it landed in the lede's cell and rendered on top of it.
     forms.css:19's `margin-top: 1.4rem` cannot separate them - grid placement,
     not flow, is what positions both. Row 5 rather than 4 because a second
     lede owns row 4 and ACF gives exactly two.

     Only below 900px does this look right without help: the placement above
     lives in a media query, so on a phone the two are in normal flow and the
     margin does its job. This is a desktop-only defect.

     Scoped to --center because that is what all three of those templates
     carry. The --wide block above has the same hole for a back link, but no
     template pairs --wide with one, so it is left alone rather than fixed
     speculatively. */
  .pagehead .section__head--center > .pagehead__back {
    grid-column: 1;
    grid-row: 5;
  }
}

/* Wider than the 46ch a section lede gets, because a masthead lede is a
   paragraph of body copy and About's runs three sentences. UNCONDITIONAL, and
   that is the point: the 46ch cap applies at every width, so scoping this to
   900px and up left the phone and tablet ledes as exactly the narrow ribbon it
   was written to avoid - 441.6px of text inside an 809px head, measured at 899. */
.pagehead .section__head--center > .pagehead__lead { max-width: 62ch; }

/* ═══════════════ the new pages: ground, entrance, interaction ═══════════════
   About and News were correct but inert: flat grounds, one reveal per section,
   and nothing that answered a cursor. This block gives them the same texture
   and the same motion vocabulary the rest of the site already uses. Nothing
   here invents a new mechanism - the ground is site.css's .imgband, the reveal
   progress is js/scrub.js's `--p`, the depth is js/parallax-scroll.js's `--pv`,
   and the filter chips are the Knowledge Hub's own .kh__filter pills. */

/* ── the masthead's entrance ──────────────────────────────────────────
   A masthead is above the fold, so js/scrub.js cannot drive it: `--p` is a
   scroll-progress value and there is no scroll yet. A keyframed entrance is the
   right tool for exactly this one case, and it is the only animation on either
   page that is not scroll-driven.

   `both` matters. Without it the element is at its natural opacity for the
   length of the delay, so the line would flash in, vanish, then rise - the fill
   holds it at the from-state until its turn. */
@keyframes ph-rise {
  from { opacity: 0; translate: 0 14px; }
  to   { opacity: 1; translate: 0 0; }
}
/* Opt-in, not opt-out: with no-preference the animation is never declared for a
   reader who asked for less motion, rather than declared and then cancelled. */
@media (prefers-reduced-motion: no-preference) {
  .pagehead .eyebrow,
  .pagehead h1,
  .pagehead .pagehead__lead { animation: ph-rise 0.85s var(--ease-out) both; }
  .pagehead .eyebrow { animation-delay: 0.06s; }
  .pagehead h1 { animation-delay: 0.16s; }
  .pagehead .pagehead__lead { animation-delay: 0.30s; }
  .pagehead .pagehead__lead + .pagehead__lead { animation-delay: 0.40s; }
}

/* ── depth on the banded grounds ──────────────────────────────────────
   js/parallax-scroll.js writes `--pv` (0 at the bottom edge of the viewport, 1
   at the top) to every [data-parallax]. The 0.5 offset is what makes the band
   sit at rest when it is centred, so the art drifts through rather than starting
   displaced.

   `translate` and not `transform`: .imgband::before is already running
   band-drift on `transform`, and the two are separate properties, so this
   composes with the drift instead of overwriting it. The negative inset is the
   bleed that pays for the travel - without it a 70px drift would expose the
   band's own edge, since background-size: cover leaves nothing spare. */
.imgband[data-parallax]::before {
  inset: -80px 0;
  translate: 0 calc((0.5 - var(--pv, 0.5)) * -70px);
}

/* ── the info lists ───────────────────────────────────────────────────
   The list revealed as one block, which wasted the one thing a list is good at:
   arriving in order. js/scrub.js puts `--p` on the <ul> and custom properties
   inherit, so every <li> can read its parent's progress and offset itself
   against it - one scrub subscriber, seven staggered items, no extra JS.

   `--i` comes from :nth-child rather than an inline style on every item,
   because these lists are hand-written markup in two builds and a per-item
   attribute is two places to forget.

   The clamp normalises against (1 - --lag) so every item still reaches 1 when
   `--p` does; dividing by a fixed ramp instead would leave the last items
   permanently short of full opacity. --lag is capped so a long list does not
   push its tail off the end of the scroll window. If the JS never runs, `--p`
   falls back to 1 and the whole list is simply visible. */
.info-list li {
  --lag: min(calc(var(--i, 0) * 0.07), 0.45);
  --lp: clamp(0, calc((var(--p, 1) - var(--lag)) / (1 - var(--lag))), 1);
  opacity: var(--lp);
  translate: 0 calc((1 - var(--lp)) * 9px);
  transition: color 0.3s var(--ease-out);
}
.info-list li:nth-child(1) { --i: 0; }
.info-list li:nth-child(2) { --i: 1; }
.info-list li:nth-child(3) { --i: 2; }
.info-list li:nth-child(4) { --i: 3; }
.info-list li:nth-child(5) { --i: 4; }
.info-list li:nth-child(6) { --i: 5; }
.info-list li:nth-child(7) { --i: 6; }
.info-list li:nth-child(8) { --i: 7; }
.info-list li:nth-child(9) { --i: 8; }
.info-list li:nth-child(10) { --i: 9; }
@media (prefers-reduced-motion: reduce) {
  .info-list li { opacity: 1; translate: none; }
}

/* The marker answers the cursor. A list of capabilities is the part of About a
   reader scans rather than reads, so the row under the pointer earns the
   stronger ink and the marker grows into it. transform on the marker only -
   moving the row itself would fight the stagger's own translate. */
.info-list li::before {
  transition: transform 0.3s var(--ease-out), background-color 0.3s var(--ease-out);
}
.info-list li:hover { color: var(--ink); }
.info-list li:hover::before { transform: scale(1.45); }
.section--dark .info-list li:hover { color: #fff; }
.section--dark .info-list li:hover::before { background: var(--gold-200, var(--gold-300)); }

/* The .mv-card hover block that used to stand here - a copy of .card's lift,
   with the lid rule on ::after - was folded into the diptych block further up
   when the section was redesigned on 2026-09-04. The lid rule is ::before there;
   nothing on the plate itself needs ::after any more. */

/* ── the news grid ────────────────────────────────────────────────────
   The grid and the cards are the homepage's, so their entrance is the
   homepage's too: each .pub is its own `.scrub`, revealed by js/scrub.js. The
   `--i` opacity cascade that used to live here went with the .newscard it was
   written for.

   What stays is the filter's re-entrance. `--d` is written by initNewsFilter
   when a chip changes what is showing, so a filtered grid cascades from its new
   first card rather than keeping the gaps the hidden ones left. It runs only
   after a press - the scroll reveal is finished by then, because a chip cannot
   be pressed before the grid has been scrolled to - so the two never overlap. */
/* .pub declares `display: block`, and an author declaration beats the browser's
   own [hidden] rule - so without this a filtered-out card would keep its place
   in the grid while the JS believed it had put it away. */
.pub[hidden] { display: none; }

@keyframes card-in {
  from { opacity: 0; translate: 0 10px; }
  to   { opacity: 1; translate: 0 0; }
}
@media (prefers-reduced-motion: no-preference) {
  [data-news-grid].is-filtered > .pub:not([hidden]) {
    animation: card-in 0.5s var(--ease-out) both;
    animation-delay: var(--d, 0s);
    opacity: 1;
  }
}

/* The chip strip. .kh__filters / .kh__filter / .kh__filter-pill are site.css's
   Knowledge Hub filter, reused wholesale: same pill, same sliding indicator,
   same aria-pressed contract. Only the spacing above the grid is local. */
.news-filters { margin: 1.6rem 0 1.5rem; }
.news-filters:empty { display: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE ABOUT PAGE'S FIGURES
   Added 2026-09-01 on the client's instruction: no section on About is to read
   as a block of text - each one carries a diagram or something the reader can
   point at, and everything arrives on scroll.

   Four components, and every label in all four is a phrase already in the
   page's own copy. Nothing here invents a fact about the Center:

     .flowmap   the operating model as what comes in, the hub, and what goes
                out - the two things the model connects and the four things the
                body paragraph says it produces.
     .ecoweb    the six stakeholder groups either side of a hub plate, with the
                strands drawn by js/about-art.js from the measured positions.
     .maturity  the five kinds of access the SME paragraph lists, as a rail,
                because the paragraph ends on a journey.
     .res-grid  a list of many short items as plates rather than bullets. Used
                twice, on the light ground for the library and on the dark band
                for the role, which is why the dark rules are on .section--dark
                rather than on a modifier.

   Motion. Everything is driven off `--p`, the 0-1 the scrub writes to the
   container, so it is a function of scroll position rather than an animation
   with its own clock - see js/scrub.js. `[data-lag]` gives each child its own
   share of that progress so a row arrives in sequence off one subscriber; the
   `--i` rules below are what a stylesheet can offer instead of counting
   children in JS. Twelve of them: the theme build fills three of these lists
   from repeater fields, and past twelve the lag is capped anyway.
   ═══════════════════════════════════════════════════════════════════════════ */

[data-lag] > * {
  /* `--lead` is a head start for the whole group, set on the [data-lag] element
     itself, so one row can wait for another without every child needing its own
     offset. It is 0 unless a component asks for it. */
  --lag: min(calc(var(--lead, 0) + var(--i, 0) * var(--lag-step, 0.075)), 0.45);
  --lp: clamp(0, calc((var(--p, 1) - var(--lag)) / (1 - var(--lag))), 1);
}
[data-lag] > *:nth-child(1)  { --i: 0; }
[data-lag] > *:nth-child(2)  { --i: 1; }
[data-lag] > *:nth-child(3)  { --i: 2; }
[data-lag] > *:nth-child(4)  { --i: 3; }
[data-lag] > *:nth-child(5)  { --i: 4; }
[data-lag] > *:nth-child(6)  { --i: 5; }
[data-lag] > *:nth-child(7)  { --i: 6; }
[data-lag] > *:nth-child(8)  { --i: 7; }
[data-lag] > *:nth-child(9)  { --i: 8; }
[data-lag] > *:nth-child(10) { --i: 9; }
[data-lag] > *:nth-child(11) { --i: 10; }
[data-lag] > *:nth-child(12) { --i: 11; }

/* ── the operating model ─────────────────────────────────────────────────── */

.flowmap { display: grid; gap: clamp(1.2rem, 3vw, 2.2rem); margin-top: 2rem; }
@media (min-width: 980px) {
  /* The columns are top-aligned and the hub is the one thing centred: with
     everything centred, a two-chip column and a four-chip column put their
     labels at two different heights and the figure reads as misaligned. */
  /* Everything sits on the figure's middle line. The columns were top-aligned
     so the two labels would agree, but a two-chip column beside a four-chip one
     then left a hole under the short side, and the connectors - which leave the
     hub at its own centre - crossed that hole pointing at nothing. Centred, the
     labels sit at two heights and the figure reads as one object. */
  .flowmap { grid-template-columns: 1fr minmax(210px, 0.95fr) 1fr; align-items: center; }
}
.flowmap__col { display: grid; gap: 0.7rem; align-content: center; }
.flowmap__label {
  margin: 0 0 0.15rem;
  text-align: center;
  font-size: var(--fs-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  opacity: var(--lp, 1);
}
.flowmap__chip {
  margin: 0;
  text-align: center;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: var(--corner-chip);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: 1.45;
  opacity: var(--lp, 1);
  translate: 0 calc((1 - var(--lp, 1)) * 8px);
  transition: border-color 0.3s var(--ease-out), background-color 0.3s var(--ease-out),
              color 0.3s var(--ease-out), transform 0.3s var(--ease-out);
}
.flowmap__chip:hover {
  border-color: var(--blue-300);
  background: var(--surface);
  color: var(--ink);
  transform: translateY(-2px);
}
/* The figure reads left to right - what the model connects, then the hub, then
   what it produces - so the out column waits for the in column to land before
   it starts. The lead is on the column, so its label travels with its own chips
   rather than in step with the other column's; the tighter step keeps four
   chips from outrunning the section's scroll window. */
.flowmap__col--out { --lead: 0.16; --lag-step: 0.06; }

.flowmap__hub {
  position: relative;
  padding: 1.5rem 1.4rem 1.35rem;
  border-radius: var(--corner-box);
  background: var(--blue-900);
  border-top: 2px solid var(--gold-500);
  color: #fff;
  text-align: center;
  /* Its own share of the progress, on the same shape as a chip's `--lp`, so it
     lands between the two columns. It used to run at 1.6x the figure's progress
     with no lag at all, which put the plate on screen before a single chip had
     appeared. */
  --hp: clamp(0, calc((var(--p, 1) - 0.08) / 0.92), 1);
  translate: 0 calc((1 - var(--hp)) * 12px);
  /* Still a touch ahead of its own lag, so the plate is solid at about the
     point the first column is, rather than sitting half-opaque - and so a
     near-black card on a white ground reads as grey - beside chips that have
     already landed. */
  opacity: clamp(0, calc(var(--hp) * 1.4), 1);
}
.flowmap__hub-name { margin: 0; font-size: var(--fs-md); font-weight: 600; letter-spacing: -0.01em; }
.flowmap__hub-role { margin: 0.35rem 0 0; font-size: var(--fs-sm); color: rgba(255, 255, 255, 0.72); text-wrap: balance; }
.ecoweb__hub-name, .flowmap__hub-name { text-wrap: balance; }
.flowmap__tags { display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center; margin: 1rem 0 0; padding: 0; list-style: none; }
.flowmap__tags li {
  padding: 0.3rem 0.6rem;
  border: 1px solid rgba(164, 223, 245, 0.35);
  border-radius: 999px;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-300);
}
/* The two connectors, drawn only where there are three columns to connect.
   Travelling dashes rather than a plain rule: the section's claim is that the
   hub carries traffic both ways, and a static line says only that the columns
   are near each other. */
@media (min-width: 980px) {
  .flowmap__hub::before,
  .flowmap__hub::after {
    content: '';
    position: absolute;
    top: 50%;
    width: clamp(1rem, 2.6vw, 2.2rem);
    height: 2px;
    background: repeating-linear-gradient(90deg, var(--blue-300) 0 5px, transparent 5px 11px);
    /* last, and only once the hub and the first chips are solid: a travelling
       dash between two things that are still fading in reads as a loose thread */
    opacity: calc(clamp(0, calc((var(--p, 1) - 0.45) / 0.3), 1) * 0.85);
  }
  .flowmap__hub::before { right: 100%; }
  .flowmap__hub::after { left: 100%; }
  @media (prefers-reduced-motion: no-preference) {
    .flowmap__hub::before,
    .flowmap__hub::after { animation: flow-dash 0.9s linear infinite; }
  }
}
@keyframes flow-dash { to { background-position-x: 11px; } }

/* ── the ecosystem web ───────────────────────────────────────────────────────
   The grid is the figure: the hub takes the middle column and every row, and
   js/about-art.js puts each node in column 1 or 3 and on its own row. The
   node count is not fixed, so the placement is inline style from that file and
   not nth-child rules here - see its header. Until it runs, and under the
   two-column threshold, this is a hub plate above a plain list. */

.ecoweb { position: relative; margin-top: 2rem; }
.ecoweb__strands {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 0;
}
.ecoweb__strand {
  fill: none;
  stroke: rgba(164, 223, 245, 0.3);
  stroke-width: 1.25;
  stroke-linecap: round;
  /* pathLength is 1, so one dash covers the whole strand and the offset is the
     fraction still undrawn: the web draws itself as the section arrives */
  stroke-dasharray: 1;
  stroke-dashoffset: calc(1 - var(--p, 1));
  /* Paint only. The lit strand used to thicken to 2 as well, and the animated
     width was the whole of what a reader could not point at: the highlight is
     an opacity step from 0.22 to 1 and a pale blue turning gold, and 0.75px of
     extra stroke underneath that is below the noise floor. Dropped 2026-09-11
     so nothing here animates a property with "width" in its name - stroke-width
     reflows nothing in the document, but it costs the web nothing to be plainly
     paint-only rather than to need that explained. */
  transition: stroke 0.35s var(--ease-out), opacity 0.35s var(--ease-out);
}
.ecoweb__strands.is-lit .ecoweb__strand { opacity: 0.22; }
.ecoweb__strand.is-on { opacity: 1; stroke: var(--gold-300); }
.ecoweb__mote { fill: var(--gold-300); opacity: calc(var(--p, 1) * 0.9); }

.ecoweb__hub {
  position: relative;
  z-index: 1;
  padding: 1.35rem 1.2rem;
  text-align: center;
  border: 1px solid rgba(164, 223, 245, 0.38);
  border-radius: var(--corner-box);
  background: rgba(20, 30, 38, 0.62);
  translate: 0 calc((1 - var(--p, 1)) * 10px);
}
.ecoweb__hub-name { margin: 0; color: #fff; font-size: var(--fs-md); font-weight: 600; }
.ecoweb__hub-role {
  margin: 0.35rem 0 0;
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--blue-300);
}
.ecoweb__nodes { list-style: none; margin: 1.2rem 0 0; padding: 0; display: grid; gap: 0.7rem; }
.ecoweb__node {
  position: relative;
  text-align: center;
  z-index: 1;
  padding: 0.7rem 0.95rem;
  border: 1px solid rgba(164, 223, 245, 0.22);
  border-radius: var(--corner-chip);
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.82);
  font-size: var(--fs-sm);
  line-height: 1.45;
  opacity: var(--lp, 1);
  /* No translate and no scale on hover, deliberately: the strands are drawn
     from the resting layout box, so anything that moves a node visually would
     slide it off the end of its own strand. Colour and ring only. */
  transition: border-color 0.3s var(--ease-out), background-color 0.3s var(--ease-out),
              color 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}
.ecoweb__node:hover {
  border-color: var(--gold-300);
  background: rgba(164, 223, 245, 0.12);
  color: #fff;
  box-shadow: 0 0 0 1px rgba(218, 165, 32, 0.35);
}
@media (min-width: 720px) {
  .ecoweb[data-web='on'] { display: grid; grid-template-columns: 1fr minmax(190px, 250px) 1fr; column-gap: clamp(1.6rem, 5vw, 3.6rem); row-gap: 0.7rem; align-items: center; }
  /* the row span is inline style from js/about-art.js - it depends on the node
     count, and `1 / -1` cannot reach rows the grid never declared */
  .ecoweb[data-web='on'] .ecoweb__hub { grid-column: 2; }
  /* the list box is removed so its items become items of the figure's own grid;
     role="list" in the markup is what keeps the semantics through that */
  .ecoweb[data-web='on'] .ecoweb__nodes { display: contents; }
  .ecoweb[data-web='on'] .ecoweb__node[data-side='a'] { grid-column: 1; }
  .ecoweb[data-web='on'] .ecoweb__node[data-side='b'] { grid-column: 3; }
}

/* ── the people plates (the two committees) ───────────────────────────────
   Added 2026-09-19 with the establishment sections. Two lists of the same
   shape - a portrait, a name, a title, sometimes a ministry, and the member
   state - so one component renders both.

   GOIC supplied the portraits as transparent cutouts on a square canvas, all
   framed identically: the head centred, the shoulders on the bottom edge. A
   disc is what that framing is for, and it also means the cards do not have to
   carry a 380px-wide photo each - fifteen of those in a column would be a wall
   of faces rather than a list of people. The disc has a ground of its own
   because the artwork has no background: on bare white the cutouts would float.

   Flex-wrapped and centre-justified, not a grid, for the reason .res-grid
   gives: seven and eight plates over three columns each leave a short last row,
   and a grid parks it against the inline start. */
.people-grid {
  list-style: none;
  margin: 2.1rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;
}
.person {
  position: relative;
  flex: 0 1 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 1.2rem 1.5rem;
  border: 1px solid var(--line);
  border-radius: var(--corner-box);
  background: var(--surface);
  opacity: var(--lp, 1);
  translate: 0 calc((1 - var(--lp, 1)) * 12px);
  transition: border-color 0.35s var(--ease-out), transform 0.35s var(--ease-out),
              box-shadow 0.35s var(--ease-out);
}
@media (min-width: 620px) {
  .person { flex: 0 1 calc(50% - 0.5rem); }
}
@media (min-width: 980px) {
  .person { flex: 0 1 calc(33.3333% - 0.6667rem); }
}
.person:hover {
  border-color: var(--blue-300);
  transform: translateY(-3px);
  box-shadow: 0 18px 40px -24px rgba(20, 30, 38, 0.35);
}

.person__frame {
  position: relative;
  display: block;
  flex: none;
  width: clamp(7.2rem, 17vw, 9rem);
  aspect-ratio: 1;
  margin: 1.7rem 0 1.1rem;
  border-radius: 50%;
  /* The ground the cutout stands on. A soft vertical wash rather than a flat
     tint, so the white thobes at the foot of most of these portraits still
     have an edge to read against. */
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--c-mist) 26%, #fff) 0%,
              color-mix(in srgb, var(--c-mist) 62%, #fff) 100%);
  box-shadow: inset 0 0 0 1px var(--line-soft);
  /* Deliberately not `overflow: hidden`. The flag below is a child of this box
     and sits on the disc's edge, which is outside a circular clip - clipping
     here would eat most of it. The photo carries its own radius instead, and
     nothing else in the box can overflow it. */
  transition: box-shadow 0.35s var(--ease-out);
}
.person:hover .person__frame { box-shadow: inset 0 0 0 1px var(--blue-300); }
.person__photo {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  /* The source is square and so is the box, so `cover` crops nothing; the
     radius takes the corners off. */
  object-fit: cover;
}
/* The member state's flag, on the disc's trailing edge. Decorative - the state
   is written out below it - and the same 5:3 frame the country cards use, so
   Qatar's 3:2 artwork covers rather than letterboxes. See js/flags.js. */
.person__flag {
  position: absolute;
  inset-inline-end: 4%;
  bottom: 6%;
  width: 30%;
  max-width: 34px;
  aspect-ratio: 5 / 3;
  overflow: hidden;
  border-radius: var(--corner-chip);
  box-shadow: 0 0 0 2px var(--surface), 0 3px 8px -3px rgba(20, 30, 38, 0.45);
}
.person__flag img { display: block; width: 100%; height: 100%; object-fit: cover; }

.person__body { display: block; }
.person__name {
  display: block;
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
}
.person__role {
  display: block;
  margin-top: 0.45rem;
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-2);
}
.person__org {
  display: block;
  margin-top: 0.3rem;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--ink-3);
}
.person__place {
  display: block;
  margin-top: 0.7rem;
  font-size: var(--fs-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-700);
}

/* The Secretary General chairs the Industrial Cooperation Committee and the
   deck gives him his own row above the six ministers. The plate spans the grid
   and turns on its side once there is width for it. */
.person--lead { flex: 0 1 100%; }
@media (min-width: 760px) {
  .person--lead {
    flex-direction: row;
    align-items: center;
    gap: clamp(1.4rem, 3vw, 2.4rem);
    text-align: start;
    padding: 1.6rem clamp(1.4rem, 4vw, 2.6rem);
  }
  .person--lead .person__frame { margin: 0; width: clamp(8rem, 13vw, 10rem); }
  .person--lead .person__name { font-size: clamp(1.25rem, 1.8vw, 1.5rem); }
  .person--lead .person__role { max-width: 46ch; }
}

/* ── the founding team ──────────────────────────────────────────────────── */

/* One column at every width, so the groups read as stacked rows. Group sizes
   vary a lot - the General Secretariat is one person, the technical team is
   nine, GOIC is five - so side by side left uneven plates; stacked, each gets
   the full measure and the order reads as the sequence it is. */
.teamlist { display: grid; gap: 1rem; margin-top: 2.1rem; }
.teamgroup {
  padding: 1.6rem clamp(1.3rem, 3vw, 1.9rem) 1.7rem;
  border: 1px solid var(--line);
  border-radius: var(--corner-box);
  background: var(--surface);
  opacity: var(--lp, 1);
  translate: 0 calc((1 - var(--lp, 1)) * 10px);
}
.section--dark .teamgroup {
  border-color: rgba(164, 223, 245, 0.2);
  background: rgba(255, 255, 255, 0.05);
}
.teamgroup__org {
  margin: 0;
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
}
.section--dark .teamgroup__org { color: #fff; }
.teamgroup__rows {
  counter-reset: tm;
  list-style: none;
  margin: 1.1rem 0 0;
  padding: 0;
}
.teamgroup__row {
  counter-increment: tm;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 0.85rem;
  padding: 0.75rem 0;
  border-top: 1px solid var(--line-soft);
}
.section--dark .teamgroup__row { border-top-color: rgba(164, 223, 245, 0.16); }
.teamgroup__row::before {
  content: counter(tm, decimal-leading-zero);
  grid-row: 1 / span 2;
  align-self: center;
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  color: var(--gold-700);
}
.section--dark .teamgroup__row::before { color: var(--gold-300); }
/* Full width, the stacked name-over-role leaves most of the plate empty, so
   from here the row opens out into the deck's own shape: number, name, title.
   The counter stops spanning two rows because there is now only one. */
@media (min-width: 720px) {
  .teamgroup__row {
    grid-template-columns: auto minmax(0, 15rem) minmax(0, 1fr);
    gap: 0 1.4rem;
    align-items: baseline;
  }
  .teamgroup__row::before { grid-row: auto; align-self: baseline; }
}
.teamgroup__name {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink);
}
.section--dark .teamgroup__name { color: #fff; }
.teamgroup__role {
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--ink-3);
}
.section--dark .teamgroup__role { color: rgba(255, 255, 255, 0.7); }

@media (prefers-reduced-motion: reduce) {
  .person,
  .teamgroup { opacity: 1; translate: none; }
  .person:hover { transform: none; }
}

/* ── the SME rail ────────────────────────────────────────────────────────── */

.maturity { margin-top: 2rem; }
.maturity__steps { position: relative; list-style: none; margin: 0; padding: 0; display: grid; gap: 0.9rem; }
.maturity__step {
  position: relative;
  /* the dot sits above the label and both are centred on the same axis, so the
     stack reads as one mark rather than as a bullet with text beside it */
  text-align: center;
  padding-top: 1.15rem;
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: 1.45;
  opacity: var(--lp, 1);
  transition: color 0.3s var(--ease-out);
}
.maturity__step::before {
  content: '';
  position: absolute;
  inset-inline-start: 50%;
  margin-inline-start: -5px;
  top: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--blue-600);
  transition: transform 0.3s var(--ease-out), background-color 0.3s var(--ease-out);
}
.maturity__step:hover { color: var(--ink); }
.maturity__step:hover::before { transform: scale(1.5); background: var(--gold-500); }
.maturity__out {
  position: relative;
  margin: 2rem auto 0;
  padding-top: 1.5rem;
  text-align: center;
  color: var(--ink);
  font-size: var(--fs-base);
  line-height: 1.6;
  max-width: 52ch;
  opacity: clamp(0, calc((var(--p, 1) - 0.5) * 2), 1);
}
/* The gold rule the paragraph used to carry down its inline start. Centred, it
   has to be a short mark rather than a full-height edge: a 52ch border across
   the top would read as the section's own divider. */
.maturity__out::before {
  content: '';
  position: absolute;
  top: 0;
  inset-inline-start: 50%;
  translate: -50% 0;
  width: 46px;
  height: 2px;
  background: var(--gold-500);
}
/* Wide enough for a rail: the five steps become a row threaded on one line,
   with the line's filled part following the section's own progress. */
@media (min-width: 820px) {
  .maturity__steps {
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0;
    padding-top: 2.6rem;
  }
  .maturity__steps::before,
  .maturity__steps::after {
    content: '';
    position: absolute;
    top: 0.75rem;
    height: 2px;
    inset-inline: 0;
  }
  .maturity__steps::before { background: var(--line); }
  /* The fill is anchored at the inline start and grows toward the end, so the
     gradient has to be re-aimed for Arabic: `inset-inline` mirrors itself, a
     gradient angle does not. */
  .maturity__steps::after {
    inset-inline-end: auto;
    width: calc(100% * clamp(0, var(--p, 1), 1));
    background: linear-gradient(to right, var(--blue-600), var(--gold-500));
  }
  [dir='rtl'] .maturity__steps::after {
    background: linear-gradient(to left, var(--blue-600), var(--gold-500));
  }
  /* On the rail the dot is above the line's own top, so the padding that made
     room for it in the stack is not needed. Centred in its column, the five
     dots are inset from both ends of the rail rather than starting at zero. */
  .maturity__step { padding-block: 0; padding-inline: 0.45rem; }
  .maturity__step::before { top: -2.1rem; }
  .maturity__out { margin-top: 2.4rem; }
}

/* ── the plate lists (library, role) ─────────────────────────────────────── */

/* Flex rather than a grid, for one reason: seven tiles in three columns leave a
   row of one, and a grid parks that last tile against the inline start with two
   empty cells beside it. Flex-wrapped and centre-justified, the short row sits
   under the middle of the ones above it. The column widths are written out per
   breakpoint so the tiles still line up with each other. */
.res-grid {
  counter-reset: res;
  list-style: none;
  margin: 1.9rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.85rem;
}
.res-tile {
  counter-increment: res;
  position: relative;
  overflow: hidden;
  flex: 0 1 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 1.4rem 1.2rem 1.35rem;
  border: 1px solid var(--line);
  border-radius: var(--corner-box);
  background: var(--surface);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: 1.5;
  opacity: var(--lp, 1);
  translate: 0 calc((1 - var(--lp, 1)) * 10px);
  transition: border-color 0.35s var(--ease-out), transform 0.35s var(--ease-out),
              box-shadow 0.35s var(--ease-out), color 0.35s var(--ease-out);
}
@media (min-width: 640px) {
  .res-tile { flex: 0 1 calc(50% - 0.425rem); }
}
@media (min-width: 1000px) {
  .res-tile { flex: 0 1 calc(33.3333% - 0.5667rem); }
}
.res-tile::before {
  content: counter(res, decimal-leading-zero);
  display: block;
  margin-bottom: 0.55rem;
  font-size: var(--fs-micro);
  letter-spacing: 0.12em;
  color: var(--gold-700);
  transition: color 0.35s var(--ease-out);
}
/* the rule that runs in along the top edge on hover, the same gesture the
   mission plates use */
.res-tile::after {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--blue-600);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.45s var(--ease-out);
}
[dir='rtl'] .res-tile::after { transform-origin: right; }
.res-tile:hover {
  border-color: var(--blue-300);
  color: var(--ink);
  transform: translateY(-3px);
  box-shadow: 0 18px 40px -24px rgba(20, 30, 38, 0.35);
}
.res-tile:hover::after { transform: scaleX(1); }

/* ── the tile drawing ──
   Client instruction 2026-09-04: the plate lists read as bland. Each tile now
   carries one line drawing from js/pub-art.js - the set the publication cards
   use - injected by initTileArt in js/pages.js from the tile's `data-art`.
   Reusing that set rather than drawing a second one is the point: one drawing
   system on the site, at the size it was drawn for (see the note at the top of
   pub-art.js - these are icons and are not to be scaled up to display size).

   Decorative and aria-hidden: the tile's own text says the same thing, so a
   failure to inject costs the page nothing but the picture. */
.res-tile__art {
  display: block;
  width: 5.1rem;
  margin: 0.15rem 0 0.9rem;
  color: var(--blue-600);
  transition: color 0.35s var(--ease-out), transform 0.35s var(--ease-out);
}
.res-tile__art svg { display: block; width: 100%; height: auto; }
.res-tile__art .pa-fill { fill: var(--gold-500); }
.res-tile__art .pa-stroke-accent { stroke: var(--gold-500); }
.res-tile:hover .res-tile__art { transform: translateY(-2px) scale(1.04); }
.section--dark .res-tile__art { color: var(--blue-300); }
.section--dark .res-tile__art .pa-fill { fill: var(--gold-300); }
.section--dark .res-tile__art .pa-stroke-accent { stroke: var(--gold-300); }
@media (prefers-reduced-motion: reduce) {
  .res-tile:hover .res-tile__art { transform: none; }
}

.section--dark .res-tile {
  border-color: rgba(164, 223, 245, 0.2);
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.8);
}
.section--dark .res-tile::before { color: var(--gold-300); }
.section--dark .res-tile::after { background: var(--gold-500); }
.section--dark .res-tile:hover {
  border-color: rgba(218, 165, 32, 0.5);
  background: rgba(164, 223, 245, 0.09);
  color: #fff;
  box-shadow: none;
}

/* Client instruction 2026-09-04: every block on About reads centred, so the
   closing paragraph of a section is centred under its figure rather than
   hanging off the left edge of the wrap. Alignment only. The three type values
   this paragraph carries in the PHP-only build (max-width:68ch, --ink-2,
   1.05rem/1.65) are not here, and were not here before this change either. */
/* Client instruction 2026-09-04: every block on About reads centred, so the
   closing paragraph of a section is centred under its figure rather than
   hanging off the left edge of the wrap. */
.pagehead__body {
  margin-inline: auto;
  text-align: center;
  max-width: 68ch;
  color: var(--ink-2);
  font-size: 1.05rem;
  line-height: 1.65;
}
.pagehead__body + .pagehead__body { margin-top: 1rem; }
.info-list + .pagehead__body { margin-top: 1.6rem; }
/* On a dark band the ink scale inverts. template-about.php:268 emits
   pagehead__body--dark and had no rule for it anywhere in the theme, so that
   paragraph was set in --ink-2 on a near-black band. */
.section--dark .pagehead__body,
.pagehead__body--dark { color: rgba(255, 255, 255, 0.78); }

/* The figures replaced the lists that used to sit above these closing
   paragraphs, so the pairing that set the gap (.info-list + .pagehead__body)
   no longer matches anything on About. */
.ecoweb + .pagehead__body,
.res-grid + .pagehead__body,
.flowmap + .pagehead__body,
.maturity + .pagehead__body { margin-top: 2.1rem; }

/* Reduced motion: the figures keep their shape and their hover colours, and
   lose the travel. The strand draw-in and the rail fill are left at 1 rather
   than at var(--p) - the scrub already parks every element at 1 under this
   preference, but these two read as broken rather than as still if a stale 0
   ever reaches them. */
@media (prefers-reduced-motion: reduce) {
  .flowmap__chip,
  .ecoweb__node,
  .maturity__step,
  .res-tile { opacity: 1; translate: none; }
  .flowmap__chip:hover,
  .res-tile:hover { transform: none; }
  .flowmap__hub,
  .ecoweb__hub { translate: none; opacity: 1; }
  .ecoweb__strand { stroke-dashoffset: 0; }
  .ecoweb__mote { opacity: 0.9; }
  .maturity__out { opacity: 1; }
  .maturity__steps::after { width: 100%; }
  .maturity__step:hover::before,
  .res-tile:hover::after { transform: none; }
}
