/* ===========================================================================
   AFRINIC Elections Portal — components not present in the house stylesheet.

   Additive only. Everything here uses the house tokens from css/style.css and
   obeys the house aesthetic: flat hairlines, no shadows, no border-radius, no
   gradients, Burnished Clay as the sole hue. If a component already exists in
   style.css (buttons, tables, sidebar, breadcrumb, hero) it is NOT redefined
   here.
   =========================================================================== */

/* --- Timeline ------------------------------------------------------------ */
.el-timeline { list-style: none; margin: 0; padding: 0; border-top: var(--hairline); }
.el-timeline__row {
  display: grid;
  grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  padding: 1.25rem 0;
  border-bottom: var(--hairline);
}
@media (max-width: 700px) { .el-timeline__row { grid-template-columns: 1fr; gap: 0.35rem; } }
.el-timeline__date {
  font-family: var(--font-numeral);
  color: var(--clay-deep);
  margin: 0;
}
.el-timeline__title { font-family: var(--font-label); margin: 0 0 0.25rem; color: var(--ink); }
.el-timeline__detail { margin: 0; color: var(--g700); font-size: 0.9375rem; }
.el-timeline__row[hidden] { display: none; }

.el-filter { margin: 0 0 1.5rem; }
.el-filter__legend {
  font-family: var(--font-label); font-size: 0.875rem;
  color: var(--g700); margin: 0 0 0.5rem;
}
.el-filter__btn {
  font-family: var(--font-label);
  font-size: 0.875rem;
  background: var(--white);
  border: 1px solid var(--g300);
  color: var(--g700);
  padding: 0.45rem 1rem;
  margin: 0 0.35rem 0.35rem 0;
  cursor: pointer;
}
.el-filter__btn:hover { border-color: var(--clay); color: var(--clay-deep); }
.el-filter__btn[aria-pressed="true"] {
  background: var(--clay); border-color: var(--clay); color: var(--white);
}

/* --- Candidate ----------------------------------------------------------- */
.el-candidate { border-top: 2px solid var(--clay); padding: 1.5rem 0 2rem; margin: 0 0 2rem; }
.el-candidate__head { display: flex; gap: 1.25rem; align-items: flex-start; margin: 0 0 1.25rem; }
.el-candidate__photo { width: 96px; height: 96px; object-fit: cover; flex: none; }
.el-candidate__eyebrow {
  font-family: var(--font-label); font-size: 0.75rem; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--g500); margin: 0 0 0.25rem;
}
.el-candidate__name { font-family: var(--font-display); font-size: 1.5rem; margin: 0 0 0.25rem; color: var(--ink); }
.el-candidate__meta { color: var(--g700); margin: 0; }
.el-candidate__facts { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1.5rem; }
@media (max-width: 800px) { .el-candidate__facts { grid-template-columns: 1fr; } }
.el-candidate__facts .el-span2 { grid-column: 1 / -1; }
.el-candidate__block-title {
  font-family: var(--font-label); font-size: 0.8125rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--clay-deep);
  margin: 0 0 0.5rem; padding-bottom: 0.35rem; border-bottom: var(--hairline);
}

/* --- Callout ------------------------------------------------------------- */
.el-callout { border: var(--hairline); padding: 1.25rem 1.5rem; margin: 1.5rem 0; background: var(--g50); }
.el-callout__title { font-family: var(--font-label); margin: 0 0 0.5rem; color: var(--ink); }
.el-callout > :last-child { margin-bottom: 0; }

/* --- Announcements ------------------------------------------------------- */
.el-news { list-style: none; margin: 0; padding: 0; border-top: var(--hairline); }
.el-news__item { position: relative; padding: 1.5rem 0; border-bottom: var(--hairline); }
.el-news__item:hover { background: var(--g50); }
.el-news__meta { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: baseline; margin: 0 0 0.5rem; }
.el-news__date { font-family: var(--font-numeral); color: var(--clay-deep); }
.el-news__tag {
  font-family: var(--font-label); font-size: 0.6875rem; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--g700);
  border: 1px solid var(--g300); padding: 0.1rem 0.5rem;
}
.el-news__title { font-family: var(--font-display); font-size: 1.25rem; margin: 0 0 0.35rem; }
.el-news__title a { text-decoration: none; }
.el-news__title a::after { content: ""; position: absolute; inset: 0; }
.el-news__excerpt { margin: 0; color: var(--g700); }

/* --- Register ------------------------------------------------------------ */
.el-register-toolbar { border: var(--hairline); padding: 1.25rem; margin: 0 0 1.5rem; background: var(--g50); }
.el-register-toolbar label { display: block; font-family: var(--font-label); margin: 0 0 0.35rem; }
.el-register-toolbar input[type="search"] {
  width: 100%; max-width: 32rem; padding: 0.6rem 0.75rem;
  border: 1px solid var(--g300); background: var(--white); font: inherit;
}
.el-register-toolbar input[type="search"]:focus-visible { outline: 2px solid var(--clay); outline-offset: 1px; }
.el-register-hint { font-size: 0.8125rem; color: var(--g500); margin: 0.5rem 0 0; }
.el-register-count { font-family: var(--font-label); margin: 1rem 0 0; }
.el-pagination { list-style: none; display: flex; flex-wrap: wrap; gap: 0.35rem; padding: 0; margin: 1.5rem 0 0; }
.el-pagination button {
  font: inherit; background: var(--white); border: 1px solid var(--g300);
  color: var(--g700); padding: 0.4rem 0.8rem; cursor: pointer;
}
.el-pagination button:hover:not(:disabled) { border-color: var(--clay); color: var(--clay-deep); }
.el-pagination button[aria-current="page"] { background: var(--clay); border-color: var(--clay); color: var(--white); }
.el-pagination button:disabled { opacity: 0.45; cursor: default; }

/* --- Guidelines table of contents ---------------------------------------- */
/* Was position:sticky inside the content column, which had no background, so
   the article text scrolled straight through it. It is a contents panel at the
   top of the document, not a second sidebar — the page already has one on the
   left. Static, boxed, and two columns so 21 entries stay compact. */
.el-toc {
  position: static;
  border: var(--hairline);
  background: var(--g50);
  padding: 1.25rem 1.5rem;
  margin: 0 0 2.5rem;
}
.el-toc h2 {
  font-family: var(--font-label);
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--g500);
  margin: 0 0 0.9rem;
}
.rich-text .el-toc ul {
  columns: 2;
  column-gap: 2.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Specificity note: the house rules are `.rich-text ul li` (0,1,3). Selectors
   below must match or beat that or they silently lose — `.el-toc li` (0,1,2)
   did, which left the checkmarks and their reserved indent in place. */
.rich-text .el-toc ul li {
  break-inside: avoid;          /* never split an entry across the columns */
  position: static;
  padding-left: 0;
  margin: 0 0 0.5rem;
}
/* The house list style adds a checkmark to every item. A table of contents is
   navigation, not a list of achievements. */
.rich-text .el-toc ul li::before { content: none; }
.rich-text .el-toc a {
  font-size: 0.875rem;
  line-height: 1.35;
  text-decoration: none;
  color: var(--g700);
  display: inline-block;
}
.rich-text .el-toc a:hover,
.rich-text .el-toc a.is-current { color: var(--clay-deep); text-decoration: underline; }

@media (max-width: 700px) {
  .el-toc ul { columns: 1; }
}

/* --- Numbered step list -------------------------------------------------- */
.el-steps { list-style: none; counter-reset: el-step; margin: 1.5rem 0; padding: 0; }
.el-steps > li {
  counter-increment: el-step;
  position: relative;
  padding: 0 0 1rem 3rem;
  border-bottom: var(--hairline);
  margin: 0 0 1rem;
}
.el-steps > li:last-child { border-bottom: none; }
.el-steps > li::before {
  content: counter(el-step);
  position: absolute; left: 0; top: 0;
  font-family: var(--font-numeral); font-size: 1.25rem; color: var(--clay);
  width: 2rem; text-align: left;
}
.el-steps__title { font-family: var(--font-label); margin: 0 0 0.35rem; }

/* --- Utility ------------------------------------------------------------- */
.el-lead { font-size: 1.0625rem; color: var(--g700); }
.el-section { padding: clamp(2rem, 5vw, 3.5rem) 0; }
.el-section--tint { background: var(--g50); border-top: var(--hairline); border-bottom: var(--hairline); }
/* Homepage sections sit outside .rich-text, so their paragraphs inherit the
   reset's zero margin and run together. Give them article rhythm here. */
.el-section h2 { margin: 0 0 1rem; line-height: 1.25; text-wrap: balance; }
.el-section p { margin: 0 0 1.25rem; text-wrap: pretty; }
.el-section p:last-child { margin-bottom: 0; }
.el-btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; margin: 1.75rem 0 0; }
/* The row is a <p>, so `.el-section p` (0,1,1) outranks `.el-btn-row` (0,1,0)
   and zeroed its top margin; under the news grid nothing else supplied the
   gap and the button sat on the cards. Restate at (0,2,0). */
.el-section .el-btn-row { margin: 1.75rem 0 0; }
.el-eyebrow {
  font-family: var(--font-label); font-size: 0.75rem; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--clay-deep); margin: 0 0 0.5rem;
}

/* --- Responsive table wrapper -------------------------------------------- */
/* The house stylesheet styles tables but has no scroll container; wide tables
   must scroll inside their own box so the page body never scrolls sideways. */
.table-scroll { overflow-x: auto; margin: 1.25rem 0; }
.table-scroll table { margin: 0; min-width: 34rem; }

/* --- Page hero: long titles ---------------------------------------------- */
/* The house hero is a flex row (title | breadcrumb). Election page titles are
   much longer than afrinic.net's ("Final Designated Representative Register
   2026" vs "About Us"), so the title wrapped to two lines and squeezed the
   breadcrumb into a narrow column that wrapped as well. Scale the title down,
   keep the breadcrumb on one line, and stack the two below 900px. */
.page-hero-container {
  gap: clamp(1rem, 3vw, 2.5rem);
  align-items: baseline;
}
.page-title {
  font-size: clamp(1.35rem, 2.4vw, 1.9rem);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0;
  min-width: 0;            /* allow the flex item to shrink instead of pushing */
}
.page-hero .breadcrumb {
  flex: 0 0 auto;          /* never absorb the squeeze the title creates */
  white-space: nowrap;
  max-width: 100%;
  overflow-x: auto;        /* scrolls rather than wrapping if truly too narrow */
}
@media (max-width: 900px) {
  .page-hero-container { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
  .page-hero .breadcrumb { white-space: normal; }
}

/* ===========================================================================
   ELEVATION LAYER

   A deliberate departure from the afrinic.net flat-hairline aesthetic: boxes
   and cards on this portal get rounded corners and layered shadows for depth,
   per request. Palette, typography and spacing stay strictly house — only the
   surface treatment changes.

   Shadows are stacked in three low-opacity layers (contact edge / ambient /
   diffuse) rather than one dark blob; a single heavy shadow reads as 2010.

   NOTE ON HOVER: cards whose whole surface is a link (.el-news__item uses a
   stretched ::after) deepen their shadow but are NOT translated. A hover lift
   on a stretched-link card previously caused a 40fps oscillation when the
   cursor sat near the moving edge. Shadow-only hover gives the same affordance
   with none of that risk.
   =========================================================================== */
:root {
  --el-radius:    10px;
  --el-radius-sm:  6px;
  --el-shadow:
    0 1px 2px  rgba(26, 26, 26, .05),
    0 4px 12px rgba(26, 26, 26, .07),
    0 12px 28px rgba(26, 26, 26, .06);
  --el-shadow-lift:
    0 2px 4px  rgba(26, 26, 26, .07),
    0 8px 20px rgba(26, 26, 26, .10),
    0 20px 44px rgba(26, 26, 26, .09);
}

/* --- Panels -------------------------------------------------------------- */
.rich-text .el-toc,
.el-callout,
.el-register-toolbar {
  border-radius: var(--el-radius);
  box-shadow: var(--el-shadow);
  background: var(--white);
}
/* --- Candidate cards ----------------------------------------------------- */
.el-candidate {
  border: var(--hairline);
  border-top: var(--hairline);
  border-radius: var(--el-radius);
  box-shadow: var(--el-shadow);
  padding: 1.75rem;
  margin: 0 0 1.5rem;
  background: var(--white);
  transition: box-shadow var(--transition-fast);
}
.el-candidate:hover { box-shadow: var(--el-shadow-lift); }
.el-candidate__photo { border-radius: var(--el-radius-sm); }

/* --- Announcement cards -------------------------------------------------- */
.el-news { border-top: none; display: grid; gap: 1.25rem; }
.el-news__item {
  border: var(--hairline);
  border-bottom: var(--hairline);
  border-radius: var(--el-radius);
  box-shadow: var(--el-shadow);
  background: var(--white);
  padding: 1.5rem 1.75rem;
  transition: box-shadow var(--transition-fast);
}
.el-news__item:hover { background: var(--white); box-shadow: var(--el-shadow-lift); }

/* --- Tables, media, controls --------------------------------------------- */
.table-scroll {
  border-radius: var(--el-radius);
  box-shadow: var(--el-shadow);
  background: var(--white);
  overflow: hidden;          /* clip the table's corners to the rounded box */
  overflow-x: auto;
}
.el-steps img,
.subpage-content img { border-radius: var(--el-radius-sm); }
.el-filter__btn,
.el-pagination button,
.el-register-toolbar input[type="search"] { border-radius: var(--el-radius-sm); }
.btn, .custom-button { box-shadow: var(--el-shadow); }
.btn:hover, .custom-button:hover { box-shadow: var(--el-shadow-lift); }

/* --- Homepage announcements: compact three-across -------------------------
   The full-width stacked cards used on the announcements index are far too
   heavy for the homepage, where this is a teaser between the hero and the
   timeline. Three across, smaller type, excerpt clamped to four lines so the
   cards stay the same height regardless of copy length. */
.el-news--grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.25rem;
  grid-auto-rows: 1fr;       /* equal rows, so the odd card left alone on the
                                second row at the 2-column breakpoint still
                                matches the others */
  align-items: stretch;      /* was `start`, which sized each card to its own
                                content — cards diverged whenever excerpts
                                wrapped to a different number of lines */
}
/* Stretch gives the cards equal height; the flex column then keeps the excerpt
   top-aligned instead of centring in the leftover space. */
.el-news--grid .el-news__item {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.el-news--grid .el-news__excerpt { margin-top: 0; }
.el-news--grid .el-news__item { padding: 1.25rem 1.35rem; }
.el-news--grid .el-news__meta { gap: 0.5rem; margin-bottom: 0.4rem; }
.el-news--grid .el-news__date { font-size: 0.8125rem; }
.el-news--grid .el-news__tag { font-size: 0.625rem; padding: 0.05rem 0.4rem; }
.el-news--grid .el-news__title { font-size: 1.0625rem; line-height: 1.3; margin-bottom: 0.5rem; }
.el-news--grid .el-news__excerpt {
  font-size: 0.875rem;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
@media (max-width: 980px) { .el-news--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .el-news--grid { grid-template-columns: 1fr; } }

/* --- Main menu: match afrinic.net exactly ---------------------------------
   Both sites declare the same rules — .nav-link 0.8rem, .dropdown-item
   0.875rem — but they resolve differently because the ROOT size differs:

     afrinic.net  html 16px  (constant)   -> nav 12.8px, dropdown 14px
     this portal  html 17px  (fluid)      -> nav 13.6px, dropdown 14.9px

   afrinic.net serves css/style.min.css?v=12, a build without the fluid
   `html { font-size: clamp(...) }` that is in the unminified style.css this
   portal copies. That made the menu 6.25% larger here.

   Pinned in px rather than rem on purpose: the reference is a fixed size, so
   a root-relative unit would reintroduce the same drift. Scoped to the menu
   only — the rest of the portal keeps the fluid type scale. */
.nav-link { font-size: 12.8px; }
.nav-dropdown .dropdown-item,
.nav-dropdown .dropdown-item > a { font-size: 14px; }

/* --- Content hyperlinks: no resting underline -----------------------------
   style.css underlines every editorial link. The selector list is repeated
   verbatim below rather than simplified: it carries eight :not() clauses and
   scores (0,9,1), so anything shorter loses and the underline stays. Equal
   specificity + later sheet = this wins.

   The underline returns on hover and keyboard focus. Colour alone is not a
   sufficient affordance under WCAG 1.4.1, and clay-on-white link text against
   near-black body copy is below the 3:1 non-text threshold, so the pointer and
   focus states have to carry the signal instead. */
.rich-text a:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card),
.subpage-content a:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card),
[itemprop="articleBody"] a:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card),
.download-list a .dl-title,
.minutes-timeline a {
  text-decoration: none;
}
.rich-text a:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card):hover,
.rich-text a:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card):focus-visible,
.subpage-content a:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card):hover,
.subpage-content a:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card):focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* The table of contents marked its current entry with an underline; with links
   no longer underlined by default that read as a link, not a state.

   The colour it was supposed to use never applied: `.rich-text .el-toc
   a.is-current` scores (0,3,1) against the house link rule's (0,9,1), so the
   scroll-spy class has been inert since it shipped. Repeating the :not() chain
   inside .el-toc takes this to (0,11,1) and the colour finally lands.

   Colour only, no heavier face — bolding the entry made it read as a header
   sitting above the list rather than one item within it. */
.rich-text .el-toc a:hover { text-decoration: underline; }
.rich-text .el-toc a.is-current:not([class*="btn"]):not(.custom-button):not(.view-all-link):not(.nav-link):not(.tab-btn):not(.pdp-node):not(.idp-card):not(.gov-card) {
  color: var(--clay-deep);
  text-decoration: none;
}

/* --- List markers: round bullet, not a checkmark --------------------------
   style.css sets `content: "✓"` on every `.rich-text ul li`. A checkmark reads
   as "done" or "included", which is wrong for eligibility criteria and
   procedural steps — most of the lists on this site. Same selector, later
   sheet, so this replaces it.

   Drawn as a box rather than a "•" glyph: the bullet character's size and
   baseline shift between fonts, a background disc does not. */
.sppb-addon-content ul li::before,
.sppb-addon-text-block ul li::before,
.rich-text ul li::before {
  content: "";
  position: absolute;
  left: 0.3rem;
  top: 0.62em;                 /* optical centre of the first line */
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--clay);
  font-size: inherit;          /* the tick's 1.1rem no longer applies */
}
.sppb-addon-content ul li,
.sppb-addon-text-block ul li,
.rich-text ul li { padding-left: 1.25rem; }

/* --- Components are not prose: one reset, not a patch per symptom --------
   Everything inside .rich-text inherits the house article rules:

     .subpage-content.rich-text > p|ul|ol|blockquote   max-width: 75ch   (0,2,1)
     .rich-text ul li                                  padding-left      (0,1,2)
     .rich-text ul li::before                          a marker          (0,1,3)
     .rich-text p                                      margin-bottom     (0,1,1)

   Right for an article, wrong for a component — and every component selector
   scores lower, so they lose silently. Three visible symptoms came from this:
   the announcements list sat in 709px of an 1189px column leaving a 480px
   gutter; announcement cards had their left padding stripped so the text met
   the border; and the last paragraph in every boxed component left a band of
   dead space under it. Cancel the inheritance once, here.

   Direct-child selectors are (0,3,0) and beat the (0,2,1) measure rule. */
.subpage-content.rich-text > .el-news,
.subpage-content.rich-text > .el-cards,
.subpage-content.rich-text > .el-steps,
.subpage-content.rich-text > .el-timeline,
.subpage-content.rich-text > .el-pagination,
.subpage-content.rich-text > .el-toc,
.subpage-content.rich-text > .el-callout,
.subpage-content.rich-text > .el-register-toolbar,
.subpage-content.rich-text > .el-filter,
.subpage-content.rich-text > .table-scroll {
  max-width: none;
}

/* Indent belongs to the component, not the prose rule. Deliberately excludes
   .el-callout and .el-register-toolbar — those are boxes whose left padding is
   real, and zeroing it would push their text onto the border. */
.subpage-content.rich-text > .el-news,
.subpage-content.rich-text > .el-cards,
.subpage-content.rich-text > .el-steps,
.subpage-content.rich-text > .el-timeline,
.subpage-content.rich-text > .el-pagination {
  padding-left: 0;
}

/* Component list items carry their own padding and need no prose marker.
   (0,2,0) beats `.rich-text ul li` (0,1,2). */
.rich-text .el-news__item::before,
.rich-text .el-pagination li::before,
.rich-text .el-timeline__row::before { content: none; }
.rich-text .el-news__item { padding: 1.5rem 1.75rem; }
.rich-text .el-news--grid .el-news__item { padding: 1.25rem 1.35rem; }
.rich-text .el-pagination li { padding-left: 0; margin-bottom: 0; }
/* The timeline row is a grid, not prose: no marker indent, no item margin,
   and its date and title paragraphs sit tight. (0,2,1) beats `.rich-text p`. */
.rich-text .el-timeline__row { padding: 1.25rem 0; margin-bottom: 0; }
.rich-text .el-timeline p { margin: 0; }
.rich-text .el-timeline p.el-timeline__title { margin-bottom: 0.25rem; }
.subpage-content.rich-text > .el-timeline { list-style: none; margin: 0.5rem 0 1.5rem; }

/* A boxed component ends at its padding, not a paragraph's bottom margin.
   `.rich-text .el-x > :last-child` is (0,3,0) against `.rich-text p` (0,1,1). */
.rich-text .el-news__item > :last-child,
.rich-text .el-card > :last-child,
.rich-text .el-callout > :last-child,
.rich-text .el-register-toolbar > :last-child,
.rich-text .el-steps > li > :last-child {
  margin-bottom: 0;
}
.rich-text .el-steps > li:last-child { margin-bottom: 0; }

/* --- Election cards: three across ----------------------------------------
   The hub lists each contest in the 2026 cycle. Same surface treatment as the
   announcement cards (hairline, radius, layered shadow) so the two card types
   read as one system.

   EVERY selector here is scoped under .rich-text on purpose. The house styles
   prose by element — `.rich-text p` (0,1,1), `.rich-text h3` (0,1,1) — which
   outrank a bare `.el-card__body` (0,1,0). Unscoped, the card text rendered at
   body size with 1.5rem paragraph margins and the cards came out 472px tall. */
.subpage-content.rich-text > .el-cards { max-width: none; }
.rich-text .el-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.25rem;
  list-style: none;
  margin: 1.5rem 0;
  padding: 0;
  align-items: stretch;      /* equal height without forcing a row size */
}
.rich-text .el-cards .el-card {
  display: flex;
  flex-direction: column;
  position: relative;
  padding: 1.35rem 1.5rem;
  margin: 0;
  background: var(--white);
  border: var(--hairline);
  border-radius: var(--el-radius);
  box-shadow: var(--el-shadow);
  transition: box-shadow var(--transition-fast);
}
.rich-text .el-cards .el-card::before { content: none; }
.rich-text .el-cards .el-card:hover { box-shadow: var(--el-shadow-lift); }

.rich-text .el-cards .el-card__title {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  line-height: 1.3;
  margin: 0 0 0.35rem;
}
/* Whole card is the target; the title anchor stretches over it. */
.rich-text .el-cards .el-card__title a { text-decoration: none; }
.rich-text .el-cards .el-card__title a::after {
  content: ""; position: absolute; inset: 0;
}
.rich-text .el-cards .el-card__meta {
  font-family: var(--font-label);
  font-size: 0.75rem;
  line-height: 1.4;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--clay-deep);
  margin: 0 0 0.6rem;
}
.rich-text .el-cards .el-card__body {
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--g700);
  margin: 0;
}
@media (max-width: 980px) { .rich-text .el-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .rich-text .el-cards { grid-template-columns: 1fr; } }

/* --- Content column must be allowed to shrink -----------------------------
   .subpage-content is a grid item, so its default min-width:auto is min-content
   — it refuses to go narrower than its widest child. A four-column candidate
   table therefore pushed the column to 851px inside a 600px viewport and the
   whole page scrolled sideways, instead of the table scrolling inside its own
   .table-scroll box. min-width:0 restores the intended behaviour. */
/* min-width:0 alone is not enough: below the sidebar breakpoint the layout
   becomes a flex column, and the content item still sized to its widest child,
   so .table-scroll inherited an 851px containing block and could never clip.
   max-width:100% resolves against the flex container and clamps it. */
.sidebar-layout-grid > .subpage-content { min-width: 0; max-width: 100%; }
.table-scroll { max-width: 100%; }

/* --- Footer legal row: wrap on small screens -----------------------------
   The house rule leaves this flex row at nowrap, so at 375px its 401px of
   links pushed the document to 416px and every page scrolled sideways. The
   links are independent items, so wrapping is the intended behaviour — this
   is the last horizontal-overflow source on the site. */
.footer-legal-links { flex-wrap: wrap; }

/* --- Search page form ----------------------------------------------------
   The results list itself uses the house .search-result-* classes already in
   style.css, so only the query form is defined here. */
.el-search-form { margin: 0 0 1.5rem; }
.el-search-form label {
  display: block;
  font-family: var(--font-label);
  font-size: 0.875rem;
  color: var(--g700);
  margin: 0 0 0.5rem;
}
.el-search-form__row { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.el-search-form input[type="search"] {
  flex: 1 1 18rem;
  min-width: 0;                /* flex item must be allowed to shrink */
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--g300);
  border-radius: var(--el-radius-sm);
  background: var(--white);
  font: inherit;
}
.el-search-form input[type="search"]:focus-visible {
  outline: 2px solid var(--clay);
  outline-offset: 1px;
}
.el-search-status {
  font-family: var(--font-label);
  color: var(--g700);
  margin: 0 0 1rem;
}
.rich-text .search-result:last-child { border-bottom: none; }

/* --- Utility bar: wrap once the search box no longer fits -----------------
   Adding the search box to this row left it 6px wider than a 320px viewport
   (113px input + 1rem margin + 166px of quick links in 290px of usable
   space), so the whole page scrolled sideways. Wrapping only engages when
   the row cannot fit, so 375px and up are unchanged; below that the search
   box drops to its own line rather than being shrunk to an unusable width. */
.utility-bar .container { flex-wrap: wrap; row-gap: 0.35rem; }

/* --- Numbered steps: one number, not two ---------------------------------
   .el-steps sets `list-style: none` at (0,1,0) and loses to the house
   `.subpage-content.rich-text > ol` at (0,2,1), so the browser drew its own
   "1." next to our counter-generated number. (0,3,0) settles it. */
.subpage-content.rich-text > .el-steps { list-style: none; }

/* --- Button rows ---------------------------------------------------------
   Buttons are inline-flex, so in a bare <p> they sat hard against each other
   and collided once the line wrapped. A flex row with a real gap aligns them
   on one baseline and wraps cleanly. */
.el-btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin: 1.25rem 0;
}
.rich-text .el-btn-row .btn { margin: 0; }
/* .btn-outline carries a 2px border and .btn-primary none, so side by side the
   outline button stood 4px taller than its neighbours. A transparent border of
   the same width evens the box heights without touching either colour. */
.el-btn-row .btn-primary { border: 2px solid transparent; }

/* --- Registration-form screenshots: three across -------------------------
   Was one full-width screenshot per numbered step, which made the page very
   long. Three to a row, each opening full size in a modal. */
.rich-text .el-shots {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem 1.25rem;
  list-style: none;
  margin: 1.5rem 0 2rem;
  padding: 0;
}
.rich-text .el-shots .el-shot {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.rich-text .el-shots .el-shot::before { content: none; }
.rich-text .el-shots .el-shot__label {
  font-family: var(--font-label);
  font-size: 0.875rem;
  line-height: 1.4;
  color: var(--ink);
  margin: 0 0 0.6rem;
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
}
.el-shot__n {
  flex: none;
  font-family: var(--font-numeral);
  color: var(--clay);
  font-size: 1.125rem;
  line-height: 1;
}
/* The thumbnail is the control: a real button, so it is keyboard reachable
   and announced as actionable. */
.rich-text .el-shots .el-shot__btn {
  position: relative;
  display: block;
  width: 100%;
  margin-top: auto;
  padding: 0;
  border: var(--hairline);
  border-radius: var(--el-radius-sm);
  background: var(--white);
  cursor: zoom-in;
  overflow: hidden;
  transition: box-shadow var(--transition-fast), border-color var(--transition-fast);
}
.rich-text .el-shots .el-shot__btn img {
  display: block;
  width: 100%;
  height: auto;
}
.el-shot__btn:hover { border-color: var(--clay); box-shadow: var(--el-shadow); }
.el-shot__btn:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.el-shot__zoom {
  position: absolute;
  right: 0.4rem;
  bottom: 0.4rem;
  background: rgba(26, 26, 26, 0.78);
  color: var(--white);
  font-family: var(--font-label);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
  border-radius: 3px;
}
@media (max-width: 900px) { .rich-text .el-shots { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .rich-text .el-shots { grid-template-columns: 1fr; } }

/* --- Lightbox ------------------------------------------------------------ */
.el-lightbox {
  /* A modal <dialog> centres itself with the browser's own `margin: auto`,
     which the house reset (`*, *::before, *::after { margin: 0 }`) wipes —
     so the box opened in the top-left corner of the viewport. Restored here;
     `inset: 0` with `position: fixed` is what the auto margins centre
     against, and both are stated rather than assumed from the UA sheet. */
  position: fixed;
  inset: 0;
  margin: auto;
  border: none;
  border-radius: var(--el-radius);
  padding: 0;
  max-width: min(92vw, 1100px);
  max-height: 92vh;
  overflow: visible;
  background: var(--white);
}
.el-lightbox::backdrop { background: rgba(26, 26, 26, 0.72); }
.el-lightbox__figure { margin: 0; padding: 0.75rem; }
.el-lightbox__img {
  display: block;
  max-width: 100%;
  max-height: calc(92vh - 5rem);
  width: auto;
  margin: 0 auto;
}
.el-lightbox__caption {
  font-family: var(--font-label);
  font-size: 0.875rem;
  color: var(--g700);
  padding: 0.6rem 0.25rem 0.1rem;
  text-align: center;
}
.el-lightbox__close {
  position: absolute;
  top: -0.9rem;
  right: -0.9rem;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  border: none;
  background: var(--ink);
  color: var(--white);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
}
.el-lightbox__close:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }

/* --- Reading width -------------------------------------------------------
   Prose runs the full content column, by client instruction (5 September
   2026). The house caps article prose at 75ch with
   `.subpage-content.rich-text > p|ul|ol|blockquote` (0,2,1); the same
   selectors below, later in the cascade, clear it. The tokens are kept so the
   cap can be restored in one line: `--el-measure: 72ch; --el-measure-lead:
   58ch` is the value the 3 September review recommended, when full-width
   lines measured about 135 characters at 1600px and 172 at 1920px. Justified
   setting with hyphenation, below, is the compensating choice at this width.

   Components (.el-callout, the candidate card, .el-cand__facts, .el-span2)
   own their width and are not listed here. */
:root {
  --el-measure:      none;
  --el-measure-lead: none;
}
.subpage-content.rich-text > p,
.subpage-content.rich-text > ul,
.subpage-content.rich-text > ol,
.subpage-content.rich-text > blockquote { max-width: var(--el-measure); }
.rich-text .el-lead { max-width: var(--el-measure-lead); }

/* The guidelines wrap each numbered clause in a <section>, one level deeper
   than the direct-child rule. <section> is the only structural wrapper around
   prose anywhere in the build. */
.subpage-content.rich-text > section > p,
.subpage-content.rich-text > section > ul,
.subpage-content.rich-text > section > ol,
.subpage-content.rich-text > section > blockquote { max-width: var(--el-measure); }

/* Candidate statements: prose inside the card component. */
.el-cand__body p,
.el-cand__body ul,
.el-cand__body ol { max-width: var(--el-measure); }

/* --- Justified prose ------------------------------------------------------
   The house sets prose ragged-right (`p, .rich-text p, .subpage-content p`,
   `.rich-text ul li` — style.css "PROSE ALIGNMENT"), on the argument that
   justified text without hyphenation opens rivers. Justified is the client's
   instruction; hyphenation is what makes it hold. `hyphens: auto` needs the
   document language, which the shell sets (lang="en"), and
   hyphenate-limit-chars stops two-letter fragments at line ends. Same
   specificity as the house rules, later in the cascade.

   Justification is for running text. Anything set in a narrow track or a
   short box — cards, news items, the timeline, the table of contents, the
   candidate facts, the step and screenshot lists, the toolbars, callouts —
   would show the rivers the house warns about, and stays ragged. */
.rich-text p,
.rich-text blockquote,
.rich-text dd,
.rich-text ul li,
.rich-text ol li {
  text-align: justify;
  text-justify: inter-word;
  -webkit-hyphens: auto;
  hyphens: auto;
  hyphenate-limit-chars: 6 3 3;
}
.rich-text .el-cards p,        .rich-text .el-cards li,
.rich-text .el-news p,         .rich-text .el-news li,
.rich-text .el-timeline p,     .rich-text .el-timeline li,
.rich-text .el-toc li,
.rich-text .el-cand__facts p,  .rich-text .el-cand__facts li,
.rich-text .el-steps p,        .rich-text .el-steps li,
.rich-text .el-shots p,        .rich-text .el-shots li,
.rich-text .el-callout p,      .rich-text .el-callout li,
.rich-text .el-filter p,
.rich-text .el-pagination p,
.rich-text .el-register-toolbar p,
.rich-text .el-btn-row,
.rich-text .el-lead {
  text-align: left;
  -webkit-hyphens: manual;
  hyphens: manual;
}

/* --- Heading rhythm --------------------------------------------------------
   The house reset zeroes every margin and nothing restores them for article
   headings, so an h2 sat flush on the paragraph below it with only the
   previous paragraph's 1.5rem above. Space above a heading should exceed the
   space below it — the heading belongs to what follows — so: h2 2.5rem over
   1rem, h3 2rem over 0.75rem. Adjacent margins collapse, so a paragraph's
   1.5rem bottom margin never adds to these.

   Scoped to prose headings by position (direct children of the column, or of
   a guidelines <section>) rather than by element, so component headings —
   .el-toc h2, .el-card h3, .el-cand__block, .el-callout__title — keep their
   own rules. */
.subpage-content.rich-text > h2,
.subpage-content.rich-text > section > h2 {
  margin: 2.5rem 0 1rem;
  line-height: 1.25;
  text-wrap: balance;
}
.subpage-content.rich-text > h3,
.subpage-content.rich-text > section > h3 {
  margin: 2rem 0 0.75rem;
  line-height: 1.3;
  text-wrap: balance;
}
.subpage-content.rich-text > h2:first-child,
.subpage-content.rich-text > section:first-child > h2:first-child {
  margin-top: 0;
}

/* --- Homepage banner (Claude Design "15 · Homepage banner") ---------------
   Rebuilt from the approved design rather than imported. The design project
   ships an "industry" design-system bundle (_ds_bundle.js + styles.css);
   dropping that in would put a second, conflicting token set and a runtime
   next to the house stylesheet, which this portal explicitly does not do.
   Composition, hierarchy and detailing are reproduced with house tokens.

   The artboard is a fixed 1600x500 image; here it is fluid and collapses to
   one column below 900px.

   Deliberate departures, both flagged to the client:
   - The design repeats the AFRINIC wordmark. In place it sits ~60px under
     the real header logo, so only the "Membership" chip is kept.
   - The CTA uses --clay-deep, not the design's lighter orange: white on
     --clay is 3.95:1 and fails WCAG AA, white on --clay-deep is 5.82:1. */
.el-banner {
  background: #000;                 /* the design's ground is pure black */
  padding: clamp(1.5rem, 3vw, 2.5rem) 0 clamp(2rem, 4vw, 3rem);
  border-top: 6px solid var(--clay);
}
.el-banner__frame {
  position: relative;
  border: 1px solid rgba(255, 255, 255, 0.16);
  padding: clamp(1.75rem, 4vw, 3.25rem) clamp(1.25rem, 3vw, 3rem);
}
.el-banner__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 5vw, 4.5rem);
  align-items: center;
}
@media (max-width: 900px) {
  .el-banner__grid { grid-template-columns: 1fr; align-items: start; }
}
/* A grid item defaults to min-width:auto, so the 1fr track grew to the
   headline's min-content width — "representative" at 32px is ~240px — and the
   banner pushed the page sideways at 320px. min-width:0 lets the track shrink;
   break-word then lets that one long word break rather than spill out. */
.el-banner__grid > * { min-width: 0; }
.el-banner__title { overflow-wrap: break-word; }

.el-banner__kicker {
  font-family: var(--font-label);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--white);
  margin: 0 0 0.9rem;
}
.el-banner__title {
  font-family: var(--font-display);
  font-size: clamp(2.1rem, 5.2vw, 4rem);
  line-height: 1.02;
  color: var(--white);
  margin: 0 0 1rem;
}
.el-banner__accent { color: #E0895A; }
.el-banner__lede {
  font-size: clamp(0.95rem, 1.2vw, 1.0625rem);
  line-height: 1.6;
  color: #C9C4BF;
  max-width: 56ch;                  /* a lede, not body copy — keep it read-able */
  margin: 0 0 1.75rem;
}

.el-banner__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
  margin: 0;
}
.el-banner__cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  background: var(--clay-deep);
  color: var(--white);
  font-family: var(--font-label);
  font-size: 0.8125rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.95rem 1.6rem;
  text-decoration: none;
  transition: background var(--transition-fast);
}
.el-banner__cta:hover { background: var(--clay); color: var(--white); }
.el-banner__cta:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }
.el-banner__link {
  color: #C9C4BF;
  text-decoration: none;
  font-size: 0.9375rem;
}
.el-banner__link:hover { color: var(--white); text-decoration: underline; }
.el-banner__link:focus-visible { outline: 2px solid var(--clay); outline-offset: 3px; }

/* Right-hand requirements plate */
.el-banner__plate {
  position: relative;
  border: 1px solid rgba(224, 137, 90, 0.5);
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
}
.el-banner__plate-title {
  font-family: var(--font-label);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #A79E96;
  margin: 0 0 1rem;
}
.el-banner__needs { list-style: none; margin: 0; padding: 0; }
.el-banner__needs li {
  position: relative;
  padding-left: 1.35rem;
  margin: 0 0 0.7rem;
  color: #D8D4D0;
  font-size: 0.875rem;
  line-height: 1.5;
}
.el-banner__needs li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--clay);
}
.el-banner__needs li:last-child { margin-bottom: 0; }
.el-banner__plate-link {
  margin: 1.1rem 0 0;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.el-banner__plate-link a {
  font-family: var(--font-label);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #E0895A;
  text-decoration: none;
}
.el-banner__plate-link a:hover { text-decoration: underline; }
.el-banner__plate-link a:focus-visible { outline: 2px solid var(--clay); outline-offset: 3px; }

/* --- Corner crosshairs ---------------------------------------------------
   The design marks the corners of the frame, the plate and the CTA with a
   small "+". Drawn as background gradients on two pseudo-elements (two
   corners each) so no extra markup is needed. inset:-4px centres each cross
   on its corner rather than tucking it inside the box. */
.el-ticks { --el-tick: rgba(224, 137, 90, 0.85); }
.el-ticks::before,
.el-ticks::after {
  content: "";
  position: absolute;
  inset: -4px;
  pointer-events: none;
}
.el-ticks::before {
  background:
    linear-gradient(var(--el-tick), var(--el-tick)) left  top / 9px 1px no-repeat,
    linear-gradient(var(--el-tick), var(--el-tick)) left  top / 1px 9px no-repeat,
    linear-gradient(var(--el-tick), var(--el-tick)) right top / 9px 1px no-repeat,
    linear-gradient(var(--el-tick), var(--el-tick)) right top / 1px 9px no-repeat;
}
.el-ticks::after {
  background:
    linear-gradient(var(--el-tick), var(--el-tick)) left  bottom / 9px 1px no-repeat,
    linear-gradient(var(--el-tick), var(--el-tick)) left  bottom / 1px 9px no-repeat,
    linear-gradient(var(--el-tick), var(--el-tick)) right bottom / 9px 1px no-repeat,
    linear-gradient(var(--el-tick), var(--el-tick)) right bottom / 1px 9px no-repeat;
}
.el-banner__frame { --el-tick: rgba(255, 255, 255, 0.45); }

/* The CTA label is long uppercase text with wide tracking — at 320px it was
   261px inside a 233px frame and pushed the page sideways. Below 560px it
   takes the full column and the label wraps instead. min-width:0 is required
   or the flex item refuses to shrink under its min-content width. */
@media (max-width: 560px) {
  .el-banner__actions { gap: 1rem; }
  .el-banner__cta {
    flex: 1 1 100%;
    min-width: 0;
    justify-content: center;
    text-align: center;
    letter-spacing: 0.06em;
    padding: 0.9rem 1rem;
  }
  .el-banner__frame { padding-left: 1rem; padding-right: 1rem; }
}

/* --- Inline icons ----------------------------------------------------------
   Replaces the Font Awesome web font. .el-icon sits where an <i class="fa">
   sat, sized from the parent's font-size exactly as the glyph was, and takes
   the parent's colour so the house hover and focus rules still apply. The
   sprite is in the flow but occupies nothing: display:none would stop some
   engines resolving <use> references into it. */
.el-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.el-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  fill: currentColor;
}

/* --- Sidebar heading: level 2, styled as before ---------------------------
   The sidebar title was an h3 sitting between the page h1 and the content h2,
   so every page with a sidebar — 24 of 25 — skipped a level for anyone
   navigating by headings. The markup is now h2; this rule carries across the
   house .sidebar-nav h3 styling so nothing moves visually. */
.sidebar-nav h2 {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--secondary-color);
  margin-top: 0;
  margin-bottom: 1.25rem;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid var(--primary-color);
}

/* --- Button contrast -----------------------------------------------------
   White on --clay measured 3.89:1 and the outline label 3.54:1, both under
   the 4.5:1 AA threshold for text at this size. --clay-deep is the value the
   homepage banner CTA already ships at 5.82:1, so this brings the rest of the
   site to the standard one component was already meeting. The outline border
   stays --clay: at 3.95:1 it clears the 3:1 required of a UI boundary. */
.btn-primary { background-color: var(--clay-deep); }
.btn-primary:hover { background-color: #93400E; }
.btn-outline { color: var(--clay-deep); }
.btn-outline:hover { background-color: var(--clay-deep); color: var(--white); }

/* --- Contest state -------------------------------------------------------
   Each contest now carries its own state, so the hub can say which of the
   three concerns the reader instead of filing them all under a date. */
.rich-text .el-cards .el-card__status {
  font-family: var(--font-label);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0 0 0.5rem;
  color: var(--g500);
}
.rich-text .el-cards .el-card__status--upcoming { color: var(--clay-deep); }

/* --- Candidate statements ------------------------------------------------
   The bios, credentials and objectives were collected but rendered nowhere;
   members were choosing between people on the strength of a job title. A
   native <details> keeps the page scannable and needs no JavaScript. */
.rich-text .el-cand {
  border-top: var(--hairline);
  margin: 0;
  padding: 0;
}
.rich-text .el-cand:last-of-type { border-bottom: var(--hairline); }
.rich-text .el-cand__summary {
  cursor: pointer;
  padding: 1rem 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 1rem;
  list-style: none;
}
.rich-text .el-cand__summary::-webkit-details-marker { display: none; }
.rich-text .el-cand__summary::after {
  content: "+";
  margin-left: auto;
  font-family: var(--font-numeral);
  color: var(--clay-deep);
  font-size: 1.25rem;
  line-height: 1;
}
.rich-text .el-cand[open] > .el-cand__summary::after { content: "\2212"; }
.rich-text .el-cand__summary:hover .el-cand__name { color: var(--clay-deep); }
.rich-text .el-cand__summary:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.el-cand__name { font-family: var(--font-label); color: var(--ink); }
.el-cand__where { color: var(--g700); font-size: 0.875rem; }
.rich-text .el-cand__body { padding: 0 0 1.5rem; }
.rich-text .el-cand__facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem;
  margin-top: 1.25rem;
}
@media (max-width: 800px) { .rich-text .el-cand__facts { grid-template-columns: 1fr; } }
.rich-text .el-cand__facts .el-span2 { grid-column: 1 / -1; }
.rich-text .el-cand__block {
  font-family: var(--font-label);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--clay-deep);
  margin: 0 0 0.5rem;
  padding-bottom: 0.35rem;
  border-bottom: var(--hairline);
}


/* ===========================================================================
   BROWSER SURFACES

   The parts nobody draws still carry the design. Text selection, the caret,
   form accents and the scrollbar under a wide table all ship with browser
   defaults: a system-blue highlight and a grey trough on a page whose only
   hue is Burnished Clay. Themed from the house tokens, so a page reads as
   built rather than assembled.
   =========================================================================== */

::selection { background: var(--clay-tint); color: var(--ink); }

input, textarea { caret-color: var(--clay); }
input, textarea, select { accent-color: var(--clay); }

/* Wide tables scroll horizontally inside .table-scroll. Firefox reads the two
   standard properties; WebKit and Blink need the pseudo-elements. The thumb
   carries a white border so it reads as inset within the rounded panel rather
   than as a bar laid across it. */
.table-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--g300) transparent;
}
.table-scroll::-webkit-scrollbar       { height: 12px; }
.table-scroll::-webkit-scrollbar-track { background: transparent; }
.table-scroll::-webkit-scrollbar-thumb {
  background: var(--g300);
  border: 3px solid var(--white);
  border-radius: 999px;
}
.table-scroll:hover::-webkit-scrollbar-thumb { background: var(--g500); }

/* Figures in a column must align on the digit, not the glyph: terms of office,
   register row numbers, announcement dates. Proportional numerals make a date
   column ragged even when the text is flush. Scoped to the content area by
   element, not by class: only 3 of the 13 tables in the build carry
   .table-afrinic, the rest are bare <table> or #register-table. */
.subpage-content table td,
.subpage-content table th,
.el-register-count,
.el-news__date,
time { font-variant-numeric: tabular-nums; }

/* A card heading sets its own break rather than leaving one word stranded. */
.el-card__title,
.el-news__title,
.el-callout__title { text-wrap: balance; }

/* This sheet adds transitions but no keyframes; honour the OS setting for the
   ones it owns. The house sheet already guards its own. */
@media (prefers-reduced-motion: reduce) {
  .el-candidate,
  .el-news__item,
  .el-card,
  .el-filter__btn,
  .btn,
  .custom-button { transition: none; }
}

/* The homepage section's paragraph rhythm (`.el-section p`, 0,1,1) would
   otherwise push the timeline's date and title paragraphs apart. */
.el-section .el-timeline { margin: 0.5rem 0 0; }
.el-section .el-timeline p { margin: 0; }
.el-section .el-timeline p.el-timeline__title { margin-bottom: 0.25rem; }
.el-section h3 { margin: 2rem 0 0.75rem; }

/* Key dates in the hero plate: the date leads in white, the milestone follows
   in the plate's muted grey. */
.el-banner__needs strong { color: #FFFFFF; font-weight: 600; font-family: var(--font-numeral); }

/* Anchor targets land under the sticky header without this: the TOC links to
   each guideline section, and the 6.3 heading the PDP page links to. */
.subpage-content [id] { scroll-margin-top: calc(var(--header-height) + 1rem); }
