@charset "UTF-8";
/* =========================================================================
   ROMSEY MILL  --  BRAND SKIN  --  v8.5
   Pairs with the Tabernacle style.css / script.js in this folder
   =========================================================================
   A per-site token override. Tabernacle's style.css stays vanilla and is
   never edited for a client; the brand lives here. Load order matters:

       style.css        (vanilla library, shared, updatable)
       skin.css?v=0.1   (this file, site-specific, loaded second)

   Both declare tokens in :root at equal specificity, so the later file wins.
   The version query is load-bearing: without it browsers serve a stale skin
   and changes appear not to have shipped. Bump it on every deploy.

   PROVISIONAL COLOURS AND TYPE
   Every value in section 1 and 2 is sampled from screenshots of the live
   romseymill.org, not taken from a brand document. They are close, but they
   are not authoritative. Replace them once Neil supplies the real palette,
   the real face and the logo files. Nothing else in this file needs touching
   when that happens, which is the point of keeping the brand in tokens.

   ACCESSIBILITY
   Ratios below are calculated, not eyeballed. Two brand colours cannot carry
   white text at small sizes: the mid blue is 3.86:1 with white, which passes
   only at large-text sizes, and the brand orange is 3.37:1, which passes
   nowhere. The blue is therefore used for large display text only, and the
   Donate pill takes a deepened orange where white reaches 5.00:1.

   PAGE RHYTHM ON THE LIVE SITE
   White is the page base. Their design alternates it with three bands:

       hero            photography
       strapline       blue
       three stories   white
       our impact      dark grey
       latest news     white
       events          purple
       footer          dark grey

   The band classes are not wired up yet: that is Phase 2, along with the
   angled dividers between them. This file supplies the colours they will use.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. TOKEN OVERRIDES
   ------------------------------------------------------------------------- */
:root {
  --bg:          #FFFFFF;
  --bg-rgb:      255, 255, 255;
  --surface:     #FFFFFF;

  /* Ink is a dark slate rather than pure black, matching the headings on the
     live site. 13.3:1 on white. --ink-soft is 5.57:1, so it clears AA for
     body copy and is safe for captions and summaries. */
  --ink:         #23313D;
  --ink-soft:    #5A6A78;

  /* Accent is the heather purple, which is the strongest colour in their
     palette and the one that reads as Romsey Mill. White on it is 6.98:1, so
     unlike most brand colours it can carry a white label at any size. That
     makes it the right choice for card headers and solid buttons. */
  --accent:      #7A4A69;
  --accent-ink:  #FFFFFF;
  --accent-soft: #F3EBF0;
  --accent-dark: #5E3751;

  --rule:              #DDDDDD;
  --section-light:     #F4F4F4;
  --section-light-rgb: 244, 244, 244;
  --section-dark:      #3F3F3F;

  --font-display: "Poppins", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body:    "Poppins", -apple-system, BlinkMacSystemFont, sans-serif;

  /* Motion. The library ships 200ms ease, which reads mechanically because
     ease accelerates and decelerates symmetrically. Material's standard curve
     departs softly and settles definitely. Safe to hold a curve in this
     token: it is only ever consumed inside transition shorthands. */
  --transition: 240ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* -------------------------------------------------------------------------
   2. BRAND EXTRAS
   -------------------------------------------------------------------------
   Named rather than inlined so Phase 2 can paint the bands without hunting
   for hex values. Measured against white unless stated.

       purple        6.98:1 with white  -- safe for white text at any size
       blue          3.86:1 with white  -- LARGE TEXT ONLY
       blue-deep     6.31:1 with white  -- use where small white text is
                                           needed on blue
       grey-dark    10.53:1 with white  -- safe for white text at any size
       orange        3.37:1 with white  -- decorative fills only, never a
                                           background for white text
       orange-deep   5.00:1 with white  -- the Donate pill fill
   ------------------------------------------------------------------------- */
:root {
  --rm-purple:      #7A4A69;
  --rm-purple-deep: #5E3751;
  --rm-blue:        #4E87B3;
  --rm-blue-deep:   #3A6A8E;
  --rm-grey-dark:   #3F3F3F;
  --rm-orange:      #F15A29;
  --rm-orange-deep: #C93F12;
  --rm-orange-edge: #A83208;
  --rm-neutral:     #F1F1F1;
  --rm-haze:        #F3EBF0;
  --rm-ink-tint:    rgba(35, 49, 61, 0.06);
}

/* -------------------------------------------------------------------------
   3. LIBRARY BUG FIX  --  the missing space token
   -------------------------------------------------------------------------
   Found while reading the reference build, and it affects every Tabernacle
   site rather than just this one.

   The content article sets its heading and rule margins from --space-10:

       h2, h3, h4 { margin: var(--space-10) 0 var(--space-3) 0; }
       hr         { margin: var(--space-10) 0; }

   but --space-10 is never defined in style.css. The scale runs 1, 2, 3, 4, 5,
   6, 8, 12, 16, 24 and skips 10. An undefined custom property is invalid at
   computed-value time, which does not fall back to the rest of the shorthand:
   the whole declaration is dropped, and margin does not inherit, so it
   computes to zero.

   So every heading on every content page has had no margin above or below it,
   which is why headings sit jammed against the text around them. Defining the
   token repairs both rules at once. 2.5rem is where it belongs on the scale,
   between --space-8 at 2rem and --space-12 at 3rem.

   This belongs in the library. It is here because a skin can be deployed
   without re-uploading 650KB.
   ------------------------------------------------------------------------- */
:root {
  --space-10: 2.5rem;
}

/* A major section heading should carry more air above it than a subsection
   heading. The library groups h2, h3 and h4 in one rule, so they all get the
   same and read as a flat list rather than a structure. */
.content_article .ArticleBody h2 {
  margin-top: var(--space-12);
}

/* A heading opening the article needs no space above it: the card already
   provides the padding. */
.content_article .ArticleBody > :is(h2, h3, h4):first-child {
  margin-top: 0;
}

/* -------------------------------------------------------------------------
   4. HEADER  --  the Donate pill
   -------------------------------------------------------------------------
   Their live header uses a solid orange pill with a white label. At the brand
   orange that pairing is 3.37:1, which fails AA at any size, so the fill is
   the deepened orange where white reaches 5.00:1. Visually it is the same
   button; it is simply a shade further down the same hue.

   Both header variants are named because the site uses the two-row header and
   the library only attaches some pill rules to one variant.
   ------------------------------------------------------------------------- */
.site-header--tworow .header-giving,
.site-header--split  .header-giving {
  background-color: var(--rm-orange-deep);
  color:            #FFFFFF;
  border:           1px solid var(--rm-orange-edge);
  transition:       opacity 350ms ease;
}
.site-header--tworow .header-giving:hover,
.site-header--split  .header-giving:hover {
  background-color: var(--rm-orange-deep);
  border-color:     var(--rm-orange-edge);
  color:            #FFFFFF;
  opacity:          0.7;
}

/* -------------------------------------------------------------------------
   5. SECTION PILLS
   -------------------------------------------------------------------------
   "Read more news" and "See all events" at the foot of the homepage feeds.
   The library ships .section-link as a quiet ghost pill; purple with a white
   label at 6.98:1 makes them read as real buttons and keeps the brand colour
   working above the fold rather than only in bands.
   ------------------------------------------------------------------------- */
.section-link {
  background-color: var(--accent);
  color:            var(--accent-ink);
  border:           1px solid var(--accent);
  text-decoration:  none;
  transition:       opacity 350ms ease;
}
.section-link:hover {
  background-color: var(--accent);
  border-color:     var(--accent);
  color:            var(--accent-ink);
  opacity:          0.7;
}

/* -------------------------------------------------------------------------
   6. CONTENT PAGE SHELL  --  article card left, sidebar right
   -------------------------------------------------------------------------
   A tinted full-bleed band holding a two-column grid: the current article in
   a white card on the left, a stack of cards on the right. The card is the
   only editable region, which makes the boundary of what the client changes
   visually obvious on screen.

   Neutral grey behind the card rather than a brand tint, so content pages
   read as workspace rather than as more marketing.

   PROGRESSIVE: the grid collapses to a single column until something is
   actually placed in the side column, so there is never a broken half-built
   state while modules are being added.
   ------------------------------------------------------------------------- */
.content-shell {
  background:    var(--rm-neutral);
  padding-block: clamp(var(--space-8), 4vw, var(--space-16));
}

.content-shell__inner {
  max-width:             var(--container-width);
  margin-inline:         auto;
  padding-inline:        var(--container-gutter);
  display:               grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap:                   clamp(var(--space-6), 3vw, var(--space-12));
  align-items:           start;
}

/* The editable card. */
.content-shell__main {
  background:    var(--surface);
  border-radius: 16px;
  padding:       clamp(var(--space-6), 3vw, var(--space-12));
  box-shadow:    0 12px 40px -28px rgba(35, 49, 61, 0.22);
}

/* .content_article ships as an 820px centred column with large block margins,
   which is right when it IS the page but wrong inside a card that already
   provides the measure and the padding. Neutralised here rather than in the
   library, so a site not using the shell keeps the original behaviour. */
.content-shell__main .content_article {
  max-width:      none;
  margin:         0;
  padding-inline: 0;
}

/* Page title. The library sets the content h1 at --text-3xl, up to 72px,
   which was tuned around a narrow serif. A geometric sans sets far wider at
   the same size and fills the card before the reader reaches any prose. Down
   one step, with the accent rule beneath it kept. */
.content_article .ArticleBody h1 {
  font-size: var(--text-2xl);
}

/* Sticky sidebar, so it stays with the reader on long articles. The grid's
   align-items:start is what makes sticky work in a grid child. The offset
   clears the sticky site header. */
.content-shell__side {
  position:       sticky;
  top:            calc(84px + var(--space-4));
  display:        flex;
  flex-direction: column;
  gap:            var(--space-6);
}

/* Collapse to one column while the side column is still empty. */
.content-shell__inner:not(:has(.content-shell__side > *)) {
  grid-template-columns: minmax(0, 1fr);
}
.content-shell__inner:not(:has(.content-shell__side > *)) .content-shell__side {
  display: none;
}

/* Stack below 1024px. Sticky comes off, or a full-width sidebar would cover
   the article beneath it. */
@media (max-width: 1024px) {
  .content-shell__inner {
    grid-template-columns: minmax(0, 1fr);
  }
  .content-shell__side {
    position: static;
  }
}

/* -------------------------------------------------------------------------
   7. SIDEBAR CARDS
   -------------------------------------------------------------------------
   The sidebar is a stack of .side-card blocks, each with a coloured head and
   a body. Purple heads with white labels at 6.98:1.
   ------------------------------------------------------------------------- */
.side-card {
  background:    var(--surface);
  border-radius: 16px;
  overflow:      hidden;
  box-shadow:    0 12px 40px -28px rgba(35, 49, 61, 0.22);
}

.side-card__head {
  background:  var(--accent);
  color:       var(--accent-ink);
  font-family: var(--font-display);
  font-size:   var(--text-base);
  font-weight: 600;
  padding:     var(--space-4) var(--space-5);
}

.side-card__body {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-4);
  padding:        var(--space-5);
}

/* Captions are hand-authored in Body Contents rather than generated here. The
   Site Phone and Site Email modules are Hubb's own and their internal markup
   is not documented, so anything this file asserts about it is a guess.
   Authoring the label as real markup keeps it certain, and leaves the module
   to supply only the value. */
.side-card__label {
  display:        block;
  color:          var(--ink-soft);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom:  var(--space-1);
}

/* Deliberately shallow styling of the module output: the link treatment and
   nothing else, so unexpected wrappers still render sensibly. */
.side-card__body .phone_number,
.side-card__body .site_email {
  margin: 0;
}
.side-card__body .phone_number a,
.side-card__body .site_email a {
  color:           var(--ink);
  font-size:       var(--text-base);
  font-weight:     600;
  text-decoration: none;
  word-break:      break-word;
}
.side-card__body .phone_number a:hover,
.side-card__body .site_email a:hover {
  color: var(--accent);
}

/* CTA. Its own class rather than .section-link, which carries slice-level
   geometry that makes no sense inside a card. */
.side-card__cta {
  display:          flex;
  align-items:      center;
  justify-content:  center;
  gap:              var(--space-2);
  padding:          var(--space-3) var(--space-5);
  background-color: var(--accent);
  color:            var(--accent-ink);
  border:           1px solid var(--accent);
  border-radius:    999px;
  font-family:      var(--font-body);
  font-size:        var(--text-xs);
  font-weight:      700;
  letter-spacing:   0.12em;
  text-transform:   uppercase;
  text-decoration:  none;
  transition:       opacity 350ms ease;
}
.side-card__cta:hover {
  opacity: 0.7;
}

/* -------------------------------------------------------------------------
   8. GROUP NAVIGATION  --  inside a sidebar card
   -------------------------------------------------------------------------
   The module renders as Hubb's classic boxout: an outer layout table wrapping
   a header strip table and a contents table, with two spacer cells either
   side of the header label that exist only to carry themed corner images.

   Rather than unpick that into blocks, the rules below let the table fill the
   card and restyle the header strip in place. Far less fragile than fighting
   Hubb's own boxout markup. The module is deliberately left on its legacy
   rendering, matching the reference build, so this is the shape to expect.
   ------------------------------------------------------------------------- */
.content-shell__side table {
  border-collapse: collapse;
  width:           100%;
}

.content-shell__side .boxout_header {
  background: var(--accent);
}
.content-shell__side .boxout_header_left,
.content-shell__side .boxout_header_right {
  display: none;
}
.content-shell__side .boxout_header_middle {
  padding: var(--space-4) var(--space-5);
}
.content-shell__side .boxout_header_middle,
.content-shell__side .boxout_header_middle a {
  color:           var(--accent-ink);
  font-family:     var(--font-display);
  font-size:       var(--text-base);
  font-weight:     600;
  text-decoration: none;
}

/* Contents cell. cellpadding is an HTML attribute rather than CSS, so it has
   to be overridden with real padding, not removed. */
.content-shell__side .boxout_contents > tbody > tr > td {
  padding: var(--space-3) var(--space-2) var(--space-4);
}

/* Items. Block links, so the whole row is the target and not just the words. */
.content-shell__side .group_nav_selected > a,
.content-shell__side .group_nav_sibling > a,
.content-shell__side .group_nav_child > a {
  display:         block;
  padding:         var(--space-2) var(--space-3);
  border-radius:   8px;
  color:           var(--ink);
  font-size:       var(--text-sm);
  line-height:     1.35;
  text-decoration: none;
  transition:      background-color var(--transition), color var(--transition);
}
.content-shell__side .group_nav_selected > a:hover,
.content-shell__side .group_nav_sibling > a:hover,
.content-shell__side .group_nav_child > a:hover {
  background-color: var(--rm-haze);
}

/* The section the visitor is currently in. Purple at 6.98:1 on white, and it
   is also the only bolded item, so current state is not carried by colour
   alone. */
.content-shell__side .group_nav_selected > a {
  font-weight: 600;
  color:       var(--accent);
}

/* Children indent beneath their parent and step back in weight. */
.content-shell__side .group_nav_child {
  padding-left: var(--space-4);
}
.content-shell__side .group_nav_child > a {
  color: var(--ink-soft);
}

/* -------------------------------------------------------------------------
   9. SIDEBAR ADVERTS
   -------------------------------------------------------------------------
   A Gallery module reading the Content page Adverts folder, rendered as a
   plain stack. No card wrapper: promotional images usually carry their own
   frame, and a card around a card reads as clutter.
   ------------------------------------------------------------------------- */
.content-adverts .gallery_ul {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-4);
  margin:         0;
  padding:        0;
  list-style:     none;
}
.content-adverts .gallery_li {
  margin: 0;
}
.content-adverts .gallery_li img {
  display:       block;
  width:         100%;
  height:        auto;
  border-radius: 12px;
}

/* -------------------------------------------------------------------------
   10. NARROW SHELL  --  system pages
   -------------------------------------------------------------------------
   System pages are log in, register, forgotten password, the profile editor,
   basket and checkout, search results and the policy pages. They are forms
   and results rather than editorial content, and none benefits from a
   sidebar: a login form should be focused, and a full content measure is far
   too wide for one.

   Single column, card capped at a readable measure. Everything else about the
   shell is inherited.
   ------------------------------------------------------------------------- */
.content-shell--narrow .content-shell__inner {
  grid-template-columns: minmax(0, 1fr);
  max-width:             920px;
}

/* System content is not wrapped in .content_article, so the heading rules
   above never reach it and the h1 falls back to the library's base size of up
   to 72px. Matched to a content page, with the same accent rule beneath it,
   so a system page and a content page open identically. */
.content-shell--narrow .content-shell__main h1 {
  font-size:      var(--text-2xl);
  position:       relative;
  padding-bottom: var(--space-4);
  margin-bottom:  var(--space-6);
}
.content-shell--narrow .content-shell__main h1::after {
  content:          "";
  position:         absolute;
  left:             0;
  bottom:           0;
  width:            clamp(60px, 7vw, 88px);
  height:           3px;
  background-color: var(--accent);
}
.content-shell--narrow .content-shell__main h2 { font-size: var(--text-xl); }
.content-shell--narrow .content-shell__main h3 { font-size: var(--text-lg); }

/* -------------------------------------------------------------------------
   11. HUBB FORM STYLING  --  inside the content card
   -------------------------------------------------------------------------
   NOT scoped with a bare form selector. Hubb is ASP.NET Web Forms and wraps
   the whole document in a single form element, so a bare form rule styles the
   entire page. Everything here hooks Hubb's own form classes instead, scoped
   inside the content card so it cannot reach anything outside it.
   ------------------------------------------------------------------------- */
:root {
  --rm-field-border: #6E8090;   /* 4.07:1 on white, 3.61:1 on the grey panel */
  --rm-error:        #B3261E;   /* 6.54:1 on white */
}

/* Section headings such as "Basic Details". Hubb injects an inline style block
   setting div.TopCaption, which out-specifies a lone class, so this has to
   match at least as tightly to win. */
.content-shell__main div.TopCaption {
  margin:      var(--space-6) 0 var(--space-4);
  font-family: var(--font-display);
  font-size:   var(--text-lg);
  font-weight: 600;
  color:       var(--ink);
}

/* Field rows. Hubb's default is a 150px right-aligned label column, which
   wraps longer labels onto two lines and leaves them floating away from their
   inputs. A fixed label column beside the control fixes both.

   The column width has to be fixed rather than content-sized: Hubb renders
   every field as its own separate table, so each row is an independent grid
   and a max-content track would size to its own label alone, stepping in and
   out down the form instead of lining up.

   !important is unavoidable on the cell: Hubb writes text-align and width as
   inline styles on the td, and no class selector beats an inline style.

   :has(.SubCaption) guards the rule so it can only match a table that is
   genuinely a form row, never a real data table sharing the SubTable class. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td {
  display:    block;
  width:      100% !important;
  text-align: left !important;
}

/* Labels stay plain blocks, so the required-field marker stays inline
   alongside the text rather than stacking above it. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div.SubCaption {
  display:        block;
  margin-bottom:  0;
  color:          var(--ink-soft);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height:    1.35;
}
.content-shell__main .SubCaption b {
  color:        var(--rm-error);
  margin-right: 0.35em;
  font-weight:  700;
}

/* Control wrappers only: a flex column, which is what removes the stray line
   box Hubb's markup indentation would otherwise generate inside the cell.
   Whitespace-only text nodes do not generate flex items, so there is nothing
   left to produce a line box or a baseline to align to. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div:not(.SubCaption) {
  display:        flex;
  flex-direction: column;
  align-items:    flex-start;
}

.content-shell__main .SubTable:has(.SubCaption) {
  margin-bottom: var(--space-2);
}

@media (min-width: 721px) {
  .content-shell__main .SubTable:has(.SubCaption) > tbody > tr {
    display:               grid;
    grid-template-columns: 190px minmax(0, 1fr);
    gap:                   var(--space-5);
    align-items:           center;
  }
}

/* Controls. --rule is far too faint for a field boundary, hence a dedicated
   border colour. display: block rather than the browser default inline-block,
   which would sit the control on a text baseline inside the article's 1.65
   line-height and add dead space inside every field. Capped in width because
   a full-width text input inside a wide card is a worse target than a
   comfortable one. */
.content-shell__main input.text,
.content-shell__main select.text,
.content-shell__main textarea.text {
  display:          block;
  width:            100%;
  max-width:        400px;
  padding:          var(--space-3) var(--space-4);
  border:           1px solid var(--rm-field-border);
  border-radius:    8px;
  background-color: var(--surface);
  color:            var(--ink);
  font-family:      var(--font-body);
  font-size:        var(--text-base);
  line-height:      1.3;
  transition:       border-color var(--transition);
}
.content-shell__main input.text:focus-visible,
.content-shell__main select.text:focus-visible,
.content-shell__main textarea.text:focus-visible {
  outline:        3px solid var(--accent);
  outline-offset: 2px;
  border-color:   var(--accent);
}

/* Consent block. */
.content-shell__main ul.consent-questions {
  list-style: none;
  margin:     0;
  padding:    0;
}
.content-shell__main li.consent-question {
  margin-bottom: var(--space-3);
}
.content-shell__main li.consent-question label {
  color:       var(--ink);
  font-size:   var(--text-sm);
  line-height: 1.5;
}
.content-shell__main li.consent-question input[type="checkbox"] {
  width:          1.15rem;
  height:         1.15rem;
  margin-right:   var(--space-2);
  accent-color:   var(--accent);
  vertical-align: -0.15rem;
}

.content-shell__main .validator {
  color:       var(--rm-error);
  font-size:   var(--text-sm);
  line-height: 1.3;
}

/* Submit. Hubb renders a second, hidden button beside it for the reCAPTCHA
   postback, which carries its own class and must stay hidden, so it is not
   touched here. */
.content-shell__main input.button {
  display:          inline-flex;
  align-items:      center;
  justify-content:  center;
  padding:          var(--space-3) var(--space-6);
  background-color: var(--accent);
  color:            var(--accent-ink);
  border:           1px solid var(--accent);
  border-radius:    999px;
  font-family:      var(--font-body);
  font-size:        var(--text-xs);
  font-weight:      700;
  letter-spacing:   0.12em;
  text-transform:   uppercase;
  cursor:           pointer;
  transition:       opacity 350ms ease;
}
.content-shell__main input.button:hover {
  opacity: 0.7;
}

/* The registration control is wrapped by Hubb in a div whose id ends in
   divRegister, which is the only element enclosing both the fields and the
   submit button, so it is the hook for a panel. The id prefix is ASP.NET's
   generated control path and varies by page; the suffix is stable. */
.content-shell__main [id$="divRegister"] {
  background:    var(--rm-neutral);
  border-radius: 12px;
  padding:       clamp(var(--space-5), 3vw, var(--space-8));
  margin-block:  var(--space-6);
}
.content-shell__main [id$="divRegister"] input.button {
  margin-top: var(--space-4);
}

/* -------------------------------------------------------------------------
   12. NEWSLETTER BAND
   -------------------------------------------------------------------------
   Built entirely from the library's --signup-* tokens, so this is a token
   remap and touches none of the band's layout. Neutral grey rather than a
   brand tint: the page already carries purple, blue and dark grey bands, and
   one more branded panel immediately above a dark footer tips it from
   confident into insistent.

   Measured on #F1F1F1: ink 12.4:1, --ink-soft 5.39:1, purple 6.75:1. The
   button takes the purple fill with a white label. The scrim exists only to
   hold text legible over a background image, and there is none here, so it
   goes transparent rather than veiling the panel.
   ------------------------------------------------------------------------- */
.slice--signup {
  --signup-bg:           var(--rm-neutral);
  --signup-ink:          var(--ink);
  --signup-muted:        var(--ink-soft);
  --signup-tint:         var(--accent);
  --signup-btn-bg:       var(--accent);
  --signup-btn-ink:      var(--accent-ink);
  --signup-btn-bg-hover: var(--accent-dark);
  --signup-decor:        var(--rm-ink-tint);
  --signup-scrim:        transparent;
}

/* Where the band is the last thing in the main content area, two margins
   stack under it: its own bottom margin and the footer's margin-top. A
   full-bleed panel running into the footer wants no gap. Only in that
   position: elsewhere the footer still needs its margin or it crowds the
   content above. */
.slice--signup:last-child {
  margin-bottom: 0;
}
main:has(> .slice--signup:last-child) + .site-footer {
  margin-top: 0;
}

/* -------------------------------------------------------------------------
   13. FOOTER
   -------------------------------------------------------------------------
   The library italicises the charity and company number line. Italic signals
   an aside, a quotation or a foreign term, and a charity number is none of
   those: it is the one piece of text on the page present for statutory
   reasons, and it should read as a fact of record. Roman, with a little
   letter-spacing so a run of digits stays legible at the small size the
   footer uses.

   This site's footer keeps the accreditation logos strip above the contact
   band, so the library's own top padding is doing real work and is left
   alone.
   ------------------------------------------------------------------------- */
.site-footer #footer_charity {
  font-style:     normal;
  letter-spacing: 0.02em;
}

/* -------------------------------------------------------------------------
   14. v0.2  --  impact stats, and the header social links
   ------------------------------------------------------------------------- */

/* STATS INTRO PARAGRAPH
   The library's stats slice expects a heading and then the list. It has no
   rule for an intro paragraph between them, so the sub-line inherits the
   article's default left alignment and sits hard against the left edge under
   a centred heading. Centred and given a readable measure. */
.slice--stats .ArticleBody > p {
  max-width:     52ch;
  margin-inline: auto;
  text-align:    center;
}

/* STATS ON THE DARK BAND
   The band flips text and hairlines to white on its own, but the stat icons
   read from --stat-icon-color, which defaults to the accent. Purple on
   #3F3F3F is far too close to work, so the icons go white on this band only.
   The default on light bands is left alone. */
.slice--stats.slice--dark-grey {
  --stat-icon-color: #FFFFFF;
}

/* HEADER SOCIAL LINKS
   The social links article is a list of plain text links, which the library
   has no rule for in the utility row, so they stack vertically and push the
   row three lines tall. Laid out in a row and set at caption size until the
   real icon markup or an icon font is decided.

   Deliberately shallow: the article's internal markup is the client's to
   edit, so this styles the container and the anchors and asserts nothing
   about what sits between them. */
.header-social {
  display: flex;
  align-items: center;
}
.header-social .ArticleBody,
.header-social .ArticleBody ul,
.header-social .ArticleBody p {
  display:        flex;
  flex-wrap:      wrap;
  align-items:    center;
  gap:            var(--space-3);
  margin:         0;
  padding:        0;
  list-style:     none;
}
.header-social .ArticleBody li {
  margin: 0;
}
.header-social .ArticleBody a {
  font-size:       var(--text-xs);
  font-weight:     700;
  letter-spacing:  0.04em;
  text-transform:  uppercase;
  text-decoration: none;
  color:           var(--ink-soft);
  transition:      color var(--transition);
}
.header-social .ArticleBody a:hover {
  color: var(--accent);
}

/* -------------------------------------------------------------------------
   15. v0.3  --  newsletter band back to the dark treatment
   -------------------------------------------------------------------------
   Supersedes the token remap in section 12, which inverted the band to a
   light grey panel with a purple button. Adam wants the library's original
   treatment: a dark band carrying a single white pill.

   Section 12 is left in place rather than deleted so the reasoning stays
   readable, and because this file is appended to rather than rewritten. The
   values below win on load order. Fold section 12 away at the next
   consolidation.

   The dark is --section-dark, the same #3F3F3F as the Our Impact band and
   the footer, so the page has one dark rather than two nearly-identical
   ones. If it wants to be heavier than the impact band, this is a one-token
   change.

   Measured on #3F3F3F: white 10.53:1, the muted white 8.0:1 or better, and
   the ink label on the white pill 13.3:1. The decorative rings drop to a 7%
   white tint so they read as texture rather than as marks on the panel.

   The scrim exists only to hold text legible over a background image, and
   there is none, so it stays transparent rather than veiling the panel.

   ON THE JOIN BELOW: section 12's rules pull this band flush against the
   footer when it is the last thing in the main content area. That still
   works, because the footer now opens with the safeguarding band on white,
   so a dark newsletter band is followed by white rather than by more dark.
   If the safeguarding band is ever removed, revisit that join or the two
   dark areas will merge into one.
   ------------------------------------------------------------------------- */
.slice--signup {
  --signup-bg:           var(--section-dark);
  --signup-ink:          #FFFFFF;
  --signup-muted:        rgba(255, 255, 255, 0.78);
  --signup-tint:         #FFFFFF;
  --signup-btn-bg:       #FFFFFF;
  --signup-btn-ink:      var(--ink);
  --signup-btn-bg-hover: #FFFFFF;
  --signup-decor:        rgba(255, 255, 255, 0.07);
  --signup-scrim:        transparent;
}

/* -------------------------------------------------------------------------
   16. v0.4  --  header social icons
   -------------------------------------------------------------------------
   Supersedes the text treatment in section 14. That was written when the
   social article held plain word links; it now holds the library's own icon
   markup, so forcing a caption font size onto the anchors would shrink the
   glyphs and uppercasing them does nothing useful.

   The icon markup is a set of anchors carrying footer_icon_block plus a
   channel name, each wrapping an italic element carrying an icomoon class.
   The library styles that pattern, but its rules may well be scoped to the
   footer, so the sizing and colour below are stated here rather than assumed.
   If the header icons come out looking identical to the footer ones, these
   three declarations are redundant and can go.

   Colour: the utility row sits on white, so the muted ink at 5.57:1 reads as
   secondary without disappearing, and hovers to the accent.
   ------------------------------------------------------------------------- */
.header-social .ArticleBody a {
  font-size:      inherit;
  letter-spacing: normal;
  text-transform: none;
}

.header-social .footer_icon_block {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  font-size:       1rem;
  line-height:     1;
  color:           var(--ink-soft);
  text-decoration: none;
  transition:      color var(--transition);
}
.header-social .footer_icon_block:hover {
  color: var(--accent);
}

/* -------------------------------------------------------------------------
   17. v0.5  --  header over the hero, homepage only
   -------------------------------------------------------------------------
   Reverses CSS-7. Their live site floats the header over the hero
   photograph with white type, and Adam wants that back.

   Opt-in via site-header--over-hero, which is set only on the Home template.
   The Content and System templates keep the solid header, which is what they
   want: there is no photograph behind them to float over.

   WHY ABSOLUTE AND NOT STICKY
   The library gives the header a sticky position. A transparent sticky header
   works over the hero and then becomes white type on white content the moment
   the reader scrolls past it. Absolute positioning means the header scrolls
   away with the hero instead, which is legible at every scroll position. The
   trade-off is no sticky navigation on the homepage. Making it both would
   mean a script toggling a solid state on scroll, which is a library job
   rather than a skin one.

   THE SCRIM
   A photograph has no predictable contrast, so white type over it is a gamble
   unless something guarantees the backdrop. The gradient runs from 55% black
   at the top to nothing by the bottom of the header, which holds the type
   without visibly banding across the image.

   DROPDOWN PANELS -- READ THIS BEFORE CHANGING THE COLOUR RULES
   The Drop-down Menus module has no documented markup, so the white type is
   applied to links inside the nav and then explicitly reset to dark inside
   any NESTED list. Dropdown panels are nested lists in practically every
   implementation, and the reset out-specifies the white rule, so a panel that
   opens on a white background keeps dark links.

   If the panels come out white on white, this pair of rules is why, and the
   fix is to name the panel's actual class rather than relying on nesting.
   Removing site-header--over-hero from the Home template turns all of this
   off in one edit.
   ------------------------------------------------------------------------- */
.site-header.site-header--over-hero {
  position:   absolute;
  top:        0;
  left:       0;
  right:      0;
  z-index:    200;
  background: linear-gradient(to bottom,
                              rgba(0, 0, 0, 0.55),
                              rgba(0, 0, 0, 0));
}

/* The utility row carries its own band colour in the library. Over a
   photograph it has to give way to the scrim. */
.site-header--over-hero .site-header__utility,
.site-header--over-hero .site-header__main {
  background: transparent;
}

/* Type and icons to white. The Donate pill is excluded: it carries its own
   fill and label, and both already clear AA. */
.site-header--over-hero,
.site-header--over-hero .site-name,
.site-header--over-hero a:not(.header-giving),
.site-header--over-hero .header-social .footer_icon_block {
  color: #FFFFFF;
}

/* Reset inside nested lists, so an opened dropdown panel keeps dark links
   whatever background it paints itself. Higher specificity than the rule
   above, deliberately. */
.site-header--over-hero .site-header__nav ul ul a,
.site-header--over-hero .site-header__nav ul ul a:hover {
  color: var(--ink);
}

.site-header--over-hero .header-social .footer_icon_block:hover {
  color: #FFFFFF;
  opacity: 0.7;
}

/* Room for the header to float over. Without this the caption can sit under
   the bar on a short viewport. Additive, so it only ever raises the height
   the library already gives the hero. */
.site-header--over-hero + main .slice--hero,
.site-header--over-hero ~ main .slice--hero {
  min-height: min(88vh, 780px);
}

/* -------------------------------------------------------------------------
   18. v0.6  --  sections 12 and 15 are now dormant
   -------------------------------------------------------------------------
   Both templates have moved their newsletter band from slice--signup to
   slice--signup-cta, which is the centred single-pill treatment on a dark
   band with the concentric rings. That is the one Adam wanted, and it is the
   library's own styling: it needs nothing from this file.

   The two slices are separate classes rather than a base and a modifier, so
   nothing in sections 12 or 15 matches any more. Both are left in place
   rather than deleted, because this file is appended to over the API rather
   than rewritten, and a reader should be able to see that the token remap
   was tried and abandoned rather than wonder why the newsletter band ignores
   it. Fold both away at the next consolidation.

   WHAT WAS LOST WITH THEM: section 12 also carried the rules that pull the
   band flush against the footer when it is the last thing in the main content
   area. Those targeted slice--signup too, so the standard gap before the
   footer is now back. That is the right result here, because the footer opens
   with the safeguarding band on white and the reference screenshot shows
   clear space below the band. No replacement rule is added on purpose: if a
   flush join is ever wanted, it should be written against the class actually
   in use rather than inherited by accident.
   ------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------
   19. v0.7  --  hero: fixing what section 17 broke, and closing the gap
   -------------------------------------------------------------------------
   Four faults, compared against their live homepage.

   1. THE WHITE BAND BELOW THE HERO. Mine. Section 17 set a min-height on the
      hero section so the caption could not hide behind the floating header.
      But the slideshow inside is sized by bxSlider from the image, around
      520px, so the section grew to 780 and the remaining 260 rendered as
      white inside the hero. Withdrawn. The caption is pushed clear of the
      header below instead, which is what should have been done first.

   2. THE CAPTION IS CENTRED AND TOO BIG. The library centres it and its title
      clamp tops out at 5rem, which was tuned around a narrow serif. Poppins
      sets appreciably wider at the same size, so the string wraps to four
      lines, fills the frame and runs up under the menu. Theirs is
      left-aligned, lower in the frame and around half that size, with the
      photograph left to do its job. Same correction Wight Dive's skin makes
      for the same reason.

   3. THE CAPTION RENDERS TWICE. With ShowCaptions on, bxSlider draws its own
      caption strip along the bottom of the slide, and the library separately
      builds the big overlay from the same text. So the headline appears once
      large over the image and once small on a grey band. The strip is hidden
      here rather than switching ShowCaptions off, because that text is the
      source the overlay is built from: turn it off and the headline goes too.

   4. NO SUBTITLE OR BUTTON. Not a CSS matter. The image captions were
      uploaded as a single line, so there was nothing for the subtitle to
      come from. Rewritten in the two-part format the library documents,
      title then asterisk then subtitle. The Read more button needs the
      three-part format and its delimiter is not documented anywhere I can
      reach, so it is left alone rather than guessed at.
   ------------------------------------------------------------------------- */

/* 1. Withdraw the min-height from section 17. */
.site-header--over-hero + main .slice--hero,
.site-header--over-hero ~ main .slice--hero {
  min-height: auto;
}

/* 3. Hide bxSlider's own caption strip. The overlay is the caption. */
.slice--hero .bx-caption {
  display: none;
}

/* 2. Caption to the lower left, at a size that leaves the photograph
      visible. The padding-top is what clears the floating header: it pushes
      the text down inside the caption box whether the library centres that
      box with flex or positions it absolutely, so it does not depend on
      knowing which. */
.slice--hero .hero-caption,
.hero_slideshow .hero-caption {
  max-width:      44rem;
  margin-left:    0;
  margin-right:   auto;
  padding-top:    var(--space-16);
  padding-bottom: var(--space-12);
  text-align:     left;
  align-items:    flex-start;
}

@media (min-width: 721px) {
  .hero_slideshow .hero-caption__title {
    font-size:   clamp(1.75rem, 3.4vw, 3rem);
    line-height: 1.1;
  }
}

.hero_slideshow .hero-caption__subtitle {
  font-size:  var(--text-base);
  max-width:  34rem;
  text-align: left;
}

/* -------------------------------------------------------------------------
   20. v0.8  --  header type legible over the photograph
   -------------------------------------------------------------------------
   Section 17 set the header type to white with
   `.site-header--over-hero a:not(.header-giving)`, which scores two classes
   and one element. The nav links stayed dark, so the library must set their
   colour with a longer selector, most likely something on the order of
   `.site-header--tworow .site-header__nav ul li a`. Since this file loads
   last, equal specificity would have been enough; losing means the library's
   selector is genuinely longer, so the rules below are written to win rather
   than to rely on load order.

   THE SCRIM WAS ALSO TOO SHORT. It ran from 55% black to nothing across the
   whole header, so by the nav row, roughly two thirds down, it had faded to
   about 16%. That is fine over the dark sky at the top of one slide and
   useless over the sunlit hillside of another. It now holds most of its
   weight to the foot of the header.

   AND A TEXT SHADOW, because a scrim tuned for one photograph is a guess for
   the next. It is faint enough not to read as a shadow, and it is what keeps
   the nav legible if someone loads a pale image.

   Dropdown panels are reset to dark ink as before, with `ul ul li a` so the
   reset is inherently more specific than the white rule rather than depending
   on order.
   ------------------------------------------------------------------------- */

/* Scrim: hold the weight further down. */
.site-header.site-header--over-hero {
  background: linear-gradient(to bottom,
                              rgba(0, 0, 0, 0.70) 0%,
                              rgba(0, 0, 0, 0.55) 55%,
                              rgba(0, 0, 0, 0.25) 100%);
}

/* Utility row: greeting, session links, social icons. Donate keeps its own
   fill and label. */
.site-header.site-header--over-hero .site-header__utility,
.site-header.site-header--over-hero .site-header__utility *:not(.header-giving) {
  color: #FFFFFF;
}

/* Brand and main row. */
.site-header.site-header--over-hero .site-header__brand,
.site-header.site-header--over-hero .site-name,
.site-header.site-header--over-hero .site-header__nav,
.site-header.site-header--over-hero .site-header__nav a,
.site-header.site-header--over-hero .site-header__nav ul li a {
  color: #FFFFFF;
}

/* Legibility insurance over unpredictable photography. */
.site-header.site-header--over-hero .site-header__nav ul li a,
.site-header.site-header--over-hero .site-name,
.site-header.site-header--over-hero .site-header__utility *:not(.header-giving) {
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* Dropdown panels keep dark ink and no shadow: they paint their own
   background, so white type there would be invisible. */
.site-header.site-header--over-hero .site-header__nav ul ul li a,
.site-header.site-header--over-hero .site-header__nav ul ul li a:hover {
  color:       var(--ink);
  text-shadow: none;
}

/* -------------------------------------------------------------------------
   21. v0.8  --  the Account control, second attempt
   -------------------------------------------------------------------------
   HDR-1. The library's own site-header--no-account switch is on the header
   and has not hidden it, so that rule is presumably scoped to the split
   header variant, which is the only place the reference build uses it.

   Without the control's class name this is an attribute match on any class
   containing "account", scoped inside the main row of a header that has
   already asked for no account control. The blast radius is small: if it
   matches nothing, nothing changes, and the only thing in a site header with
   "account" in its class is the account control.

   Replace this with the real class name once someone has looked.
   ------------------------------------------------------------------------- */
.site-header--no-account .site-header__main [class*="account" i],
.site-header--no-account .site-header__main [class*="Account"] {
  display: none;
}

/* -------------------------------------------------------------------------
   22. v0.9  --  three feature cards in one row of three
   -------------------------------------------------------------------------
   The inherited variant library offered four arrangements for this slice: a
   3-up grid as the default with no module class, a wider 4-up on
   explore_church_2, square overlay blocks on explore_church_3, and a 3-up
   two-row grid on explore_church_4. The module carries a CustomClass of
   "explore_church", which is none of those, so which arrangement it lands on
   is not something to rely on.

   Rather than work out which class the library wants and hope, the grid is
   stated here: three equal columns, one row, which is what the three cards
   need and what their live site does. Deterministic, and it does not depend
   on the module class being right.

   minmax(0, 1fr) rather than 1fr, because a grid item defaults to
   min-width:auto and a long caption word can otherwise push a column past
   its share and overflow the row.

   Collapses to a single column at the library's own 720px phone breakpoint
   rather than introducing a new one.
   ------------------------------------------------------------------------- */
.slice--explore-church .gallery_ul {
  display:               grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap:                   clamp(var(--space-5), 3vw, var(--space-8));
  margin:                0;
  padding:               0;
  list-style:            none;
}

.slice--explore-church .gallery_li {
  margin:    0;
  min-width: 0;
}

.slice--explore-church .gallery_li img {
  display: block;
  width:   100%;
  height:  auto;
}

@media (max-width: 720px) {
  .slice--explore-church .gallery_ul {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* -------------------------------------------------------------------------
   23. v1.0  --  feature cards: text under the image, whatever the variant
   -------------------------------------------------------------------------
   The module's CustomClass has been cleared, so it should now get the
   library's default 3-up treatment, which puts the image on top and the
   title and description in a body beneath it. That is the arrangement both
   of Adam's references show.

   The rules below are insurance rather than styling. One of the four
   inherited variants renders the caption as an overlay across the image, and
   if anything routes us to that treatment the text ends up on top of the
   photograph instead of under it. Forcing the list item to a flex column
   guarantees the children stack in document order, image first, and
   returning non-image children to static positioning undoes an overlay if
   one is applied.

   Neither declaration changes anything if the library is already laying the
   card out correctly, which is why it is safe to leave in place.

   Deliberately no card background, radius or shadow: their live site sets
   these three on plain white with no card around them, and adding one would
   be my invention rather than their design.
   ------------------------------------------------------------------------- */
.slice--explore-church .gallery_li {
  display:        flex;
  flex-direction: column;
}

.slice--explore-church .gallery_li > *:not(img) {
  position: static;
}

/* -------------------------------------------------------------------------
   24. v1.1  --  feature cards: correcting section 23
   -------------------------------------------------------------------------
   Section 23 cleared the module's CustomClass on the reasoning that
   "explore_church" is not one of the four documented variants. That was
   wrong: the class is what renders the caption at all, and clearing it left
   three bare images with no title or description. Restored.

   So the caption is drawn by the class and the remaining question is only
   where it sits. Section 23's static-positioning rule targeted DIRECT
   children of the list item, which misses the caption if it is nested any
   deeper, for example inside an anchor wrapping the image. Widened to all
   descendants, and the image is explicitly ordered first so it leads the
   card whatever order the markup puts things in.

   The image may be a direct child of the list item or wrapped in a link, so
   both are given the order, which covers either markup.

   Scoped tightly to this one slice. Blanket position:static inside a card is
   heavy-handed, and it is here because it reliably undoes an overlay without
   needing to know which element carries it. If it turns out to break a hover
   effect, replace it with the caption element's real class name.
   ------------------------------------------------------------------------- */
.slice--explore-church .gallery_li {
  display:        flex;
  flex-direction: column;
}

.slice--explore-church .gallery_li > img,
.slice--explore-church .gallery_li > a {
  order: -1;
}

.slice--explore-church .gallery_li * {
  position: static;
}

/* -------------------------------------------------------------------------
   25. v1.2  --  breathing room under the impact sub-line
   -------------------------------------------------------------------------
   Section 14 centred this paragraph and gave it a measure but no bottom
   margin, and the library resets margins globally, so the sub-line sat
   almost against the icon row.

   2.5rem off the existing scale rather than a hand-picked pixel value, which
   keeps it in step with the rest of the vertical rhythm. It reads as a
   deliberate gap between the section's introduction and its figures without
   detaching the two from each other.
   ------------------------------------------------------------------------- */
.slice--stats .ArticleBody > p {
  margin-bottom: var(--space-10);
}

/* -------------------------------------------------------------------------
   26. v1.3  --  feature cards: releasing the fixed-height overlay card
   -------------------------------------------------------------------------
   The render finally showed what the class does. `explore_church` is an
   overlay treatment: a card of fixed height, the image filling it, and the
   caption laid across the bottom on a dark band. Three consequences, all
   visible at once:

     - the card has a set height and clips its overflow, so once sections 23
       and 24 pushed the caption into normal flow BELOW the image, it fell
       past the bottom edge and was cut off mid-line
     - the caption sits on a dark band because it was designed to be read
       over a photograph
     - the text colour was chosen for that dark band, so against anything
       else it is close to invisible

   So the earlier rules were right in intent and useless in effect: the
   caption was moved out of the overlay but the card was never told it could
   grow. This releases the height, drops the band, and lets the text read as
   text.

   The declarations are applied to the list item AND its descendants, because
   the height, the clipping and the band could each sit on the item or on an
   inner wrapper and there is no documented markup to tell me which. Scoped
   to this one slice, so the breadth costs nothing elsewhere.

   Still not styling the title and description differently from each other:
   that needs their real class names. This gets the words on the page,
   readable, under the image.
   ------------------------------------------------------------------------- */
.slice--explore-church .gallery_li,
.slice--explore-church .gallery_li * {
  position:     static;
  height:       auto;
  min-height:   0;
  max-height:   none;
  aspect-ratio: auto;
  overflow:     visible;
  background:   none;
}

.slice--explore-church .gallery_li {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-3);
}

.slice--explore-church .gallery_li > img,
.slice--explore-church .gallery_li > a {
  order: -1;
}

.slice--explore-church .gallery_li img {
  display: block;
  width:   100%;
  height:  auto;
}

/* Text back to ink, left-aligned, at a readable size. Their live site sets
   these three on plain white with no card, so this matches that rather than
   the dark band the overlay assumed. */
.slice--explore-church .gallery_li *:not(img) {
  color:       var(--ink);
  text-align:  left;
  text-shadow: none;
}

/* -------------------------------------------------------------------------
   27. v1.4  --  header type white, forced
   -------------------------------------------------------------------------
   Third attempt at this, so a different approach rather than a longer
   selector. Sections 17 and 20 tried increasing specificity, reaching three
   classes and three elements with
   `.site-header.site-header--over-hero .site-header__nav ul li a`, and the
   nav links stayed dark. Since this file loads after style.css, that leaves
   only a few explanations: the library declares the colour with !important,
   or it sets it inline, or the script reparents the nav out of
   .site-header__nav on desktop so the selector never matches at all.

   Any of those beats a longer selector, so this uses !important. Not
   something to reach for casually, but this is the case it exists for: an
   undocumented third-party stylesheet setting a value that has to be
   overridden, three polite attempts already spent, and the alternative being
   another round of guessing at markup I cannot see.

   Scoped to the floating header only, so nothing outside it is affected, and
   the Donate pill is excluded because it carries its own fill and label.

   The dropdown reset also takes !important and is more specific than the
   white rule, so panels that paint their own background keep dark links.

   If someone later reads the library and finds the real selector, this whole
   section can be replaced by one rule without !important.
   ------------------------------------------------------------------------- */
.site-header--over-hero .site-header__utility,
.site-header--over-hero .site-header__main,
.site-header--over-hero .site-name,
.site-header--over-hero a:not(.header-giving),
.site-header--over-hero a:not(.header-giving) *,
.site-header--over-hero button:not(.header-giving) {
  color: #FFFFFF !important;
}

/* Dropdown panels keep dark ink. More specific than the rule above, and also
   important, so it wins in both dimensions. */
.site-header--over-hero .site-header__nav ul ul a,
.site-header--over-hero .site-header__nav ul ul a *,
.site-header--over-hero .site-header__nav ul ul a:hover {
  color:       var(--ink) !important;
  text-shadow: none;
}

/* -------------------------------------------------------------------------
   28. v1.5  --  nav colour, done properly against the real markup
   -------------------------------------------------------------------------
   The rendered markup finally explains why sections 17, 20 and 27 all failed.
   The Drop-down Menus module is not a list of links. It is a table:

       table#m.t_horizontal
         td.t_horizontal          one per top-level item
           div.t                  the label -- NOT an anchor
           div.tah                the dropdown panel, hidden until hover
             table.t
               tr.t               carries an onclick handler
                 td.t
                   div.t          the dropdown label
         td.t_horizontal
           div.t_separator        a pipe character between items
         td.nav-more
           button.nav-more__btn   overflow menu when items do not fit
           div.nav-more__panel

   So the top-level items are divs with click handlers rather than anchors.
   Every rule I wrote targeted `a`, which matches nothing here, and the
   nested-list reset targeted `ul ul a`, which matches nothing either. It was
   never a specificity problem: the selectors simply had no targets, and
   piling on !important could not help.

   Two things make the correct rules easy. Top-level labels sit in a
   `td.t_horizontal`, whereas dropdown labels sit in a plain `td.t` inside a
   `.tah` panel, so `td.t_horizontal > div.t` picks out the top level alone.
   And the panels can be reset by scoping to `.tah`.

   !important is kept only because the library's own colour on `.t` may itself
   be declared that way; there is no way to tell from the markup alone. Drop
   it if it turns out to be unnecessary.

   NOTE FOR LATER, NOT A STYLING MATTER: a div with an onclick handler is not
   keyboard focusable and carries no link semantics, so this navigation cannot
   be tabbed through and is invisible to a screen reader as a set of links.
   That belongs with the accessibility work already parked, and it is a
   library issue rather than anything this site introduced.
   ------------------------------------------------------------------------- */

/* Top-level labels, the pipe separators and the overflow button. */
.site-header--over-hero .site-header__nav td.t_horizontal > div.t,
.site-header--over-hero .site-header__nav div.t_separator,
.site-header--over-hero .site-header__nav .nav-more__btn {
  color:       #FFFFFF !important;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* Dropdown panels and the overflow panel paint their own light background,
   so their labels stay dark. Scoped to the panel rather than to nesting
   depth, which is what the earlier attempts got wrong. */
.site-header--over-hero .site-header__nav div.tah div.t,
.site-header--over-hero .site-header__nav div.tah td.t,
.site-header--over-hero .site-header__nav .nav-more__panel,
.site-header--over-hero .site-header__nav .nav-more__panel div.t,
.site-header--over-hero .site-header__nav .nav-more__panel a {
  color:       var(--ink) !important;
  text-shadow: none;
}

/* -------------------------------------------------------------------------
   29. v1.6  --  precise rules, replacing the inference in 23, 24 and 26
   -------------------------------------------------------------------------
   Adam supplied the rendered markup. The feature card is:

       li.gallery_li
         a                              wraps the whole card, links the article
           img                          800x400
           div.explore-church__caption
             span.explore-church__title
             span.explore-church__subtitle

   Three things that were guesswork are now settled. The caption IS split on
   the asterisk into a title and a subtitle, so the two-part format works. The
   caption already sits AFTER the image in document order, so nothing needed
   reordering. And the whole card is wrapped in an anchor carrying the link
   Adam set on each image, so the cards do link through.

   Sections 23, 24 and 26 reached the right result with broad rules across all
   descendants because they had no class names to aim at. Those sections stay
   in the file since it is appended to rather than rewritten, but everything
   below is targeted, and it out-specifies them so it wins.

   The one thing the broad rules got right and should be kept: the caption was
   absolutely positioned over the image, so returning it to static flow is
   what puts the text under the picture. That is now stated on the caption
   itself rather than on every descendant.
   ------------------------------------------------------------------------- */
.slice--explore-church .gallery_li > a {
  display:         block;
  text-decoration: none;
}

.slice--explore-church .gallery_li .explore-church__caption {
  position:    static;
  display:     block;
  padding-top: var(--space-4);
  background:  none;
}

.slice--explore-church .gallery_li .explore-church__title {
  display:     block;
  font-family: var(--font-display);
  font-size:   var(--text-lg);
  font-weight: 600;
  line-height: 1.25;
  color:       var(--ink);
}

.slice--explore-church .gallery_li .explore-church__subtitle {
  display:     block;
  margin-top:  var(--space-2);
  font-size:   var(--text-sm);
  line-height: 1.55;
  color:       var(--ink-soft);
}

/* -------------------------------------------------------------------------
   30. v1.6  --  the Account control, by its real name
   -------------------------------------------------------------------------
   It is `button.account-toggle`, carrying an inline SVG and a span reading
   Account. Section 21 guessed at it with an attribute match on any class
   containing "account", which does happen to hit this, but a named selector
   is better left behind for whoever reads this next.
   ------------------------------------------------------------------------- */
.site-header--no-account .account-toggle {
  display: none;
}

/* -------------------------------------------------------------------------
   31. v1.6  --  the status bar greeting
   -------------------------------------------------------------------------
   The Status Bar renders a greeting div which, to a visitor who is not
   logged in, contains nothing but today's date. Their live site carries no
   date in the header and no visitor needs telling what day it is, so it goes.

   Only the greeting. The Login link beside it stays, and the status bar's own
   search box is left alone: there is a separate search toggle button in the
   header and the script may well be moving this input into it, so hiding it
   could break search rather than tidy it.
   ------------------------------------------------------------------------- */
.site-header__utility .statusbar .greeting_div {
  display: none;
}

/* -------------------------------------------------------------------------
   32. v1.7  --  no hairlines on the floating header
   -------------------------------------------------------------------------
   The library gives the utility row and the main row a bottom hairline, which
   is right when the header sits on a solid background and separates it from
   the page. Over a photograph it is wrong twice over: there is nothing to
   separate, and because a dark band flips hairlines to white, the two rules
   render as bright lines straight across the image.

   Removed on the floating header only. The Content and System templates keep
   their hairlines, where they are doing the job they were written for.

   Borders and shadows are both cleared, and the row pseudo-elements are
   hidden too, because a full-bleed rule of this kind is as often an ::after
   as a border and there is no way to tell which from the render.
   ------------------------------------------------------------------------- */
.site-header.site-header--over-hero,
.site-header--over-hero .site-header__utility,
.site-header--over-hero .site-header__utility-inner,
.site-header--over-hero .site-header__main {
  border:     0;
  box-shadow: none;
}

.site-header--over-hero .site-header__utility::before,
.site-header--over-hero .site-header__utility::after,
.site-header--over-hero .site-header__main::before,
.site-header--over-hero .site-header__main::after {
  display: none;
}

/* -------------------------------------------------------------------------
   33. v1.8  --  taller hero, at the source images' own proportions
   -------------------------------------------------------------------------
   The slides are now 1600x700, matching the crop their live site uses, so the
   hero can show them at their real proportions instead of the 520px band it
   had been squeezed into.

   WHY THE HEIGHT NEEDS FORCING. bxSlider writes the viewport height as an
   inline style, measured once at initialisation, so a class alone cannot
   change it. A stylesheet declaration marked important does beat an inline
   style, which is why it is used here and only here.

   HOW THE HEIGHT IS DERIVED. `aspect-ratio: 1600 / 700` gives the viewport a
   definite height from its own width, which also avoids the trap in setting
   `height: auto` on this element: the slides are floated, so they contribute
   nothing to their parent's height and the viewport would collapse to
   nothing.

   THE CAP IS DELIBERATE AT 700px, because that is the source images' actual
   pixel height. Beyond it the browser would be upscaling and the photographs
   would visibly soften, so the cap is the point where quality would start to
   go rather than an arbitrary number. The 78vh alongside it keeps the hero
   from filling a laptop screen entirely, so a sliver of the next section
   stays visible and the page reads as scrollable.

   Once capped, the image crops rather than distorts: object-fit cover with a
   centred focal point. Worth knowing that on a very wide window the crop will
   take from the top and bottom, so a slide whose subject sits near an edge
   will lose some of it.
   ------------------------------------------------------------------------- */
.slice--hero .bx-viewport {
  height:       auto !important;
  aspect-ratio: 1600 / 700;
  max-height:   min(78vh, 700px);
}

.slice--hero .gallery_ul,
.slice--hero .gallery_li {
  height: 100%;
}

.slice--hero .gallery_li img {
  width:           100%;
  height:          100%;
  object-fit:      cover;
  object-position: center;
}

/* -------------------------------------------------------------------------
   34. v1.9  --  hero caption to the content gutter, and a taller cap
   -------------------------------------------------------------------------
   Compared side by side with their live homepage, two things were off.

   1. THE CAPTION SAT TOO FAR RIGHT. Theirs begins at the same left edge as
      the wordmark, on the site's content gutter. Ours began around twice as
      far in. Section 19 tried to fix this with `margin-left: 0`, which did
      nothing, because the caption is absolutely positioned and centred with a
      transform. Margins have no effect on it. So it is positioned properly
      here instead: stretched across the slide, then padded in to the content
      gutter, which is what puts its left edge in line with the logo above it.

      The padding uses `max()` against the container width, so the caption
      lines up with the site's centred content column on wide screens and
      falls back to the plain gutter on narrow ones. That is the same
      calculation the container itself uses, which is why the two agree at
      every width rather than only at one.

   2. THE HEIGHT WAS BEING CAPPED BY 78vh, NOT BY THE ASPECT RATIO. On a
      laptop-height window that works out around 520px, which is exactly the
      height the hero was stuck at before, so section 33 looked as though it
      had not applied. Raised to 92vh: still leaves a sliver of the next
      section visible so the page reads as scrollable, but no longer overrides
      the proportions on a normal screen. The 700px source-height cap is
      unchanged and still the real limit on a large monitor.

   ON POINTER EVENTS. Stretching the caption across the slide puts it over the
   image, which would swallow clicks intended for the image's own link. The
   caption is therefore transparent to the pointer, and only the call-to-action
   inside it takes clicks back. Without this, setting a Link on a hero image
   would silently stop working.
   ------------------------------------------------------------------------- */
.slice--hero .hero_slideshow .hero-caption {
  position:        absolute;
  inset:           0;
  transform:       none;
  width:           auto;
  max-width:       none;
  display:         flex;
  flex-direction:  column;
  align-items:     flex-start;
  justify-content: flex-end;
  text-align:      left;
  padding-block:   clamp(var(--space-6), 5vh, var(--space-12));
  padding-inline:  max(var(--container-gutter),
                       calc((100% - var(--container-width)) / 2));
  pointer-events:  none;
}

.slice--hero .hero_slideshow .hero-caption__cta {
  pointer-events: auto;
}

/* Wrap the text at a comfortable measure rather than letting it run the full
   width of a wide photograph. Their titles break to two or three short lines,
   which is what gives the hero its shape. */
.slice--hero .hero_slideshow .hero-caption__title {
  max-width: 32rem;
}

.slice--hero .hero_slideshow .hero-caption__subtitle {
  max-width: 34rem;
}

/* Raise the viewport cap so the aspect ratio, not the window height, decides
   how tall the hero is on a normal screen. */
.slice--hero .bx-viewport {
  max-height: min(92vh, 700px);
}

/* -------------------------------------------------------------------------
   35. v2.0  --  drop the source-height cap: it was cropping, not protecting
   -------------------------------------------------------------------------
   Section 33 capped the hero at 700px on the reasoning that this is the source
   images' pixel height, so anything taller would upscale and soften. Sound in
   principle, wrong in effect, and this is what it actually did on a wide
   window.

   At around 2520px wide the aspect ratio wants a hero 1102px tall. The cap
   held it at 700. The image still had to cover the full width, so the browser
   scaled it up by about 1.6 anyway and then discarded roughly 400px from the
   top and bottom to fit the shorter box. So the cap did not prevent any
   upscaling at all: it simply threw away a third of the photograph on top of
   it. Compared against the source image, the subject reads as heavily zoomed
   and the band as a thin letterbox.

   Traded a visible crop for a theoretical gain in sharpness, and got it the
   wrong way round. A slightly soft photograph that is whole beats a crisp
   detail of somebody's face.

   The vh ceiling stays, because it is doing real work: on an unusually short
   or ultrawide window the aspect ratio would otherwise make the hero taller
   than the viewport and push the caption below the fold. On a normal screen it
   is never the binding constraint, since window height tends to scale with
   window width, so the hero settles around three quarters of the viewport at
   any size.

   The upscaling this now permits is real, and the answer to it is better
   source files rather than a cap. See HERO-4: 2400px-wide sources at the same
   proportions would remove it entirely with no change here.
   ------------------------------------------------------------------------- */
.slice--hero .bx-viewport {
  max-height: 92vh;
}

/* -------------------------------------------------------------------------
   36. v2.1  --  hero: one explicit height, applied all the way down
   -------------------------------------------------------------------------
   Sections 33 to 35 grew the viewport but left the image at its old height,
   so the hero opened up and the new space was simply white. Two mistakes,
   both mine.

   1. THE PERCENTAGE CHAIN COULD NOT WORK. Those sections sized the viewport
      with `height: auto` plus `aspect-ratio`, then tried to fill it with
      `height: 100%` on the list, the item and the image. A percentage height
      resolves against the parent's height only when that height is definite,
      and `height: auto` is treated as indefinite even when aspect-ratio
      supplies a used height. So every `height: 100%` in the chain fell back to
      auto and none of them did anything.

   2. THE IMAGE HEIGHT WAS NOT BEING OVERRIDDEN ANYWAY. The library holds the
      hero image at around 520px with a longer selector than
      `.slice--hero .gallery_li img`, so that rule lost on specificity.

   Both are fixed by dropping percentages entirely. The height is computed once
   as a custom property on the slice and then applied as an explicit length to
   the viewport, the list, the item and the image. An explicit length needs no
   parent to resolve against, so nothing can silently fall back, and it is
   marked important because bxSlider writes the viewport height inline and the
   library out-specifies plain selectors on the image.

   The ratio has to be expressed in vw rather than a percentage: percentages in
   a height resolve against height, so `calc(100% * 700 / 1600)` would be
   measuring the wrong axis. 0.4375 is 700/1600. Using vw includes the
   scrollbar width, which makes the hero a few pixels taller than strictly
   needed, which cover absorbs without anything visible.

   ON THE REMAINING CROP. Where 92vh is the smaller of the two, the image is
   taller than the box and cover trims it top and bottom. On a window that is
   wide but not tall, that is unavoidable: either the hero exceeds the viewport
   and the caption drops below the fold, or some of the photograph goes. The
   cap chooses the caption. If the whole frame matters more, remove the min()
   and let the ratio alone decide.
   ------------------------------------------------------------------------- */
.slice--hero {
  --hero-h: min(92vh, calc(100vw * 0.4375));
}

.slice--hero .bx-viewport {
  height:       var(--hero-h) !important;
  max-height:   none;
  aspect-ratio: auto;
}

.slice--hero .bx-viewport .gallery_ul,
.slice--hero .bx-viewport .gallery_li {
  height: var(--hero-h) !important;
}

.slice--hero .bx-viewport .gallery_li img {
  height:          var(--hero-h) !important;
  width:           100% !important;
  max-height:      none !important;
  object-fit:      cover;
  object-position: center;
}

/* -------------------------------------------------------------------------
   37. v2.2  --  no scrim, no status bar, and social icons that exist
   -------------------------------------------------------------------------

   SCRIM REMOVED. Sections 17 and 20 laid a dark gradient across the floating
   header so white type stayed legible over any photograph. Adam wants it gone.
   It goes, and the text shadow underneath does the work instead: slightly
   strengthened, since it is now the only thing standing between the nav and a
   pale slide. That is a weaker guarantee than a scrim, so if a light image is
   ever added to the slideshow this is the first place to look.

   Adam's note was that the shading is only wanted on scroll, which is a
   different thing: a header that becomes solid once the hero has passed. That
   needs a script watching scroll position and toggling a class, which belongs
   in the library rather than a client skin. Logged as HDR-4.

   STATUS BAR LINKS REMOVED. Only the links, not the whole status bar. The
   module also contains a search input and its button, and there is a separate
   search toggle in the header that the script may well be moving that input
   into, so hiding the whole thing risks breaking search to tidy something that
   is already invisible. Hiding the anchors removes everything a visitor can
   actually see there, which is what was asked.

   SOCIAL ICONS. The markup was `i` elements carrying icomoon classes with a
   thin space as the glyph, and that icon font belongs to the Aspire stack
   which this build does not load, so nothing rendered at all. Rather than
   chase a font, the article now holds inline SVG, in the same outline style
   as the header's own account icon and the impact stats icons.

   Each icon draws with `currentColor`, so it takes the colour of whatever it
   sits in and needs no per-context override: white against the hero, white
   against the dark footer, and it would follow the ink on a light background
   without further work. Sizing is set here rather than in the article, so the
   markup stays free of presentation.
   ------------------------------------------------------------------------- */

/* No gradient on the floating header. */
.site-header.site-header--over-hero {
  background: transparent;
}

/* The shadow is now the only legibility guarantee, so it carries more weight. */
.site-header.site-header--over-hero .site-header__nav td.t_horizontal > div.t,
.site-header.site-header--over-hero .site-name,
.site-header.site-header--over-hero .site-header__nav .nav-more__btn,
.site-header.site-header--over-hero .header-social .footer_icon_block {
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

/* Status bar: hide the links, leave the search machinery alone. */
.site-header__utility .statusbar > a {
  display: none;
}

/* Social icons, header and footer. */
.header-social .footer_icon_block,
.social_media_icons .footer_icon_block {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  text-decoration: none;
}

.header-social .footer_icon_block svg,
.social_media_icons .footer_icon_block svg {
  display: block;
  width:   1.2rem;
  height:  1.2rem;
}

/* The footer band is dark, so the icons take white there too. */
.site-footer .social_media_icons .footer_icon_block {
  color: #FFFFFF;
}

/* A drop shadow rather than a text shadow, since an SVG stroke does not take
   text-shadow. Same purpose: hold the icons against a pale photograph. */
.site-header--over-hero .header-social .footer_icon_block svg {
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
}

/* -------------------------------------------------------------------------
   38. v2.3  --  one logo lockup, replacing logo plus site name
   -------------------------------------------------------------------------
   Their logo already contains the wordmark, so the Site Name module beside it
   was printing "Romsey Mill" a second time. Both modules are gone from all
   three templates and replaced by a single linked image.

   WHY NOT THE SITE LOGO MODULE. It reads its image from Site Details, which
   this API does not expose, so there was no way to point it at the right file.
   Hard-coding the lockup into the header is the trade: it works now, but it
   means the logo is no longer set in one place for emails and the app as well.
   Worth still setting it in Site Details when someone is next in the Web
   Office, at which point the module could come back and this markup go.

   THE LOCKUP IS WHITE. Its SVG carries a colour-matrix filter forcing every
   channel to 1, so it is white by construction, which is right over the hero
   photograph and invisible on the solid headers of the content and system
   templates. Rather than ask for a second file, those get `brightness(0)`,
   which on an all-white image with transparency renders it black while leaving
   the alpha alone. Not the brand's own dark version, but honest black rather
   than nothing, and one line to remove if a proper dark asset appears.

   Height rather than width, so the lockup scales with the header rather than
   with the viewport, and `width: auto` keeps its proportions whatever file is
   swapped in later.
   ------------------------------------------------------------------------- */
.site-header__brand .site-lockup {
  display:         inline-flex;
  align-items:     center;
  text-decoration: none;
}

.site-header__brand .site-lockup img {
  display: block;
  width:   auto;
  height:  clamp(2.25rem, 3.2vw, 3.25rem);
}

/* Solid headers: the white lockup would disappear, so render it black. */
.site-header:not(.site-header--over-hero) .site-lockup img {
  filter: brightness(0);
}

/* Over the photograph, the same drop shadow the icons and nav rely on now the
   scrim has gone. */
.site-header--over-hero .site-lockup img {
  filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.45));
}

/* -------------------------------------------------------------------------
   39. v2.4  --  logo scale, social spacing, a light scrim, hover colour
   -------------------------------------------------------------------------

   THE HOVER COLOUR WAS A SELECTOR GAP, NOT A CHOICE. The menu module swaps
   the cell's class on hover: `td.t_horizontal` becomes `td.t_horizontal_hi`.
   Section 28 only ever targeted `t_horizontal`, so the moment the pointer
   arrived the white rule stopped matching and the label fell back to the
   library's own colour, which is the accent purple. Light purple on a
   photograph is close to unreadable, which is what Adam saw. Both states are
   now covered.

   Hover is marked with an underline rather than a colour change. Over
   photography a colour shift is unreliable, and an underline does not depend
   on the eye resolving two similar tones. It also means the affordance is not
   carried by colour alone, which matters for anyone who cannot distinguish
   them.

   A LIGHT SCRIM RETURNS. Section 37 removed the gradient entirely and left the
   text shadow to hold everything on its own, which is why the social icons
   became hard to pick out. This is roughly half the weight of the original:
   0.32 at the top rather than 0.70, and gone by the foot of the header. Enough
   to lift the row off the image without reading as a dark band across the
   photograph.

   LOGO. Larger everywhere, so the brand is consistent across the three
   templates. Lifted only on the floating header, where the bar overlaps the
   photograph and there is room above it. The lift is a transform rather than a
   margin, so it cannot alter the header's height or push anything else around
   whatever the library's layout is doing.

   SOCIAL SPACING. Up one step on the scale, from 0.75rem to 1.25rem. Enough to
   read as five separate marks rather than a strip, without them drifting apart.
   ------------------------------------------------------------------------- */

/* A much lighter top scrim than the one removed in section 37. */
.site-header.site-header--over-hero {
  background: linear-gradient(to bottom,
                              rgba(0, 0, 0, 0.32) 0%,
                              rgba(0, 0, 0, 0.14) 55%,
                              rgba(0, 0, 0, 0) 100%);
}

/* Logo: bigger on every template. */
.site-header__brand .site-lockup img {
  height: clamp(2.75rem, 4.2vw, 4.25rem);
}

/* Lifted only where the header floats over the image. */
.site-header--over-hero .site-header__brand .site-lockup img {
  transform: translateY(-0.85rem);
}

/* Social icons: a little further apart. */
.header-social .ArticleBody,
.header-social .ArticleBody ul,
.header-social .ArticleBody p {
  gap: var(--space-5);
}

/* Nav: the hovered cell keeps white type, and gains an underline. */
.site-header--over-hero .site-header__nav td.t_horizontal_hi > div.t,
.site-header--over-hero .site-header__nav td.t_horizontal:hover > div.t {
  color:       #FFFFFF !important;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.site-header--over-hero .site-header__nav td.t_horizontal_hi > div.t,
.site-header--over-hero .site-header__nav td.t_horizontal:hover > div.t,
.site-header--over-hero .site-header__nav .nav-more__btn:hover {
  text-decoration:       underline;
  text-underline-offset: 6px;
  text-decoration-thickness: 2px;
}

/* The solid headers on Content and System: hover stays on the ink colour with
   the same underline, rather than the accent, for the same reason. */
.site-header:not(.site-header--over-hero) .site-header__nav td.t_horizontal_hi > div.t,
.site-header:not(.site-header--over-hero) .site-header__nav td.t_horizontal:hover > div.t {
  color:                 var(--ink);
  text-decoration:       underline;
  text-underline-offset: 6px;
  text-decoration-thickness: 2px;
}

/* =========================================================================
   40. v2.5  --  MOTION SYSTEM
   =========================================================================
   A consistent, quiet motion language, replacing the assorted one-off
   transitions written earlier in this file.

   THE PRINCIPLES, so later additions can follow them rather than guess.

   ONE CURVE. Everything decelerates on the same curve. A single easing across
   a site is most of what makes motion feel considered rather than assembled:
   the eye learns the timing and stops noticing it. No bounce or overshoot
   anywhere. Overshoot reads as playful on a product site and as flippant on a
   charity's, and this is a charity working with vulnerable young people.

   THREE DURATIONS, and only three.
     fast  160ms  colour and opacity on small things
     base  240ms  hovers, lifts, underlines
     slow  520ms  entrances
   Entrances are slower than responses on purpose. An entrance is the page
   introducing itself and can afford to be gentle; a response to a pointer must
   feel immediate or it reads as lag rather than polish.

   ONE DISTANCE. Reveals rise 18px. Large travel looks cheap because it draws
   attention to the animation rather than the content. Small travel reads as
   the page settling.

   THE STAGGER IS THE BIG ONE. The library's reveal delays came out as 0, 450,
   900 and 1350ms, so on the news row the fourth card landed nearly a second
   and a half after the first. That is what makes a page feel slow rather than
   expensive. Retuned to 70ms, which is enough for the eye to read the sequence
   as a sequence and quick enough that the whole row has settled inside half a
   second.

   The stagger token is set in case the script reads it at runtime, and the
   per-position delays are also stated directly, because those values arrive as
   inline styles and an inline style can only be beaten by an important
   declaration. If the script turns out to compute the delays from a constant of
   its own, the token change will do nothing on its own and the enumerated rules
   are what actually take effect.

   FILLED BUTTONS DARKEN, THEY DO NOT FADE. Earlier sections dropped several
   pills to `opacity: 0.7` on hover, which dims the label along with the fill
   and, over a photograph, lets the background show through the button. Every
   filled control now darkens its fill and lifts a single pixel instead. One
   pixel is deliberate: enough to feel responsive, not enough to look like the
   button is trying to escape.

   WHY THERE IS NO PAGE-LOAD FADE. A fade applied to the whole page means
   starting at zero opacity and depending on something to bring it back. If a
   script fails or a stylesheet is slow, the visitor gets a blank white page
   rather than an unstyled one, which is a far worse failure. The library's
   reveal system already handles entrances element by element and degrades to
   simply being visible, so it is the safer place to invest.

   REDUCED MOTION IS HONOURED AT THE END OF THIS SECTION. A reveal system that
   ignores it is an accessibility fault, not a stylistic choice: for some people
   this kind of movement causes nausea or headaches.
   ========================================================================= */

:root {
  --motion-ease:   cubic-bezier(0.2, 0, 0, 1);
  --motion-fast:   160ms;
  --motion-base:   240ms;
  --motion-slow:   520ms;

  /* Realign the token set earlier in this file onto the scale above. */
  --transition:    var(--motion-base) var(--motion-ease);

  /* The library's own reveal tokens. */
  --reveal-distance: 18px;
  --reveal-duration: var(--motion-slow);
  --reveal-stagger:  70ms;
  --ease-out-soft:   var(--motion-ease);
}

/* -------------------------------------------------------------------------
   40.1  Reveal stagger
   -------------------------------------------------------------------------
   Position-based, because the delay arrives inline. Eight is more than any row
   on this site needs; beyond that the delay holds at the last value rather than
   growing, since a ninth item waiting 630ms would be worse than one arriving
   with its neighbour.
   ------------------------------------------------------------------------- */
.hubb-reveal:nth-child(1)  { --reveal-delay:   0ms !important; }
.hubb-reveal:nth-child(2)  { --reveal-delay:  70ms !important; }
.hubb-reveal:nth-child(3)  { --reveal-delay: 140ms !important; }
.hubb-reveal:nth-child(4)  { --reveal-delay: 210ms !important; }
.hubb-reveal:nth-child(5)  { --reveal-delay: 280ms !important; }
.hubb-reveal:nth-child(6)  { --reveal-delay: 350ms !important; }
.hubb-reveal:nth-child(n+7){ --reveal-delay: 420ms !important; }

/* -------------------------------------------------------------------------
   40.2  Filled controls: darken and lift
   -------------------------------------------------------------------------
   Replaces the opacity fades in sections 4, 5, 7 and 11.
   ------------------------------------------------------------------------- */
.header-giving,
.section-link,
.side-card__cta,
.content-shell__main input.button {
  transition: background-color var(--motion-base) var(--motion-ease),
              border-color     var(--motion-base) var(--motion-ease),
              transform        var(--motion-base) var(--motion-ease),
              box-shadow       var(--motion-base) var(--motion-ease);
  will-change: transform;
}

.site-header--tworow .header-giving:hover,
.site-header--split  .header-giving:hover {
  background-color: var(--rm-orange-edge);
  border-color:     var(--rm-orange-edge);
  opacity:          1;
  transform:        translateY(-1px);
  box-shadow:       0 6px 16px -8px rgba(0, 0, 0, 0.5);
}

.section-link:hover,
.side-card__cta:hover,
.content-shell__main input.button:hover {
  background-color: var(--accent-dark);
  border-color:     var(--accent-dark);
  opacity:          1;
  transform:        translateY(-1px);
  box-shadow:       0 6px 16px -10px rgba(35, 49, 61, 0.55);
}

/* Pressed state. Returning to zero on click is what makes a button feel
   physical rather than decorative. */
.header-giving:active,
.section-link:active,
.side-card__cta:active,
.content-shell__main input.button:active {
  transform:  translateY(0);
  box-shadow: none;
  transition-duration: var(--motion-fast);
}

/* -------------------------------------------------------------------------
   40.3  Nav underline, drawn rather than switched on
   -------------------------------------------------------------------------
   Section 39 marked hover with `text-decoration: underline`, which appears
   instantly and reads as abrupt. A pseudo-element bar scaling out from the left
   does the same job and settles instead of snapping. Same treatment on both the
   floating and the solid headers, so the interaction is identical wherever the
   visitor is.
   ------------------------------------------------------------------------- */
.site-header .site-header__nav td.t_horizontal > div.t,
.site-header .site-header__nav td.t_horizontal_hi > div.t {
  position:        relative;
  text-decoration: none;
}

.site-header .site-header__nav td.t_horizontal > div.t::after,
.site-header .site-header__nav td.t_horizontal_hi > div.t::after {
  content:          "";
  position:         absolute;
  left:             0;
  right:            0;
  bottom:           -6px;
  height:           2px;
  background-color: currentColor;
  transform:        scaleX(0);
  transform-origin: left center;
  transition:       transform var(--motion-base) var(--motion-ease);
}

.site-header .site-header__nav td.t_horizontal:hover > div.t::after,
.site-header .site-header__nav td.t_horizontal_hi > div.t::after {
  transform: scaleX(1);
}

/* -------------------------------------------------------------------------
   40.4  Feature cards
   -------------------------------------------------------------------------
   A slow, small zoom on the photograph and the title picking up the accent.
   Four per cent over half a second is at the threshold of being noticed, which
   is the point: it should feel like the card responding rather than performing.

   The anchor needs its overflow clipped for the zoom to stay inside the frame.
   Section 26 set `overflow: visible` across every descendant to release the
   old fixed-height overlay, so this puts it back on the anchor alone.
   ------------------------------------------------------------------------- */
.slice--explore-church .gallery_li > a {
  overflow: hidden;
}

.slice--explore-church .gallery_li img {
  transition: transform var(--motion-slow) var(--motion-ease);
}

.slice--explore-church .gallery_li > a:hover img,
.slice--explore-church .gallery_li > a:focus-visible img {
  transform: scale(1.04);
}

.slice--explore-church .gallery_li .explore-church__title {
  transition: color var(--motion-base) var(--motion-ease);
}

.slice--explore-church .gallery_li > a:hover .explore-church__title,
.slice--explore-church .gallery_li > a:focus-visible .explore-church__title {
  color: var(--accent);
}

/* -------------------------------------------------------------------------
   40.5  News and event cards
   -------------------------------------------------------------------------
   Titles and Read more links share one treatment, so the two feeds behave
   identically. Colour only: these sit on white and a lift would need a card
   behind it to lift from.
   ------------------------------------------------------------------------- */
.al-cards-feature .articleListTitle a,
.al-cards-feature .articleListImage img {
  transition: color     var(--motion-base) var(--motion-ease),
              opacity   var(--motion-base) var(--motion-ease),
              transform var(--motion-slow) var(--motion-ease);
}

.al-cards-feature .articleListTitle a:hover {
  color: var(--accent);
}

/* -------------------------------------------------------------------------
   40.6  Social icons and the sidebar navigation
   ------------------------------------------------------------------------- */
.header-social .footer_icon_block,
.social_media_icons .footer_icon_block {
  transition: color     var(--motion-fast) var(--motion-ease),
              transform var(--motion-base) var(--motion-ease);
}

.header-social .footer_icon_block:hover,
.social_media_icons .footer_icon_block:hover {
  transform: translateY(-2px);
}

.content-shell__side .group_nav_selected > a,
.content-shell__side .group_nav_sibling > a,
.content-shell__side .group_nav_child > a {
  transition: background-color var(--motion-fast) var(--motion-ease),
              color            var(--motion-fast) var(--motion-ease),
              padding-left     var(--motion-base) var(--motion-ease);
}

/* A small indent on hover, which suggests moving into the section rather than
   just highlighting a row. */
.content-shell__side .group_nav_sibling > a:hover,
.content-shell__side .group_nav_child > a:hover {
  padding-left: var(--space-4);
}

/* -------------------------------------------------------------------------
   40.7  Focus, visible and consistent
   -------------------------------------------------------------------------
   Same ring everywhere, so keyboard users get one predictable signal rather
   than whatever each component happened to inherit. White over the hero, where
   the accent would be lost against the photograph.
   ------------------------------------------------------------------------- */
.header-giving:focus-visible,
.section-link:focus-visible,
.side-card__cta:focus-visible,
.site-lockup:focus-visible,
.slice--explore-church .gallery_li > a:focus-visible,
.al-cards-feature a:focus-visible,
.footer_icon_block:focus-visible,
.content-shell__side a:focus-visible {
  outline:        3px solid var(--accent);
  outline-offset: 3px;
  border-radius:  4px;
}

.site-header--over-hero a:focus-visible,
.site-header--over-hero .footer_icon_block:focus-visible {
  outline: 3px solid #FFFFFF;
}

/* -------------------------------------------------------------------------
   40.8  Reduced motion
   -------------------------------------------------------------------------
   Not a preference to be weighed against the design. For some people this kind
   of movement causes nausea or headaches, so when it is asked for, all of it
   stops. The near-zero duration rather than `none` is deliberate: it keeps
   transitionend events firing, so any script waiting on one does not stall.

   Revealed elements are forced visible, because the reveal starts from zero
   opacity and without this the content would simply never appear.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration:        0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration:       0.01ms !important;
    transition-delay:          0ms !important;
    scroll-behavior:           auto !important;
  }

  .hubb-reveal {
    opacity:   1 !important;
    transform: none !important;
  }

  .slice--explore-church .gallery_li > a:hover img {
    transform: none !important;
  }
}

/* =========================================================================
   41. v2.6  --  COLOUR SYSTEM
   =========================================================================
   Their logo is monochrome, so all the colour on this site comes from the
   three brand colours on their current homepage: blue, purple and orange. This
   section gives each one a job and stops using them interchangeably.

   THE RULE, in a form that fits in your head:

       PURPLE  anything you can click
       BLUE    anything that organises or informs
       ORANGE  give money, and nothing else

   WHY THOSE ROLES, and not the other way round. Contrast decides most of it
   rather than taste. Measured on white:

       purple      #7A4A69   6.98:1   safe for text at any size
       blue        #4E87B3   3.86:1   fails for body text, large only
       blue-deep   #3A6A8E   5.78:1   safe for text
       orange      #F15A29   3.37:1   fails at every size

   Only purple among the three originals can carry small text, so purple has to
   be the interactive colour: links and controls are the one thing that must
   always be legible and must always look the same. Orange cannot carry text at
   all, so it can only ever be a fill, which suits a single loud action and
   nothing else. Blue sits between them, and the deepened blue is what gets
   used wherever a stroke or a word is involved.

   ORANGE APPEARS ONCE. Their own site already does this: orange is on the
   Donate button and nowhere else, and that scarcity is exactly what gives it
   force. Put orange on a second button and the first one stops meaning "this
   is how you give".

   HIGHLIGHTS, NOT BLOCKS. Per Adam: colour arrives as rules, icons, small
   fills and states. Sections are white or light grey. The two card headers
   that were solid purple keep their structure but reduce to a light fill with
   a 3px blue rule, which is enough to read as a header without becoming a
   coloured panel.

   ONE GREY, SLIGHTLY COOL. There were two near-identical light greys in this
   file, #F1F1F1 and #F4F4F4, which is the sort of thing that reads as
   inattention. Now one value, and very slightly cool at #F2F4F6: a neutral
   grey next to purple tends to look muddy, whereas a cool grey reads as
   deliberate and sits with both the blue and the purple.

   WHAT IS DELIBERATELY LEFT DARK. The newsletter band and the footer. Adam
   chose that newsletter treatment specifically, so it is not being quietly
   undone here, and their own footer is dark. Say the word if the newsletter
   band should join the white and grey scheme.
   ========================================================================= */

:root {
  /* One grey, used by every tinted section and panel. */
  --section-light:     #F2F4F6;
  --section-light-rgb: 242, 244, 246;
  --rm-neutral:        #F2F4F6;

  /* Blue is now a working colour rather than a spare. The deep value is the
     one used for anything with a stroke or a word in it. */
  --rm-info:      #3A6A8E;
  --rm-info-soft: #E8EEF4;
}

/* -------------------------------------------------------------------------
   41.1  BLUE: things that organise or inform
   ------------------------------------------------------------------------- */

/* The rule under a section heading. Structure rather than action, so blue. */
.slice__heading::after,
.slice__head--centred .slice__heading::after {
  background-color: var(--rm-info);
}

/* The rule under a page title, on both content and system pages. */
.content_article .ArticleBody h1::after,
.content-shell--narrow .content-shell__main h1::after {
  background-color: var(--rm-info);
}

/* Impact figures. The icons are thin strokes, so they need a colour with real
   contrast rather than a decorative tint. Blue keeps purple reserved for
   things that respond to a pointer. */
.slice--stats {
  --stat-icon-color: var(--rm-info);
}

/* Sidebar card headers. Previously a solid purple bar, which is a coloured
   block rather than a highlight. Light fill, ink text, blue rule above. */
.side-card__head {
  background-color: var(--section-light);
  color:            var(--ink);
  border-top:       3px solid var(--rm-info);
}

/* The group navigation boxout header gets the same treatment, so the two
   sidebar cards match. */
.content-shell__side .boxout_header {
  background-color: var(--section-light);
  border-top:       3px solid var(--rm-info);
}
.content-shell__side .boxout_header_middle,
.content-shell__side .boxout_header_middle a {
  color: var(--ink);
}

/* Field and card labels. Small informational captions, so blue rather than the
   muted ink they had. */
.side-card__label,
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div.SubCaption {
  color: var(--rm-info);
}

/* -------------------------------------------------------------------------
   41.2  PURPLE: anything you can click
   -------------------------------------------------------------------------
   Most of this was already purple, so these rules mainly close the gaps and
   make the intent explicit rather than incidental.
   ------------------------------------------------------------------------- */

/* Body links inside articles. */
.content_article .ArticleBody a,
.al-cards-feature .articleListTitle a:hover,
.slice--explore-church .gallery_li > a:hover .explore-church__title {
  color: var(--accent);
}

/* The nav underline is an interactive state, so purple on the solid headers.
   It stays white over the hero, where purple would disappear into the
   photograph. */
.site-header:not(.site-header--over-hero) .site-header__nav td.t_horizontal > div.t::after,
.site-header:not(.site-header--over-hero) .site-header__nav td.t_horizontal_hi > div.t::after {
  background-color: var(--accent);
}

/* Sidebar navigation hover. A pale purple wash, which is a highlight rather
   than a fill. */
.content-shell__side .group_nav_selected > a:hover,
.content-shell__side .group_nav_sibling > a:hover,
.content-shell__side .group_nav_child > a:hover {
  background-color: var(--accent-soft);
}

/* -------------------------------------------------------------------------
   41.3  ORANGE: the one action
   -------------------------------------------------------------------------
   Nothing is added here. This block exists to record that orange is reserved,
   so the next person to want a coloured button reaches for purple instead.

   Donate uses --rm-orange-deep as its fill rather than the brand orange,
   because white on the brand value is 3.37:1 and fails. The deepened value
   reaches 5.00:1 and reads as the same button.
   ------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------
   41.4  Sections: white and light grey only
   -------------------------------------------------------------------------
   The band classes are set in the templates. The rhythm down the homepage is
   white for the strapline and the three cards, grey for the impact figures,
   white for the news, grey for the events, so no two adjacent sections share a
   background and nothing needs a coloured panel to separate it.

   The stats band was dark grey until now, which is why it carried white icons.
   That override was scoped to the dark class, so it simply stops applying.
   ------------------------------------------------------------------------- */
.slice--light-grey {
  background-color: var(--section-light);
}

/* =========================================================================
   42. v2.7  --  STICKY HEADER THAT SOLIDIFIES, AND ONE VECTOR LOGO
   =========================================================================

   THE LOGO FIRST, because it makes the rest simpler. The black file Adam sent
   is a genuine vector: three paths, 6KB, against 163KB for the white one, which
   turned out to be a 2400px bitmap wrapped in SVG with a filter forcing it
   white. So the black vector is now the only logo file on the site, stored
   beside this stylesheet as logo.svg.

   One file covers both states, because `invert(1)` turns black artwork white
   and `invert(0)` leaves it black, and the two interpolate smoothly. That
   replaces the `brightness(0)` workaround in section 38, drops 157KB from every
   page load, and closes both LOGO-2 and LOGO-3 at once. No second asset to keep
   in step.

   THE SCROLL STATE IS CSS-ONLY. A solid-on-scroll header is normally a script
   watching scroll position and toggling a class. A scroll-driven animation does
   the same thing in the stylesheet, which means it needs no library change and
   cannot break if a script fails to load.

   WHY THE PROPERTIES ARE REGISTERED. An unregistered custom property has no
   type, so a browser cannot interpolate it and flips it at the midpoint
   instead. Registering each one with `@property` and a syntax is what makes the
   header fade rather than snap.

   THE FALLBACK IS TODAY'S BEHAVIOUR, WHICH IS THE POINT. Everything here sits
   inside a feature query. Without support the header keeps the absolute
   positioning from section 17 and scrolls away with the hero, exactly as it does
   now. That matters: a fixed transparent header in a browser that cannot run
   the animation would be white type on white content, which is far worse than
   no stickiness at all. So the enhancement is opt-in by capability, not by
   hope.

   CONTENT AND SYSTEM PAGES STICK UNCONDITIONALLY, with no feature query needed,
   because their headers are already solid white with a black logo and have
   nothing to transition. Sticky positioning is twenty years old and safe.

   THE RANGE is 48px to 240px of scroll. Starting slightly below zero means an
   accidental one-pixel scroll does not begin the change, and finishing at 240px
   means it is complete well before the hero has left the screen, so the reader
   never sees a half-solid bar sitting over a photograph.
   ========================================================================= */

@property --hdr-surface     { syntax: "<color>";  inherits: false; initial-value: rgba(255, 255, 255, 0); }
@property --hdr-ink         { syntax: "<color>";  inherits: true;  initial-value: #FFFFFF; }
@property --hdr-logo-invert { syntax: "<number>"; inherits: true;  initial-value: 1; }
@property --hdr-veil        { syntax: "<number>"; inherits: false; initial-value: 0.32; }
@property --hdr-lift        { syntax: "<number>"; inherits: false; initial-value: 0; }

/* -------------------------------------------------------------------------
   42.1  Content and System: solid headers, simply made sticky
   ------------------------------------------------------------------------- */
.site-header:not(.site-header--over-hero) {
  position:         sticky;
  top:              0;
  z-index:          200;
  background-color: var(--bg);
  --hdr-logo-invert: 0;
}

/* -------------------------------------------------------------------------
   42.2  The logo, one file, two states
   ------------------------------------------------------------------------- */
.site-header .site-header__brand .site-lockup img,
.site-header:not(.site-header--over-hero) .site-header__brand .site-lockup img {
  filter: invert(var(--hdr-logo-invert));
}

/* Over the photograph the logo keeps a shadow, and that shadow fades out with
   the veil as the bar turns white. Filters compose, so the invert and the
   shadow coexist. */
.site-header--over-hero .site-header__brand .site-lockup img {
  filter: invert(var(--hdr-logo-invert))
          drop-shadow(0 1px 4px rgba(0, 0, 0, calc(var(--hdr-veil) * 1.4)));
}

/* -------------------------------------------------------------------------
   42.3  The floating header consumes the animated properties
   -------------------------------------------------------------------------
   Written outside the feature query so the values still resolve to their
   initial state in a browser without scroll-driven animation, which is what
   makes the fallback identical to today rather than broken.
   ------------------------------------------------------------------------- */
.site-header.site-header--over-hero {
  background-color: var(--hdr-surface);
  background-image: linear-gradient(to bottom,
                                    rgba(0, 0, 0, var(--hdr-veil)) 0%,
                                    rgba(0, 0, 0, calc(var(--hdr-veil) * 0.44)) 55%,
                                    rgba(0, 0, 0, 0) 100%);
  box-shadow:       0 2px 14px rgba(35, 49, 61, var(--hdr-lift));
}

/* Type follows the animated ink. Important, because sections 27, 28 and 39
   set white with important and this has to win over them. */
.site-header--over-hero .site-header__utility,
.site-header--over-hero .site-header__utility *:not(.header-giving),
.site-header--over-hero .site-name,
.site-header--over-hero a:not(.header-giving),
.site-header--over-hero a:not(.header-giving) *,
.site-header--over-hero .site-header__nav td.t_horizontal > div.t,
.site-header--over-hero .site-header__nav td.t_horizontal_hi > div.t,
.site-header--over-hero .site-header__nav .nav-more__btn {
  color: var(--hdr-ink) !important;
}

/* The shadow that holds type over photography fades out as the bar fills in,
   so it is never sitting behind dark text on white. */
.site-header--over-hero .site-header__nav td.t_horizontal > div.t,
.site-header--over-hero .site-header__nav td.t_horizontal_hi > div.t,
.site-header--over-hero .site-header__nav .nav-more__btn,
.site-header--over-hero .site-name,
.site-header--over-hero .site-header__utility *:not(.header-giving) {
  text-shadow: 0 1px 4px rgba(0, 0, 0, calc(var(--hdr-veil) * 1.9));
}

.site-header--over-hero .header-social .footer_icon_block svg {
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, calc(var(--hdr-veil) * 1.9)));
}

/* Dropdown panels are unaffected: they paint their own light background and
   section 28 pins their labels to ink with a more specific selector. */

/* -------------------------------------------------------------------------
   42.4  The enhancement itself
   ------------------------------------------------------------------------- */
@supports (animation-timeline: scroll()) {
  .site-header.site-header--over-hero {
    position:           fixed;
    animation:          hdr-solidify 1ms linear both;
    animation-timeline: scroll(root block);
    animation-range:    48px 240px;
  }

  @keyframes hdr-solidify {
    to {
      --hdr-surface:     #FFFFFF;
      --hdr-ink:         #23313D;  /* must track --ink */
      --hdr-logo-invert: 0;
      --hdr-veil:        0;
      --hdr-lift:        0.10;
    }
  }

  /* The nav underline is drawn in currentColor, so it follows the ink without
     any rule of its own. The purple hover on the solid headers is unaffected,
     since that is a different selector. */
}

/* Reduced motion: the state change is information rather than decoration, so it
   still happens, it simply arrives without a transition. Section 40's blanket
   rule already collapses the duration; this keeps the end state correct. */
@media (prefers-reduced-motion: reduce) {
  @supports (animation-timeline: scroll()) {
    .site-header.site-href--over-hero {
      animation-range: 48px 60px;
    }
  }
}

/* -------------------------------------------------------------------------
   42.5  Correction to 42.4
   -------------------------------------------------------------------------
   The reduced-motion rule above was written with a typo, `site-href--over-hero`
   instead of `site-header--over-hero`, so it matched nothing. Left in place
   since this file is appended to rather than edited; the corrected rule is
   below and both should be folded together at the next consolidation.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  @supports (animation-timeline: scroll()) {
    .site-header.site-header--over-hero {
      animation-range: 48px 60px;
    }
  }
}

/* =========================================================================
   43. v2.8  --  the header compacts on scroll, and the shadows actually fade
   =========================================================================

   FAULT ONE: THE SHADOWS NEVER FADED. Section 42 tied every text shadow to the
   veil so they would disappear as the bar filled in. They did not, because
   sections 37 and 39 had already set the same property with a fixed 0.6 alpha
   using longer selectors:

       section 39   .site-header.site-header--over-hero ... td.t_horizontal > div.t
       section 42   .site-header--over-hero        ... td.t_horizontal > div.t

   Five classes against four, so the fixed value won and the shadow stayed at
   full strength. Once the bar turns white, a 0.6 black shadow behind dark text
   reads as a dirty halo, which is what Adam saw. Restated below at matching
   specificity, later in the file, including the hover variants which section 39
   also set.

   Worth noting the pattern, since this is the third time in this file: writing
   a shorter selector than the rule you are trying to replace fails silently.
   Nothing errors, the declaration is simply ignored. When overriding an earlier
   section here, copy its selector exactly and rely on order.

   FAULT TWO: NO COMPACTION. A sticky header should give space back as you read.
   Section 42 animated colour but not size, so the solid bar was as tall as the
   transparent one and ate a fifth of the screen on every page.

   HOW THE SQUEEZE IS DONE, and why not by overriding padding. The library sets
   its own vertical padding on both header rows and those values are not visible
   from here, so replacing them would change how the header looks before any
   scrolling happens. Instead a negative margin grows from zero as the bar
   solidifies, which pulls the rows tighter without needing to know what it is
   pulling against. At rest it is exactly zero, so the unscrolled header is
   untouched.

   The logo shrinks to 55 per cent and its lift unwinds to nothing, so it settles
   into the middle of the compact bar rather than hanging above it. The height
   stays a clamp multiplied by the squeeze rather than a fixed value, so it
   remains responsive at every scroll position.

   ONE KEYFRAME FOR THE WHOLE STATE. This supersedes the animation declared in
   section 42.4 rather than adding a second one, because re-declaring the
   animation property replaces it. Both keyframes stay in the file; only this one
   is referenced.
   ========================================================================= */

@property --hdr-squeeze { syntax: "<number>"; inherits: true; initial-value: 0; }

/* -------------------------------------------------------------------------
   43.1  Text shadows, at a specificity that wins
   ------------------------------------------------------------------------- */
.site-header.site-header--over-hero .site-header__nav td.t_horizontal > div.t,
.site-header.site-header--over-hero .site-header__nav td.t_horizontal:hover > div.t,
.site-header.site-header--over-hero .site-header__nav td.t_horizontal_hi > div.t,
.site-header.site-header--over-hero .site-header__nav .nav-more__btn,
.site-header.site-header--over-hero .site-header__nav .nav-more__btn:hover,
.site-header.site-header--over-hero .site-name,
.site-header.site-header--over-hero .site-header__utility,
.site-header.site-header--over-hero .site-header__utility *:not(.header-giving) {
  text-shadow: 0 1px 4px rgba(0, 0, 0, calc(var(--hdr-veil) * 1.9));
}

.site-header.site-header--over-hero .header-social .footer_icon_block svg {
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, calc(var(--hdr-veil) * 1.9)));
}

/* -------------------------------------------------------------------------
   43.2  Compaction
   ------------------------------------------------------------------------- */

/* Logo: down to 55 per cent, and the lift unwinds so it centres in the bar. */
.site-header .site-header__brand .site-lockup img,
.site-header:not(.site-header--over-hero) .site-header__brand .site-lockup img {
  height: calc(clamp(2.75rem, 4.2vw, 4.25rem) * (1 - var(--hdr-squeeze) * 0.45));
}

.site-header--over-hero .site-header__brand .site-lockup img {
  transform: translateY(calc(-0.85rem * (1 - var(--hdr-squeeze))));
}

/* Rows pulled tighter. Negative margin rather than a padding override, so the
   library keeps ownership of the resting values. */
.site-header--over-hero .site-header__utility {
  margin-block: calc(var(--hdr-squeeze) * -0.35rem);
}

.site-header--over-hero .site-header__main {
  margin-block: calc(var(--hdr-squeeze) * -0.7rem);
}

/* -------------------------------------------------------------------------
   43.3  One keyframe for the whole state
   -------------------------------------------------------------------------
   Supersedes the animation in section 42.4. Same range and timeline.
   ------------------------------------------------------------------------- */
@supports (animation-timeline: scroll()) {
  .site-header.site-header--over-hero {
    animation:          hdr-state 1ms linear both;
    animation-timeline: scroll(root block);
    animation-range:    48px 240px;
  }

  @keyframes hdr-state {
    to {
      --hdr-surface:     #FFFFFF;
      --hdr-ink:         #23313D;  /* must track --ink */
      --hdr-logo-invert: 0;
      --hdr-veil:        0;
      --hdr-lift:        0.10;
      --hdr-squeeze:     1;
    }
  }
}

/* The solid headers on Content and System are compact from the start, since
   there is no transparent state for them to begin in. */
.site-header:not(.site-header--over-hero) {
  --hdr-squeeze: 1;
}

/* =========================================================================
   44. v2.9  --  the veil never inherited, plus logo scale and row spacing
   =========================================================================

   FAULT: THE SHADOW COULD NEVER HAVE FADED, and the selector was not the
   problem after all. `--hdr-veil` was registered with `inherits: false`, so the
   animated value existed only on the header element. Every descendant, including
   the nav labels and the social icons, resolved `var(--hdr-veil)` to the initial
   value of 0.32 instead, at every scroll position.

   So section 43 corrected a real specificity problem and still changed nothing
   visible, because the value being read was wrong rather than the rule being
   overridden. Two different faults with one symptom, which is why it survived
   two attempts.

   Re-declared below as inheriting. A later `@property` rule for the same name
   replaces the earlier one, so this is the whole fix.

   Worth keeping in mind for the rest of this file: any animated custom property
   read by a DESCENDANT of the element being animated must inherit. Of the six
   here, --hdr-surface and --hdr-lift are only ever read on the header itself, so
   they are correctly non-inheriting. The other four are read further down.

   LOGO: BIGGER, AND ALIGNED BY THE WORDMARK RATHER THAN THE BOX.

   The artwork is a cross rising well above a wordmark, so the wordmark occupies
   only the lower part of the file: roughly the bottom fifth of the viewBox. Any
   layout that aligns the image by its box therefore puts the wordmark lower than
   the menu beside it, which is what Adam noticed. The cross is tall, so the box
   is tall, so the words sit low.

   Corrected with a translate expressed as a percentage of the logo's own height
   rather than a fixed measurement. 14 per cent is the distance from the bottom of
   the artwork up to the wordmark's baseline. Because it scales with the image, the
   alignment holds at every size and, more usefully, in both the full and compact
   states. The previous fixed -0.85rem lift was also unwinding as the bar
   compacted, which is why the logo dropped below the menu once scrolled.

   Larger too, as asked, which also lifts the cross higher without any extra rule.

   ROWS CLOSER. Tightened at rest as well as on scroll, and only the gap between
   the two rows is touched. Deliberately not the top of the utility row: a
   negative margin there would pull the first row up past the header's own
   padding and risk clipping it against the viewport edge.
   ========================================================================= */

/* The fix. Same name, same syntax, same initial value, now inheriting. */
@property --hdr-veil { syntax: "<number>"; inherits: true; initial-value: 0.32; }

/* -------------------------------------------------------------------------
   44.1  Logo: larger, and baseline-aligned to the wordmark
   ------------------------------------------------------------------------- */
:root {
  /* Distance from the foot of the artwork to the wordmark's baseline, as a
     share of the logo's height. One number to tune if the alignment is out. */
  --logo-baseline-nudge: -14%;
}

.site-header .site-header__brand .site-lockup img,
.site-header:not(.site-header--over-hero) .site-header__brand .site-lockup img {
  height:    calc(clamp(3.25rem, 5vw, 5.25rem) * (1 - var(--hdr-squeeze) * 0.38));
  transform: translateY(var(--logo-baseline-nudge));
}

/* Over the hero the logo keeps its shadow, which now genuinely fades with the
   veil. The transform has to be restated because this rule also sets filter and
   the two live on the same element. */
.site-header--over-hero .site-header__brand .site-lockup img {
  transform: translateY(var(--logo-baseline-nudge));
  filter:    invert(var(--hdr-logo-invert))
             drop-shadow(0 1px 4px rgba(0, 0, 0, calc(var(--hdr-veil) * 1.4)));
}

/* -------------------------------------------------------------------------
   44.2  Row spacing
   -------------------------------------------------------------------------
   Applied to every header rather than just the floating one, using the squeeze
   variable, which sits at 1 on the Content and System headers. Those are in
   effect always in the compact state, so they get the same treatment.
   ------------------------------------------------------------------------- */
.site-header .site-header__utility {
  margin-top:    0;
  margin-bottom: calc(-0.3rem - var(--hdr-squeeze) * 0.35rem);
}

.site-header .site-header__main {
  margin-top:    0;
  margin-bottom: calc(var(--hdr-squeeze) * -0.5rem);
}

/* =========================================================================
   45. v3.0  --  strapline, and three section buttons in three brand colours
   =========================================================================

   THE STRAPLINE. Smaller and on a wider measure, so it settles into two lines
   rather than four. The library's size was tuned for a short line of text and
   this one is long, which is why it was filling the section on its own.

   THREE BUTTONS, ONE PER BRAND COLOUR. This is a deliberate departure from the
   colour system in section 41, which reserved orange for Donate alone and gave
   purple the job of meaning "interactive". Three differently coloured buttons
   side by side is brand expression rather than semantics: the colour no longer
   tells the visitor anything about the action, since all three do the same kind
   of thing. Adam's call, made knowingly, and it is a reasonable one for a single
   deliberate moment under the strapline.

   The cost, stated plainly so it is not discovered later: Donate is no longer
   the only orange thing on the page, so its distinctiveness now rests on its
   position in the header and its pill shape rather than on colour. If that turns
   out to matter, the third button taking the dark grey instead would restore it
   and cost nothing else.

   THE BRAND VALUES CANNOT CARRY WHITE TEXT, so the fills are the deepened
   variants. Measured against white:

       #4E87B3  brand blue      3.86:1   fails
       #3A6A8E  deepened        5.78:1   used here
       #7A4A69  purple          6.98:1   used as-is
       #F15A29  brand orange    3.37:1   fails
       #C93F12  deepened        5.00:1   used here

   They read as the same three colours. The deepening is what makes the labels
   legible rather than a change of palette.

   ORDER IS COOL TO WARM, left to right, ending on the most emotive section with
   the warmest colour. About us, Our work, Our impact. Not arbitrary: reading
   order and temperature move together, which gives the row a direction rather
   than looking like three colours that happened to be available.

   THE MARKUP LIVES IN THE ARTICLE, not the template, so Neil can change the
   labels without touching a layout. The classes are what drive the colour, and
   the article summary says so, because an editor who strips them will get three
   plain links and no clue why.
   ========================================================================= */

/* -------------------------------------------------------------------------
   45.1  Strapline
   ------------------------------------------------------------------------- */
.slice--welcome .welcome_article .ArticleBody h2 {
  max-width:     56rem;
  margin-inline: auto;
  font-size:     clamp(1.5rem, 2.5vw, 2.2rem);
  line-height:   1.25;
}

/* -------------------------------------------------------------------------
   45.2  The button row
   ------------------------------------------------------------------------- */
.welcome-cta {
  display:         flex;
  flex-wrap:       wrap;
  justify-content: center;
  gap:             var(--space-4);
  margin-top:      var(--space-8);
}

.welcome-cta__btn {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  padding:         var(--space-3) var(--space-6);
  border:          1px solid transparent;
  border-radius:   999px;
  color:           #FFFFFF;
  font-family:     var(--font-body);
  font-size:       var(--text-xs);
  font-weight:     700;
  letter-spacing:  0.12em;
  text-transform:  uppercase;
  text-decoration: none;
  transition:      background-color var(--motion-base) var(--motion-ease),
                   border-color     var(--motion-base) var(--motion-ease),
                   transform        var(--motion-base) var(--motion-ease),
                   box-shadow       var(--motion-base) var(--motion-ease);
}

/* Same hover language as every other filled control on the site: darken and
   lift a single pixel, drop back on press. Set in section 40. */
.welcome-cta__btn:hover {
  transform:  translateY(-1px);
  box-shadow: 0 6px 16px -10px rgba(35, 49, 61, 0.55);
}

.welcome-cta__btn:active {
  transform:           translateY(0);
  box-shadow:          none;
  transition-duration: var(--motion-fast);
}

.welcome-cta__btn:focus-visible {
  outline:        3px solid var(--ink);
  outline-offset: 3px;
}

/* Cool to warm, left to right. */
.welcome-cta__btn--blue {
  background-color: var(--rm-info);
  border-color:     var(--rm-info);
}
.welcome-cta__btn--blue:hover {
  background-color: #2E5573;
  border-color:     #2E5573;
}

.welcome-cta__btn--purple {
  background-color: var(--accent);
  border-color:     var(--accent);
}
.welcome-cta__btn--purple:hover {
  background-color: var(--accent-dark);
  border-color:     var(--accent-dark);
}

.welcome-cta__btn--orange {
  background-color: var(--rm-orange-deep);
  border-color:     var(--rm-orange-deep);
}
.welcome-cta__btn--orange:hover {
  background-color: var(--rm-orange-edge);
  border-color:     var(--rm-orange-edge);
}

/* On a phone the three stack, and a full-width target is easier to hit than a
   centred pill. */
@media (max-width: 480px) {
  .welcome-cta {
    flex-direction: column;
    align-items:    stretch;
  }
}

/* =========================================================================
   46. v3.1  --  feature cards: light grey body, no scrim, a read more link
   =========================================================================

   THE SCRIM. The `explore_church` treatment was built to lay text over a
   photograph, so it puts a dark gradient behind the caption. Sections 26 and 29
   both tried to clear it with `background: none` and it survived, which means
   the library sets it with a longer selector than either of mine, or on a
   pseudo-element, and there is no way to tell which from a render.

   So this covers both and does it with an important declaration. That is not
   how I would normally reach for it, but this is the fourth pass at this one
   slice and each attempt costs a page load to check. Being decisive here is
   worth more than being tidy.

   THE CARD. Image at the top, then a light grey body holding the title, the
   summary and a read more link. The body grows to fill the card, so the three
   grey boxes finish level with each other even though the summaries are
   different lengths. Without that the row looks ragged along the bottom, which
   is the sort of thing that reads as unfinished rather than as variety.

   THE READ MORE LINK IS GENERATED CONTENT, and deliberately so. The whole card
   is already wrapped in one anchor, and an anchor cannot contain another anchor,
   so a real link is not available here. Generated content is the honest solution
   rather than a shortcut: the affordance it signals is genuine, because the
   click target really is the whole card, and the card's accessible name still
   comes from the title text rather than from these two words.

   It is pushed to the bottom with an automatic top margin and left aligned with
   the text above it, so it sits where the eye expects to finish reading.

   The image zoom on hover is untouched. The read more picks up an underline on
   the same hover, so the two respond together as one card rather than as two
   separate things.
   ========================================================================= */

/* Whichever pseudo-element carries the gradient, it goes. */
.slice--explore-church .gallery_li .explore-church__caption::before {
  display: none !important;
}

/* The card: a flex column so the body can grow, and clipped so the image zoom
   stays inside the frame. */
.slice--explore-church .gallery_li > a {
  display:        flex;
  flex-direction: column;
  height:         100%;
  overflow:       hidden;
}

.slice--explore-church .gallery_li > a > img {
  flex: 0 0 auto;
}

/* The body. */
.slice--explore-church .gallery_li .explore-church__caption {
  position:         relative;
  flex:             1 1 auto;
  display:          flex;
  flex-direction:   column;
  align-items:      flex-start;
  gap:              var(--space-2);
  padding:          var(--space-5) var(--space-5) var(--space-5);
  background-color: var(--section-light) !important;
  background-image: none !important;
}

/* Read more, bottom left, aligned with the text above it. */
.slice--explore-church .gallery_li .explore-church__caption::after {
  content:         "Read more";
  display:         inline-block;
  margin-top:      auto;
  padding-top:     var(--space-5);
  background:      none;
  color:           var(--accent);
  font-family:     var(--font-body);
  font-size:       var(--text-xs);
  font-weight:     700;
  letter-spacing:  0.1em;
  text-transform:  uppercase;
  text-decoration: none;
  transition:      color var(--motion-base) var(--motion-ease);
}

.slice--explore-church .gallery_li > a:hover .explore-church__caption::after,
.slice--explore-church .gallery_li > a:focus-visible .explore-church__caption::after {
  color:                 var(--accent-dark);
  text-decoration:       underline;
  text-underline-offset: 4px;
}

/* The summary should not stretch to fill the gap: the automatic margin on the
   read more link is what does that, so the text stays where it was written. */
.slice--explore-church .gallery_li .explore-church__subtitle {
  flex: 0 0 auto;
}

/* =========================================================================
   47. v3.2  --  feature card hover: fade the photograph, not the words
   =========================================================================
   The `explore_church` treatment fades the caption on hover. That is correct
   for what it was built to be: the caption was a scrim laid over the image, so
   fading it on hover reveals more of the photograph underneath. Now that the
   caption is a solid grey card sitting below the image, the same behaviour just
   makes the title, summary and read more link go pale for no reason.

   Opacity applies to a whole subtree as a single group, so if the fade is on the
   anchor then resetting the anchor is the entire fix. The caption and its
   children are reset as well, in case the library fades them separately, which
   costs nothing if it does not.

   The fade moves to the photograph, where Adam wants it, alongside the zoom
   already there from section 40. Ten per cent, and on the shorter interaction
   duration rather than the slower zoom duration, so the tone shifts as the
   pointer arrives while the movement keeps its own unhurried pace. The two
   reading at slightly different speeds is what stops it feeling mechanical.

   Box shadows are deliberately not touched, so whatever the card does there
   stays as it is.
   ========================================================================= */

/* Nothing inside the card fades. */
.slice--explore-church .gallery_li > a:hover,
.slice--explore-church .gallery_li > a:focus-visible,
.slice--explore-church .gallery_li > a:hover .explore-church__caption,
.slice--explore-church .gallery_li > a:focus-visible .explore-church__caption,
.slice--explore-church .gallery_li > a:hover .explore-church__title,
.slice--explore-church .gallery_li > a:hover .explore-church__subtitle {
  opacity: 1 !important;
}

/* The photograph carries both the zoom and the fade. */
.slice--explore-church .gallery_li > a > img {
  transition: transform var(--motion-slow) var(--motion-ease),
              opacity   var(--motion-base) var(--motion-ease);
}

.slice--explore-church .gallery_li > a:hover > img,
.slice--explore-church .gallery_li > a:focus-visible > img {
  opacity:   0.9;
  transform: scale(1.04);
}

/* =========================================================================
   48. v3.3  --  header padding taken over, and a larger compact logo
   =========================================================================

   WHY THIS CHANGES APPROACH. Sections 43 and 44 tightened the header with
   negative margins, deliberately, so the library kept ownership of its own
   resting padding. Three rounds later the bar is still tall, and the arithmetic
   says why: the logo and the Donate pill account for roughly 124px of it, and it
   renders around 245px. So something close to 120px is padding, and a negative
   margin of a third of a rem was never going to touch that.

   So the padding is set here now. The cost is that this skin owns two values the
   library used to own, which is a small loss: a library change to header spacing
   will no longer reach this site. Worth it, and recorded rather than glossed
   over. The negative margins from section 44.2 are zeroed rather than left to
   stack, because two mechanisms fighting over the same space is how you end up
   unable to predict either.

   THE COMPACT LOGO WAS TOO SMALL. It was shrinking to 62 per cent, which at the
   same time as the bar tightening made it look shrunken rather than compact.
   Now 80 per cent. The two changes work together: taking the padding out is what
   creates room for the logo to stay generous while the bar still gets shorter.

   BOTH NUMBERS ARE DIALS. If the bar is now too tight, the two padding values
   are the only things to change, and they are next to each other.
   ========================================================================= */

/* Padding, tight and scaling with the squeeze. Replaces the negative margins. */
.site-header .site-header__utility {
  margin-top:     0;
  margin-bottom:  0;
  padding-block:  calc(0.75rem - 0.3rem * var(--hdr-squeeze));
}

.site-header .site-header__main {
  margin-top:     0;
  margin-bottom:  0;
  padding-block:  calc(0.7rem - 0.3rem * var(--hdr-squeeze));
}

/* Logo: shrinks to 80 per cent rather than 62. */
.site-header .site-header__brand .site-lockup img,
.site-header:not(.site-header--over-hero) .site-header__brand .site-lockup img {
  height: calc(clamp(3.25rem, 5vw, 5.25rem) * (1 - var(--hdr-squeeze) * 0.2));
}

/* =========================================================================
   49. v3.4  --  a heading on the featured items section
   =========================================================================
   The three cards had no heading, because their live site has none there and
   the inherited one said "Explore Our Church". But an unlabelled row of three
   cards between a strapline and a set of figures leaves the reader to work out
   what it is, and it is not obvious: these are not the latest news, since that
   has its own section further down, and they are not an about section either.

   Same markup as the news and events headings, so the three sections read as
   siblings.

   The accent rule beneath is restated here rather than assumed. The library
   draws one, but whether it hangs off the generic heading class or off the
   per-slice one used by the news sections cannot be told from a render. Since
   both would target the same pseudo-element, restating it cannot double it up:
   at worst these declarations are redundant, at best they are what makes this
   heading match the other two.
   ========================================================================= */
.slice--explore-church .slice__head--centred .slice__heading {
  position:   relative;
  text-align: center;
}

.slice--explore-church .slice__head--centred .slice__heading::after {
  content:          "";
  display:          block;
  width:            clamp(60px, 7vw, 88px);
  height:           3px;
  margin:           var(--space-4) auto 0;
  background-color: var(--rm-info);
}

/* =========================================================================
   50. v3.5  --  header height, done against the real markup
   =========================================================================
   Adam supplied the rendered header. Three things, and the first explains why
   sections 43, 44 and 48 all failed to shorten the bar.

   1. THE MAIN ROW IS AS TALL AS THE LOGO, NOT AS TALL AS THE MENU. At a 5.25rem
      ceiling the logo is around 84px and the nav labels are around 17px, so the
      nav sits vertically centred with roughly 33px of air above and below it.
      That air is what reads as a gap between the Donate row and the menu. It was
      never padding, which is why removing padding barely moved it.

      So the logo ceiling comes down from 5.25rem to 4.6rem. That is the actual
      lever, and it is a direct trade: this bar cannot be both short and carry a
      large logo, because the logo is what sets its height. Push the ceiling back
      up if the logo matters more than the height.

   2. I HAD BEEN PADDING THE WRONG ELEMENT. There is a
      `site-header__utility-inner` inside the utility row, and that is the more
      likely holder of its padding. Section 48 only ever set the outer row, so if
      the padding lives on the inner wrapper it was untouched. Both are set now.

   3. THE NAV IS FIVE WRAPPERS DEEP: site-header__nav, then main-nav, then
      menubardiv, then a table also classed menubardiv, then a cell, then the
      menu table itself. Every one of those is a candidate for stray vertical
      space, and the module even ships an inline style block setting
      `div.menubardiv` to `height: 100%`. All of them are zeroed and the target
      area is put back on the label itself, where it can be reasoned about.

   ALSO REMOVED: the second cell in the nav row, holding Help and Low Graphics
   links. That was MENU-3. Their live site has Low Graphics in the footer, not
   the header, and Help is a platform page no visitor is looking for.
   ========================================================================= */

/* Logo ceiling down. This is the real height control. */
.site-header .site-header__brand .site-lockup img,
.site-header:not(.site-header--over-hero) .site-header__brand .site-lockup img {
  height: calc(clamp(3rem, 4.4vw, 4.6rem) * (1 - var(--hdr-squeeze) * 0.2));
}

/* Row padding, on the outer row AND its inner wrapper. */
.site-header.site-header--tworow .site-header__utility,
.site-header.site-header--tworow .site-header__utility-inner {
  margin-block:  0;
  padding-block: calc(0.45rem - 0.2rem * var(--hdr-squeeze));
}

.site-header.site-header--tworow .site-header__main {
  margin-block:  0;
  padding-block: calc(0.4rem - 0.2rem * var(--hdr-squeeze));
}

/* The nav's five nested wrappers, plus the tables inside them. */
.site-header__nav .main-nav,
.site-header__nav div.menubardiv,
.site-header__nav table.menubardiv,
.site-header__nav table.menubardiv > tbody > tr,
.site-header__nav table.menubardiv > tbody > tr > td,
.site-header__nav table#m,
.site-header__nav table#m > tbody > tr,
.site-header__nav table#m > tbody > tr > td.t_horizontal {
  margin-top:     0;
  margin-bottom:  0;
  padding-top:    0;
  padding-bottom: 0;
  vertical-align: middle;
}

/* The pointer target goes back on the label, where its size is legible in the
   stylesheet rather than buried in table cell padding. */
.site-header__nav td.t_horizontal > div.t {
  padding-block: 0.3rem;
}

/* MENU-3: the Help and Low Graphics cell. Low Graphics belongs in the footer,
   which is where their live site has it, and Help is a platform page. */
.site-header__nav table.menubardiv > tbody > tr > td + td {
  display: none;
}

/* =========================================================================
   51. v3.6  --  one treatment for every section heading
   =========================================================================
   Adam is right that they look different, and the reason is that the four
   headings on the homepage arrive by three different routes:

       Featured items    h2.slice__heading
       Latest news       h2.slice__heading.latest-news__heading
       Upcoming events   h2.slice__heading.latest-news__heading
       Our Impact        a plain h2 inside the impact ARTICLE's body

   So three of them are styled by the library's slice-heading rules and the
   fourth by its generic article typography, which has no reason to match. Two of
   the three also carry a per-slice class the other does not. Nothing was going
   to make those agree by accident.

   One rule now covers all four, including the article-sourced one.

   THE ACCENT RULE, AND ONE DELIBERATE DIFFERENCE. Three of the sections are a
   heading followed straight by content, so the rule sits directly beneath the
   heading. The impact section is a heading followed by its own sub-line, and a
   bar between those two would divide a heading from its own subtitle. So there
   the rule goes below the sub-line instead, where it separates the introduction
   from the figures. The rule is present in all four; it simply sits after the
   text that belongs with the heading.

   `text-wrap: balance` is worth having on a heading: it evens the line lengths
   rather than leaving one word stranded on a second line, which is the most
   common way a centred heading looks careless.

   NOT INCLUDED: the Safeguarding heading in the footer. It is a small standing
   note rather than a section of the page, and the library sets it in italic at a
   smaller size on purpose. Say the word if it should join them.
   ========================================================================= */

.slice .slice__head .slice__heading,
.slice--latest-news .slice__head .latest-news__heading,
.slice--stats .ArticleBody h2 {
  font-family:     var(--font-display);
  font-size:       clamp(1.375rem, 2vw, 1.75rem);
  font-weight:     700;
  line-height:     1.2;
  letter-spacing:  -0.01em;
  color:           var(--ink);
  text-align:      center;
  text-wrap:       balance;
  margin:          0;
}

/* The accent rule, on the three headings that are followed straight by
   content. */
.slice .slice__head .slice__heading::after,
.slice--latest-news .slice__head .latest-news__heading::after {
  content:          "";
  display:          block;
  width:            clamp(60px, 7vw, 88px);
  height:           3px;
  margin:           var(--space-4) auto 0;
  background-color: var(--rm-info);
}

/* Consistent space between the heading block and whatever follows it. */
.slice .slice__head--centred {
  margin-bottom: var(--space-8);
}

/* Impact: the heading keeps its sub-line, so no rule directly beneath it. */
.slice--stats .ArticleBody h2::after {
  content: none;
}

/* The rule appears after the sub-line instead, separating the introduction from
   the figures. */
.slice--stats .ArticleBody > p::after {
  content:          "";
  display:          block;
  width:            clamp(60px, 7vw, 88px);
  height:           3px;
  margin:           var(--space-6) auto 0;
  background-color: var(--rm-info);
}

/* =========================================================================
   52. v3.7  --  withdrawing a permanent will-change
   =========================================================================
   Section 40 put `will-change: transform` on the Donate pill, the section
   links, the sidebar call to action and the form submit. The intent was to hint
   that those elements were about to be animated on hover.

   It was the wrong call. `will-change` asks the browser to promote an element to
   its own compositor layer and hold it there, and set in a plain rule that
   promotion lasts the entire life of the page rather than the moment of the
   interaction. Every one of those elements then occupies GPU memory
   continuously, for a one-pixel lift that may never happen. The guidance on this
   property is explicit that it should be applied shortly before a change and
   removed afterwards, not declared as a static property.

   On a page with half a dozen of them the cost is small, but it is real, it
   scales with every button added later, and it is exactly the sort of thing that
   ends up blamed for scrolling feeling uneven. A one-pixel translate on hover
   needs no hint: browsers handle that case perfectly well unaided.

   Withdrawn. `auto` is the initial value, so this restores the default rather
   than asserting anything new.
   ========================================================================= */
.header-giving,
.section-link,
.side-card__cta,
.content-shell__main input.button {
  will-change: auto;
}

/* =========================================================================
   53. v3.8  --  logo size decoupled from header height
   =========================================================================
   Adam wants the collapsed bar shorter AND the collapsed logo bigger. Those two
   have been fighting each other for four rounds, because the logo has been sized
   with `height`, and height is what sets the row's height. Every increase to one
   was a decrease to the other. There was no version of that argument either of
   us could win.

   THE WAY OUT is to stop sizing the logo and start scaling it. A transform does
   not participate in layout, so the row can be sized by a small box while the
   logo renders considerably larger and overflows it. Height and apparent size
   become independent, which is what was needed all along.

       layout box    clamp(1.9rem, 2.4vw, 2.5rem)     sets the row height
       rendered      the box multiplied by the scale   what the eye sees

   At rest that is a 2.5rem box rendering at 5rem. Collapsed it is the same box
   rendering at about 4.4rem. So the collapsed logo is now LARGER than the 3.68rem
   it was, while the row it sits in is 2.5rem rather than 3.68rem. Both of Adam's
   asks, at the same time.

   TRANSFORM ORIGIN IS THE CAREFUL PART. Scaling from the centre would push the
   logo below the header as well as above it, and on the Content and System
   templates that means spilling onto the page content beneath a solid white bar,
   which would look like a fault. With the origin at the bottom left the overflow
   goes upward only, into the header's own utility row, where there is nothing on
   the left-hand side since the status bar links are hidden. It also pins the
   bottom edge, so the wordmark stays put rather than drifting as the scale
   changes.

   The baseline nudge drops from 14 per cent to 6, because scaling from the bottom
   already lifts the wordmark and the two together were over-correcting. Still the
   one dial if the alignment reads wrong.

   ONE THING TO WATCH. Scaling doubles the logo's apparent width as well as its
   height, and that overflow reaches right, towards the menu. At these sizes there
   is a few hundred pixels of clear space between them, and below the library's
   1400px breakpoint the menu becomes a burger and the whole bar is free. But if
   the logo ever appears to touch the first menu item on a mid-sized window, the
   scale is the number to lower.

   Also trimmed: the Donate pill's vertical padding, which is now the tallest
   thing in the utility row and therefore what sets that row's height.
   ========================================================================= */

:root {
  --logo-box:             clamp(1.9rem, 2.4vw, 2.5rem);
  --logo-baseline-nudge:  -6%;
}

.site-header .site-header__brand .site-lockup img,
.site-header:not(.site-header--over-hero) .site-header__brand .site-lockup img {
  height:           var(--logo-box);
  width:            auto;
  transform-origin: left bottom;
  transform:        translateY(var(--logo-baseline-nudge))
                    scale(calc(2 - 0.25 * var(--hdr-squeeze)));
}

/* The over-hero rule sets filter as well, so the transform has to be restated
   alongside it. */
.site-header--over-hero .site-header__brand .site-lockup img {
  transform-origin: left bottom;
  transform:        translateY(var(--logo-baseline-nudge))
                    scale(calc(2 - 0.25 * var(--hdr-squeeze)));
  filter:           invert(var(--hdr-logo-invert))
                    drop-shadow(0 1px 4px rgba(0, 0, 0, calc(var(--hdr-veil) * 1.4)));
}

/* Nothing may clip the overflow, or the whole approach fails silently. */
.site-header,
.site-header .site-header__main,
.site-header .site-header__brand,
.site-header .site-header__brand .site-lockup {
  overflow: visible;
}

/* The Donate pill now sets the utility row's height, so it takes a trim too. */
.site-header .header-giving {
  padding-block: calc(0.5rem - 0.12rem * var(--hdr-squeeze));
}

/* =========================================================================
   54. v3.9  --  MOBILE PASS: hero height, logo scaling, safe overflow
   =========================================================================
   Everything in sections 33 to 53 was tuned against a 2520px window, which is
   how the following got through.

   1. THE HERO COLLAPSED TO A STRIP ON EVERY PHONE. Its height is
      `min(92vh, 100vw * 0.4375)`. At 390px wide the second term is 171px, so
      min() picks it, and 171px is less than the caption needs for a title, a
      subtitle and a button. The hero became a letterbox with its caption
      bursting out of it and the fixed header covering most of what was left.

      A 32:14 crop simply does not work at phone width. There is no version of
      this where a landscape hero keeps its full frame AND leaves room for the
      caption on a 390px screen, so the ratio is abandoned below the phone
      breakpoint and a height is set instead: 340px as a floor so the caption
      always fits, 62vh where the viewport allows, capped at 460px so it does
      not swallow the whole screen and hide that the page scrolls.

      The image then crops horizontally rather than vertically. Worth knowing
      that this is a heavy crop: a 2.29:1 photograph in a roughly 0.85:1 box
      shows about a third of its width. Centred, which suits these three
      slides because their subjects are central, but a slide whose subject
      sits to one side will lose it. The real answer, if it matters, is a
      separate portrait crop per slide, which is a content job rather than a
      CSS one.

   2. LOGO SCALING IS SWITCHED OFF ON PHONES. Section 53 scales the logo 2x
      from a small layout box, and that doubles apparent WIDTH as well as
      height. On a 390px screen a 30px-tall logo renders 180px wide, heading
      straight for the burger and the search button. Above the phone
      breakpoint there is room to spare; below it there is not, so the phone
      gets a plain height with no transform. It loses nothing: the reason for
      scaling was to keep a tall bar short, and on a phone the bar is short
      already.

      This also removes the need for the overflow allowance below 720px,
      which is the safer position: `overflow: visible` on the header was
      added for the scaling, and on mobile the header contains the slide-in
      panel and its backdrop.

   3. HEADER PADDING is left as it is. Sections 48 and 50 took ownership of it
      at every width, and the values are tight, which is right on a phone.
      Recorded rather than changed so it is a decision and not an oversight.
   ========================================================================= */

@media (max-width: 720px) {

  /* Hero: a height that fits the caption, not a ratio that cannot. */
  .slice--hero {
    --hero-h: clamp(340px, 62vh, 460px);
  }

  /* Crop from the sides, centred. */
  .slice--hero .bx-viewport .gallery_li img {
    object-position: center center;
  }

  /* Caption: tighter measures, since 32rem and 34rem exceed a phone's width
     anyway and the padding is what actually governs the line length here. */
  .slice--hero .hero_slideshow .hero-caption__title,
  .slice--hero .hero_slideshow .hero-caption__subtitle {
    max-width: 100%;
  }

  /* Logo: plain height, no scaling, so nothing overflows towards the burger.
     A small lift remains, because the wordmark still sits in the lower part
     of the artwork whatever the size. */
  .site-header .site-header__brand .site-lockup img,
  .site-header:not(.site-header--over-hero) .site-header__brand .site-lockup img,
  .site-header--over-hero .site-header__brand .site-lockup img {
    height:    2.6rem;
    transform: translateY(-6%);
  }

  /* And with no scaling to accommodate, the overflow allowance comes off. The
     header holds the slide-in panel and its backdrop on a phone, so the
     library's own clipping is the safer default. */
  .site-header,
  .site-header .site-header__main,
  .site-header .site-header__brand {
    overflow: clip;
  }
  .site-header .site-header__brand .site-lockup {
    overflow: visible;
  }
}

/* =========================================================================
   55. v4.0  --  MOBILE FIXES FROM A REAL DEVICE
   =========================================================================

   1. THE MOBILE MENU WAS UNUSABLE, and it was mine. Section 27 set
      `.site-header--over-hero button:not(.header-giving)` to white with an
      important declaration, to hold the header type legible over the hero
      photograph. But below the nav breakpoint that same header contains the
      slide-in panel, and the panel is WHITE. So every submenu button, the
      Close control and its icon were white on white, and the anchors went the
      same way via the sibling rule for links.

      Six of the seven menu items are submenu buttons, which is why the panel
      rendered as a stack of empty rows with hairlines, and Home was the only
      thing faintly visible.

      The lesson, and it is the same one as the nav colour: a rule scoped to a
      header variant reaches everything that variant contains, including
      components that only exist at another breakpoint. Whitening type for a
      photograph has to exclude anything that paints its own light surface.

      Fixed by resetting the panel's own structures to ink. Deliberately
      unscoped by width, so it holds regardless of which breakpoint the
      library moves the panel to.

   2. NO BACKDROP BEHIND THE PANEL. The library ships
      `.mobile-menu-backdrop` positioned, layered and fading correctly, with a
      transparent background, so there has never been anything to see. The page
      behind the open panel stayed fully lit, which reads as two pages at once.
      Ink at 55%: the page remains legible as context rather than going muddy,
      and the white panel stands off it emphatically.

   3. THE SLIDESHOW ARROWS SAT ON TOP OF THE CAPTION. At phone width the
      caption fills most of the frame, so the vertically centred prev and next
      controls land on the title: on the Bridge the Gap slide the arrow covers
      the B. Hidden on phones. Nothing is lost, because the pager dots remain
      and bxSlider's touch swipe is on by default, which is how anyone
      actually changes a slide on a phone.

   4. THE HERO TITLE WAS TOO LARGE. Section 19 scoped its size to 721px and
      above, on the reasoning that the library retunes the title below its own
      phone breakpoint and an unscoped rule would undo that. The library does
      retune it, but not far enough for a headline of this length: it still
      ran to two lines at around 36px and dominated the photograph. Capped
      properly now, with the viewport unit doing the work between the bounds.

   5. TOO MUCH AIR BELOW THE HERO. Around 165px of white between the hero and
      the strapline, from slice padding sized for a desktop rhythm. On a phone
      that is most of a screen of nothing. Cut to a value that still separates
      the sections without making the reader scroll to find the next one.
   ========================================================================= */

/* -------------------------------------------------------------------------
   55.1  The mobile panel keeps ink type, whatever the header variant says
   ------------------------------------------------------------------------- */
.site-header.site-header--over-hero .mobile-menu-header,
.site-header.site-header--over-hero .mobile-menu-header *,
.site-header.site-header--over-hero .mobile-nav-clean,
.site-header.site-header--over-hero .mobile-nav-clean *,
.site-header.site-header--over-hero .mobile-nav-link,
.site-header.site-header--over-hero .mobile-nav-link *,
.site-header.site-header--over-hero .mobile-nav-sublink,
.site-header.site-header--over-hero .mobile-account-links,
.site-header.site-header--over-hero .mobile-account-links *,
.site-header.site-header--over-hero .mobile-search,
.site-header.site-header--over-hero .mobile-search * {
  color:       var(--ink) !important;
  text-shadow: none !important;
}

/* Sub-items step back, as they would on any nested navigation. */
.site-header.site-header--over-hero .mobile-nav-sublink {
  color: var(--ink-soft) !important;
}

/* The chevrons are drawn from currentColor, so they follow the above. The
   close icon is a character rather than a glyph font, so it needs nothing
   further. */

/* -------------------------------------------------------------------------
   55.2  A backdrop behind the panel
   ------------------------------------------------------------------------- */
.mobile-menu-backdrop {
  background: rgba(35, 49, 61, 0.55);
}

/* -------------------------------------------------------------------------
   55.3  Phone-only hero corrections
   ------------------------------------------------------------------------- */
@media (max-width: 720px) {

  /* Arrows off: they sit on the caption at this width. Dots and swipe remain. */
  .slice--hero .bx-controls-direction {
    display: none;
  }

  /* Title, capped for real this time. */
  .slice--hero .hero_slideshow .hero-caption__title {
    font-size:   clamp(1.6rem, 6.4vw, 2.1rem);
    line-height: 1.15;
  }

  .slice--hero .hero_slideshow .hero-caption__subtitle {
    font-size:   var(--text-sm);
    line-height: 1.45;
  }

  /* Air below the hero, cut to a phone rhythm. */
  .slice--welcome {
    padding-block: var(--space-12) var(--space-8);
  }
}


/* =========================================================================
   56. v4.1  --  mobile: Donate and socials move into the panel
   =========================================================================
   Below the nav breakpoint the header becomes a single row of logo and burger,
   and the utility row's two visible items move to the foot of the slide-in
   panel: the social icons, then Donate beneath them.

   NEITHER CAN BE REPARENTED. Both live in the utility row, and the panel is
   `.site-header__nav` in the row below, so no CSS makes one a child of the
   other. They are positioned into the panel's coordinate space instead, using
   the panel's own width expression to derive their left edge, so they track any
   future change to that width with nothing to keep in step. Same technique the
   Wight Dive build uses for its booking pill.

   THE UTILITY ROW IS COLLAPSED, NOT HIDDEN. `display: none` would take its
   children with it, including the two elements now positioned elsewhere, since
   a fixed descendant of a display:none element renders nothing. So its padding
   goes to zero and it closes up around the status bar, which is already empty
   here: the greeting is hidden in section 31, the links in section 37, and the
   library moves the search input into the panel.

   THEY SLIDE WITH THE PANEL, using the library's own 0.3s ease rather than this
   skin's motion scale, deliberately: arriving at a different speed from the
   panel they sit in would read as two things happening rather than one.

   THEY STAY PUT WHEN THE PANEL SCROLLS, being fixed rather than in its flow. On
   a long menu that is the better behaviour: a give action that stays reachable
   beats one that scrolls away.

   COLOUR. Both were whitened for the hero photograph by sections 27 and 42.
   Inside a white panel that makes the icons invisible, the same fault that made
   the menu items disappear. Reset to ink, with the shadows removed since there
   is no photograph behind them any more.

   ONE THING WORTH WEIGHING, recorded rather than decided: with Donate in the
   panel there is no give action visible on a phone until the menu is opened, so
   it goes from one tap to two. For a charity that is the action paying for
   everything. If that matters more than the cleaner header, a compact Donate
   can sit beside the burger instead, and this section is what to undo.
   ========================================================================= */

@media (max-width: 1400px) {

  /* Collapse the utility row without removing it. */
  .site-header.site-header--tworow .site-header__utility,
  .site-header.site-header--tworow .site-header__utility-inner {
    padding-block: 0;
    min-height:    0;
  }

  /* The single remaining row gets more presence than the two-row values gave. */
  .site-header.site-header--tworow .site-header__main {
    padding-block: var(--space-3);
  }

  /* Shared positioning. The panel is min(85%, 360px) wide and anchored right,
     so its inner left edge is that width back from the viewport edge, plus the
     panel's own gutter. */
  .site-header--over-hero .header-social,
  .site-header--over-hero .header-giving {
    position:   fixed;
    left:       calc(100% - min(85%, 360px) + var(--space-5));
    right:      calc(var(--space-5) + env(safe-area-inset-right, 0px));
    width:      auto;
    margin:     0;
    z-index:    9051;
    transform:  translateX(120%);
    transition: transform 0.3s ease;
  }

  .menu-open .site-header--over-hero .header-social,
  .menu-open .site-header--over-hero .header-giving {
    transform: translateX(0);
  }

  /* Donate at the foot. */
  .site-header--over-hero .header-giving {
    bottom:          calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
    justify-content: center;
    text-align:      center;
  }

  /* Social icons just above it. 4.25rem clears the pill plus a gap. */
  .site-header--over-hero .header-social {
    bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px) + 4.25rem);
  }

  .site-header--over-hero .header-social .ArticleBody {
    display:         flex;
    justify-content: center;
    gap:             var(--space-6);
  }

  /* Ink, not white: no photograph behind them in the panel. */
  .site-header--over-hero .header-social .footer_icon_block,
  .site-header--over-hero .header-social .footer_icon_block svg {
    color:       var(--ink) !important;
    text-shadow: none !important;
    filter:      none !important;
  }

  .site-header--over-hero .header-social .footer_icon_block svg {
    width:  1.4rem;
    height: 1.4rem;
  }
}

/* =========================================================================
   57. v4.2  --  the socials really do move, and GO gets a white label
   =========================================================================

   1. THE GO BUTTON'S LABEL WAS INVISIBLE, and it was section 55.1. That rule
      set everything inside `.mobile-search` to ink, to rescue the panel from
      the white-on-white fault. But the search button carries a plum fill, so
      ink on plum is the same mistake in the opposite direction: a blanket
      colour reset caught a control that paints its own dark surface.

      Third time this pattern has bitten in this build. A colour rule broad
      enough to fix a container will always catch something inside it that had
      its own reason for being a different colour.

      White on plum is 6.98:1.

   2. THE SOCIAL ICONS DID NOT MOVE, and Donate did, from the same declaration
      block. So the media query applied and the rule matched, which leaves only
      one explanation: something out-specifies `position` on `.header-social`
      alone. What, I cannot see from a render, and section 56's selector was
      already two classes deep.

      Rather than spend another review cycle guessing at it, this states the
      positioning at five classes deep and marks it important. That is the
      fourth time in this build that a shorter selector has silently lost, so
      the pattern is well enough established to stop being polite about it.

      Everything else about the placement is unchanged from section 56: the
      left edge still derives from the panel's own width expression, it still
      slides on the panel's 0.3s ease, and it still sits 4.25rem up to clear
      the Donate pill beneath it.
   ========================================================================= */

@media (max-width: 1400px) {

  /* The search button's label, back to white. */
  .site-header.site-header--over-hero .mobile-search input.sitebutton,
  .site-header.site-header--over-hero .mobile-search .sitebutton {
    color: #FFFFFF !important;
  }

  /* Social icons into the panel, stated at a specificity that cannot lose. */
  .site-header.site-header--tworow.site-header--over-hero
  .site-header__utility .site-header__utility-inner .header-social {
    position:   fixed !important;
    left:       calc(100% - min(85%, 360px) + var(--space-5)) !important;
    right:      calc(var(--space-5) + env(safe-area-inset-right, 0px)) !important;
    bottom:     calc(var(--space-6) + env(safe-area-inset-bottom, 0px) + 4.25rem) !important;
    top:        auto !important;
    width:      auto !important;
    margin:     0 !important;
    z-index:    9051;
    transform:  translateX(120%);
    transition: transform 0.3s ease;
  }

  .menu-open .site-header.site-header--tworow.site-header--over-hero
  .site-header__utility .site-header__utility-inner .header-social {
    transform: translateX(0);
  }
}

/* =========================================================================
   58. v4.3  --  three stable fixes, one of them by a different mechanism
   =========================================================================

   WHAT IS ESTABLISHED. The mobile rules in sections 54 and 55 are live: the
   hero is 460px rather than 171px, the arrows are gone, the title is capped,
   the menu items are legible and the backdrop is there. Donate is at the foot
   of the panel, from section 56. So the file parses to at least that point and
   the media queries apply.

   1. THE SOCIAL ICONS, BY A DIFFERENT LEVER.
      Section 56 set `position: fixed` on `.header-social` and Donate in the
      SAME declaration block. Donate moved; the icons did not. Section 57
      restated it five classes deep with an important declaration and they
      still did not. Two attempts on the same lever have failed, so pushing it
      a third time would be hope rather than diagnosis.

      So this positions the CONTAINING ROW instead. `.site-header__utility` is
      a different element with a different set of competing rules, and moving
      it takes the icons with it wherever they sit inside it. It also collapses
      the top row for free, since a fixed row leaves the flow entirely, which
      is what makes the header a single row without needing the padding rules
      from section 56 to have worked either.

      Donate is separately fixed already and unaffected: it is out of the row's
      flow, so the row moving does not move it twice.

   2. THE SEARCH BUTTON'S LABEL.
      Section 57 targeted `input.sitebutton`, which is what the DESKTOP status
      bar renders. But `.mobile-search` is EMPTY in the markup and the script
      fills it, so the control in the panel is very probably a different
      element or carries a different class. That fits the evidence exactly:
      section 55.1's `*` selector catches it, so it went ink, while a selector
      naming one class does not, so it never went white.

      Targeted by role rather than by class now: any button, submit or button
      input inside the panel's search. One of those will be it whatever the
      script builds.

   3. THE GAP BELOW THE HERO.
      Around 260px, where section 55.3 set 48px. So that rule is losing, and a
      single class is the likely reason: the library's slice padding is
      probably `main > .slice` or similar, which out-specifies
      `.slice--welcome`. Fifth occurrence of that fault in this build.

      Restated with the class doubled and a `main` ancestor, and the article
      wrapper inside it zeroed too, since either could be holding the space.
   ========================================================================= */

@media (max-width: 1400px) {

  /* 1. The whole utility row moves to the panel foot, icons included. */
  .site-header.site-header--over-hero .site-header__utility {
    position:   fixed !important;
    left:       calc(100% - min(85%, 360px) + var(--space-5)) !important;
    right:      calc(var(--space-5) + env(safe-area-inset-right, 0px)) !important;
    bottom:     calc(var(--space-6) + env(safe-area-inset-bottom, 0px) + 4.25rem) !important;
    top:        auto !important;
    width:      auto !important;
    margin:     0 !important;
    padding:    0 !important;
    z-index:    9051;
    transform:  translateX(120%);
    transition: transform 0.3s ease;
  }

  .menu-open .site-header.site-header--over-hero .site-header__utility {
    transform: translateX(0);
  }

  /* Centre the icons in the row now that it is panel-width. */
  .site-header.site-header--over-hero .site-header__utility .site-header__utility-inner {
    display:         flex !important;
    justify-content: center !important;
    align-items:     center !important;
    gap:             var(--space-6);
    padding:         0 !important;
    max-width:       none !important;
  }

  /* 2. The search button's label, targeted by role rather than by class. */
  .site-header.site-header--over-hero .mobile-search button,
  .site-header.site-header--over-hero .mobile-search input[type="button"],
  .site-header.site-header--over-hero .mobile-search input[type="submit"],
  .site-header.site-header--over-hero .mobile-search .sitebutton,
  .site-header.site-header--over-hero .mobile-search input.sitebutton {
    color: #FFFFFF !important;
  }
}

/* 3. The gap below the hero, at a specificity that should not lose. */
@media (max-width: 720px) {
  main .slice.slice--welcome {
    padding-block: var(--space-8) var(--space-6);
    min-height:    0;
  }

  main .slice.slice--welcome .welcome_article {
    margin-block:  0;
    padding-block: 0;
    min-height:    0;
  }
}

/* =========================================================================
   59. v4.4  --  one mechanism, not two: the row carries its contents
   =========================================================================
   Adam supplied the panel's rendered markup, which settles all of this.

   WHAT THE MARKUP CONFIRMS.
     - The panel contains the menu header, the desktop nav table, the clean
       mobile list, an account block and the search. The social icons and
       Donate are NOT in it, and never will be: they live in the utility row.
     - The search control is `input type="button" class="button"`, NOT
       `sitebutton`. So section 57 could never have matched it and section 58's
       match on the input TYPE is what turned the label white. Confirmed
       working.

   WHY THE ICONS AND DONATE CAME OUT WRONG. Sections 57 and 58 ended up using
   two mechanisms at once. Section 57 fixed the icons; section 58 fixed the row
   that contains them, WITH A TRANSFORM. A transform creates a containing block
   for fixed-position descendants, so from that moment:

     - the icons' `left`, `right` and `bottom` resolved against the ROW rather
       than the viewport, putting them roughly a row-height above the row and
       then off its right edge
     - their own `translateX` stacked on top of the row's
     - Donate, also a child of that row, had its offsets rebased the same way,
       which is exactly why it came out narrower than the search box and pushed
       to the right

   So the row moving was correct and the rest was interference. One mechanism
   now: the row is the panel-foot container, and everything inside it returns to
   normal flow and is laid out with flex. Section 57's positioning is explicitly
   unwound rather than left to fight.

   THE STATUS BAR IS HIDDEN HERE. Its links are already in the panel's own
   account block and its search in the panel's search, both visible in the
   markup, so the copy in the utility row is redundant and would only add height
   to the stack.

   DONATE MATCHES THE SEARCH BOX because both are now simply full width of the
   same container. The one estimate left is the row's inset, which should equal
   the panel's own horizontal padding. That value is not in the markup, so
   1.5rem is a measured guess from the screenshot; it is the single number to
   adjust if the two are not flush.

   THE PAGER DOTS were being covered by the caption. The dots sit at the foot of
   the hero and the caption is bottom-aligned, so on a phone the button lands on
   them. The caption's bottom padding now clears them. Arrows stay hidden, which
   Adam is happy with: swipe and dots are how a phone changes a slide.
   ========================================================================= */

@media (max-width: 1400px) {

  /* Unwind section 57. The icons must be in flow, or the row's transform
     rebases them. */
  .site-header.site-header--tworow.site-header--over-hero
  .site-header__utility .site-header__utility-inner .header-social {
    position:  static !important;
    top:       auto !important;
    right:     auto !important;
    bottom:    auto !important;
    left:      auto !important;
    transform: none !important;
    width:     100% !important;
    margin:    0 !important;
  }

  /* Donate likewise: in flow, full width of the row. */
  .site-header.site-header--over-hero .site-header__utility .header-giving {
    position:        static !important;
    top:             auto !important;
    right:           auto !important;
    bottom:          auto !important;
    left:            auto !important;
    transform:       none !important;
    width:           100% !important;
    margin:          0 !important;
    display:         flex !important;
    justify-content: center !important;
    text-align:      center !important;
  }

  /* The row: the only fixed thing, sitting at the panel's foot. */
  .site-header.site-header--over-hero .site-header__utility {
    left:    calc(100% - min(85%, 360px) + var(--space-6)) !important;
    right:   calc(var(--space-6) + env(safe-area-inset-right, 0px)) !important;
    bottom:  calc(var(--space-6) + env(safe-area-inset-bottom, 0px)) !important;
    display: block !important;
  }

  /* Stack: icons, then Donate. */
  .site-header.site-header--over-hero .site-header__utility .site-header__utility-inner {
    display:        flex !important;
    flex-direction: column !important;
    align-items:    stretch !important;
    gap:            var(--space-5) !important;
  }

  /* The status bar's copy of the login link and search is redundant here. */
  .site-header.site-header--over-hero .site-header__utility-inner > div:has(.statusbar) {
    display: none !important;
  }

  /* Icons centred across the row. */
  .site-header.site-header--over-hero .header-social .ArticleBody {
    display:         flex !important;
    justify-content: center !important;
    align-items:     center !important;
    gap:             var(--space-6) !important;
  }
}

/* The caption clears the pager dots. */
@media (max-width: 720px) {
  .slice--hero .hero_slideshow .hero-caption {
    padding-bottom: var(--space-12);
  }
}

/* =========================================================================
   60. v4.5  --  the hero's own padding, and colour by changing the input
   =========================================================================

   1. THE PANEL ICONS, SOLVED BY CHANGING THE INPUT RATHER THAN THE RULE.
      Adam's own observation is the diagnosis: on desktop the icons go dark as
      the header solidifies. That only happens because they read
      `var(--hdr-ink)`, the property section 42 animates from white to ink. So
      whichever rule is currently winning on the icons, it is reading that
      variable.

      Section 56 tried to beat that rule with a more specific colour, and by my
      reading it should have won. It plainly did not, and I cannot see why from
      a render. So this stops arguing about which rule applies and changes what
      the winning rule reads: `--hdr-ink` is set to ink on the utility row for
      the whole mobile range. It is registered as inheriting, so every
      descendant picks it up, and any rule setting `color: var(--hdr-ink)`
      produces ink whether it is section 27, 42 or 56 that wins.

      Working with the mechanism instead of against it, which is what I should
      have done two attempts ago.

   2. THE WHITE GAP WAS THE HERO'S, NOT THE WELCOME SECTION'S.
      This is why setting the welcome slice's padding twice changed nothing.
      `.slice--hero` is a `.slice` like any other, so it inherits the library's
      generous section padding, and that padding sits INSIDE the hero section,
      below the slideshow. So the space was never in the section I kept
      adjusting.

      On top of that, bxSlider's own stylesheet gives `.bx-wrapper` a 60px
      bottom margin, a 5px white border, a white background and a drop shadow,
      all intended for a bordered slideshow widget on a page. On a full-bleed
      hero every one of those is wrong.

      A full-bleed hero should have no padding at any width, so this is not
      scoped to mobile: the same gap was visible on desktop and was mistaken for
      the welcome section's spacing there too.

   3. FIFTEEN MORE PIXELS between the caption and the pager dots, as asked.
      48px to 64px, staying on the scale.
   ========================================================================= */

/* -------------------------------------------------------------------------
   60.1  The hero carries no padding, at any width
   ------------------------------------------------------------------------- */
main .slice.slice--hero {
  padding-block: 0 !important;
  margin-block:  0 !important;
}

.slice--hero .hero_slideshow {
  margin-block:  0;
  padding-block: 0;
}

/* bxSlider's widget defaults: a 60px bottom margin, a white border, a white
   background and a shadow. All wrong for a full-bleed hero. */
.slice--hero .bx-wrapper {
  margin:     0 !important;
  padding:    0 !important;
  border:     0 !important;
  background: none !important;
  box-shadow: none !important;
}

/* -------------------------------------------------------------------------
   60.2  Panel icons: change the variable, not the argument
   ------------------------------------------------------------------------- */
@media (max-width: 1400px) {
  .site-header.site-header--over-hero .site-header__utility {
    --hdr-ink:   var(--ink);
    --hdr-veil:  0;
  }

  /* Belt and braces, at a specificity that should not lose to anything in this
     file. If the variable above is doing the work, this is redundant. */
  .site-header.site-header--tworow.site-header--over-hero
  .site-header__utility .site-header__utility-inner
  .header-social .footer_icon_block,
  .site-header.site-header--tworow.site-header--over-hero
  .site-header__utility .site-header__utility-inner
  .header-social .footer_icon_block svg {
    color:       var(--ink) !important;
    text-shadow: none !important;
    filter:      none !important;
  }
}

/* -------------------------------------------------------------------------
   60.3  Caption clears the pager dots by a further 16px
   ------------------------------------------------------------------------- */
@media (max-width: 720px) {
  .slice--hero .hero_slideshow .hero-caption {
    padding-bottom: var(--space-16);
  }
}

/* =========================================================================
   61. v4.6  --  the panel actions become a proper footer bar
   =========================================================================
   The icons are black and Donate is full width, so the positioning is right.
   What is wrong is that the actions are a fixed block with no background over a
   scrolling list, and nothing reserves room for them. Expand a submenu and the
   list is longer than the panel, so menu items scroll straight through the
   icons.

   That is the standard fixed-footer-over-a-scrolling-list problem, and it needs
   both halves of the standard answer. Doing only one is what makes these look
   broken:

     1. THE BAR NEEDS AN OPAQUE BACKGROUND, so content passing beneath it is
        hidden rather than showing through. It takes the panel's own white, with
        a hairline along the top so it reads as a deliberate bar rather than
        floating text.

     2. THE PANEL NEEDS BOTTOM PADDING equal to the bar's height, so the last
        menu item can always be scrolled clear of it. Without this the final
        item is unreachable, which is worse than the overlap because it is
        invisible: you would only find it by trying to tap Venue Hire.

   THE BAR NOW SPANS THE FULL PANEL WIDTH rather than being inset, because a
   background inset from the edges leaves two strips where content still shows
   through. The inset moves inside as padding, which also keeps Donate aligned
   with the search box above it, since both are now inset by the same amount
   from the same container.

   The safe-area inset moves into the padding for the same reason: the
   background needs to reach the bottom of the screen on a device with a home
   indicator, while the content stays above it.

   10rem of reserved space is the bar at its tallest: its padding, the icon row,
   the gap and the Donate pill, with a little margin for a larger text setting.
   ========================================================================= */

@media (max-width: 1400px) {

  /* The bar: full panel width, opaque, at the foot. */
  .site-header.site-header--over-hero .site-header__utility {
    left:       calc(100% - min(85%, 360px)) !important;
    right:      0 !important;
    bottom:     0 !important;
    padding:    var(--space-5) var(--space-6)
                calc(var(--space-5) + env(safe-area-inset-bottom, 0px)) !important;
    background: var(--bg) !important;
    border-top: 1px solid var(--rule) !important;
  }

  /* Room at the end of the list so the last item clears the bar. */
  .site-header.site-header--over-hero .site-header__nav {
    padding-bottom: 10rem;
  }
}

/* =========================================================================
   62. v4.7  --  the white gap, closed deterministically
   =========================================================================
   Adam supplied the markup for both sections, so the full element chain
   between the photograph and the strapline is now known:

       section.slice.slice--hero
         div.hero_slideshow
           div.bx-wrapper
             div.bx-viewport          460px, correct
             div.bx-controls
               div.bx-pager
               div.bx-controls-direction   hidden on phones
       section.slice.slice--welcome
         div.welcome_article.hubb-reveal
           div.ArticleBody
             h2
             div.welcome-cta
           div.ArticleRotaSignupLink

   That is EIGHT boxes that could be holding the space, and previous passes
   only touched four of them. Rather than work through the remaining four one
   review cycle at a time, this closes the question from both ends.

   THE HERO SIDE, DETERMINISTICALLY. Instead of hunting for which descendant
   adds height, the slideshow and its wrapper are pinned to exactly `--hero-h`
   with their overflow hidden. Nothing inside them can then contribute height
   below the image, whatever it is: not `bx-controls`, not the pager, not a
   wrapper margin, not something in the library I cannot see. Safe here because
   the pager is already positioned INSIDE the image rather than below it, so
   clipping costs nothing.

   THE WELCOME SIDE, EXPLICITLY. The remaining candidates are the section, the
   article wrapper, the ArticleBody and the heading. All four are stated,
   because any one of them could be the owner and the markup cannot tell me
   which.

   VALUES IN REM, NOT TOKENS. This library build is the one with the missing
   `--space-10`, so a token that turns out not to exist would make the whole
   declaration invalid at computed-value time and silently compute to zero. A
   clamp in rem cannot fail that way, and it serves both phone and desktop from
   one rule, which also supersedes the mobile-only attempts in sections 55 and
   58.

   NOT SCOPED TO MOBILE. The same gap was visible on desktop and blamed on the
   same wrong section there.
   ========================================================================= */

/* The hero is exactly as tall as its slideshow, and nothing else. */
.slice--hero .hero_slideshow,
.slice--hero .bx-wrapper {
  height:     var(--hero-h) !important;
  min-height: 0 !important;
  max-height: none !important;
  margin:     0 !important;
  padding:    0 !important;
  overflow:   hidden !important;
}

/* Every box on the welcome side that could hold space above the heading. */
main section.slice.slice--welcome {
  padding-block: clamp(2rem, 4vw, 4rem) clamp(1.5rem, 3vw, 3rem) !important;
  margin-block:  0 !important;
}

main section.slice.slice--welcome .welcome_article,
main section.slice.slice--welcome .ArticleBody {
  margin-block:  0 !important;
  padding-block: 0 !important;
}

main section.slice.slice--welcome .ArticleBody > h2 {
  margin-top: 0 !important;
}

/* An empty div the article module always emits. Nothing to show, so nothing to
   space. Scoped to this slice so a real rota signup elsewhere is untouched. */
main section.slice.slice--welcome .ArticleRotaSignupLink {
  display: none;
}

/* =========================================================================
   63. v4.8  --  ONE HOVER LANGUAGE: 350ms, opacity 0.7, nothing moves
   =========================================================================
   Supersedes the hover half of the motion system in section 40. That chose
   darken-and-lift; Adam wants a single fade with no movement, applied across
   the whole page, which is the house pattern on the other builds too.

   WHAT CHANGES
     - Every filled control and every icon fades to 0.7 over 350ms on hover.
     - Nothing translates. Section 40's one-pixel lifts and the two-pixel
       social icon lift are unwound.
     - No colour swaps. Each control's resting fill is RE-ASSERTED on :hover,
       because section 40's colour changes and the library's own would
       otherwise still fire; restating the resting colour is what guarantees
       opacity is the only thing that moves.
     - Press feedback is a deeper fade rather than a squeeze, so touch devices
       get an acknowledgement without anything shifting.

   THE CONTRAST COST, recorded rather than discovered later. Opacity blends the
   whole control toward the page behind it, so a white label on a coloured fill
   loses contrast as the fill lightens. Measured on Donate: 5.00:1 at rest,
   about 3.09:1 while hovered, which is below AA. The same applies in some
   degree to every coloured button here.

   Two things make that acceptable rather than careless. Every RESTING state
   still passes comfortably, and the dip exists only while the pointer is on the
   control, where the user already knows what they are pointing at. Worth
   knowing that no opacity value fixes it: even 0.85 leaves Donate near 4:1,
   because its resting contrast has little headroom to give. Holding AA through
   hover would mean going back to darkening the fill, which is what this
   replaces.

   TEXT LINKS KEEP A COLOUR SHIFT, NOT A FADE, and that is deliberate. Fading
   body text reads as disabled rather than interactive, and it reduces contrast
   on the element type that can least afford it. They take the same 350ms so the
   TIMING is consistent everywhere even where the property differs, which is
   what actually makes a site feel coherent.

   TWO DELIBERATE EXEMPTIONS
     - The feature cards keep the treatment Adam asked for in section 47: the
       photograph zooms and fades, the text does not.
     - The nav keeps its underline, since a fading menu label over a photograph
       would be unreadable.

   The reveal and card-zoom timings are untouched. Those were tuned and
   approved, and they are entrances rather than responses.
   ========================================================================= */

/* The library's own transition token, onto the new timing. */
:root {
  --transition: 350ms ease;
}

/* -------------------------------------------------------------------------
   63.1  Filled controls and icons: fade only
   ------------------------------------------------------------------------- */
.header-giving,
.hero_slideshow .hero-caption__cta,
.section-link,
.side-card__cta,
.welcome-cta__btn,
.content-shell__main input.button,
.header-social .footer_icon_block,
.social_media_icons .footer_icon_block {
  transition: opacity 350ms ease !important;
}

.header-giving:hover,
.hero_slideshow .hero-caption__cta:hover,
.section-link:hover,
.side-card__cta:hover,
.welcome-cta__btn:hover,
.content-shell__main input.button:hover,
.header-social .footer_icon_block:hover,
.social_media_icons .footer_icon_block:hover {
  opacity:    0.7;
  transform:  none !important;
  box-shadow: none !important;
}

/* Press: a deeper fade, and still nothing moves. */
.header-giving:active,
.hero_slideshow .hero-caption__cta:active,
.section-link:active,
.side-card__cta:active,
.welcome-cta__btn:active,
.content-shell__main input.button:active,
.header-social .footer_icon_block:active,
.social_media_icons .footer_icon_block:active {
  opacity:   0.55;
  transform: none !important;
}

/* -------------------------------------------------------------------------
   63.2  Resting fills restated on hover, so only opacity changes
   ------------------------------------------------------------------------- */
.site-header--tworow .header-giving:hover,
.site-header--split  .header-giving:hover {
  background-color: var(--rm-orange-deep) !important;
  border-color:     var(--rm-orange-deep) !important;
}

.section-link:hover,
.side-card__cta:hover,
.content-shell__main input.button:hover {
  background-color: var(--accent) !important;
  border-color:     var(--accent) !important;
}

.welcome-cta__btn--blue:hover {
  background-color: var(--rm-info) !important;
  border-color:     var(--rm-info) !important;
}
.welcome-cta__btn--purple:hover {
  background-color: var(--accent) !important;
  border-color:     var(--accent) !important;
}
.welcome-cta__btn--orange:hover {
  background-color: var(--rm-orange-deep) !important;
  border-color:     var(--rm-orange-deep) !important;
}

/* Icons keep their own colour and simply fade. */
.header-social .footer_icon_block:hover,
.social_media_icons .footer_icon_block:hover {
  color: inherit;
}

/* -------------------------------------------------------------------------
   63.3  Text links: same timing, colour rather than fade
   ------------------------------------------------------------------------- */
.al-cards-feature .articleListTitle a,
.content_article .ArticleBody a,
.content-shell__side .group_nav_selected > a,
.content-shell__side .group_nav_sibling > a,
.content-shell__side .group_nav_child > a {
  transition: color 350ms ease, background-color 350ms ease;
}

/* -------------------------------------------------------------------------
   63.4  The hubb.digital credit: full strength, quiet on hover
   -------------------------------------------------------------------------
   The library fades the builder's credit by default and brightens it on hover,
   which is the usual convention. The brief here is the opposite. Matched by
   destination rather than by the library's internal ids, since a link to
   hubb.digital in the bottom bar can only be this element, and the opacity is
   asserted on both the anchor and its image so it does not matter which of the
   two the library fades.
   ------------------------------------------------------------------------- */
.site-footer__bottom a[href*="hubb.digital"],
.site-footer__bottom a[href*="hubb.digital"] img {
  opacity:    1 !important;
  transition: opacity 350ms ease;
}

.site-footer__bottom a[href*="hubb.digital"]:hover,
.site-footer__bottom a[href*="hubb.digital"]:hover img {
  opacity: 0.6 !important;
}

/* -------------------------------------------------------------------------
   63.5  Reduced motion
   -------------------------------------------------------------------------
   The fade is information rather than decoration, so it still happens; it
   simply arrives without a transition. Section 40.8's blanket rule already
   collapses the duration, so nothing further is needed here beyond a note that
   this was checked rather than forgotten.
   ------------------------------------------------------------------------- */

/* =========================================================================
   64. v4.9  --  Donate joins the hover language, and the underline stops
                 outliving its dropdown
   =========================================================================

   1. DONATE WAS STILL GROWING ON HOVER. Section 63 gave it the fade, but
      something in the library also changes its size or border on hover and my
      rule was not covering the properties involved. `.header-giving:hover`
      scores two classes, and a library rule scoped through a header variant
      scores three, so it wins on anything section 63 did not explicitly name.

      Rather than work out WHICH property grows it, every property that could
      change a pill's size or edge is now pinned to the same value in all four
      states: resting, hover, focus and active. Padding, border, background,
      box-shadow, transform and the independent `scale`. If the size cannot
      change in any state, it does not matter which rule was trying to change
      it.

      `outline` is deliberately NOT pinned, because that is what draws the
      keyboard focus ring and fixing it would remove the ring.

   2. THE UNDERLINE OUTLIVED ITS DROPDOWN, and the reason is worth knowing.
      A transition on the way OUT of a state is defined by the state being
      returned to, not the one being left. Section 40 put the transition on the
      resting rule, so the bar animated in AND out. Meanwhile the dropdown panel
      is switched off instantly by the module's own script. So on mouse-out the
      panel vanished and the bar stayed behind, sliding away on its own, which
      is exactly the odd effect Adam describes.

      Moving the transition onto the hover state alone gives an animated
      appearance and an instant disappearance, which is what matches a panel
      that simply switches off.

      Note this also handles the module's close delay correctly: its script
      keeps the hovered class for a moment after the pointer leaves, so the bar
      stays as long as the panel does and then goes with it.
   ========================================================================= */

/* -------------------------------------------------------------------------
   64.1  Donate: fixed size and edge in every state, opacity the only change
   ------------------------------------------------------------------------- */
.site-header .site-header__utility-inner .header-giving,
.site-header .site-header__utility-inner .header-giving:hover,
.site-header .site-header__utility-inner .header-giving:focus,
.site-header .site-header__utility-inner .header-giving:focus-visible,
.site-header .site-header__utility-inner .header-giving:active {
  padding:          calc(0.5rem - 0.12rem * var(--hdr-squeeze)) var(--space-6) !important;
  border:           1px solid var(--rm-orange-deep) !important;
  background-color: var(--rm-orange-deep) !important;
  box-shadow:       none !important;
  transform:        none !important;
  scale:            1 !important;
  transition:       opacity 350ms ease !important;
}

.site-header .site-header__utility-inner .header-giving:hover {
  opacity: 0.7 !important;
}

.site-header .site-header__utility-inner .header-giving:active {
  opacity: 0.55 !important;
}

/* -------------------------------------------------------------------------
   64.2  Nav underline: animate in, vanish out
   -------------------------------------------------------------------------
   The resting rule carries no transition, so leaving the hovered state is
   instant. The hovered rule carries the transition, so arriving is animated.
   ------------------------------------------------------------------------- */
.site-header .site-header__nav td.t_horizontal > div.t::after,
.site-header .site-header__nav td.t_horizontal_hi > div.t::after {
  transition: none !important;
}

.site-header .site-header__nav td.t_horizontal:hover > div.t::after,
.site-header .site-header__nav td.t_horizontal_hi:hover > div.t::after {
  transition: transform 240ms cubic-bezier(0.2, 0, 0, 1) !important;
}

/* =========================================================================
   65. v5.0  --  footer social icons: legible, and stroked not filled
   =========================================================================
   Two faults compounding, one of them mine.

   1. THE ICONS WERE FILLING SOLID INSTEAD OF STROKING. The inline SVGs carry
      `fill="none"` as a PRESENTATION ATTRIBUTE, and a presentation attribute
      loses to any CSS declaration whatsoever, including a plain `svg { fill:
      currentColor }` reset. So the library's own fill wins and every shape
      floods: the Facebook f and the LinkedIn in become blobs, and the
      Instagram lens fills over its aperture.

      This is a general trap with inline SVG, and it applies to `stroke` and
      `stroke-width` too. Both are now stated in CSS rather than left on the
      element, so they win by the same rule that was beating them.

   2. WHITE ICONS ON A WHITE CHIP. Mine. Section 37 set the footer icons white
      because the footer band is dark, which was right about the band and wrong
      about the icon: the library seats each one in a light circular chip, so
      white on white.

   WHY BOTH SIDES ARE NOW STATED HERE. The chip's colour could not be read from
   the render with any confidence, and it may well be derived from
   `currentColor`, in which case changing the icon colour moves the chip with it
   and the contrast problem simply follows. So the chip and the icon are both
   given explicit values: a white chip with ink strokes, which is 12:1 and
   cannot be undone by whatever the library was doing.

   The same `fill: none` correction is applied to the header set. Those look
   right today, but they are the same markup with the same exposure, and a
   library change to an svg reset would blob them too.
   ========================================================================= */

/* Stroked, never filled. Stated in CSS so it beats the library the same way
   the library was beating the attribute. */
.header-social .footer_icon_block svg,
.social_media_icons .footer_icon_block svg,
.site-footer .social_media_icons .footer_icon_block svg {
  fill:             none !important;
  stroke:           currentColor !important;
  stroke-width:     2 !important;
  stroke-linecap:   round;
  stroke-linejoin:  round;
}

/* The footer chip: an explicit white disc with ink strokes, so the contrast is
   guaranteed rather than inherited. */
.site-footer .social_media_icons .footer_icon_block {
  display:          inline-flex !important;
  align-items:      center !important;
  justify-content:  center !important;
  width:            2.5rem;
  height:           2.5rem;
  border-radius:    999px;
  background-color: #FFFFFF !important;
  color:            var(--ink) !important;
  text-decoration:  none;
}

.site-footer .social_media_icons .footer_icon_block svg {
  width:  1.15rem;
  height: 1.15rem;
}

/* Spacing between the discs. */
.site-footer .social_media_icons .ArticleBody {
  display:   flex;
  flex-wrap: wrap;
  gap:       var(--space-3);
}

/* =========================================================================
   66. v5.1  --  the footer already had brand icons; mine were on top
   =========================================================================
   Look at what the footer actually draws: filled brand glyphs with the letter
   knocked out of them, an Instagram lens reversed out of a rounded square, a
   play triangle reversed out of a YouTube tile. My inline SVGs draw none of
   those. They are stroked outlines.

   So the library is drawing its own brand icons from the CHANNEL CLASS on each
   anchor, `facebook`, `twitter`, `instagram`, `linkedin`, `youtube`, and my SVG
   has been sitting on top of them. A dark stroked outline over a dark filled
   glyph with a light counter is exactly the muddle in the screenshot.

   THIS ALSO EXPLAINS SOMETHING THAT NEVER ADDED UP. Town Church's footer icons
   have always looked right even though the icomoon font this markup references
   is not loaded on Tabernacle. If the font were the mechanism they would have
   been blank there too. They were not, because the `<i class="icomoon-...">`
   elements are vestigial Aspire markup and the channel class was doing the work
   the whole time.

   Which means my read back in section 37 was wrong in an instructive way. The
   HEADER icons genuinely were invisible, because the library's icon rules are
   scoped to `.social_media_icons`, the footer module's class, and the header
   module carries `header-social`. So the inline SVG was the right fix for the
   header and an unnecessary one for the footer.

   The SVG is therefore hidden in the footer and kept in the header. The white
   disc and ink colour from section 65 stay, since the library's glyphs are drawn
   in `currentColor` and read cleanly at 12:1 against white.

   IF THE DISCS COME OUT EMPTY, this reading is wrong: the library draws nothing
   and the SVG was the only content. In that case the fix is to restore it and
   deal with the fill instead. Stated so the next step is obvious either way.
   ========================================================================= */
.site-footer .social_media_icons .footer_icon_block svg {
  display: none;
}

/* -------------------------------------------------------------------------
   66.1  A note for the backport list rather than a change
   -------------------------------------------------------------------------
   The library's brand icons could be brought to the header by adding
   `social_media_icons` alongside `header-social` on the header module's CSS
   Class, which would let the inline SVG go entirely and give both places the
   same set. Not done here, because the footer treatment carries footer-scale
   discs and spacing that would need unpicking in the header, and that is a
   change worth making deliberately rather than while fixing something else.
   ------------------------------------------------------------------------- */

/* =========================================================================
   67. v5.2  --  a section link on the impact band
   =========================================================================
   The homepage already has an established pattern for "there is more of this
   elsewhere": a `.section-link` pill at the foot of the section, on Latest news
   and Upcoming events. The impact band now takes the same one rather than
   inventing a third kind of call to action, so the page has one vocabulary for
   one job.

   It inherits the pill's fill, type and the 350ms fade from sections 63 and 64
   without any styling of its own. The only thing stated here is placement,
   because this slice differs from the news slices in one respect: its content
   comes from an article body rather than a feed, so it does not sit in a
   centring context and the link would otherwise align left against centred
   figures.

   `width: fit-content` with automatic side margins is what makes a flex box
   centre; `margin: auto` alone does nothing to an inline-flex element, which is
   how the library styles the pill.

   LABEL AND DESTINATION. Adam asked for "read more about what we do" pointing
   at Video Stories. The label is "Watch video stories" instead, for two
   reasons: "read" promises an article and the destination is video, and their
   own navigation calls that section Video Stories, so the link and the page it
   lands on agree. Easy to reword if the softer phrasing is preferred.

   THE HREF IS RELATIVE, deliberately. Adam supplied the full trial URL on
   romseymill.hubb.church. Hard-coding that domain would break every one of
   these links the day the site moves to its real address, and it is the kind of
   breakage nobody finds until after launch.
   ========================================================================= */
.slice--stats .section-link {
  display:     flex;
  width:       fit-content;
  margin:      var(--space-8) auto 0;
}

/* =========================================================================
   68. v5.3  --  content-page header and sidebar
   =========================================================================

   1. THE HEADER SPLIT ACROSS THE LOGO. On the solid templates the utility row
      keeps the library's tinted band while the main row is white, and since
      section 53 scales the logo up out of its box, the logo now straddles the
      join. So the tint reads as a line drawn through the wordmark.

      Both rows go white, and the utility row's bottom hairline comes off, which
      is the other half of the same line. Whatever hairline the main row carries
      stays, so the header still separates from the page.

   2. TWO UNDERLINES ON HOVER. Section 39 marked hover with
      `text-decoration: underline` on the solid headers. Section 40 then replaced
      that with a drawn `::after` bar for all headers, but only set
      `text-decoration: none` on the RESTING state, so on hover both were
      drawing. On items with a dropdown the panel covers the second line, which
      is why it only showed on Home.

      The text-decoration is now cleared on hover too, leaving the bar alone, so
      the content pages behave exactly as the homepage does.

   3. THE GET IN TOUCH CARD WAS THE SAME COLOUR AS THE PAGE. Section 41 gave the
      sidebar card heads a light grey fill to replace their solid purple, and
      section 41 also made the page band that same light grey. Two changes in one
      pass, each sensible alone, and together they made the card invisible.

      The cards go white, so they lift off the tinted page, with the blue rule
      along the top as the accent and a soft shadow to seat them. Applied to both
      the modern `side-card` markup and the legacy `boxout` tables the Group
      Navigation module emits, since the sidebar contains both.

   4. ON MINIMISING THE HEADER ON CONTENT PAGES, which Adam asked about: it
      already is. Section 43 sets `--hdr-squeeze: 1` on every non-hero header, so
      a content page's header is permanently in the state the homepage animates
      INTO. There is nothing to minimise because it starts minimised, and that is
      the consistency: the compact bar is the same compact bar in both places.
   ========================================================================= */

/* -------------------------------------------------------------------------
   68.1  Solid headers: one white surface, no line through the middle
   ------------------------------------------------------------------------- */
.site-header:not(.site-header--over-hero),
.site-header:not(.site-header--over-hero) .site-header__utility,
.site-header:not(.site-header--over-hero) .site-header__utility-inner,
.site-header:not(.site-header--over-hero) .site-header__main {
  background-color: var(--bg) !important;
  background-image: none !important;
}

.site-header:not(.site-header--over-hero) .site-header__utility {
  border-bottom: 0 !important;
  box-shadow:    none !important;
}

.site-header:not(.site-header--over-hero) .site-header__utility::before,
.site-header:not(.site-header--over-hero) .site-header__utility::after {
  display: none;
}

/* -------------------------------------------------------------------------
   68.2  One underline, matching the homepage
   ------------------------------------------------------------------------- */
.site-header:not(.site-header--over-hero) .site-header__nav td.t_horizontal_hi > div.t,
.site-header:not(.site-header--over-hero) .site-header__nav td.t_horizontal:hover > div.t {
  text-decoration: none !important;
}

/* -------------------------------------------------------------------------
   68.3  Sidebar cards lift off the tinted page
   ------------------------------------------------------------------------- */
.content-shell__side .side-card,
.content-shell__side .boxout,
.content-shell__side table.boxout {
  background-color: var(--bg) !important;
  border-radius:    12px;
  overflow:         hidden;
  box-shadow:       0 8px 24px -18px rgba(35, 49, 61, 0.45);
}

.content-shell__side .side-card__head,
.content-shell__side .boxout_header,
.content-shell__side .boxout_header_middle,
.content-shell__side .boxout_contents {
  background-color: var(--bg) !important;
  background-image: none !important;
}

.content-shell__side .side-card__head,
.content-shell__side .boxout_header {
  border-top: 3px solid var(--rm-info);
  color:      var(--ink);
}

/* =========================================================================
   69. v5.3  --  an empty sidebar card must not look like a card
   =========================================================================
   Reading the Content template settles what the screenshot implied: Group
   Navigation 1426665 IS in the sidebar, as the first `side-card`, so it is
   rendering nothing rather than being missing.

   Which makes section 68.3 a hazard as written. Giving every `.side-card` a
   white fill, a radius and a shadow turns that empty wrapper into a blank white
   box floating above the contact card. The chrome is therefore conditional: a
   card only gets it if it actually contains a head or a legacy boxout header,
   which is to say if it has content to frame.

   A card that contains nothing at all is hidden outright, so a module that
   suppresses itself takes its wrapper with it rather than leaving a hole in the
   sidebar. That is worth having as a standing guard regardless of this
   particular module: any of these cards can empty out when a client deletes the
   content behind it, and a site that degrades to a blank box is worse than one
   that degrades to nothing.
   ========================================================================= */

/* Chrome only for cards with something in them. */
.content-shell__side .side-card:not(:has(.side-card__head)):not(:has(.boxout_header)):not(:has(ul)) {
  background:    none !important;
  box-shadow:    none !important;
  border-radius: 0 !important;
  padding:       0 !important;
}

/* And nothing at all for a card with no element content whatsoever. */
.content-shell__side .side-card:not(:has(*)) {
  display: none !important;
}

/* =========================================================================
   70. v5.4  --  the group navigation, against its real markup
   =========================================================================
   Adam supplied the rendered markup and it settles NAV-1. The module was never
   missing and never empty. It emits this:

       div.group-nav
         table                       inline border-collapse
           tbody
             tr                      ONE row
               td  div.GroupNavTitle > a      Staff
               td  div.GroupNavTitle > a      Trustees and Patrons
               td  div.GroupNavTitle > a      Volunteers

   A single table ROW, so the three links sit side by side across the sidebar,
   which is why "Trustees and Patrons" was breaking into a column of three
   words. Unstyled and horizontal, in a space 300px wide.

   AND EVERY GROUP-NAV RULE IN THIS FILE HAS BEEN DEAD CODE. Sections 8, 41 and
   63 all style `.group_nav_selected`, `.group_nav_sibling` and
   `.group_nav_child`. None of those classes exist anywhere in this module's
   output. The real class is `.GroupNavTitle`. Three separate sections written
   against invented class names, carried through several passes without ever
   being questioned, because a rule that matches nothing looks exactly like a
   rule that is working when you cannot see the markup.

   NO BOXOUT WRAPPER EITHER, which explains the other half. `HasTitleBar` is
   True on the module but produces nothing, so the "In this section" title never
   appeared and sections 41 and 68's `.boxout_header` rules were also matching
   nothing. The heading is now a `side-card__head` in the template, the same
   element the contact card uses, so the two sidebar cards are built the same
   way.

   THE TABLE IS TURNED INTO A LIST rather than the module's format being changed.
   There is very likely a format value that emits an unordered list, since the
   settings carry an `UnorderedListMaxChildDepth`, and that would be better
   markup. But the enum is undocumented and guessing at it risks emitting nothing
   at all, which is worse than a styled table. Worth testing deliberately later.
   ========================================================================= */

/* The one-row table becomes a vertical stack. */
.content-shell__side .group-nav table,
.content-shell__side .group-nav tbody,
.content-shell__side .group-nav tr,
.content-shell__side .group-nav td {
  display:         block !important;
  width:           auto !important;
  border-collapse: separate !important;
  vertical-align:  baseline !important;
}

/* Each link becomes a full-width row, matching the sidebar navigation pattern
   the rest of this shell uses. */
.content-shell__side .group-nav .GroupNavTitle > a {
  display:         block;
  padding:         var(--space-3) var(--space-5);
  border-bottom:   1px solid var(--rule);
  color:           var(--ink);
  font-weight:     500;
  text-decoration: none;
  transition:      background-color 350ms ease,
                   color 350ms ease,
                   padding-left 350ms ease;
}

.content-shell__side .group-nav .GroupNavTitle > a:hover {
  background-color: var(--accent-soft);
  color:            var(--accent);
  padding-left:     var(--space-6);
}

/* No rule under the last row: the card edge already closes the list. */
.content-shell__side .group-nav td:last-child .GroupNavTitle > a {
  border-bottom: 0;
}

/* The module's own wrapper carries no padding, so the list sits flush inside
   the card beneath its head, which is what a navigation list should do. */
.content-shell__side .group-nav {
  padding: 0;
}

/* =========================================================================
   71. v5.5  --  sidebar cards on the Wight Dive pattern
   =========================================================================
   Adam prefers the Wight Dive treatment: a filled dark head with white bold
   type, a white body beneath, the whole thing rounded and seated on the tinted
   page. It is a stronger structure than the hairline used here so far, because
   the head reads as a label attached to the card rather than a heading floating
   above it.

   ONE DELIBERATE DIFFERENCE FROM A STRAIGHT COPY. Wight Dive fills its heads
   with its dark navy, which is that site's ink rather than its accent. So this
   uses Romsey Mill's `--ink` rather than the purple. That matters: section 41
   moved these heads OFF solid purple precisely because Adam asked for
   highlights rather than bold coloured blocks, and a dark neutral head is not a
   return to that. White on ink measures 13.3:1.

   The blue top rule from sections 41 and 68 comes off, since the filled head is
   now the marker and two markers on one card is one too many.

   The row dividers added in section 70 also come off, to match: Wight Dive
   spaces its navigation rows rather than ruling between them, which is quieter
   and reads better in a short list.

   The card keeps the radius and shadow from section 68. The head fills to the
   top corners because the card clips its overflow, so no separate radius is
   needed on the head itself.
   ========================================================================= */

/* -------------------------------------------------------------------------
   71.1  The head: filled, dark, white, bold
   ------------------------------------------------------------------------- */
.content-shell__side .side-card__head,
.content-shell__side .boxout_header {
  background-color: var(--ink) !important;
  background-image: none !important;
  color:            #FFFFFF !important;
  border-top:       0 !important;
  padding:          var(--space-5);
  font-family:      var(--font-display);
  font-size:        var(--text-lg);
  font-weight:      700;
  line-height:      1.25;
}

.content-shell__side .boxout_header_middle,
.content-shell__side .boxout_header_middle a {
  color: #FFFFFF !important;
}

/* -------------------------------------------------------------------------
   71.2  Navigation rows: spaced, not ruled
   ------------------------------------------------------------------------- */
.content-shell__side .group-nav .GroupNavTitle > a {
  border-bottom: 0;
  padding:       var(--space-3) var(--space-5);
}

/* The list needs a little breathing room top and bottom now that nothing
   rules it off from the head. */
.content-shell__side .group-nav table {
  padding-block: var(--space-3);
}

/* -------------------------------------------------------------------------
   71.3  Body padding, so the contact card matches the navigation card
   ------------------------------------------------------------------------- */
.content-shell__side .side-card__body {
  padding: var(--space-5);
}

/* =========================================================================
   72. v5.6  --  footer columns aligned
   =========================================================================
   The footer is a three-column grid, but the contents were grouped wrongly: the
   phone sat alone in the middle column while the email and the social icons
   shared the third. So the email was in the wrong place and the icons were
   left-aligned inside a wide cell, which left a large empty margin on the right
   and made the whole footer read as though it had drifted left.

   The templates now group it as Adam describes: address on the left, phone with
   email beneath it in the middle, social icons alone on the right. Same three
   grid children as before, so the library's grid is untouched.

   ALIGNMENT IS SET ON THE COLUMNS RATHER THAN THE GRID. Middle column centres
   its contents, right column pushes to the end. `justify-self` covers the grid
   case and the automatic left margin covers a flex one, because the library's
   own layout for this row is not visible from here and one of the two will
   apply.

   The icon row itself also needs its own alignment: a flex row inside a
   right-aligned cell still packs its items from the left unless told otherwise.
   That is the sort of thing that looks like the cell has not moved when in fact
   only its contents have not.
   ========================================================================= */

/* Middle column: phone and email, centred. */
.site-footer__inner > .site-footer__stack {
  text-align:  center;
  align-items: center;
}

/* Right column: the social icons, pushed to the end. */
.site-footer__inner > *:last-child {
  justify-self: end;
  margin-left:  auto;
  text-align:   right;
}

/* And the icon row itself aligned right within that cell. */
.site-footer .social_media_icons .ArticleBody {
  justify-content: flex-end;
}

/* =========================================================================
   73. v5.7  --  the sticky sidebar clears the sticky header
   =========================================================================
   Two sticky things on one page. The header sticks at the top of the viewport
   on content pages, and the sidebar cards stick below it as the article scrolls
   past. The sidebar's offset was smaller than the header is tall, so for the
   moment the card is pinned it sits hard against the underside of the nav with
   no space at all.

   The offset is now stated as two separate named numbers rather than one magic
   value, because they mean different things and only one of them is a guess:

     --hdr-solid-height   how tall the compact header is. An estimate, from the
                          parts this skin now owns: the Donate pill and its row
                          padding, plus the logo's layout box and its row
                          padding, plus the hairline. Around 100px.

     the 20px             the gap Adam asked for. Not a guess, and it should
                          stay separate so tuning the height never quietly
                          changes the gap.

   ERRING GENEROUS ON THE HEIGHT is deliberate. Too large and there is simply
   more space, which nobody notices. Too small and the card touches the nav,
   which is the fault being fixed. So if the estimate is out, it is out in the
   harmless direction.

   The offset is applied to both the column and the cards, because whichever of
   the two carries `position: sticky` is not visible from here, and setting
   `top` on an element that is not sticky does nothing at all.
   ========================================================================= */

:root {
  /* Compact header height. One number to correct if the gap looks wrong. */
  --hdr-solid-height: 6.25rem;
}

.content-shell__side,
.content-shell__side .side-card {
  top: calc(var(--hdr-solid-height) + 20px);
}

/* While here: an in-page anchor should not land under the header either. Same
   header height, no extra gap needed, since a heading sitting just below the
   nav reads correctly. */
.content-shell__main :is(h1, h2, h3, h4) {
  scroll-margin-top: var(--hdr-solid-height);
}

/* =========================================================================
   74. v5.8  --  event cards: centred, and in keeping with the site
   =========================================================================
   Adam supplied the markup. Two things in it decide the approach.

   1. EACH CARD CARRIES AN INLINE `style="float: left"`. So the library lays
      this feed out with floats, which is why the cards pack to the left and
      leave a gap when there are fewer than the configured four. An inline style
      can only be overridden with an important declaration, so that is what the
      float unset needs, and it is the one place here that earns it.

   2. THE STAGGER RETUNE IS CONFIRMED WORKING. The inline reveal delays read
      0ms and 70ms, not 0ms and 450ms, so section 40.1 is doing its job on a
      feed it was never specifically written for.

   THE LAYOUT: a centred flex row with a FIXED basis of one quarter, so four
   events fill the row and two sit centred at the same width they would have had
   in a four-up row. That is the point Adam is making: cards that grow to fill
   the space would give two enormous cards rather than a tidy pair.

   THE CARD, and why its colours are the inverse of the homepage feature cards.
   The feature cards are a grey body on a white section; these are a white body
   on a tinted section. That looks like an inconsistency and is in fact the same
   rule applied twice: a card lifts off its background. Matching the grey here
   would make the card disappear into the band, which is exactly the fault fixed
   on the sidebar cards in section 68.

   Everything else follows the established language: the display face for
   titles, muted ink for the location, the blue informational colour left on the
   date and time where the library already puts it, and the same image zoom and
   fade on hover that the feature cards use.

   HOVER IS DRIVEN FROM THE CARD, not from a wrapping anchor, because there
   isn't one: the image, the title and the location are three separate links.
   So the card element carries the hover and the parts respond to it.
   ========================================================================= */

.slice--upcoming-events .upcoming_events {
  --ev-gap:        1.5rem;
  display:         flex;
  flex-wrap:       wrap;
  justify-content: center;
  gap:             var(--ev-gap);
}

/* Unset the inline float, and fix the width at a quarter of the row. */
.slice--upcoming-events .upcoming_events > div {
  float:            none !important;
  flex:             0 0 calc((100% - 3 * var(--ev-gap)) / 4);
  min-width:        0;
  background-color: var(--bg);
  border-radius:    12px;
  overflow:         hidden;
  box-shadow:       0 8px 24px -18px rgba(35, 49, 61, 0.45);
}

/* Image sits flush at the top of the card. */
.slice--upcoming-events .upcoming_events .eventListImage {
  margin: 0;
  line-height: 0;
}

.slice--upcoming-events .upcoming_events .eventListImage img {
  display:    block;
  width:      100%;
  height:     auto;
  transition: transform var(--motion-slow) var(--motion-ease),
              opacity   var(--motion-base) var(--motion-ease);
}

.slice--upcoming-events .upcoming_events > div:hover .eventListImage img {
  transform: scale(1.04);
  opacity:   0.9;
}

/* The date, title and location share one padded body. */
.slice--upcoming-events .upcoming_events .eventListDate,
.slice--upcoming-events .upcoming_events .eventListTitle,
.slice--upcoming-events .upcoming_events .eventListLocation {
  padding-inline: var(--space-5);
}

.slice--upcoming-events .upcoming_events .eventListDate {
  padding-top: var(--space-5);
}

.slice--upcoming-events .upcoming_events .eventListLocation {
  padding-bottom: var(--space-5);
}

.slice--upcoming-events .upcoming_events .eventListTitle a {
  display:         block;
  font-family:     var(--font-display);
  font-size:       var(--text-lg);
  font-weight:     600;
  line-height:     1.25;
  color:           var(--ink);
  text-decoration: none;
  transition:      color 350ms ease;
}

.slice--upcoming-events .upcoming_events > div:hover .eventListTitle a {
  color: var(--accent);
}

.slice--upcoming-events .upcoming_events .eventListLocation a {
  font-size:       var(--text-sm);
  color:           var(--ink-soft);
  text-decoration: none;
}

.slice--upcoming-events .upcoming_events .eventListLocation a:hover {
  color:                 var(--accent);
  text-decoration:       underline;
  text-underline-offset: 3px;
}

/* -------------------------------------------------------------------------
   74.1  Narrower viewports
   ------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .slice--upcoming-events .upcoming_events > div {
    flex: 0 0 calc((100% - var(--ev-gap)) / 2);
  }
}

@media (max-width: 720px) {
  .slice--upcoming-events .upcoming_events > div {
    flex: 0 0 100%;
  }
}

/* =========================================================================
   75. v5.9  --  event cards: close the gaps
   =========================================================================
   Two problems, one cause each.

   THE PALE BAND UNDER THE IMAGE. The image container is taller than the
   photograph inside it, and it has a background of its own, so an accent-tinted
   strip shows through underneath. Section 74 set its margin and line-height but
   not its height or its background, so the container kept whatever fixed height
   and fill the library gives it. Both are cleared here.

   That tint is almost certainly `--accent-soft` or similar, which section 41
   remapped to a pale purple. So a library placeholder colour nobody chose is
   now visible because the token behind it changed. Worth remembering: remapping
   a token changes everything that reads it, including things you have never
   seen.

   THE LOOSE SPACING BETWEEN THE ROWS. The date, title and location each carry
   their own margins from the library, tuned for a different layout. Section 74
   added padding on top of those margins rather than replacing them, so the
   spacing compounded. Margins are now zeroed on the card's direct children and
   all the spacing comes from one set of paddings, which is the only way to
   reason about it.

   The date badge is a grandchild rather than a direct child, so its own white
   box and border survive the reset untouched. That is deliberate: it is the one
   part of the library's treatment here that already looked right.
   ========================================================================= */

/* Nothing on the card's direct children brings its own space or fill. */
.slice--upcoming-events .upcoming_events > div > * {
  margin:     0 !important;
  background: none !important;
  min-height: 0 !important;
}

/* The image container holds the image and nothing else. */
.slice--upcoming-events .upcoming_events .eventListImage,
.slice--upcoming-events .upcoming_events .eventListImage > a {
  display:      block;
  height:       auto !important;
  aspect-ratio: auto !important;
  padding:      0 !important;
  line-height:  0;
  background:   none !important;
}

/* One set of paddings for the whole body, so the rhythm is legible in one
   place rather than spread across three elements' margins. */
.slice--upcoming-events .upcoming_events .eventListDate {
  padding: var(--space-5) var(--space-5) var(--space-3) !important;
}

.slice--upcoming-events .upcoming_events .eventListTitle {
  padding: 0 var(--space-5) var(--space-2) !important;
}

.slice--upcoming-events .upcoming_events .eventListLocation {
  padding: 0 var(--space-5) var(--space-5) !important;
}

/* =========================================================================
   76. v6.0  --  event cards adopt the featured card treatment
   =========================================================================
   Featured items is the reference, so the event cards drop the radius and the
   shadow and become the same flat square block with a tinted body.

   THE CONSEQUENCE, WHICH IS A REAL TRADE. A tinted card cannot sit on a tinted
   band or it disappears, which is why the event cards were white in the first
   place. So the events section loses its light-grey class in the template and
   goes white, and news and events are now both white.

   That breaks the alternating rhythm set in section 41, where no two adjacent
   sections shared a background. The rhythm now comes from the impact band and
   the newsletter band instead. Worth stating because it was a deliberate choice
   being deliberately reversed: identical cards mattered more to Adam than
   alternating bands, and the two cannot both be had here.

   The alternative, if the grey band matters more, is to keep it and accept white
   cards in that one section. One class in the template either way.

   IMAGE PROPORTIONS ARE NOW FIXED at 2:1 to match the featured cards. The event
   feed serves images at whatever the source happens to be, 600x338 and 800x400
   in the current pair, so without this the two cards' images differ in height
   and the bodies start at different points. That is the sort of raggedness that
   reads as carelessness rather than variety.

   HOVER UNDERLINES. The event title was set to `display: block` in section 74,
   so the library's hover underline spanned the full card width rather than the
   words. Two changes: the anchor becomes inline-block so any underline hugs its
   text, and the underline is removed entirely on the title, because the featured
   cards mark a title hover with colour alone. The location keeps an underline on
   its OWN hover, since it leads off to a map and an underline is the honest
   signal for that.
   ========================================================================= */

/* Flat, square, tinted body: the featured card treatment. */
.slice--upcoming-events .upcoming_events > div {
  background-color: var(--section-light);
  border-radius:    0;
  box-shadow:       none;
}

/* Uniform image proportions, as the featured cards have. */
.slice--upcoming-events .upcoming_events .eventListImage img {
  aspect-ratio: 2 / 1;
  object-fit:   cover;
}

/* Title: colour on hover, never an underline, and never one wider than the
   words it belongs to. */
.slice--upcoming-events .upcoming_events .eventListTitle a,
.slice--upcoming-events .upcoming_events > div:hover .eventListTitle a {
  display:         inline-block;
  text-decoration: none !important;
}

.slice--upcoming-events .upcoming_events > div:hover .eventListTitle a {
  color: var(--accent);
}

/* Location: no underline from the card hover, only from its own. It is a map
   link, so an underline there is the correct signal. */
.slice--upcoming-events .upcoming_events > div:hover .eventListLocation a {
  text-decoration: none;
}

.slice--upcoming-events .upcoming_events .eventListLocation a:hover {
  color:                 var(--accent);
  text-decoration:       underline;
  text-underline-offset: 3px;
}

/* =========================================================================
   77. v6.1  --  group listing pages as a two-column card grid
   =========================================================================
   The News group page lists its articles with NO wrapper class at all: a bare
   div holding one div per article, each containing `articleListImage`,
   `articleListTitle`, `articleListSummary` and `articleListLink`. So there is
   nothing for the library's card variants to hook onto, which is why the page
   renders as plain stacked text while the homepage has cards.

   TARGETED BY STRUCTURE RATHER THAN BY CLASS, since there is no class to use:
   the container is identified as the element whose direct children each hold an
   `articleListTitle`. Scoped inside `content-shell__main` so it cannot reach the
   homepage's own feeds.

   A SIDE EFFECT WORTH HAVING. This is not News-specific: it will style every
   group listing page on the site the same way, so Our Work, Get Involved and the
   rest all get consistent cards without further work. That is the right outcome,
   and it is why the rule is written against the shell rather than against one
   group.

   THE CARD IS THE SAME ONE AS EVERYWHERE ELSE, following section 76: flat,
   square, tinted body, image at 2:1, display face for the title, muted ink for
   the summary, and the small-caps read more at the foot.

   `articleListLink` IS A DIV, NOT A LINK. The module emits "Read more" as plain
   text with no anchor, so it cannot be clicked. Styling it as though it were
   would be a lie, so it is styled as a label that responds to the card's hover
   alongside the title, which is honest: the title above it IS the link, and the
   card as a whole reads as one target.
   ========================================================================= */

/* The container: two columns. */
.content-shell__main div:has(> div > .articleListTitle) {
  display:               grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap:                   clamp(1.5rem, 3vw, 2rem);
  align-items:           stretch;
}

/* Each item: the site's card. */
.content-shell__main div:has(> div > .articleListTitle) > div {
  display:          flex;
  flex-direction:   column;
  min-width:        0;
  background-color: var(--section-light);
}

/* Image flush at the top, uniform proportions. */
.content-shell__main div:has(> div > .articleListTitle) > div .articleListImage {
  margin:      0;
  line-height: 0;
}

.content-shell__main div:has(> div > .articleListTitle) > div .articleListImage img {
  display:      block;
  width:        100%;
  height:       auto;
  aspect-ratio: 2 / 1;
  object-fit:   cover;
  transition:   transform var(--motion-slow) var(--motion-ease),
                opacity   var(--motion-base) var(--motion-ease);
}

.content-shell__main div:has(> div > .articleListTitle) > div:hover .articleListImage img {
  transform: scale(1.04);
  opacity:   0.9;
}

/* Body. */
.content-shell__main div:has(> div > .articleListTitle) > div .articleListTitle {
  padding: var(--space-5) var(--space-5) var(--space-2);
  margin:  0;
}

.content-shell__main div:has(> div > .articleListTitle) > div .articleListTitle a {
  display:         inline-block;
  font-family:     var(--font-display);
  font-size:       var(--text-lg);
  font-weight:     600;
  line-height:     1.25;
  color:           var(--ink);
  text-decoration: none;
  transition:      color 350ms ease;
}

.content-shell__main div:has(> div > .articleListTitle) > div:hover .articleListTitle a {
  color: var(--accent);
}

.content-shell__main div:has(> div > .articleListTitle) > div .articleListSummary {
  padding:     0 var(--space-5);
  margin:      0;
  font-size:   var(--text-sm);
  line-height: 1.55;
  color:       var(--ink-soft);
}

/* Read more: a label, not a link, because the module emits no anchor here. It
   is pushed to the foot so the cards finish level. */
.content-shell__main div:has(> div > .articleListTitle) > div .articleListLink {
  margin:          auto 0 0;
  padding:         var(--space-5);
  font-size:       var(--text-xs);
  font-weight:     700;
  letter-spacing:  0.1em;
  text-transform:  uppercase;
  color:           var(--accent);
  transition:      color 350ms ease;
}

.content-shell__main div:has(> div > .articleListTitle) > div:hover .articleListLink {
  color:                 var(--accent-dark);
  text-decoration:       underline;
  text-underline-offset: 4px;
}

/* One column on a phone. */
@media (max-width: 720px) {
  .content-shell__main div:has(> div > .articleListTitle) {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* =========================================================================
   78. v6.2  --  the event title underline, and a See event label
   =========================================================================

   1. THE UNDERLINE IS A BORDER, NOT A TEXT DECORATION. Section 76 set
      `text-decoration: none !important` on the event title and the underline
      survived, which rules text-decoration out. A border-bottom explains both
      the persistence and the symptom Adam describes: a border sits hard against
      the box edge with no offset available, so it crowds the descenders, whereas
      a text decoration can be lifted clear.

      Both are now cleared, on the resting and hovered states.

      AND IT GOES RATHER THAN GETTING AN OFFSET, because the featured cards mark
      a title hover with colour alone and that is the pattern being standardised
      on. Two different hover signals for the same kind of element is the
      inconsistency this whole pass has been closing.

   2. A "SEE EVENT" LABEL, matching "Read more" on the article cards. Added as
      generated content on the card rather than as markup, for the same reason
      the featured cards' read more is: the card already contains three separate
      anchors and a fourth would give a screen reader four links to the same
      event. This is a label that responds to the card's hover, and the title
      above it is the actual link.

      The card becomes a flex column so the label can be pushed to the foot with
      an automatic margin, which also makes the cards finish level however long
      the titles run. The location's bottom padding drops accordingly, since the
      label now closes the card.

      An underline on the label's hover is kept, unlike on the title. A label
      like this has no other affordance, and at a 4px offset it reads as
      deliberate rather than cramped.
   ========================================================================= */

/* No underline on the title, in any state, by either mechanism. */
.slice--upcoming-events .upcoming_events .eventListTitle a,
.slice--upcoming-events .upcoming_events > div:hover .eventListTitle a,
.slice--upcoming-events .upcoming_events .eventListTitle a:hover {
  border-bottom:   0 !important;
  text-decoration: none !important;
}

/* The card becomes a column so the label can sit at its foot. */
.slice--upcoming-events .upcoming_events > div {
  display:        flex;
  flex-direction: column;
}

.slice--upcoming-events .upcoming_events .eventListLocation {
  padding: 0 var(--space-5) var(--space-3) !important;
}

/* The label. Generated, because the card has enough anchors already. */
.slice--upcoming-events .upcoming_events > div::after {
  content:        "See event";
  display:        block;
  margin-top:     auto;
  padding:        0 var(--space-5) var(--space-5);
  font-family:    var(--font-body);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color:          var(--accent);
  transition:     color 350ms ease;
}

.slice--upcoming-events .upcoming_events > div:hover::after {
  color:                 var(--accent-dark);
  text-decoration:       underline;
  text-underline-offset: 4px;
}

/* =========================================================================
   79. v6.3  --  listing cards clip their zoom, like the homepage
   =========================================================================
   The zoom on the group listing cards was growing the image out past the card's
   edges rather than magnifying it within its frame. The featured cards clip
   their overflow, section 46 sets it on the wrapping anchor, and section 77
   omitted it. So the same transform read as two different effects.

   Clipping is the correct one: the frame stays put and the photograph moves
   inside it, which is a change of scale rather than a change of layout. An image
   growing past its container also lifts over the neighbouring card in a grid,
   which is what the second card's edge was showing.

   Also brought into line: the small gap between the title and the summary, so
   the two card types share their internal rhythm as well as their proportions.
   ========================================================================= */

.content-shell__main div:has(> div > .articleListTitle) > div {
  overflow: hidden;
}

/* Same rhythm inside the body as the featured cards. */
.content-shell__main div:has(> div > .articleListTitle) > div .articleListSummary {
  padding-top: var(--space-2);
}

/* =========================================================================
   80. v6.4  --  the event title underline, closed off completely
   =========================================================================
   Third attempt at this one line, so it stops being a guessing game.

   Section 76 removed `text-decoration`. Section 78 also removed
   `border-bottom`. Both with important declarations, and the underline is still
   there. So it is neither of those, which leaves the other three ways an
   underline gets drawn on a link:

     box-shadow          an inset or offset shadow reading as a rule
     background-image    a gradient with a small background-size
     a pseudo-element    a bar positioned under the text

   All five mechanisms are now closed off on the title, in the resting state, its
   own hover, and the card hover. Whichever one it was, it is covered, and no
   further round trip is needed to find out which.

   Broad rather than surgical, deliberately. A narrower fix would need me to
   identify the mechanism from a screenshot, which is exactly what has failed
   twice. The cost is a few redundant declarations on one element.

   THE LABEL KEEPS ITS UNDERLINE, as Adam asked. That distinction holds
   throughout the site now: a title is already large and takes a colour change on
   hover, so an underline as well is belt and braces. A small-caps label has no
   other affordance, so the underline is the only signal it has.
   ========================================================================= */

.slice--upcoming-events .upcoming_events .eventListTitle,
.slice--upcoming-events .upcoming_events .eventListTitle a,
.slice--upcoming-events .upcoming_events .eventListTitle a:hover,
.slice--upcoming-events .upcoming_events .eventListTitle a:focus,
.slice--upcoming-events .upcoming_events > div:hover .eventListTitle a {
  border:            0 !important;
  border-bottom:     0 !important;
  text-decoration:   none !important;
  box-shadow:        none !important;
  background-image:  none !important;
  background:        none !important;
}

.slice--upcoming-events .upcoming_events .eventListTitle a::before,
.slice--upcoming-events .upcoming_events .eventListTitle a::after,
.slice--upcoming-events .upcoming_events .eventListTitle a:hover::before,
.slice--upcoming-events .upcoming_events .eventListTitle a:hover::after,
.slice--upcoming-events .upcoming_events > div:hover .eventListTitle a::before,
.slice--upcoming-events .upcoming_events > div:hover .eventListTitle a::after {
  content: none !important;
  display: none !important;
}

/* =========================================================================
   81. v6.5  --  the whole event card becomes the link
   =========================================================================
   "See event" is generated content, and generated content cannot be a link: it
   is drawn by the stylesheet and does not exist in the document, so there is
   nothing for a browser to make clickable. The module gives us three anchors per
   card, the image, the title and the location, and no way to add a fourth.

   So instead of trying to make the label a link, the CARD becomes one. The image
   anchor is stretched over the whole card with a positioned pseudo-element, so a
   click anywhere on the card follows it to the event, and the label sits inside
   that target. That is what a card should do anyway: the small text link was
   always the weaker interaction.

   THE IMAGE ANCHOR CARRIES THE OVERLAY, NOT THE TITLE, for a practical reason.
   Section 80 had to disable the title anchor's pseudo-elements to kill the
   underline, so reusing one there would mean switching it back on and leaving a
   maintainer with two rules that appear to contradict each other. The image
   anchor points at the same event and its pseudo-elements are untouched.

   THE LOCATION LINK IS LIFTED ABOVE THE OVERLAY. It goes somewhere different, to
   a map, so it has to stay separately clickable. Without this it would be
   swallowed and every click on the address would open the event instead, which
   is the classic fault with this pattern.

   NO EXTRA TAB STOPS. The overlay is a pseudo-element on an anchor that already
   existed, so the keyboard order is unchanged: image, title, location, as before.
   A focus ring is drawn around the whole card when any of them takes focus, so a
   keyboard user sees the same target a mouse user clicks.
   ========================================================================= */

.slice--upcoming-events .upcoming_events > div {
  position: relative;
  cursor:   pointer;
}

/* The image's link, stretched over the card. */
.slice--upcoming-events .upcoming_events .eventListImage > a::after {
  content:  "" !important;
  display:  block !important;
  position: absolute;
  inset:    0;
  z-index:  1;
}

/* The map link stays above it, since it leads somewhere else. */
.slice--upcoming-events .upcoming_events .eventListLocation a {
  position: relative;
  z-index:  2;
}

/* The label reads as part of the target, so it responds to keyboard focus as
   well as to hover. */
.slice--upcoming-events .upcoming_events > div:has(a:focus-visible)::after {
  color:                 var(--accent-dark);
  text-decoration:       underline;
  text-underline-offset: 4px;
}

.slice--upcoming-events .upcoming_events > div:has(a:focus-visible) .eventListTitle a {
  color: var(--accent);
}

/* One ring around the whole card, matching what a click actually does. */
.slice--upcoming-events .upcoming_events > div:has(a:focus-visible) {
  outline:        3px solid var(--accent);
  outline-offset: 3px;
}

.slice--upcoming-events .upcoming_events .eventListImage > a:focus-visible,
.slice--upcoming-events .upcoming_events .eventListTitle a:focus-visible {
  outline: none;
}

/* =========================================================================
   82. v6.6  --  square image corners on every card type
   =========================================================================
   The library rounds the corners of a listing image. Section 76 squared off the
   event CARD but nothing squared off the images themselves, and section 77 never
   addressed it on the listing cards, so the photographs kept a radius the cards
   around them no longer had.

   Applied to all three card types rather than only the one Adam is looking at:
   the featured gallery cards, the event cards and the group listing cards. They
   are meant to be identical, and fixing one at a time is how this particular
   inconsistency keeps reappearing. CSS-14 is the standing note about that
   duplication.

   Both the image and its wrapping anchor are covered, because a radius on either
   would clip the other.
   ========================================================================= */

.slice--explore-church .gallery_li > a,
.slice--explore-church .gallery_li img,
.slice--upcoming-events .upcoming_events .eventListImage,
.slice--upcoming-events .upcoming_events .eventListImage > a,
.slice--upcoming-events .upcoming_events .eventListImage img,
.content-shell__main div:has(> div > .articleListTitle) > div .articleListImage,
.content-shell__main div:has(> div > .articleListTitle) > div .articleListImage > a,
.content-shell__main div:has(> div > .articleListTitle) > div .articleListImage img {
  border-radius: 0 !important;
}

/* =========================================================================
   83. v6.7  --  the newsletter band: flat plum, no decoration
   =========================================================================
   The library's signup band carries a decorative pattern of concentric rings
   behind its content. Adam wants the band flat: plum, and nothing else.

   THE FILL IS THE ACCENT, so this is the one large area of brand colour on the
   page. Worth noting because section 41 deliberately kept purple to highlights
   rather than blocks: a single full-width band at the foot of the page is a
   different thing from purple appearing throughout, and it gives the newsletter
   ask the weight it needs to stand apart from the content above it.

   THE DECORATION IS CLOSED OFF THREE WAYS, for the same reason the underline
   was: a background pattern like this can be a `background-image` on the
   section, a `background-image` on an inner wrapper, or a pseudo-element. All
   three are covered so this does not come back for a second look.

   `background-color` is set separately from `background-image` rather than using
   the shorthand, because the shorthand would reset the colour every time
   something else in the file touched the image. Two properties, two
   responsibilities.

   The white pill inside is untouched: only `background-image` is cleared on the
   inner wrappers, never `background-color`, so anything that paints itself a
   solid colour keeps it.
   ========================================================================= */

.slice--signup-cta {
  background-color: var(--accent) !important;
  background-image: none !important;
}

/* Any inner wrapper carrying the pattern. Image only, so solid fills survive. */
.slice--signup-cta > *,
.slice--signup-cta .ArticleBody {
  background-image: none !important;
}

/* Or a pseudo-element carrying it. */
.slice--signup-cta::before,
.slice--signup-cta::after,
.slice--signup-cta > *::before,
.slice--signup-cta > *::after {
  content: none !important;
  display: none !important;
}

/* =========================================================================
   84. v6.8  --  ONE card for every content-shell listing
   =========================================================================
   The Events group page lists with the same `eventList*` classes as the
   homepage, but in a bare div rather than `.upcoming_events`, so none of
   sections 74 to 82 reach it. The obvious move would be a fourth copy of the
   card rules. That is what CSS-14 warns against, and the last four exchanges
   have each been a divergence between copies, so this does the opposite:

   ONE SET OF RULES FOR BOTH CONTENT-SHELL LISTINGS, articles and events,
   matched with `:is()`. This SUPERSEDES section 77, which covered articles
   only. Section 77 can be deleted at consolidation; nothing in it is not
   restated here.

   That takes the count from four copies to three, and the two homepage variants
   are deliberately left alone. Folding those in as well means touching three
   working card types days before a client presentation, and the risk is not
   worth it in the same pass as adding a new page. They join at consolidation.

   WHAT CANNOT MATCH THE HOMEPAGE, and it is worth being straight about it. On
   the homepage the Calendar module emits the date as structured spans, a day, a
   month and a time, which is what the boxed day-and-month badge is built from.
   This listing emits one plain string: "13 Sep 9:30am - 2:00pm". There is no
   badge to be had without those spans, so the date is set as a single
   informational line in the blue. Everything else, the proportions, the tint,
   the hover, the label and the click target, is identical.

   THE WHOLE CARD IS THE LINK, using the image anchor stretched over it, as
   section 81 does for the homepage events. Applying it here also closes CARD-4
   for the article listings, where "Read more" was a label with nothing behind
   it. The location link is lifted above the overlay because it leads to a map.

   THE LABEL AT THE FOOT differs by list type. The article listing already emits
   a "Read more" div, so that is styled. The event listing emits nothing, so a
   "See event" label is generated, and only on cards that have no label of their
   own.
   ========================================================================= */

/* ---- the grid -------------------------------------------------------- */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) {
  display:               grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap:                   clamp(1.5rem, 3vw, 2rem);
  align-items:           stretch;
}

/* ---- the card -------------------------------------------------------- */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div {
  display:          flex;
  flex-direction:   column;
  position:         relative;
  min-width:        0;
  background-color: var(--section-light);
  border-radius:    0;
  box-shadow:       none;
  overflow:         hidden;
  cursor:           pointer;
}

/* ---- image ----------------------------------------------------------- */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListImage, .eventListImage) {
  margin:        0;
  line-height:   0;
  border-radius: 0;
}

.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListImage, .eventListImage) img {
  display:       block;
  width:         100%;
  height:        auto;
  aspect-ratio:  2 / 1;
  object-fit:    cover;
  border-radius: 0;
  transition:    transform var(--motion-slow) var(--motion-ease),
                 opacity   var(--motion-base) var(--motion-ease);
}

.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div:hover
  :is(.articleListImage, .eventListImage) img {
  transform: scale(1.04);
  opacity:   0.9;
}

/* The image's link, stretched over the whole card. */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListImage, .eventListImage) > a::after {
  content:  "";
  display:  block;
  position: absolute;
  inset:    0;
  z-index:  1;
}

/* ---- date, events only ----------------------------------------------- */
.content-shell__main div:has(> div > .eventListTitle) > div .eventListDate {
  padding:     var(--space-5) var(--space-5) var(--space-2);
  margin:      0;
  font-size:   var(--text-sm);
  font-weight: 600;
  color:       var(--rm-info);
}

/* ---- title ----------------------------------------------------------- */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListTitle, .eventListTitle) {
  padding: var(--space-5) var(--space-5) var(--space-2);
  margin:  0;
}

/* An event card has a date above the title, so the title needs no top padding
   of its own there. */
.content-shell__main div:has(> div > .eventListTitle) > div .eventListTitle {
  padding-top: 0;
}

.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListTitle, .eventListTitle) a {
  display:          inline-block;
  font-family:      var(--font-display);
  font-size:        var(--text-lg);
  font-weight:      600;
  line-height:      1.25;
  color:            var(--ink);
  border:           0;
  border-bottom:    0;
  text-decoration:  none;
  box-shadow:       none;
  background-image: none;
  transition:       color 350ms ease;
}

.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListTitle, .eventListTitle) a::before,
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListTitle, .eventListTitle) a::after {
  content: none;
  display: none;
}

.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div:hover
  :is(.articleListTitle, .eventListTitle) a {
  color: var(--accent);
}

/* ---- summary and location -------------------------------------------- */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListSummary, .eventListLocation) {
  padding:     var(--space-2) var(--space-5) 0;
  margin:      0;
  font-size:   var(--text-sm);
  line-height: 1.55;
  color:       var(--ink-soft);
}

/* The map link stays clickable above the card overlay. */
.content-shell__main div:has(> div > .eventListTitle) > div .eventListLocation a {
  position:        relative;
  z-index:         2;
  color:           var(--ink-soft);
  text-decoration: none;
}

.content-shell__main div:has(> div > .eventListTitle) > div .eventListLocation a:hover {
  color:                 var(--accent);
  text-decoration:       underline;
  text-underline-offset: 3px;
}

/* ---- the label at the foot ------------------------------------------- */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  .articleListLink {
  margin:         auto 0 0;
  padding:        var(--space-5);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color:          var(--accent);
  transition:     color 350ms ease;
}

/* Events emit no label of their own, so one is generated. */
.content-shell__main div:has(> div > .eventListTitle) > div:not(:has(.articleListLink))::after {
  content:        "See event";
  display:        block;
  margin-top:     auto;
  padding:        var(--space-5);
  font-family:    var(--font-body);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color:          var(--accent);
  transition:     color 350ms ease;
}

.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div:hover
  .articleListLink,
.content-shell__main div:has(> div > .eventListTitle) > div:hover::after {
  color:                 var(--accent-dark);
  text-decoration:       underline;
  text-underline-offset: 4px;
}

/* ---- focus ----------------------------------------------------------- */
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div:has(a:focus-visible) {
  outline:        3px solid var(--accent);
  outline-offset: 3px;
}

.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListImage, .eventListImage) > a:focus-visible,
.content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) > div
  :is(.articleListTitle, .eventListTitle) a:focus-visible {
  outline: none;
}

/* ---- one column on a phone ------------------------------------------- */
@media (max-width: 720px) {
  .content-shell__main div:has(> div > :is(.articleListTitle, .eventListTitle)) {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* =========================================================================
   85. v6.9  --  group navigation hover: a plain fade
   =========================================================================
   Section 70 gave these rows a tinted background on hover plus a shift to the
   right, on the reasoning that an indent suggests moving INTO a section. Three
   things are wrong with it in practice, and Adam has named all three.

     The tint carries a radius from the library, so the highlight reads as a
     pill inside a list rather than a row of it.

     It runs the full width of the row, which after section 70 flattened the
     table means the whole cell, so it reaches the card's edge and is cut off by
     the clip rather than ending anywhere deliberate.

     The shift to the right makes the row look more indented than its
     neighbours the moment a pointer touches it, which is especially odd on the
     child rows that are already indented.

   Replaced with the hover language the rest of the site now uses: opacity 0.7
   over 350ms, and nothing else moves. That matches every filled control and
   icon on the site from section 63, so a sidebar row behaves the way a button
   does.

   OPACITY RATHER THAN A COLOUR CHANGE, deliberately. The current page in this
   list is marked in the accent by the library, so a colour change on hover
   would either fight that marker or make an ordinary row look like the current
   one. A fade leaves every row's own colour intact and still reads clearly as a
   response.

   Padding is restated at its resting value on both states, so nothing can
   reintroduce a shift.
   ========================================================================= */

.content-shell__side .group-nav .GroupNavTitle > a,
.content-shell__side .group-nav .GroupNavTitle > a:hover,
.content-shell__side .group-nav .GroupNavTitle > a:focus-visible {
  background:      none !important;
  border-radius:   0 !important;
  padding:         var(--space-3) var(--space-5) !important;
  transition:      opacity 350ms ease !important;
}

.content-shell__side .group-nav .GroupNavTitle > a:hover {
  opacity: 0.7;
}

.content-shell__side .group-nav .GroupNavTitle > a:active {
  opacity: 0.55;
}

/* =========================================================================
   86. v7.0  --  the footer bottom bar, put back
   =========================================================================
   Section 72 pushed the LAST child of `site-footer__inner` to the right, so the
   social icons would sit at the end of the three-column row. But the bottom bar
   reuses the same class name for its own wrapper, and that wrapper has exactly
   one child, which is therefore also the last child.

   So the credit, the legal links and the charity number were all shrink-wrapped
   and shoved to the right-hand end of the bar, with the whole left half empty.
   One rule written for a three-column row landed on a one-column one.

   THE LESSON, which is worth more than the fix: a class reused for two different
   layouts cannot safely be targeted by position. `:last-child` means something
   quite different in a row of three than in a row of one, and the library reuses
   `site-footer__inner` for both. Where a rule depends on there being siblings,
   it should say so.

   Both properties are returned to their defaults on the bottom bar only. The
   three-column row above keeps section 72's alignment, which is correct there.

   `text-align` is also reset. It is almost certainly irrelevant to this layout,
   since the three groups sit at the left, centre and right of the bar and no
   text-align can produce that, but leaving a stray `right` on the wrapper serves
   no purpose either.
   ========================================================================= */

.site-footer__bottom .site-footer__inner,
.site-footer__bottom .site-footer__inner > *,
.site-footer__bottom .site-footer__inner > *:last-child {
  justify-self: stretch !important;
  margin-left:  0 !important;
  margin-right: 0 !important;
  text-align:   start !important;
  width:        auto !important;
}

/* =========================================================================
   87. v7.1  --  the group navigation has TWO output forms
   =========================================================================
   A correction to section 70, and to my reading of it.

   Section 70 recorded that the group navigation module emits a table of
   `div.GroupNavTitle` cells, and concluded that the `group_nav_selected`,
   `group_nav_sibling` and `group_nav_child` rules in sections 8, 41 and 63 were
   dead code written against invented class names.

   THAT WAS WRONG. The module has two output forms:

     A flat list of siblings renders as a one-row table of `.GroupNavTitle`
     cells, which is what the Our Team page showed.

     A list WITH HIERARCHY, a current page and its children, renders as nested
     divs carrying `group_nav_selected`, `group_nav_sibling` and
     `group_nav_child`, which is what the News page shows.

   So both sets of classes are real, and each page gets whichever form fits its
   position in the tree. The original rules were not invented; they were written
   for a form I had not seen at that point and then dismissed when I saw the
   other one.

   Which is why the tinted rounded hover is back on this page: sections 8, 41 and
   63 are the rules matching here, and section 85's fade targets `.GroupNavTitle`
   and so does nothing. Both forms now get the same treatment.

   THE STANDING LESSON, sharper than RULE-19: seeing a module's markup once tells
   you what it emits in that situation, not what it emits. A module that adapts
   its output to context needs checking in more than one context before its
   markup is treated as known.

   The child row indents from its own padding rather than a hover shift, and is
   set a step smaller, so the hierarchy reads without anything moving.
   ========================================================================= */

/* Rows: no tint, no radius, consistent padding, plain fade. */
.content-shell__side .group-nav
  :is(.group_nav_selected, .group_nav_sibling, .group_nav_child) > a,
.content-shell__side .group-nav
  :is(.group_nav_selected, .group_nav_sibling, .group_nav_child) > a:hover,
.content-shell__side .group-nav
  :is(.group_nav_selected, .group_nav_sibling, .group_nav_child) > a:focus-visible {
  display:         block;
  background:      none !important;
  border-radius:   0 !important;
  border-bottom:   0 !important;
  padding:         var(--space-3) var(--space-5) !important;
  text-decoration: none;
  transition:      opacity 350ms ease !important;
}

.content-shell__side .group-nav
  :is(.group_nav_selected, .group_nav_sibling, .group_nav_child) > a:hover {
  opacity: 0.7;
}

.content-shell__side .group-nav
  :is(.group_nav_selected, .group_nav_sibling, .group_nav_child) > a:active {
  opacity: 0.55;
}

/* Child rows: one step of indent from their own padding, and a step smaller, so
   the hierarchy reads without anything moving on hover. */
.content-shell__side .group-nav .group_nav_child > a {
  padding-left: calc(var(--space-5) + var(--space-5)) !important;
  font-size:    var(--text-sm);
  color:        var(--ink-soft);
}

/* The list needs the same breathing room the table form gets in section 71. */
.content-shell__side .group-nav > div {
  padding-block: var(--space-3);
}

/* =========================================================================
   88. v7.2  --  the footer bottom bar, centred properly
   =========================================================================
   Section 86 overcorrected. To undo section 72's `margin-left: auto`, which was
   pushing the bar right, it set BOTH side margins to zero and the width to auto.
   But the library centres this container with automatic side margins against a
   max-width, so zeroing them removed the centring as well as the push, and
   `width: auto` on a flex child shrank it to its content. Hence a bar sitting
   hard left at content width in a wide viewport.

   Stated positively this time rather than as a correction of a correction: the
   container is full width up to whatever max-width the library gives it, centred
   with automatic side margins, and its child fills it so the module's own
   three-part layout has the whole bar to spread across.

   THIS IS THE THIRD RULE ON THIS ONE ELEMENT: section 72 pushed it right,
   section 86 pushed it left, and this one centres it. That is a straightforward
   sign the first rule was wrong to target by position at all, which is RULE-22.
   All three must collapse into one at consolidation, with section 72's original
   rule made conditional on there being siblings so nothing downstream is needed.
   Recorded as CSS-15.
   ========================================================================= */

.site-footer__bottom .site-footer__inner {
  width:         100% !important;
  margin-inline: auto !important;
  justify-self:  center !important;
  text-align:    start !important;
}

.site-footer__bottom .site-footer__inner > * {
  width:         100% !important;
  margin-inline: 0 !important;
  justify-self:  stretch !important;
}

/* =========================================================================
   89. v7.3  --  the child row stops jumping on hover
   =========================================================================
   Section 87 set the row padding with the `padding` SHORTHAND, on the resting,
   hover and focus states alike, and then gave the child row a deeper
   `padding-left` in a separate rule.

   The shorthand is the problem. `padding: a b` sets all four sides, so the hover
   rule resets `padding-left` to the shallow value, and it out-specifies the
   child rule because a pseudo-class counts towards specificity. So the moment a
   pointer touches a child row it loses its indent, shifts left, and its text
   re-wraps to a different number of lines. Hence the jump.

   Fixed by restating the child's `padding-left` in every state, later in the
   file, so nothing can reset it.

   The underlying mistake is worth naming: a shorthand in a hover rule quietly
   resets every longhand its resting counterpart set. When a hover state should
   change only one thing, it should declare only that one thing. Section 87's
   hover rule needed nothing but `opacity`, and it repeated the padding for no
   reason at all.
   ========================================================================= */

.content-shell__side .group-nav .group_nav_child > a,
.content-shell__side .group-nav .group_nav_child > a:hover,
.content-shell__side .group-nav .group_nav_child > a:focus-visible,
.content-shell__side .group-nav .group_nav_child > a:active {
  padding-left: calc(var(--space-5) * 2) !important;
}

/* =========================================================================
   90. v7.4  --  content page: the newsletter band butts up to its neighbours
   =========================================================================
   On a content page the newsletter band floats in white space: a gap above it
   between the tinted content band and the plum, and another below it before the
   footer. Closing both makes the page read as three stacked bands rather than
   two bands with a stripe drifting between them.

   THREE BOXES OWN THAT SPACE and all three are dealt with, because the gap above
   and the gap below have different owners and fixing one would have left the
   other:

     the content shell's bottom margin        above the band
     the band's own margins                   both sides
     main's bottom padding                    below the band

   The content shell's PADDING is deliberately untouched. That is the tinted
   band's internal space, holding the white card away from its edges, and zeroing
   it would crush the card against the band rather than closing a gap.

   SCOPED TO PAGES WITH A CONTENT SHELL, as asked, so the homepage rhythm is
   unchanged. The homepage could take the same treatment if the same streamlined
   look is wanted there, which is one selector.
   ========================================================================= */

main:has(> .content-shell) {
  padding-block-end: 0 !important;
}

main:has(> .content-shell) > .content-shell {
  margin-block-end: 0 !important;
}

main:has(> .content-shell) > .slice--signup-cta {
  margin-block: 0 !important;
}

/* =========================================================================
   91. v7.5  --  closing the gap BELOW the newsletter band
   =========================================================================
   Section 90 closed the gap above the band and left the one below, which is
   informative: the two gaps have different owners, and section 90 only found
   one of them. It zeroed `main`'s bottom PADDING. It did not touch `main`'s
   bottom MARGIN, and it did not consider that the footer may carry padding of
   its own above the hairline.

   Both are dealt with here. Which of the two was actually holding the space I
   cannot tell from a render, because the hairline could belong either to the
   footer itself or to the safeguarding block inside it, and the white sits above
   the line in one case and below it in the other.

   SO THE SAFEGUARDING BLOCK'S OWN SPACING IS RESTORED EXPLICITLY. If the
   hairline turns out to be the footer's own top border, then zeroing the footer's
   padding would have crushed the Safeguarding heading against that line, trading
   one fault for another. Stating its padding separately means the result is
   right whichever way round it is: the gap closes, and the heading keeps its
   distance from the rule above it.

   That is the lesson from the underline and the newsletter decoration applied
   again: when a symptom has more than one possible owner and a render cannot
   tell them apart, close off every candidate in one pass and protect anything
   that could be damaged by doing so.
   ========================================================================= */

main:has(> .content-shell) {
  margin-block-end: 0 !important;
}

main:has(> .content-shell) + .site-footer {
  margin-block-start:  0 !important;
  padding-block-start: 0 !important;
}

/* The safeguarding block keeps its own breathing room regardless. */
main:has(> .content-shell) + .site-footer .footer-safeguarding {
  padding-block-start: var(--space-12);
}

/* =========================================================================
   92. v7.6  --  newsletter moves into the sidebar, contact card removed
   =========================================================================
   The newsletter band has left the bottom of the content page and become a
   sidebar card, sitting under the section navigation, with a Donate button
   beneath it. The Get in touch card is gone: its phone number and email address
   appear in the footer and its Contact us link is in the menu, so it was a third
   copy of both.

   DONE AS ONE SAVE, deliberately. Moving a module means its tag is absent for as
   long as the template is saved without it, and a saved state without the tag
   destroys the module's stored configuration. Removing the contact card, moving
   the newsletter and deleting the slice therefore all happened in a single write.

   TWO MODULE CONFIGURATIONS WERE DISCARDED, knowingly: the sidebar's Site Phone
   and Site Email Address instances. Neither carried anything worth keeping,
   since both simply render a value from Site Details, but it is recorded because
   the hard rule says to assume a rebuild rather than discover one.

   THE NEWSLETTER MODULE'S OWN MARKUP HAS NOT BEEN SEEN in this context. In the
   band it rendered as a wide white pill, styled by the library's signup-slice
   rules, and none of those apply in a sidebar. So the rules below shape whatever
   link it emits into the same pill the Donate button uses, written broadly enough
   to catch a bare anchor or one wrapped in a paragraph. If it comes out wrong,
   the rendered markup will settle it in one pass rather than three.

   DONATE IS ORANGE, and that is the one place this site's colour rules say it
   should be: orange is reserved for giving. The newsletter action stays purple,
   so the two read as different asks rather than a matching pair, and the more
   important one wins.

   THE CONTENT BAND NOW MEETS THE FOOTER DIRECTLY, since sections 90 and 91
   removed the space that used to sit between the band and the footer for the
   newsletter's benefit. That is worth a look: it was the right result with a
   plum band in between and may read as abrupt without one.
   ========================================================================= */

/* The newsletter module's link becomes a pill matching the card's own CTA. */
.side-card--newsletter .side-card__body .ArticleBody,
.side-card--newsletter .side-card__body .ArticleBody p {
  margin:  0;
  padding: 0;
}

.side-card--newsletter .side-card__body a {
  display:          flex;
  align-items:      center;
  justify-content:  center;
  gap:              var(--space-2);
  width:            100%;
  padding:          var(--space-3) var(--space-4);
  border-radius:    var(--radius-pill, 999px);
  background-color: var(--accent);
  color:            #FFFFFF;
  font-size:        var(--text-xs);
  font-weight:      700;
  letter-spacing:   0.08em;
  text-transform:   uppercase;
  text-align:       center;
  text-decoration:  none;
  transition:       opacity 350ms ease;
}

.side-card--newsletter .side-card__body a:hover {
  opacity: 0.7;
}

.side-card--newsletter .side-card__body a:active {
  opacity: 0.55;
}

/* Any icon the module emits alongside its label. */
.side-card--newsletter .side-card__body a svg,
.side-card--newsletter .side-card__body a img {
  width:  1.1em;
  height: 1.1em;
  flex:   0 0 auto;
}

/* Stack the two actions with a gap between them. */
.side-card--newsletter .side-card__body {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-3);
}

/* Donate: orange, which on this site means giving and nothing else. */
.side-card__cta--donate,
.side-card--newsletter .side-card__body a.side-card__cta--donate {
  background-color: var(--rm-orange-deep) !important;
  color:            #FFFFFF !important;
}

/* =========================================================================
   93. v7.7  --  events band grey again, and closing the welcome gap
   =========================================================================

   1. THE EVENTS BAND IS TINTED AGAIN, so its cards go white.

      This reverses section 76's trade rather than discovering a new problem.
      Section 76 made the event cards match the featured cards by giving them the
      same tinted fill, which forced the band to go white, which put news and
      events both on white. Adam has now seen that and prefers the alternating
      rhythm. So the band returns to grey and the cards go white, which is the
      alternative section 76 recorded at the time.

      THE COST, STATED PLAINLY: the event cards no longer match the featured
      cards on fill. Everything else about them matches, the proportions, the
      flat square edges, the type, the hover, the label. Only the fill differs,
      and it differs because it has to: a card must contrast with the band it
      sits on, and these two sit on different bands.

      THE WAY TO HAVE BOTH, if it is ever worth a pass: make every card white
      with a hairline border instead of a fill. A white card with a one-pixel
      rule reads as a card on white AND on grey, so the fills stop depending on
      the band and every section can be any colour. That is the proper fix and
      it belongs with the card consolidation, not in a spacing pass.

   2. TOO MUCH AIR BETWEEN THE STRAPLINE BUTTONS AND FEATURED ITEMS.

      Around 350px, which is three separate spaces stacked: the welcome slice's
      bottom padding, whatever margin the CTA block carries, and the featured
      slice's own top padding. Section 62 tightened the first of those and left
      the other two, which is why it still reads as a hole.

      All three are dealt with. The featured slice's top padding is reduced only
      where it follows the welcome slice, so every other section keeps its own
      rhythm: this is a junction problem rather than a section problem, and
      treating it as the latter would flatten the whole page.
   ========================================================================= */

/* -------------------------------------------------------------------------
   93.1  White cards on the tinted events band
   ------------------------------------------------------------------------- */
.slice--upcoming-events.slice--light-grey .upcoming_events > div {
  background-color: var(--bg);
}

/* -------------------------------------------------------------------------
   93.2  The welcome to featured junction
   ------------------------------------------------------------------------- */
main section.slice.slice--welcome {
  padding-bottom: clamp(1rem, 2vw, 1.75rem) !important;
}

main .slice--welcome .welcome-cta {
  margin-bottom: 0 !important;
}

/* Only at this junction. Other sections keep their own top padding. */
main .slice--welcome + .slice--explore-church {
  padding-top: clamp(1.25rem, 2.5vw, 2.25rem) !important;
}

/* =========================================================================
   94. v7.8  --  space restored between the content band and the footer
   =========================================================================
   Sections 90 and 91 closed the space between the content band and the footer.
   That was right at the time: the newsletter band sat in between and Adam wanted
   the three bands stacked without gaps. Section 92 then moved the newsletter into
   the sidebar, which left the tinted band meeting the footer directly, exactly as
   SHELL-1 predicted it might read as abrupt.

   So the space goes back, and on `main` rather than the footer. Two reasons: it
   belongs to the content area finishing rather than to the footer starting, and
   the footer's own top padding is shared with the homepage, which does not want
   changing.

   Set a little tighter than the original, which was around 113px. The band no
   longer has a plum stripe beneath it competing for separation, so it needs less.

   This also applies to the System template, whose main content likewise ends with
   the shell.
   ========================================================================= */

main:has(> .content-shell) {
  margin-block-end: clamp(2rem, 4vw, 3.5rem) !important;
}

/* =========================================================================
   95. v7.9  --  homepage: events band meets the newsletter band
   =========================================================================
   The signup slice carries its own vertical margins, which is what section 90
   had to zero on the content page. The same margins are still in force on the
   homepage, so a white stripe sits between the tinted events band and the plum,
   and two full-bleed bands that should meet read as three bands with a gap.

   Closed on the junction rather than on either section, so it cannot reach
   anything else: `.slice--upcoming-events` exists only on the homepage, and the
   adjacency selector means the rule applies to this pair and no other.

   THE GAP BELOW THE PLUM IS LEFT ALONE, deliberately. Adam asked for the space
   above the footer to be restored on the content page an hour ago, so removing
   the equivalent space here would make the two templates disagree about the same
   boundary. If the homepage should be tighter, that is a decision about the
   footer rather than about this band.
   ========================================================================= */

main > .slice--upcoming-events {
  margin-block-end: 0 !important;
}

main > .slice--upcoming-events + .slice--signup-cta {
  margin-block-start: 0 !important;
}

/* =========================================================================
   96. v8.0  --  mobile: sidebar card clears the footer, news rows breathe
   =========================================================================

   1. THE KEEP IN TOUCH CARD TOUCHED THE SAFEGUARDING SECTION.

      The cause is not the space between them, which section 94 restored and
      which is present. It is that the tinted band has almost no bottom padding
      at phone width, so the white card's lower edge meets the band's edge, and
      the white page below it continues without a break. The card and the
      Safeguarding block read as one white area, so the gap that exists is
      invisible.

      Fixed by giving the band its own bottom padding on phones, which is what
      frames the cards inside it. This is why the same page looks right on
      desktop: there the band's padding is generous enough to show.

   2. THE MOBILE NEWS ROWS ARE CRAMPED, and this is a partial fix.

      The compact rows put a thumbnail beside the text with no space between
      them, and "Read more" slides underneath the image rather than clearing it.
      Both are float behaviour: the thumbnail is floated, the title and summary
      wrap beside it, and a block-level element after them starts at the
      container's edge and tucks under the float.

      What is applied here is the safe half: space to the right of the thumbnail
      so the text is not touching it, and a clear on the read-more so it cannot
      sit under the image.

      WHAT IS NOT APPLIED, and why. The proper fix is a two-column grid per row,
      thumbnail in one column and the three text blocks in the other, which
      removes the float entirely. I have not written that because the lead story
      at the top of this feed uses the same class names in a different
      arrangement, and a structural selector that catches the rows would very
      likely catch the lead as well and turn a full-width card into a thumbnail
      row. That needs the rendered markup to do safely rather than a guess, which
      is the lesson this build has paid for repeatedly.
   ========================================================================= */

@media (max-width: 720px) {

  /* The tinted band frames its cards rather than ending flush against them. */
  main > .content-shell {
    padding-bottom: var(--space-8);
  }

  /* News rows: space beside the thumbnail, and nothing tucked under it. */
  .slice--latest-news .articleListImage {
    margin-right: var(--space-4);
  }

  .slice--latest-news .articleListLink {
    clear: both;
  }
}

/* =========================================================================
   97. v8.1  --  two boundary fixes at the content-area / footer join
   =========================================================================
   Two requests from Adam about the SAME boundary on two templates, pulling
   in opposite directions, so recorded together.

   A. HOMEPAGE: the plum newsletter band should sit directly above the
      safeguarding block, with no white gap before the hairline.

      Section 95 closed the gap ABOVE the plum band (events meets plum) and
      deliberately LEFT the gap below it, noting that tightening the homepage
      would be "a decision about the footer". That decision is now made. Two
      owners hold the gap below the plum:
        - the signup slice's own bottom margin: clamp(space-8, 6vw, space-16)
          from the library, up to 64px. Section 95 zeroed only its TOP edge.
        - the footer's top margin: var(--space-16) = 64px from the library,
          untouched on the homepage.
      Both zeroed, homepage only. The footer's padding-top is NOT touched, so
      the safeguarding band's own -64px pull-through still seats its hairline
      at the footer's top edge exactly as before. The band, its grey rule and
      its heading spacing are unchanged; only the white gap above the rule goes.

      Scoped by a direct-child signup slice, which exists only on the homepage,
      so no other page's footer join is affected.

   B. CONTENT PAGES, PHONES: the Keep in touch card reads as touching the
      Safeguarding block.

      Root cause, traced through the box model rather than guessed. The library
      gives .footer-safeguarding margin-top: -64px (-space-16) so its cream band
      pulls up THROUGH the footer's 64px padding-top and seats its hairline at
      the footer's top edge. Section 91 zeroed that footer padding-top on
      content pages. With no padding left to pull through, the -64px instead
      drags the whole safeguarding band UP into the content area, cancelling the
      space section 94 (main's bottom margin) and section 96 (the band's own
      bottom padding) put above it. Section 96 also did nothing at phone width:
      the value it set, space-8, equals the base padding clamp's floor at 720px.

      Fixed by neutralising the now-purposeless pull-up on content pages, then
      setting a deliberate gap on main. Phones only: desktop content pages read
      correctly as-is, because there the surrounding space is large enough to
      absorb the -64px. Homepage is untouched: its footer padding-top is not
      zeroed, so the pull-up there is still load-bearing and correct.
   ========================================================================= */

/* ---- A. Homepage: plum band meets the safeguarding hairline ---- */
main > .slice--upcoming-events + .slice--signup-cta {
  margin-block-end: 0 !important;   /* was clamp(space-8, 6vw, space-16) */
}

main:has(> .slice--signup-cta) + .site-footer {
  margin-block-start: 0 !important; /* was var(--space-16); homepage only */
}

/* ---- B. Content pages, phones: card clears the safeguarding block ---- */
@media (max-width: 720px) {

  /* Neutralise the library's -64px pull-up: section 91 removed the footer
     padding-top it was meant to pull through, so here it only eats the gap. */
  main:has(> .content-shell) + .site-footer .footer-safeguarding {
    margin-block-start: 0 !important;
  }

  /* A clean, deliberate gap above the safeguarding band, placed on the content
     area finishing rather than the footer starting (matches section 94). */
  main:has(> .content-shell) {
    margin-block-end: var(--space-12) !important;   /* 48px */
  }
}
/* =========================================================================
   98. v8.2  --  four fixes: footer icons, signup pill, news rows, menu icon
   =========================================================================

   A. SOCIAL ICONS CENTRED IN THE STACKED FOOTER.

      Section 72 right-aligns the icons for the three-column desktop footer,
      globally. The library centres the whole footer at 720px and below, but
      section 72 outranks it there: justify-self on the item beats the grid's
      justify-items, and the flex-end rule beats the library's centre rule by
      load order at equal specificity. So on a phone everything centres except
      the icons, which stay pinned to the right. Countered below at matching
      selectors, later in the file, per this file's own overriding rule.
      Desktop is untouched. The 721-900px band, where the footer can stack
      before the library starts centring it, is left as it is: nothing has
      been reported there and it is a separate decision.

   B. THE NEWSLETTER PILL SITS LOW IN ITS BAND.

      The band's own padding is symmetric and the library zeroes the paragraph
      margin, so the imbalance belongs to a layer neither file styles: Hubb's
      default stylesheet, which gives article-module wrappers their own block
      margins. The slice has overflow hidden, so a child's top margin cannot
      collapse away and shows up as extra space above the pill. Every inner
      block the module emits is zeroed; the band's padding alone then centres
      the pill. If the offset survives this, the owner is something Hubb
      injects at runtime and the rendered computed styles are needed next.

   C. MOBILE NEWS ROWS: THE THUMBNAIL OVERFLOWS ITS COLUMN.

      Correcting section 96, which called this float behaviour. It is not:
      the rows are the library's own grid, and floats do not apply to grid
      items, so both of section 96's rules were no-ops. The real mechanism is
      a LIBRARY BUG: the library's phone block narrows the image column from
      104px to 84px but never overrides the image div's own width, which
      stays at the desktop 104px. The image therefore overflows its track by
      20px, swallowing the 16px column gap and clipping 4px into the text,
      which is exactly the render: text starting on the image edge and the
      read-more label tucked under its corner.

      Fixed here by bringing the image width down to match the 84px track on
      phones, with a little more padding between rows than the library's
      default. Section 96's margin-right is also withdrawn: it was inert on
      the row thumbnails but it does reach the lead card's image, shaving
      16px off its right edge for no reason.

      FLAG FOR THE LIBRARY: the missing width override belongs in Tabernacle
      style.css's phone block (with the 84px track rule). Fold it into the
      next core release and this subsection becomes redundant but harmless.

   D. THE MENU ICON DISAPPEARS WHEN THE HOMEPAGE HEADER SOLIDIFIES.

      Section 39 sets every header button white with !important so the
      hamburger reads over the hero photograph. Section 42's solidify rule
      then walks the type colour from white to dark via --hdr-ink as the bar
      fills in on scroll, but its selector list covers anchors and the nav
      cells only, never generic buttons. So the toggle stays white at every
      scroll position: visible over the scrim at the top, invisible on the
      solid white bar. Content pages are unaffected, which is why the same
      button reads fine there.

      Fixed by pointing the three BAR-MOUNTED buttons at --hdr-ink, at higher
      specificity than section 39 and later in the file. Deliberately NOT a
      blanket button rule: the drawer's own Close and search buttons sit on
      the dark panel and must stay white, and the mobile-search rules in
      sections 56-57 that keep them white are more specific than section 39,
      so a blanket rule here would have flipped Close dark-on-dark the moment
      the drawer opened after a scroll.
   ========================================================================= */

/* ---- A. Stacked footer: icons centre with everything else ---- */
@media (max-width: 720px) {
  .site-footer__inner > *:last-child {
    justify-self: center;
    margin-left:  0;
    text-align:   center;
  }
  .site-footer .social_media_icons .ArticleBody {
    justify-content: center;
  }
}

/* ---- B. Signup band: strip Hubb's default block margins, all layers ---- */
.slice--signup-cta > div,
.slice--signup-cta .ArticleBody,
.slice--signup-cta .ArticleBody p {
  margin-block: 0 !important;
}

/* ---- C. Phone news rows: image matches its 84px track ---- */
@media (max-width: 720px) {
  .al-cards-feature > div:not(:first-child) .articleListImage {
    width: 84px;   /* the library narrows the track but not the image */
  }
  .al-cards-feature > div:not(:first-child) {
    padding-block: var(--space-4);   /* a touch more air between rows */
  }
  /* Withdraw section 96's margin: inert on the rows, harmful on the lead. */
  .slice--latest-news .articleListImage {
    margin-right: 0;
  }
}

/* ---- D. Bar-mounted header buttons follow the animated ink ---- */
.site-header--over-hero button.mobile-menu-toggle,
.site-header--over-hero button.mobile-menu-toggle *,
.site-header--over-hero button.site-search-toggle,
.site-header--over-hero button.account-toggle,
.site-header--over-hero button.account-toggle * {
  color: var(--hdr-ink) !important;
}

/* =========================================================================
   99. v8.3  --  menu flyouts flip left at the viewport edge (with skin.js)
   =========================================================================
   Venue Hire sits at the right end of the nav, so its third-level flyouts
   (Our Rooms, What's On) open past the right edge of the viewport.

   The library owns the panel geometry outright: Hubb's JS only toggles
   display, while style.css pins .tah below-left of its menu cell and .tav
   to the right of its parent row, both with !important. So the flip is
   pure CSS. What CSS cannot do cross-browser is DETECT the edge, so this
   version introduces skin.js, loaded after script.js in all three
   templates' Head Contents. It watches Hubb's display toggles, measures
   each panel as it opens, and applies is-edge-flipped when the panel
   would cross the right edge; the two rules below then mirror it. The
   class is re-derived on every open and on resize, so it never sticks.

   The rules match the library's own positioning rules but with one extra
   class, so they win on specificity with both sides !important, and this
   file loads later besides.

   A flipped .tah right-aligns to its own menu item. A flipped .tav opens
   to the LEFT of its parent panel, mirroring the 9px overlap the natural
   side has, so the two directions read identically.

   FLAG FOR THE LIBRARY, alongside the section 98C image-width bug: this
   is generic Tabernacle behaviour, not Romsey styling. Fold the detector
   into script.js core and these two rules into style.css at the next
   library release; this section and skin.js's copy then become redundant
   but harmless.
   ========================================================================= */

.main-nav .tah.is-edge-flipped {
  left:  auto !important;
  right: 0    !important;
}

.main-nav .tav.is-edge-flipped {
  left:  auto !important;
  right: 100% !important;
}

/* =========================================================================
   100. v8.4  --  Content and System pages: Donate and socials join the drawer
   =========================================================================
   On the homepage the mobile header is just the logo and the MENU button;
   the social icons and the Donate pill live at the foot of the slide-in
   drawer as an opaque footer bar. On Content and System pages the utility
   row was still sitting inline at the top of the mobile header. Adam wants
   the homepage treatment everywhere.

   WHY THE DIFFERENCE EXISTED. The drawer treatment was built across
   sections 56 to 61, iteratively, and every rule in it is scoped to
   site-header--over-hero, which only the Home template carries. The other
   two templates got only section 56's generic row-collapse, so their row
   stayed visible.

   RESTATED AS NET STATE rather than unscoping the originals, per this
   file's append-only convention. Sections 56 to 61 fought each other to a
   final answer (58 fixed the row, 59 put the children back in flow, 61
   made it an opaque full-width bar with reserved scroll room); only that
   final answer is restated here, for headers WITHOUT over-hero. The
   colour rules from 60.2 are deliberately omitted: these headers never
   whiten their icons, so there is nothing to reset.

   The bar is position fixed from inside a sticky header, which is safe:
   nothing in either file puts a transform or filter on the header element
   itself, so the fixed bar positions against the viewport. If a header
   transform is ever introduced, this bar will visibly rebase into the
   header and this note is the diagnosis.
   ========================================================================= */

@media (max-width: 1400px) {

  /* The row becomes the drawer's footer bar: full panel width, opaque,
     off-canvas until the menu opens, sliding on the panel's own ease. */
  .site-header.site-header--tworow:not(.site-header--over-hero) .site-header__utility {
    position:   fixed !important;
    left:       calc(100% - min(85%, 360px)) !important;
    right:      0 !important;
    bottom:     0 !important;
    top:        auto !important;
    width:      auto !important;
    margin:     0 !important;
    padding:    var(--space-5) var(--space-6)
                calc(var(--space-5) + env(safe-area-inset-bottom, 0px)) !important;
    background: var(--bg) !important;
    border-top: 1px solid var(--rule) !important;
    display:    block !important;
    z-index:    9051;
    transform:  translateX(120%);
    transition: transform 0.3s ease;
  }

  .menu-open .site-header.site-header--tworow:not(.site-header--over-hero) .site-header__utility {
    transform: translateX(0);
  }

  /* Stack: icons, then Donate. */
  .site-header.site-header--tworow:not(.site-header--over-hero) .site-header__utility-inner {
    display:        flex !important;
    flex-direction: column !important;
    align-items:    stretch !important;
    gap:            var(--space-5) !important;
    padding:        0 !important;
    max-width:      none !important;
  }

  /* The status bar's login/search copy is redundant in the drawer. */
  .site-header.site-header--tworow:not(.site-header--over-hero)
  .site-header__utility-inner > div:has(.statusbar) {
    display: none !important;
  }

  /* Icons: full row, centred, at the homepage bar's size. */
  .site-header.site-header--tworow:not(.site-header--over-hero) .header-social {
    width:  100% !important;
    margin: 0 !important;
  }

  .site-header.site-header--tworow:not(.site-header--over-hero) .header-social .ArticleBody {
    display:         flex !important;
    justify-content: center !important;
    align-items:     center !important;
    gap:             var(--space-6) !important;
  }

  .site-header.site-header--tworow:not(.site-header--over-hero) .header-social .footer_icon_block svg {
    width:  1.4rem;
    height: 1.4rem;
  }

  /* Donate: a full-width centred pill at the foot, matching the homepage. */
  .site-header.site-header--tworow:not(.site-header--over-hero) .site-header__utility .header-giving {
    width:           100% !important;
    margin:          0 !important;
    display:         flex !important;
    justify-content: center !important;
    text-align:      center !important;
  }

  /* Room at the end of the menu list so the last item clears the bar. */
  .site-header.site-header--tworow:not(.site-header--over-hero) .site-header__nav {
    padding-bottom: 10rem;
  }
}

/* =========================================================================
   101. v8.5  --  parent menu items are hover-only (documented; lives in JS)
   =========================================================================
   Adam's ask, from the desktop menu: the top-level group items (About
   Romsey Mill, Our Work, Our Impact, Get Involved, News and Events, Venue
   Hire) should not navigate anywhere, and the duplicate of each one that
   Hubb writes as the first row of its own dropdown should go. Hovering a
   parent should reveal only its children.

   NO RULES IN THIS FILE. The condition is a text comparison, which CSS
   cannot express: a dropdown's first row is removed exactly when its label
   duplicates its parent's label, and only a parent whose duplicate was
   found becomes hover-only. That rule is what keeps Home clickable (its
   first row says Homepage, not Home), keeps any future childless top-level
   page clickable (no dropdown rows, no duplicate), and guarantees no real
   link is ever hidden. skin.js v8.5's initHoverOnlyMenuParents does the
   matching, hides the duplicate rows, suppresses clicks on the marked
   cells in the capture phase, and applies the default cursor inline.

   The same rule runs on the library's rebuilt mobile drawer, whose submenus
   repeat the parent as their first link; parents there are already
   non-navigating expand buttons. Net effect on both layouts: the group
   landing pages are unreachable from the menu, as asked. They remain
   reachable from the In this section sidebar on content pages.

   Site policy, not a library candidate: other churches may want clickable
   parents. This section exists so the skin's numbered history stays the
   single place a behaviour can be looked up, wherever it is implemented.
   ========================================================================= */