@charset "UTF-8";
/* =========================================================================
   WIGHT DIVE SCHOOL  --  BRAND SKIN  --  v0.6
   Pairs with Tabernacle style.css / script.js v0.276
   =========================================================================
   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.6   (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.

   ACCESSIBILITY
   Ratios are measured, not eyeballed, and recorded beside each rule. Three
   brand colours cannot be used as bare fills: sea foam is 1.80:1 on white,
   ocean blue 2.80:1 and coral orange 2.28:1, all under the 3:1 non-text
   floor, so each takes a deeper border. None can carry white text, so every
   label on them is navy.

   PAGE RHYTHM
   White is the page base and sand is the band, so white is what a section
   gets with no class and sand is opt-in:

       welcome          white  (no band class)
       why train        sand   (slice--light-grey)
       our courses      white  (no band class)
       from our blog    sand   (slice--light-grey)
       what divers say  white  (no band class)
       newsletter       grey   (neutral, section 11)
       footer           navy   (library default, deliberately not overridden)

   v0.6 -- newsletter band moved off the pale ocean tint onto a neutral grey,
   so the palette is carried by the logo, the sand bands, the pills and the
   hero CTA rather than by one more branded panel.

   v0.5 -- footer returned to the library's dark treatment and the newsletter
   band inverted to light instead; faint testimonial quote mark removed; blog
   lead story retuned; hover transitions stated explicitly so they ease rather
   than snap.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. TOKEN OVERRIDES
   ------------------------------------------------------------------------- */
:root {
  --bg:          #FFFFFF;
  --bg-rgb:      255, 255, 255;
  --surface:     #FFFFFF;

  --ink:         #0D2B45;
  --ink-soft:    #4F6274;

  --accent:      #0D2B45;
  --accent-ink:  #FFFFFF;
  --accent-soft: #E4F4F8;
  --accent-dark: #071D31;

  --rule:              #DCD6C9;
  --section-light:     #F7F5EF;
  --section-light-rgb: 247, 245, 239;
  --section-dark:      #0D2B45;

  --font-display: "Poppins", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body:    "Poppins", -apple-system, BlinkMacSystemFont, sans-serif;

  /* Motion. The library ships 200ms ease, which is short enough but reads
     mechanically because ease accelerates and decelerates symmetrically. A
     near-pure ease-out leaves immediately and settles gently, which is the
     difference between snappy and friendly. Safe to put a curve in this
     token: it is only ever consumed inside transition shorthands, never as a
     bare transition-duration, checked across the whole library. */
  --transition: 220ms cubic-bezier(0.2, 0, 0, 1);
}

/* -------------------------------------------------------------------------
   2. BRAND EXTRAS
   ------------------------------------------------------------------------- */
:root {
  --wds-ocean:        #00A9C6;
  --wds-ocean-deep:   #008096;
  --wds-foam:         #64D0E1;
  --wds-orange:       #F7941E;
  --wds-orange-edge:  #CE7305;
  --wds-orange-hover: #DE7F0C;
  --wds-sand:         #F7F5EF;
  --wds-haze:         #EAF5F8;   /* pale ocean, currently unused, kept as an option */
  --wds-neutral:      #F1F1F1;
  --wds-ink-tint:     rgba(13, 43, 69, 0.06);
}

/* -------------------------------------------------------------------------
   3. HEADER
   -------------------------------------------------------------------------
   No background rule needed: the library paints the bar with --bg, now white,
   and the mobile slide-in panel uses --surface, also white.

   Site name VISUALLY hidden, not display:none. The logo carries the brand so
   the words are visual duplication, but a screen reader still needs the site
   name and an image alt cannot be relied on to supply it. Same clip technique
   the library uses on the utility row. Check the logo alt is empty or
   decorative so the name is not announced twice. */
.site-header__brand .site-name {
  position:    absolute;
  width:       1px;
  height:      1px;
  padding:     0;
  margin:      -1px;
  overflow:    hidden;
  clip:        rect(0, 0, 0, 0);
  white-space: nowrap;
  border:      0;
}

/* Centre the nav: logo left, menu middle, actions right. The library's split
   variant pins the nav left with margin-left:0 / margin-right:auto; setting
   both to auto splits the free space either side.

   Scoped to min-width 1401px and NOT a new breakpoint: below that the nav is
   a fixed slide-in panel where margins mean nothing, and the library's own
   split rules sit at the same breakpoint. */
@media (min-width: 1401px) {
  .site-header--split .site-header__nav {
    margin-inline: auto;
  }
}

/* Book a course: sea foam with a navy label at 8.04:1. The fill is 1.80:1
   against the white bar, under the 3:1 non-text floor, so the deeper ocean
   border is what gives the pill a perceivable boundary. Hover steps to full
   ocean, where navy still holds 5.17:1. */
.site-header--split  .header-giving,
.site-header--tworow .header-giving {
  background-color: var(--wds-foam);
  color:            var(--ink);
  border:           1px solid var(--wds-ocean-deep);
}
.site-header--split  .header-giving:hover,
.site-header--tworow .header-giving:hover {
  background-color: var(--wds-ocean);
  border-color:     var(--wds-ocean-deep);
  color:            var(--ink);
}

/* -------------------------------------------------------------------------
   4. HERO
   -------------------------------------------------------------------------
   CTA in coral. Navy label is 6.36:1; white would be 2.28:1, so the label has
   to stay navy. The border matters most here because the button sits over
   photography, where the fill has no predictable contrast behind it. */
.hero_slideshow .hero-caption__cta {
  background-color: var(--wds-orange);
  color:            var(--ink);
  border:           1px solid var(--wds-orange-edge);
}
.hero_slideshow .hero-caption__cta:hover {
  background-color: var(--wds-orange-hover);
  border-color:     var(--wds-orange-edge);
  color:            var(--ink);
}

/* Type scale. The library's title clamp tops out at 5rem (80px) in a 720px
   box, tuned around Fraunces. Poppins sets appreciably wider at the same
   size, so the same string ran to three lines and swamped the photograph.
   Ceiling down to 3.5rem and the box widened so it breaks where it should.
   Subtitle off a hardcoded 1.125rem onto the fluid step.

   Scoped above 721px on purpose: the library retunes the hero title below its
   720px phone breakpoint, and since this file loads last an unscoped rule
   would out-order that and quietly undo it. */
@media (min-width: 721px) {
  .hero_slideshow .hero-caption {
    max-width: 880px;
  }
  .hero_slideshow .hero-caption__title {
    font-size: clamp(2rem, 4.5vw, 3.5rem);
  }
}
.hero_slideshow .hero-caption__subtitle {
  font-size: var(--text-lg);
}

/* -------------------------------------------------------------------------
   5. HOVER TRANSITIONS  --  stated explicitly
   -------------------------------------------------------------------------
   Two real gaps caused hovers to snap rather than ease.

   First, .header-giving only receives a transition inside the --tworow
   variant in the library, so the split header's pill had none at all.

   Second, our own pills declared `transition: background ...`. Naming the
   background shorthand is less dependable than naming background-color
   outright, so every fill above is now set with background-color and the
   transitions below name the exact longhand properties that change.

   Grouped in one rule so any new pill added to this skin inherits it by
   being added to the selector list, rather than each one carrying its own. */
.site-header--split  .header-giving,
.site-header--tworow .header-giving,
.slice--welcome .ArticleBody p > a,
.slice--stats.slice--stats-reasons .ArticleBody > p > a,
.section-link {
  transition: background-color var(--transition),
              border-color     var(--transition),
              color            var(--transition);
}

/* -------------------------------------------------------------------------
   6. SECTION PILLS  --  sea foam
   -------------------------------------------------------------------------
   "See all courses", "Read more posts" and the About Us link at the foot of
   the Why train with us article. The library ships .section-link as a quiet
   ghost pill; the brand wants these reading as real buttons.

   The About Us link is typed into an article and cannot carry a class, so it
   is matched structurally: the only top-level paragraph in that article is
   the one holding the link, because the four reasons sit inside list items. */
.section-link,
.slice--stats.slice--stats-reasons .ArticleBody > p > a {
  background-color: var(--wds-foam);
  color:            var(--ink);
  border:           1px solid var(--wds-ocean-deep);
  text-decoration:  none;
}
.section-link:hover,
.slice--stats.slice--stats-reasons .ArticleBody > p > a:hover {
  background-color: var(--wds-ocean);
  border-color:     var(--wds-ocean-deep);
  color:            var(--ink);
}

/* The article link needs the pill geometry too, since .section-link takes its
   shape from the library and this one has no class to inherit it from. */
.slice--stats.slice--stats-reasons .ArticleBody > p {
  text-align: center;
  margin-top: var(--space-12);
}
.slice--stats.slice--stats-reasons .ArticleBody > p > a {
  display:        inline-flex;
  align-items:    center;
  gap:            var(--space-3);
  padding:        var(--space-3) var(--space-6);
  font-family:    var(--font-body);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border-radius:  999px;
}

/* -------------------------------------------------------------------------
   7. WELCOME ARTICLE CTAs  --  one per brand colour
   -------------------------------------------------------------------------
   Three plain links in a single paragraph of the Welcome article, turned into
   buttons. No usable class, so matched by position: 1 About Us, 2 Contact Us,
   3 How to Book.

   Label colours are forced by contrast, not taste. Navy on ocean is 5.17:1
   and navy on coral 6.36:1, where white on either would be 2.80:1 and 2.28:1
   and fail. Only the navy button carries white text. Ocean and coral take a
   deeper border because their fills are under the 3:1 non-text floor on
   white. */
.slice--welcome .ArticleBody p > a {
  display:         inline-flex;
  align-items:     center;
  margin:          var(--space-2) var(--space-2) var(--space-2) 0;
  padding:         var(--space-3) var(--space-6);
  font-family:     var(--font-body);
  font-size:       var(--text-xs);
  font-weight:     700;
  letter-spacing:  0.12em;
  text-transform:  uppercase;
  text-decoration: none;
  border-radius:   999px;
}

/* 1 -- ocean blue */
.slice--welcome .ArticleBody p > a:nth-of-type(1) {
  background-color: var(--wds-ocean);
  color:            var(--ink);
  border:           1px solid var(--wds-ocean-deep);
}
.slice--welcome .ArticleBody p > a:nth-of-type(1):hover {
  background-color: var(--accent);
  border-color:     var(--accent);
  color:            var(--accent-ink);
}

/* 2 -- deep navy. The only one that can carry white text. */
.slice--welcome .ArticleBody p > a:nth-of-type(2) {
  background-color: var(--accent);
  color:            var(--accent-ink);
  border:           1px solid var(--accent);
}
.slice--welcome .ArticleBody p > a:nth-of-type(2):hover {
  background-color: var(--accent-dark);
  border-color:     var(--accent-dark);
  color:            var(--accent-ink);
}

/* 3 -- coral orange */
.slice--welcome .ArticleBody p > a:nth-of-type(3) {
  background-color: var(--wds-orange);
  color:            var(--ink);
  border:           1px solid var(--wds-orange-edge);
}
.slice--welcome .ArticleBody p > a:nth-of-type(3):hover {
  background-color: var(--wds-orange-hover);
  border-color:     var(--wds-orange-edge);
  color:            var(--ink);
}

/* -------------------------------------------------------------------------
   8. WHY TRAIN WITH US  --  icon tint
   -------------------------------------------------------------------------
   Ocean blue is 2.57:1 on sand, so the darkened ocean is used at 4.25:1.
   Same brand blue, and it passes as a graphic. */
.slice--stats.slice--stats-reasons {
  --stat-icon-color: var(--wds-ocean-deep);
}

/* -------------------------------------------------------------------------
   9. BLOG LEAD STORY  --  retuned
   -------------------------------------------------------------------------
   The feature variant sets its lead title at --text-2xl, which tops out at
   48px. In Fraunces that reads as an editorial lead; in Poppins, which is far
   wider and has a much larger x-height, it dominates the section and pushes
   the excerpt out of proportion. Down to the --text-xl step, so 32px at the
   ceiling, which keeps the lead clearly dominant over the sibling item
   without shouting.

   The underline is the other half of the problem. The library draws it as a
   background gradient animating from 0% to 100% width, which cannot skip
   descenders, so at 48px it cut straight through the y and g. The gradient is
   switched off and replaced with a real text-decoration underline, which
   respects text-decoration-skip-ink and clears descenders properly, offset
   far enough to read as deliberate. */
.al-cards-feature > div:first-child .articleListTitle {
  font-size: var(--text-xl);
}
.al-cards-feature .articleListTitle a {
  background-image: none;
}
.al-cards-feature > div:hover .articleListTitle a {
  background-image:         none;
  text-decoration:          underline;
  text-decoration-thickness: 1px;
  text-underline-offset:     0.18em;
}

/* -------------------------------------------------------------------------
   10. TESTIMONIALS  --  drop the quote mark
   -------------------------------------------------------------------------
   The library sets a large decorative left double quote above the quote,
   tinted --accent-soft. On this palette that token is a very pale blue, so at
   4rem it reads as a smudge rather than a flourish. Removed rather than
   re-tinted: the block is already unmistakably a testimonial from its layout
   and controls, so the mark adds nothing. */
.slice--testimonials .testimonial__quote::before {
  display: none;
}

/* -------------------------------------------------------------------------
   11. NEWSLETTER BAND  --  neutral grey
   -------------------------------------------------------------------------
   Inverted from the library's accent band to a light panel, so the dark
   weight at the foot of the page belongs to the footer instead. Built entirely
   from --signup-* tokens, so this is a token remap and touches none of the
   band's layout.

   Neutral grey rather than the brand's pale ocean: the palette is already
   carried by the logo, the sand bands, the sea foam pills and the coral hero
   CTA, and one more branded tint here tipped the page from confident into
   insistent.

   Measured on #F1F1F1: navy 12.83:1, --ink-soft 5.58:1, and the heading's
   tint 4.11:1 against the 3:1 large-text threshold it sits at. The button
   flips to a navy fill with a white label at 14.5:1, needing no border since
   navy on this grey is 12.83:1 unaided. The scrim exists only to hold text
   legible over a background image, and there is none, so it goes transparent
   rather than veiling the panel. */
.slice--signup {
  --signup-bg:           var(--wds-neutral);
  --signup-ink:          var(--ink);
  --signup-muted:        var(--ink-soft);
  --signup-tint:         var(--wds-ocean-deep);
  --signup-btn-bg:       var(--accent);
  --signup-btn-ink:      var(--accent-ink);
  --signup-btn-bg-hover: var(--accent-dark);
  --signup-decor:        var(--wds-ink-tint);
  --signup-scrim:        transparent;
}

/* -------------------------------------------------------------------------
   12. NEWSLETTER BAND MEETS THE FOOTER
   -------------------------------------------------------------------------
   Two margins were stacking under the band: its own bottom margin and the
   footer's margin-top, both --space-16. A full-bleed panel running straight
   into the footer wants no gap, and now that the band is light and the footer
   dark the flush join is the whole transition.

   Only where the band is the last thing in the main content: on a content
   page the footer still needs its margin or it crowds the article above. */
.slice--signup:last-child {
  margin-bottom: 0;
}
main:has(> .slice--signup:last-child) + .site-footer {
  margin-top: 0;
}

/* NOTE: the footer itself is deliberately NOT overridden. v0.3 inverted it to
   light, which meant remapping eleven hardcoded white values inside it; v0.5
   reverts to the library's dark treatment, so all of that is simply gone
   rather than left in place and re-overridden. */
/* -------------------------------------------------------------------------
   14. v0.7  --  QA fixes: motion curve, testimonial gap, slimmer signup band
   ------------------------------------------------------------------------- */

/* MOTION CURVE  --  correcting v0.4
   cubic-bezier(0.2, 0, 0, 1) was too aggressive at the front: measured, it
   completes 50% of the change in the first 20% of the duration and then
   crawls the remaining half. That reads exactly as a snap followed by a lazy
   tail, which is what it was doing.

   The hero CTA escaped it because the library hardcodes
   `transition: background 0.2s ease` there rather than reading the token,
   which is why that one alone felt right.

   Material's standard curve completes 13% at the 20% mark and 78% by the
   halfway point: a soft departure and a definite settle, with no jump. */
:root {
  --transition: 240ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* TESTIMONIAL SPACING
   Two gaps were stacking under the heading. The head carries a 48px bottom
   margin, and the quote block carries a 48px padding-top that existed only to
   clear the big decorative quote mark. Section 10 removed that mark, so the
   padding was reserving space for something no longer there.

   Padding to zero and the head margin down to 24px. One unscoped rule covers
   the tablet breakpoint too, where the library sets its own padding-top. */
.slice--testimonials .slice__head--centred {
  margin-bottom: var(--space-6);
}
.slice--testimonials .testimonial__quote {
  padding-top: 0;
}

/* SIGNUP BAND  --  slimmer, and the phantom space removed
   Two separate problems.

   The band was bottom-heavy because its grid uses row-gap 12px while the
   button paragraph spans `grid-row: 1 / span 10`, a defensive over-span so
   align-self can centre the button against any amount of copy. This article
   fills three rows, so seven surplus implicit rows are created and each one
   still contributes a row gap: 84px of dead space below the copy and none
   above it. Spanning three rows matches the copy exactly (heading, intro,
   privacy line) and still centres the button. If a fourth block is ever added
   to this article, raise the span to match.

   Separately the band was simply tall. Vertical padding comes down from a
   64px ceiling to 32px for the thinner, more streamlined panel, and the
   decorative envelope shrinks with it so it stays in proportion rather than
   being clipped by a much shorter band. */
.slice--signup {
  --signup-pad-block: clamp(var(--space-5), 3vw, var(--space-8));
}
.slice--signup .ArticleBody p:has(a) {
  grid-row: 1 / span 3;
}
.slice--signup::before {
  width: clamp(160px, 16vw, 240px);
}

/* -------------------------------------------------------------------------
   15. v0.8  --  CONTENT PAGE SHELL: article card left, section nav right
   -------------------------------------------------------------------------
   The Aspire pattern. A tinted full-bleed band holds a two-column grid: the
   current article in a white card on the left, the section navigation on the
   right. The card is the only editable region, which makes the boundary of
   "what the client changes" visually obvious.

   The white card sits on the neutral grey rather than the brand sand, so the
   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 no broken half-built state
   while the Group Navigation module is being added. Once the module is in,
   the second column appears on its own. */
.content-shell {
  background:    var(--wds-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(13, 43, 69, 0.22);
}

/* .content_article ships as an 820px centred column with 64px 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;
}

/* Section nav column. Sticky so it stays with the reader on long articles;
   align-items:start on the grid is what makes sticky work in a grid child.
   The offset clears the sticky site header, which is around 84px tall. */
.content-shell__side {
  position: sticky;
  top:      calc(84px + var(--space-4));
}

/* 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, matching the library's existing card-collapse
   breakpoint rather than introducing a new one. Sticky comes off, or a
   full-width nav panel would cover the article beneath it. */
@media (max-width: 1024px) {
  .content-shell__inner {
    grid-template-columns: minmax(0, 1fr);
  }
  .content-shell__side {
    position: static;
  }
}

/* PAGE TITLE
   The library sets the content h1 at --text-3xl, which tops out at 72px. In
   Fraunces at 820px wide that reads as an editorial title; in Poppins it
   filled half the viewport before the reader reached a word of prose. Down to
   the --text-2xl step, so 48px at the ceiling, which still opens the page
   firmly. The accent rule beneath it is left alone. */
.content_article .ArticleBody h1 {
  font-size: var(--text-2xl);
}

/* -------------------------------------------------------------------------
   16. v0.9  --  mobile QA: menu scrim, blog card overflow
   -------------------------------------------------------------------------
   Both of these are library gaps rather than skin regressions, so both are
   candidates for promoting into style.css once confirmed here.
   ------------------------------------------------------------------------- */

/* MOBILE MENU SCRIM
   The backdrop element already exists, is positioned fixed across the
   viewport, sits at z-index 9000 just under the 9050 nav panel, and fades in
   over 0.3s when the menu opens. It simply has `background: transparent`, so
   there has never been anything to see.

   Navy at 55% rather than plain black: the page behind stays legible as
   context instead of going muddy, and it reads as part of the brand rather
   than a generic overlay. The panel itself is white, so the contrast between
   panel and scrim is emphatic either way. */
:root {
  --wds-scrim: rgba(13, 43, 69, 0.55);
}
.mobile-menu-backdrop {
  background: var(--wds-scrim);
}

/* BLOG FEATURE CARDS ON MOBILE
   The non-featured items are a two-column grid, thumbnail then text, and the
   library places every child correctly: image in column 1 spanning the three
   text rows, title, summary and read-more stacked in column 2. That placement
   carries over to mobile intact.

   What fails is the track definition. Both the desktop `104px 1fr` and the
   mobile `84px 1fr` leave the text column as a bare 1fr, and a grid item
   defaults to min-width:auto. The summary is a -webkit-box with a line clamp,
   whose intrinsic minimum can exceed the track, so the column grows past its
   share and the text runs out of the card. Desktop has enough slack to
   absorb it; a phone does not, which is why it only shows on mobile.

   minmax(0, 1fr) lets the track shrink below the content's intrinsic minimum,
   and min-width: 0 on the children is the same guard applied one level down.
   Scoped to the library's existing 720px phone breakpoint. */
@media (max-width: 720px) {
  .al-cards-feature > div:not(:first-child) {
    grid-template-columns: 84px minmax(0, 1fr);
  }
  .al-cards-feature > div:not(:first-child) .articleListTitle,
  .al-cards-feature > div:not(:first-child) .articleListSummary,
  .al-cards-feature > div:not(:first-child) .articleListLink {
    min-width: 0;
  }
}

/* -------------------------------------------------------------------------
   17. v1.0  --  simplified hovers, feature-card image, mobile band padding
   ------------------------------------------------------------------------- */

/* FEATURE CARD IMAGE  --  the actual cause of the cramped blog item
   The image wrapper carries a hardcoded width: 104px, matching the DESKTOP
   track of `104px 1fr`. The mobile rule narrows the track to `84px 1fr` but
   never touches the image, so it overhangs its own column by 20px into the
   text beside it. That is what was clipping the first character of the
   summary, and the reason the title lost a sliver of its W.

   width: 100% makes the image fill whatever the track happens to be, which
   fixes both breakpoints and removes the coupling that caused this: the track
   width can now change without anyone remembering to change the image too.

   (The minmax(0, 1fr) added in v0.9 was sound practice but was not the cause,
   and by letting the text track shrink more willingly it made the overhang
   proportionally worse. It stays, because a text track should be able to
   shrink below its content's intrinsic minimum.) */
.al-cards-feature > div:not(:first-child) .articleListImage {
  width: 100%;
}

/* NEWSLETTER BAND ON MOBILE
   The desktop clamp bottoms out at 20px, and 3vw of a phone viewport never
   reaches the ceiling, so phones were getting the thinnest padding of any
   screen. Backwards. A flat 48px on mobile instead. */
@media (max-width: 720px) {
  .slice--signup {
    --signup-pad-block: var(--space-12);
  }
}

/* BUTTON HOVERS  --  one behaviour, everywhere
   Every brand button now fades to 0.7 opacity over 350ms on hover and does
   nothing else. No colour swaps, no border changes, no moving parts.

   Each resting fill is re-asserted on :hover below. That is not redundancy:
   both the library's own colour-swap hovers and the earlier skin ones would
   otherwise still fire, so restating the resting colours is what guarantees
   opacity is the only thing that changes.

   Measured: at 0.7 the labels sit at 3.68:1 on the foam pills, 2.67:1 on the
   ocean button, 3.11:1 on coral and 5.57:1 on navy, so three of the four dip
   under the 4.5:1 AA threshold while hovered. Every RESTING state still
   passes comfortably, from 5.17:1 to 14.5:1, and the dip only exists while
   the pointer is on the control. Worth knowing that no opacity value fixes
   this: even 0.90 leaves the ocean button at 4.21:1, because its resting
   contrast of 5.17:1 has no headroom to give. Holding hover at AA as well
   would mean going back to a lighter fill per button, which is the
   complication this replaces. */
.site-header--split  .header-giving,
.site-header--tworow .header-giving,
.hero_slideshow .hero-caption__cta,
.section-link,
.slice--stats.slice--stats-reasons .ArticleBody > p > a,
.slice--welcome .ArticleBody p > a {
  transition: opacity 350ms ease;
}
.site-header--split  .header-giving:hover,
.site-header--tworow .header-giving:hover,
.hero_slideshow .hero-caption__cta:hover,
.section-link:hover,
.slice--stats.slice--stats-reasons .ArticleBody > p > a:hover,
.slice--welcome .ArticleBody p > a:hover {
  opacity: 0.7;
}

/* Resting colours restated on :hover so nothing but opacity moves. */
.site-header--split  .header-giving:hover,
.site-header--tworow .header-giving:hover {
  background-color: var(--wds-foam);
  border-color:     var(--wds-ocean-deep);
  color:            var(--ink);
}
.hero_slideshow .hero-caption__cta:hover {
  background-color: var(--wds-orange);
  border-color:     var(--wds-orange-edge);
  color:            var(--ink);
}
.section-link:hover,
.slice--stats.slice--stats-reasons .ArticleBody > p > a:hover {
  background-color: var(--wds-foam);
  border-color:     var(--wds-ocean-deep);
  color:            var(--ink);
  gap:              var(--space-3);
}
.slice--welcome .ArticleBody p > a:nth-of-type(1):hover {
  background-color: var(--wds-ocean);
  border-color:     var(--wds-ocean-deep);
  color:            var(--ink);
}
.slice--welcome .ArticleBody p > a:nth-of-type(2):hover {
  background-color: var(--accent);
  border-color:     var(--accent);
  color:            var(--accent-ink);
}
.slice--welcome .ArticleBody p > a:nth-of-type(3):hover {
  background-color: var(--wds-orange);
  border-color:     var(--wds-orange-edge);
  color:            var(--ink);
}

/* -------------------------------------------------------------------------
   18. v1.1  --  newsletter band padding
   -------------------------------------------------------------------------
   Twenty pixels more top and bottom. The desktop ceiling was --space-8, so
   32px; this composes 32 + 20 from the existing scale rather than hardcoding
   52px, which keeps the value on the token system and makes the intent
   readable.

   Set flat rather than clamped, and unscoped. Because this file loads after
   the v1.0 block, an unscoped rule at equal specificity also overrides the
   720px mobile override added there, so phones and desktop now share one
   value instead of drifting apart each time one of them is adjusted. That
   earlier mobile rule is now dead and gets removed at the next
   consolidation. */
.slice--signup {
  --signup-pad-block: calc(var(--space-8) + var(--space-5));
}

/* -------------------------------------------------------------------------
   19. v1.2  --  staggered reveal on the Why train with us columns
   -------------------------------------------------------------------------
   No library change was needed for this. setupScrollReveal collects its
   targets with document.querySelectorAll('.hubb-reveal'), so a class written
   into Body Contents is observed exactly like one the script adds itself.
   The two sections therefore just carry hubb-reveal in the markup.

   The Welcome section needs nothing beyond that: the library's own reveal
   rule fades and rises the whole block, buttons included, which is what was
   asked for.

   Why train with us wants its four columns arriving one after another rather
   than the block moving as one, so the section's own fade is neutralised and
   the list items animate instead. Timings read the library's three reveal
   tokens rather than inventing numbers, so this follows any future change to
   the reveal feel, and it picks up the mobile values automatically (600ms
   duration and a 150ms stagger instead of 1100 and 450).

   SAFETY: a markup-added hubb-reveal would normally risk leaving content
   hidden if the script never runs. Two things cover it. The library's
   reduced-motion block forces .hubb-reveal back to full opacity, and
   setupScrollReveal bails out under that preference, so the pair behave
   correctly together. The reduced-motion override below does the same job for
   the list items, which the library knows nothing about. Beyond that, a total
   script failure already leaves the page's own load fade unresolved, so this
   adds no new failure mode. */

/* Section is an observer hook only; the columns are what moves. */
.slice--stats-reasons.hubb-reveal {
  opacity:   1;
  transform: none;
}

.slice--stats-reasons.hubb-reveal .ArticleBody li {
  opacity:    0;
  transform:  translateY(var(--reveal-distance));
  transition: opacity   var(--reveal-duration) var(--ease-out-soft),
              transform var(--reveal-duration) var(--ease-out-soft);
}
.slice--stats-reasons.hubb-reveal.is-visible .ArticleBody li {
  opacity:   1;
  transform: none;
}

/* One stagger step per column, straight off the library's token. */
.slice--stats-reasons.hubb-reveal .ArticleBody li:nth-child(2) {
  transition-delay: calc(1 * var(--reveal-stagger));
}
.slice--stats-reasons.hubb-reveal .ArticleBody li:nth-child(3) {
  transition-delay: calc(2 * var(--reveal-stagger));
}
.slice--stats-reasons.hubb-reveal .ArticleBody li:nth-child(4) {
  transition-delay: calc(3 * var(--reveal-stagger));
}

@media (prefers-reduced-motion: reduce) {
  .slice--stats-reasons.hubb-reveal .ArticleBody li {
    opacity:    1;
    transform:  none;
    transition: none;
  }
}

/* -------------------------------------------------------------------------
   20. v1.3  --  footer spacing
   -------------------------------------------------------------------------
   The footer was carrying 128px above its first line of content, from two
   paddings stacking: .site-footer's own padding-top of --space-16, plus
   --space-16 again on the padding-block of .site-footer__inner.

   That is not a library fault. On a church build there is content BETWEEN
   those two: the Safeguarding band and the partner-logo strip both sit above
   .site-footer__inner, so the outer padding spaces the safeguarding notice
   and the inner padding spaces the contact band. Removing both of those for
   this site left the contact band as the footer's first child, and the two
   paddings collapsed onto each other.

   So the outer padding is removed only WHEN there is nothing above the
   contact band. Written as a :has() test rather than a flat zero, because the
   partner-logo strip is a live candidate for returning as PADI badges, and
   when it does the padding it needs comes back on its own.

   The remaining values are then tuned for how little this footer holds:
   three short columns and a row of social icons. 48px above the content,
   32px below it, and 32px before the bottom bar, against the 64, 48 and 48
   the library ships. */
.site-footer:not(:has(> .footer-safeguarding, > .slice--partner-logos)) {
  padding-top: 0;
}

.site-footer > .site-footer__inner {
  padding-block: var(--space-12) var(--space-8);
}

.site-footer__bottom {
  margin-top: var(--space-8);
}

/* -------------------------------------------------------------------------
   21. v1.4  --  Group Navigation card on content pages
   -------------------------------------------------------------------------
   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 aside itself becomes the card.
   Background, radius, shadow and overflow all live on the grid item, so the
   header strip and the contents cell just fill it and the rounded corners
   clip correctly without touching the table structure at all. Far less
   fragile than fighting Hubb's own boxout styling. */
.content-shell__side {
  background:    var(--surface);
  border-radius: 16px;
  overflow:      hidden;
  box-shadow:    0 12px 40px -28px rgba(13, 43, 69, 0.22);
}

.content-shell__side table {
  border-collapse: collapse;
  width:           100%;
}

/* Header strip: navy with a white label, mirroring the reference build.
   White on navy is 14.5:1. */
.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 a {
  color:           var(--accent-ink);
  font-family:     var(--font-display);
  font-size:       var(--text-base);
  font-weight:     600;
  text-decoration: none;
}

/* Contents cell. cellpadding="5" 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, 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(--wds-haze);
}

/* The section the visitor is currently in. Deepened ocean at 4.64:1 on white,
   so it reads as current without relying on colour alone: it is also the only
   bolded item. */
.content-shell__side .group_nav_selected > a {
  font-weight: 600;
  color:       var(--wds-ocean-deep);
}

/* 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);
}

/* -------------------------------------------------------------------------
   22. v1.5  --  sidebar becomes a stack of cards
   -------------------------------------------------------------------------
   Section 21 put the card styling on the aside itself, which was right while
   the column held one thing. It now holds two, so the card treatment moves
   onto a .side-card wrapper and the aside becomes the stack. The nav rules in
   section 21 are all descendant selectors, so they keep matching unchanged;
   only the shell moves. The un-setting below folds away at the next
   consolidation. */
.content-shell__side {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-6);
  background:     none;
  border-radius:  0;
  overflow:       visible;
  box-shadow:     none;
}

.side-card {
  background:    var(--surface);
  border-radius: 16px;
  overflow:      hidden;
  box-shadow:    0 12px 40px -28px rgba(13, 43, 69, 0.22);
}

/* Head strip, matching the navy one the nav module renders so the two cards
   read as a set. White on navy is 14.5:1. */
.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 the module
   is left 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(--wds-ocean-deep);
}

/* CTA. Its own class rather than .section-link, which carries slice-level
   geometry (a 48px top margin and auto centring) that makes no sense inside a
   card. Same sea foam treatment and same 0.7 hover as every other button. */
.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(--wds-foam);
  color:            var(--ink);
  border:           1px solid var(--wds-ocean-deep);
  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;
}

/* -------------------------------------------------------------------------
   23. v1.6  --  heading hierarchy, and a 2-up article grid
   ------------------------------------------------------------------------- */

/* HEADING HIERARCHY  --  correcting v0.8
   Shrinking the content h1 to --text-2xl landed it on exactly the size
   .content_article already gives h2, so the page title and its section
   headings became indistinguishable. Raising h1 again is the wrong fix: the
   ratio is what reads as hierarchy, and anything close enough to h2 to feel
   restrained is also close enough to look like a mistake.

   So the levels beneath it step down one place each instead, which keeps the
   h1 at the size that was asked for and restores a clear interval:

       h1  --text-2xl   32 to 48px   (unchanged)
       h2  --text-xl    24 to 32px   (was 32 to 48)
       h3  --text-lg    18 to 22px   (was 24 to 32)
       h4  --text-base  16 to 18px   (was 18 to 22, now carries weight instead)

   h4 lands on the body size, so it takes weight 700 to stay legible as a
   heading rather than reading as a bold sentence. */
.content_article .ArticleBody h2 { font-size: var(--text-xl); }
.content_article .ArticleBody h3 { font-size: var(--text-lg); }
.content_article .ArticleBody h4 {
  font-size:   var(--text-base);
  font-weight: 700;
}

/* ARTICLES LIST  --  2-up card grid
   For a listing inside the content card, where the 300px sidebar and the
   card's own padding leave roughly 1100px rather than a full-width slice.
   Four columns would be cramped and three still narrow, so two gives the
   posts room to breathe with their summaries intact.

   Used alongside .al-cards, same as the 3-up variant, so all the card
   styling is inherited and only the track count changes. An odd item count
   leaves a single card on the last row, which for a two-column grid reads as
   normal rather than as the quarter-width hole a 3-in-4 row produces, so no
   orphan-avoiding step is needed here. Collapses at the library's 720px
   phone breakpoint. */
.al-cards.al-cards-2up {
  grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 720px) {
  .al-cards.al-cards-2up {
    grid-template-columns: 1fr;
  }
}

/* -------------------------------------------------------------------------
   24. v1.7  --  Hubb form styling: subscribe, registration, profile editor
   -------------------------------------------------------------------------
   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. That has broken a desktop layout on another build. Everything
   here hooks Hubb's own form classes instead, and is scoped inside the
   content card so it cannot reach anything outside it.

   If the member profile editor turns out to render on a template without the
   content shell, the scope needs widening to reach it; the selectors
   themselves will work unchanged.
   ------------------------------------------------------------------------- */
:root {
  --wds-field-border: #7E8F9C;   /* 3.34:1 on white, clears the non-text floor */
  --wds-error:        #B3261E;   /* 6.54:1 on white */
}

/* Section headings ("Basic Details", "Consent"). 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-8) 0 var(--space-4);
  font-family: var(--font-display);
  font-size:   var(--text-xl);
  font-weight: 600;
  color:       var(--ink);
}

/* Field rows. The default is a 150px right-aligned label column, which is
   what wraps "Email Address:" onto two lines and leaves the labels floating
   away from their inputs. Stacking the label above the control fixes both.

   !important is unavoidable on the cell: Hubb writes text-align and width as
   INLINE styles on the td, and no class selector can beat an inline style.

   :has(.SubCaption) guards the rule so it can only ever match a table that is
   genuinely a form row, never a real data table sharing the SubTable class. */
.content-shell__main .SubTable:has(.SubCaption),
.content-shell__main .SubTable:has(.SubCaption) > tbody,
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr,
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td {
  display:    block;
  width:      100% !important;
  text-align: left !important;
}
.content-shell__main .SubTable:has(.SubCaption) {
  margin-bottom: var(--space-5);
}

/* Labels, matching the caption treatment used in the sidebar contact card. */
.content-shell__main .SubCaption {
  display:        block;
  margin-bottom:  var(--space-2);
  color:          var(--ink-soft);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.content-shell__main .SubCaption b {
  color: var(--wds-error);
}

/* Controls. --rule is only 1.45:1 on white, fine for a decorative hairline
   and nowhere near the 3:1 a form field boundary needs, hence a dedicated
   border colour. Capped in width because a full-width text input inside a
   1100px card is a worse target than a comfortable one. */
.content-shell__main input.text,
.content-shell__main select.text,
.content-shell__main textarea.text {
  width:            100%;
  max-width:        420px;
  padding:          var(--space-3) var(--space-4);
  border:           1px solid var(--wds-field-border);
  border-radius:    8px;
  background-color: var(--surface);
  color:            var(--ink);
  font-family:      var(--font-body);
  font-size:        var(--text-base);
  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(--wds-ocean-deep);
  outline-offset: 2px;
  border-color:   var(--wds-ocean-deep);
}

/* 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;
}

/* Validation messages. */
.content-shell__main .validator {
  color:     var(--wds-error);
  font-size: var(--text-sm);
}

/* Submit. Same sea foam pill and 0.7 hover as every other button on the site.
   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. */
.content-shell__main input.button {
  display:          inline-flex;
  align-items:      center;
  justify-content:  center;
  padding:          var(--space-3) var(--space-6);
  background-color: var(--wds-foam);
  color:            var(--ink);
  border:           1px solid var(--wds-ocean-deep);
  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;
}

/* -------------------------------------------------------------------------
   25. v1.8  --  restoring the content-article vertical rhythm
   -------------------------------------------------------------------------
   LIBRARY BUG. 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 anywhere 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 treated as unset, and margin is not an
   inherited property, so it computes to 0.

   The result is that every heading on every content page has had no margin at
   all, above or below, on every Tabernacle site. That is why headings sit
   jammed against the text around them, and why the fix people reach for is
   adding line breaks inside the article.

   Defining the missing token repairs both rules at once, and 2.5rem is where
   it belongs on the existing scale, between --space-8 at 2rem and --space-12
   at 3rem. This belongs in the library; it is here because the skin can be
   deployed without re-uploading 660KB. */
:root {
  --space-10: 2.5rem;
}

/* With the margins working again, one refinement worth having: a major
   section heading should carry more air above it than a subsection heading.
   The library groups h2, h3 and h4 into one rule so they all get the same,
   which reads as a flat list of equal headings 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;
}

/* -------------------------------------------------------------------------
   26. v1.9  --  form in a grey panel, and tightened
   -------------------------------------------------------------------------
   WHY THE FIELDS WERE SO TALL. .content_article .ArticleBody sets
   line-height: 1.65 for readable prose, and Hubb's inputs are inline-block
   controls sitting on a text baseline inside that. So each control got a line
   box roughly 1.65x its own text height, adding around 15px of dead space
   inside every field and again between the label and the control. Nothing was
   padded wrongly; the controls were floating in oversized line boxes.

   Making the controls display: block takes them out of inline flow entirely,
   and tightening the line-height on the cell stops the surrounding whitespace
   text nodes forming line boxes of their own. That alone removes most of the
   height.

   THE PANEL. The whole registration control is wrapped by Hubb in a div whose
   id ends in divRegister, which is the only element that encloses both the
   fields and the submit button, so it is the hook for the panel. The id
   prefix is ASP.NET's generated control path and varies by page, but the
   suffix comes from the control's own name and is stable. */
:root {
  /* Raised from #7E8F9C: that cleared 3:1 against a white input but only
     managed 2.95:1 against the grey panel below. This clears both, at 4.07:1
     on white and 3.61:1 on the grey. */
  --wds-field-border: #6E8090;
}

.content-shell__main [id$="divRegister"] {
  background:    var(--wds-neutral);
  border-radius: 12px;
  padding:       clamp(var(--space-5), 3vw, var(--space-8));
  margin-block:  var(--space-6);
}

/* Section captions. These are form section labels, not page headings, so they
   step down from --text-xl to --text-lg and lose most of their air. */
.content-shell__main div.TopCaption {
  margin:    var(--space-6) 0 var(--space-4);
  font-size: var(--text-lg);
}
.content-shell__main [id$="divRegister"] > div > div:first-child > div.TopCaption {
  margin-top: 0;
}

/* Rows and labels, tightened. */
.content-shell__main .SubTable:has(.SubCaption) {
  margin-bottom: var(--space-4);
}
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td {
  line-height: 1.25;
}
.content-shell__main .SubCaption {
  margin-bottom: var(--space-2);
}

/* Controls. display: block is the fix for the inflated height, not merely a
   layout preference. Vertical padding kept at --space-3 so the target still
   clears the 44px minimum once the line box is under control. */
.content-shell__main input.text,
.content-shell__main select.text,
.content-shell__main textarea.text {
  display:     block;
  max-width:   400px;
  padding:     var(--space-3) var(--space-4);
  line-height: 1.25;
}

/* The submit sits in a Hubb-authored div carrying inline text-align:center. */
.content-shell__main [id$="divRegister"] input.button {
  margin-top: var(--space-4);
}

/* -------------------------------------------------------------------------
   27. v2.0  --  mobile menu: booking CTA, no account links, cool submenu
   ------------------------------------------------------------------------- */
@media (max-width: 1400px) {

  /* SUBMENU TINT
     The library hardcodes rgba(232, 213, 196, 0.18) here, a warm beige
     commented as an accent-soft tint. It is not a token, so it survives every
     re-skin: a navy and teal site gets a beige nested menu. Pointed at the
     same tint the desktop sidebar navigation uses for its hover, so nested
     navigation reads the same cool note wherever it appears. Navy on it is
     13.05:1. */
  .mobile-nav-sub {
    background: var(--wds-haze);
  }

  /* ACCOUNT LINKS
     JS moves the Status Bar's login and register links into the foot of the
     panel. This is an outward-facing site with no member area, so they invite
     visitors into a sign-in flow with nothing behind it. Hidden for the same
     reason the header Account control is: see site-header--no-account.

     Hidden rather than stopped at source, because the Status Bar itself must
     stay in the DOM as the script's source of truth for session URLs. */
  .mobile-account-links {
    display: none;
  }

  /* BOOK A COURSE, inside the panel
     The pill cannot be reparented with CSS: it is a sibling of the panel in
     .site-header__main, not a child of it. So it is positioned into the
     panel's coordinate space instead, inset by the panel's own horizontal
     padding and anchored to its foot, and given the panel's exact transform
     timing so the two arrive together.

     Anchored to the foot rather than sitting inline under the last menu item.
     Inline would need the pill inserted into the panel's DOM, which is a
     script change; and the foot is arguably better anyway, because the panel
     scrolls and a booking CTA that stays put is more use than one that
     scrolls away. If it must sit directly under News, that is the library job
     noted in the backlog.

     Positioning derives from the panel's own width expression, so it tracks
     any future change to that width with nothing to keep in step. */
  .site-header--split  .header-giving,
  .site-header--tworow .header-giving {
    position:   fixed;
    left:       calc(100% - min(85%, 360px) + var(--space-4));
    right:      calc(var(--space-4) + var(--safe-right));
    bottom:     calc(var(--space-6) + var(--safe-bottom));
    width:      auto;
    margin:     0;
    z-index:    9051;
    transform:  translateX(120%);
    transition: transform 0.3s ease;
  }
  .menu-open .site-header--split  .header-giving,
  .menu-open .site-header--tworow .header-giving {
    transform: translateX(0);
  }
}

/* -------------------------------------------------------------------------
   28. v2.1  --  collapsing the form's line boxes
   -------------------------------------------------------------------------
   Third pass at this, so a different approach rather than smaller numbers.

   The library resets margins globally with `* { margin: 0 }`, so no margin can
   be producing the gaps between a label and its control. What is left is line
   boxes. Hubb's markup indents each control inside a wrapper div:

       <td><div id="div_ctls_title">
               <select class="text">...</select>
           </div></td>

   Those newlines and tabs are text nodes. v1.9 set the controls to display:
   block to fix their inflated height, which worked, but it also stopped the
   whitespace sitting on the same line as the control, so each whitespace node
   now forms an anonymous line box of its own, roughly 21px at the inherited
   line-height. Two per field accounts for the remaining space, and no amount
   of margin tuning touches it.

   Collapsing line-height to zero on the cell removes those boxes entirely,
   then the elements that actually contain text get their line-height back
   explicitly. This is robust rather than a tuned value: it does not matter
   whether the space came from whitespace nodes, inline-block baselines, or
   both, because a zero line box has no height in any of those cases. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td,
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div {
  line-height: 0;
}

/* Restore it on everything that carries real text. */
.content-shell__main .SubCaption,
.content-shell__main .validator,
.content-shell__main input.text,
.content-shell__main select.text,
.content-shell__main textarea.text {
  line-height: 1.3;
}

/* With the phantom space gone, these are the only gaps left, so they can be
   set for how they should read rather than to compensate for something. */
.content-shell__main .SubCaption {
  margin-bottom: var(--space-2);
}
.content-shell__main .SubTable:has(.SubCaption) {
  margin-bottom: var(--space-5);
}

/* -------------------------------------------------------------------------
   29. v2.2  --  narrow variant of the content shell, for system pages
   -------------------------------------------------------------------------
   System pages are login, register, forgotten password, the profile editor,
   search results and the privacy policy. They are forms and results rather
   than editorial content, and none of them benefits from a sidebar: a login
   form should be focused, and a 1100px measure is far too wide for one.

   So this variant drops to a single column and caps the card at a readable
   measure, matching the 820px the library uses for .content_article when it
   is the page. Everything else about the shell is inherited, including the
   tinted band, the white card and the form styling. */
.content-shell--narrow .content-shell__inner {
  grid-template-columns: minmax(0, 1fr);
  max-width:             920px;
}

/* -------------------------------------------------------------------------
   30. v2.3  --  the panel CTA needs button padding, not header padding
   -------------------------------------------------------------------------
   The library tightens .header-giving at the phone breakpoint to
   padding: var(--space-1) var(--space-4), so 4px of vertical padding. That is
   correct for a pill sitting in a slim header strip, which is where it lived
   when that rule was written, and v0.275 extended the same rule to the split
   header without questioning it.

   Now that v2.0 has moved the pill to the foot of the mobile panel it is a
   full-width call to action rather than a header ornament, and 4px leaves it
   about 28px tall: visually squashed, and under the 44px minimum for a touch
   target. Real button padding takes it to roughly 50px.

   Also centred. The library sets inline-flex, which puts the label hard
   against the leading edge once the pill is stretched across the panel. */
@media (max-width: 1400px) {
  .site-header--split  .header-giving,
  .site-header--tworow .header-giving {
    padding:         var(--space-4) var(--space-5);
    justify-content: center;
    text-align:      center;
  }
}

/* -------------------------------------------------------------------------
   31. v2.4  --  form cells hug their contents
   -------------------------------------------------------------------------
   Supersedes the line-height approach in section 28, which was the right
   diagnosis aimed at the wrong lever.

   Computed box model on a live field: the select measures 26.667px of content
   plus 12px padding and 0.889px border top and bottom, so 52.4px overall. Its
   containing cell measures 76.44px. The 24px difference is about one line box
   at this font size, so a text node in the cell is still generating one, and
   setting line-height to zero did not remove it because the strut and the
   whitespace are not the same thing in every case.

   Making the cell and its wrapper flex containers settles it outright rather
   than by tuning: whitespace-only text nodes do not generate flex items at
   all, so there is nothing left to produce a line box, a strut, or a baseline
   to align to. It cannot be defeated by however Hubb happens to indent its
   markup, which is the point.

   (Confirmed in the same measurement: max-width 400px on the controls is
   applying exactly. 366.222 + 32 padding + 1.78 border = 400.) */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td,
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div {
  display:        flex;
  flex-direction: column;
  align-items:    flex-start;
}

/* Controls still fill up to their cap; flex-start alone would leave them at
   intrinsic width. */
.content-shell__main input.text,
.content-shell__main select.text,
.content-shell__main textarea.text {
  width:     100%;
  max-width: 400px;
}

/* -------------------------------------------------------------------------
   31. v2.4  --  system page headings and the search toolbar
   -------------------------------------------------------------------------
   Scoped to the narrow shell, which only the System template uses, so none of
   this can reach a content page.
   ------------------------------------------------------------------------- */

/* HEADINGS
   System content is not wrapped in .content_article, so the heading rules
   written for content pages never reach it and the h1 falls back to the
   library's base --text-3xl: up to 72px, which is what made "Media" so large.
   Same scale as a content page, and the accent rule beneath it too, 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); }

/* SEARCH TOOLBAR
   Hubb lays this out with float-based media_option_left and
   media_option_right classes. Flex instead, so the controls sit on one
   baseline and wrap sensibly on a phone.

   Order matters: in the markup the Search button comes before Reset Form, but
   the floats reverse them so Reset reads first, which is right, secondary
   before primary. Flex would restore document order and put the primary
   button first, so the two are ordered explicitly below and matched by what
   they contain rather than by class, since both carry the same class. */
.content-shell--narrow .system_controls {
  display:          flex;
  flex-wrap:        wrap;
  align-items:      flex-end;
  gap:              var(--space-4) var(--space-5);
  background-color: var(--wds-neutral);
  border-radius:    12px;
  padding:          var(--space-5);
  margin-block:     var(--space-6);
}

/* The label is a bare text node, not an element, so it cannot be styled
   directly. The caption treatment goes on the container and is reset on the
   control, which inherits from it. */
.content-shell--narrow .system_option {
  color:          var(--ink-soft);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height:    1.3;
}
.content-shell--narrow .system_option input.text,
.content-shell--narrow .system_option select.text {
  margin-top:     var(--space-2);
  color:          var(--ink);
  font-size:      var(--text-base);
  font-weight:    400;
  letter-spacing: 0;
  text-transform: none;
}

/* Reset Form: secondary, and it carries the whole action group to the far end
   of the bar. Keeps the caption treatment it inherits, which suits a small
   action link. */
.content-shell--narrow .system_option:has(> a) {
  order:       2;
  margin-left: auto;
}
.content-shell--narrow .system_option:has(> a) > a {
  color:           var(--ink-soft);
  text-decoration: none;
  transition:      color var(--transition);
}
.content-shell--narrow .system_option:has(> a) > a:hover {
  color:           var(--ink);
  text-decoration: underline;
}

/* Search: primary, last. */
.content-shell--narrow .system_option:has(input.button) {
  order: 3;
}

/* -------------------------------------------------------------------------
   32. v2.5  --  Universal Article List as a stacked archive
   -------------------------------------------------------------------------
   The Universal Article List mini-layout wraps its items in .UML_AL_1. Where
   a card class is also set, as on the blog index, the library's grid rules
   take over and this must not interfere, hence :not(.al-cards). Where no card
   class is set, as on the Past Newsletters list, the items render as a bare
   stack with no separation at all, which is what this styles.

   A stacked list is the right answer here rather than switching the module to
   cards: these newsletters carry no feature image, so a card grid would be a
   row of empty-topped boxes. An archive wants to be scannable, not gridded.

   THE REAL PROBLEM IS NOT SPACING. Hubb renders the call to action as
   <div class="articleListLink">Read more</div>, plain text with no anchor
   inside it. Only the title is a link, so the most obvious affordance on each
   row does nothing at all when clicked.

   Fixed the way the library fixes it for cards: the title's anchor gets a
   stretched pseudo-element covering the whole row, so the entire item becomes
   one target and Read more genuinely works. Worth knowing the trade-off,
   which the library also accepts: a stretched link sits over the summary, so
   selecting that text to copy it becomes awkward. For an archive people click
   rather than quote, that is the right way round.
   ------------------------------------------------------------------------- */
.content-shell__main .UML_AL_1:not(.al-cards) {
  display:        flex;
  flex-direction: column;
  margin-top:     var(--space-5);
  border-top:     1px solid var(--rule);
}

.content-shell__main .UML_AL_1:not(.al-cards) > div {
  position:      relative;
  padding-block: var(--space-5);
  border-bottom: 1px solid var(--rule);
}
.content-shell__main .UML_AL_1:not(.al-cards) > div:last-child {
  border-bottom: 0;
}

/* Title. Underline dropped: it is the row that is clickable now, and four
   underlined headings in a column reads as clutter. */
.content-shell__main .UML_AL_1:not(.al-cards) .articleListTitle {
  margin-bottom: var(--space-2);
}
.content-shell__main .UML_AL_1:not(.al-cards) .articleListTitle a {
  font-family:     var(--font-display);
  font-size:       var(--text-lg);
  font-weight:     600;
  color:           var(--ink);
  text-decoration: none;
  transition:      color var(--transition);
}
.content-shell__main .UML_AL_1:not(.al-cards) .articleListTitle a::before {
  content:  "";
  position: absolute;
  inset:    0;
}

/* Summary. */
.content-shell__main .UML_AL_1:not(.al-cards) .articleListSummary {
  margin-bottom: var(--space-3);
  color:         var(--ink-soft);
  font-size:     var(--text-base);
  line-height:   1.55;
}

/* Read more, now a real affordance because the row is clickable. The rule
   grows on hover, matching the card lists elsewhere on the site. */
.content-shell__main .UML_AL_1:not(.al-cards) .articleListLink {
  display:        inline-flex;
  align-items:    center;
  gap:            var(--space-2);
  color:          var(--wds-ocean-deep);
  font-size:      var(--text-xs);
  font-weight:    700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.content-shell__main .UML_AL_1:not(.al-cards) .articleListLink::after {
  content:    "";
  width:      1.1em;
  height:     1px;
  background: currentColor;
  transition: width var(--transition);
}

/* Whole-row hover, since the whole row is the target. */
.content-shell__main .UML_AL_1:not(.al-cards) > div:hover .articleListTitle a {
  color: var(--wds-ocean-deep);
}
.content-shell__main .UML_AL_1:not(.al-cards) > div:hover .articleListLink::after {
  width: 1.9em;
}

/* -------------------------------------------------------------------------
   32. v2.5  --  the required asterisk was landing on the label
   -------------------------------------------------------------------------
   A specificity error in v2.1. That pass collapsed line-height to zero on the
   field wrappers to kill Hubb's whitespace line boxes, then restored it on the
   elements carrying real text. But the two selectors are not evenly matched:

     zeroing  .content-shell__main .SubTable:has(.SubCaption)
                > tbody > tr > td > div          -> (0,3,4)
     restore  .content-shell__main .SubCaption   -> (0,2,0)

   .SubCaption is a direct child div of the cell, so it matches the zeroing
   rule, and the zeroing rule out-specifies the restore. The caption has been
   sitting at line-height: 0 ever since.

   With no line height, the required marker and the label text end up sharing
   the same vertical space instead of stacking, which is why the red asterisk
   printed on top of the first letter.

   Restored with a selector specific enough to win, rather than by weakening
   the zeroing rule, since that one is doing necessary work. The marker also
   gets a little breathing room so it reads as a marker and not as part of the
   first word. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div.SubCaption {
  line-height: 1.35;
}
.content-shell__main .SubCaption b {
  margin-right: 0.35em;
  font-weight:  700;
}

/* -------------------------------------------------------------------------
   33. v2.6  --  labels beside their controls
   -------------------------------------------------------------------------
   Section 24 stacked each label above its control, because Hubb's native
   layout is a 150px right-aligned label column that wraps "Email Address:"
   onto two lines. Stacking fixed the wrapping but doubled the form's height.
   Putting them side by side properly gets both: no wrapping, and each field
   is one row instead of two.

   THE COLUMN WIDTH HAS TO BE FIXED, and that is worth knowing before anyone
   tries to improve it. Hubb renders every field as its own separate table, so
   each row is an independent grid. A content-sized track such as max-content
   would therefore size to its own label and nothing else, and the labels
   would step in and out down the form instead of lining up. 190px clears the
   longest label here, "* Email Address:", at around 146px.

   Reverts to stacked below the phone breakpoint, where a 190px label column
   plus a control would leave the control about 110px wide inside the card. */
@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;
  }

  /* The gap between label and control is now horizontal, so the stacked
     spacing below the label is dead weight. */
  .content-shell__main .SubTable:has(.SubCaption) .SubCaption {
    margin-bottom: 0;
  }

  /* Rows can also close up, since each is half the height it was. */
  .content-shell__main .SubTable:has(.SubCaption) {
    margin-bottom: var(--space-3);
  }
}

/* -------------------------------------------------------------------------
   34. v2.7  --  form layout, consolidated
   -------------------------------------------------------------------------
   This supersedes the layout half of sections 24, 26, 28, 31 and 33. Those
   were seven passes of adding one rule to correct the last, and the sixth
   introduced the fault this fixes, so the sensible move is one coherent block
   rather than an eighth patch. The colour, border and control styling in
   section 24 still stands; only the layout is restated here.

   THE REGRESSION. Section 31 set every `td > div` to a flex column, to stop a
   stray line box inflating the control. It worked, but a label is also a
   `td > div`, so .SubCaption became a flex column too. Its two children, the
   <b>*</b> required marker and the label text, therefore became separate flex
   items and stacked: every required field's label rendered on two lines with
   the asterisk orphaned above it. With the row centring its contents, that
   pushed the height of every row that has a required field.

   THE SHAPE NOW, stated once:
     tr   is the grid, a fixed label column beside the control
     td   is a plain block, a grid item and nothing more
     the label is a plain block, so its marker stays inline where it belongs
     ONLY the control wrapper is a flex column, which is all that ever
       needed to be, and is what keeps the stray line box away

   Specificity note: the label and wrapper rules are written to full depth
   deliberately. Section 31's `td > div` rule scores three classes and four
   elements, so a shorter selector such as `.content-shell__main .SubCaption`
   loses to it and silently does nothing. */

/* Cells: plain blocks. Hubb writes text-align and width inline on the td, so
   !important is unavoidable there. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td {
  display:    block;
  width:      100% !important;
  text-align: left !important;
}

/* Labels: plain blocks, so the required marker stays on the same line. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div.SubCaption {
  display:       block;
  margin-bottom: 0;
}

/* Control wrappers only: flex column, which is what removes the stray line
   box that Hubb's indentation would otherwise generate. */
.content-shell__main .SubTable:has(.SubCaption) > tbody > tr > td > div:not(.SubCaption) {
  display:        flex;
  flex-direction: column;
  align-items:    flex-start;
}

/* Row pitch. With single-line labels and a 52px control, this is the only
   vertical space left in the form. */
.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;
  }
}

/* -------------------------------------------------------------------------
   35. v2.8  --  motion and feedback on touch devices
   -------------------------------------------------------------------------
   The reveal system is already tuned for phones: the library drops
   --reveal-distance to 12px, --reveal-duration to 600ms and
   --reveal-stagger to 150ms at the 720px breakpoint, and animates only
   opacity and transform so it stays on the compositor. It also applies
   will-change transiently rather than permanently, which matters on
   mid-range Android. None of that needed changing.

   The panel slide was checked rather than "improved". Its current ease
   reaches 29.5% of its travel in the first fifth of the duration, which
   reads as responsive without snapping. The iOS sheet curve often
   suggested for this reaches 65.6% at the same point, which is worse than
   the curve that produced the snap-then-crawl fault earlier in this build.
   Left as it is.

   WHAT WAS ACTUALLY MISSING: press feedback. Every button here responds to
   :hover, and a touch screen has no hover, so on a phone nothing on the
   site acknowledges being pressed. That absence is most of what makes a
   site feel dead under the thumb, and no amount of scroll animation
   compensates for it.
   ------------------------------------------------------------------------- */
@media (hover: none) {

  /* The platform draws its own grey flash box on tap, which looks cheap and
     fires on a different rhythm to our own states. Removed, because we
     replace it below rather than simply delete it. */
  a,
  button,
  input[type="button"],
  input[type="submit"] {
    -webkit-tap-highlight-color: transparent;
  }

  /* Press states. Deliberately fast: 120ms, not the 350ms used for hover.
     A hover can afford to be languid because the pointer lingers; a press
     has to answer immediately or it reads as lag. */
  .hero_slideshow .hero-caption__cta:active,
  .section-link:active,
  .side-card__cta:active,
  .slice--stats.slice--stats-reasons .ArticleBody > p > a:active,
  .slice--welcome .ArticleBody p > a:active,
  .content-shell__main input.button:active {
    opacity:    0.7;
    transform:  scale(0.97);
    transition: opacity 120ms ease, transform 120ms ease;
  }

  /* The booking pill needs its own rule. On a phone it is positioned into the
     mobile panel and already carries transform: translateX(0) while open, so
     a bare scale() here would replace that and throw it back off screen. The
     translate has to be restated alongside the scale. */
  .site-header--split  .header-giving:active,
  .site-header--tworow .header-giving:active {
    opacity:    0.7;
    transform:  translateX(0) scale(0.97);
    transition: opacity 120ms ease, transform 120ms ease;
  }

  /* Nav rows and list rows get a tint rather than a squeeze: scaling a
     full-width row looks like the layout is wobbling. */
  .content-shell__side .group_nav_selected > a:active,
  .content-shell__side .group_nav_sibling > a:active,
  .content-shell__side .group_nav_child > a:active,
  .mobile-nav-link:active,
  .mobile-nav-sublink:active {
    background-color: var(--wds-haze);
  }

  /* Scrolling the menu to its end should not then start scrolling the page
     behind it, which is the default and feels broken because the page moves
     under a panel that is still open. */
  .site-header__nav {
    overscroll-behavior: contain;
  }
}

/* Under reduced motion the tint and the opacity still read as feedback, so
   only the movement is dropped. */
@media (hover: none) and (prefers-reduced-motion: reduce) {
  .hero_slideshow .hero-caption__cta:active,
  .section-link:active,
  .side-card__cta:active,
  .slice--stats.slice--stats-reasons .ArticleBody > p > a:active,
  .slice--welcome .ArticleBody p > a:active,
  .content-shell__main input.button:active {
    transform: none;
  }
  .site-header--split  .header-giving:active,
  .site-header--tworow .header-giving:active {
    transform: translateX(0);
  }
}

/* -------------------------------------------------------------------------
   36. v2.9  --  mobile reveal, retuned so it can actually be seen
   -------------------------------------------------------------------------
   v0.277 fixed WHICH elements are observed. This fixes how the reveal is
   tuned once they are, which turned out to be the other half of the problem.

   The observer fires 180px before an element is visible on a 720px viewport.
   At an unhurried thumb scroll of roughly 900px per second, the element
   reaches the fold 200ms later and sits comfortably in view around 533ms
   later. Against the library's mobile duration of 600ms that leaves the
   animation 89% complete by the time anyone looks at it, so only the tail is
   ever seen. Measured, not guessed:

       600ms  -> 89% done on arrival   only the tail visible
       950ms  -> 56% done on arrival   clearly still moving

   And 12px of travel is below the threshold where movement reads at all at
   arm's length. 28px reads clearly without the heaviness that sets in around
   36px.

   Stagger comes DOWN rather than up, which looks backwards but follows from
   v0.277: every item now has its own trigger, and on a phone everything
   stacks, so items already cascade naturally from their own scroll positions.
   The artificial delay is now additive on top of that, and at 150ms the
   fourth item was waiting 450ms after its own trigger before starting. 80ms
   keeps a hint of cascade where items genuinely share a row without holding
   anything back.

   The script reads --reveal-stagger from the root element once at init, so
   this value reaches the stagger passes as well as the CSS transitions.

   Belongs in the library rather than one client's skin, since it is not
   brand-specific. Here because it deploys instantly. */
@media (max-width: 720px) {
  :root {
    --reveal-distance: 28px;
    --reveal-duration: 950ms;
    --reveal-stagger:  80ms;
  }
}

/* -------------------------------------------------------------------------
   37. v3.0  --  the company number is not an aside
   -------------------------------------------------------------------------
   The library italicises .site-footer #footer_charity. Measured across
   style.css, 24 selectors set font-style: italic, but almost all belong to
   modules and pages this site does not have: the calendar dialog, media-list
   speakers, the seeker box, the safeguarding band, error pages and the opt-in
   .hubb-form class. On this build that footer line is the only italic anyone
   actually sees.

   Italic signals an aside, a quotation or a foreign term. A company or
   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, and a touch of letter-spacing so a run of digits stays legible at
   the small size and low contrast the footer uses.

   Deliberately NOT touching the other two italics that could appear here.
   Article em/i and blockquote are both latent rather than live, both are
   ordinary typographic conventions, and the client has an italic button in
   the editor. Restyling components nobody has looked at is how regressions
   get in.

   The italic weights stay in the font request. Browsers do not download a
   font file until rendered text needs it, so unused weights cost nothing,
   and removing them would only mean a synthesised slant the first time
   anyone presses that italic button. */
.site-footer #footer_charity {
  font-style:     normal;
  letter-spacing: 0.02em;
}

/* -------------------------------------------------------------------------
   38. v3.1  --  card lists embedded inside an article
   -------------------------------------------------------------------------
   ONE CAUSE, TWO SYMPTOMS, and it is a library issue rather than a Wight Dive
   one. A card list placed inside an article, which is how a blog index is
   naturally built, sits inside .content_article .ArticleBody. Every prose rule
   there scores three classes and an element, 0,3,1. The card rules score
   0,2,1, and the title rule is worse still because it uses :is(), which takes
   the specificity of its most specific argument, so it lands at 0,2,1 too.

   The prose rules therefore win, and two things break:

   1. .content_article .ArticleBody img sets height: auto and a 24px block
      margin. The card image wrapper is aspect-ratio 4/3 with an --accent-soft
      background and expects its image to fill via height: 100% and object-fit:
      cover. With height: auto the image renders at its natural 2:1 instead, so
      the wrapper's tint shows through underneath. That is the pale blue band.

   2. .content_article .ArticleBody a sets text-decoration: underline. The card
      title expects text-decoration: none, because the library draws its own
      animated underline with a background gradient on hover. Losing that rule
      means the title is underlined at rest AND the gradient arrives on top of
      it, which is the double line.

   Restated below at 0,4,1 so the card rules win back. Border radius handed to
   the wrapper, which already rounds and clips, rather than rounding the image
   inside a clipping box twice.

   ASPECT RATIO. Fixing the fill alone would crop a 2:1 photograph to 4:3,
   discarding a third of its width, which on a landscape shot of a diver can
   easily cut the subject out. It also gives a 394px tall image on a 525px
   card, which dominates it. 16/9 crops 11% instead and comes out at 295px.
   Scoped to the 2-up variant, since that is where cards are wide enough for
   this to matter. */
.content_article .ArticleBody .al-cards .articleListImage img {
  height:        100%;
  object-fit:    cover;
  margin:        0;
  border-radius: 0;
}

.content_article .ArticleBody .al-cards .articleListTitle a {
  text-decoration: none;
}

.al-cards.al-cards-2up .articleListImage {
  aspect-ratio: 16 / 9;
}

/* -------------------------------------------------------------------------
   39. v3.2  --  card title underline: gradient out, real underline in
   -------------------------------------------------------------------------
   Same defect the blog lead hit in section 9, now visible on the card lists.

   The library animates the title underline by growing a background gradient
   from 0% to 100% width. It works, but a background image cannot skip
   descenders, and it is pinned to background-position: 0 100% with only 2px
   of padding beneath, so it runs straight through the y, g and pound sign.
   On an anchor that wraps to two lines the background is painted per line
   box, so it happens on every line.

   Replaced with a real text-decoration, which the browser positions properly
   and which breaks around descenders on its own via text-decoration-skip-ink.
   The trick that keeps it animatable: declare the underline at rest but in
   transparent, then fade the colour in on hover. text-decoration-color IS a
   transitionable property, so this keeps a 300ms fade rather than snapping,
   which is what switching to a plain hover-only underline would have cost.

   text-underline-offset gives the clearance the gradient never had.

   Scoped to .al-cards. The feature variant keeps the treatment from section
   9, which solved the same problem at a much larger type size and does not
   need disturbing. */
:is(.al-cards) .articleListTitle a {
  background-image:          none;
  padding-bottom:            0;
  text-decoration:           underline;
  text-decoration-color:     transparent;
  text-decoration-thickness: 1px;
  text-decoration-skip-ink:  auto;
  text-underline-offset:     0.2em;
  transition:                text-decoration-color 300ms ease;
}
:is(.al-cards) > div:hover .articleListTitle a {
  text-decoration-color: currentColor;
}

/* The blog index sits inside an article, where the prose rules out-specify
   the above at 0,3,1. Restated at 0,4,1, same as section 38. */
.content_article .ArticleBody .al-cards .articleListTitle a {
  background-image:          none;
  padding-bottom:            0;
  text-decoration:           underline;
  text-decoration-color:     transparent;
  text-decoration-thickness: 1px;
  text-underline-offset:     0.2em;
  transition:                text-decoration-color 300ms ease;
}
.content_article .ArticleBody .al-cards > div:hover .articleListTitle a {
  text-decoration-color: currentColor;
}

/* -------------------------------------------------------------------------
   40. v3.3  --  no company number, so no company-number line
   -------------------------------------------------------------------------
   Wight Dive School has no registered company or charity number, so the
   statutory line has nothing true to say. The value itself ("TBC") lives in
   the Web Office organisation config, which the connector does not expose;
   this hides the element so nothing renders regardless of that field.

   Hiding rather than emptying is deliberate: if the school ever registers,
   delete this rule and set the real number in the Web Office, and section
   37's roman treatment is still there waiting for it.

   The bottom bar lays its children out edge to edge, so with this third
   item gone the links block takes the right-hand slot, which is where the
   brief asks for it. If the links stay centred on review, the container is
   not space-between after all and one justify-content rule goes here. */
.site-footer #footer_charity {
  display: none;
}

/* -------------------------------------------------------------------------
   41. v3.4  --  a bigger logo
   -------------------------------------------------------------------------
   The lockup was sitting at roughly 56px tall in a header bar with room to
   spare, and the brand asked for more presence. Around a quarter bigger:
   enough to register, not enough to turn the header into a masthead.

   Height set on the image with width:auto so the lockup scales in
   proportion whatever the library set on it. Two sizes: 72px where the
   header has its full desktop height, and a modest 60px below the library's
   1400px nav breakpoint, where the bar slims down and a 72px logo would
   crowd the hamburger. */
.site-header__brand img {
  height: 60px;
  width:  auto;
}
@media (min-width: 1401px) {
  .site-header__brand img {
    height: 72px;
  }
}

/* -------------------------------------------------------------------------
   42. v3.5  --  the site-by credit, at full strength until hovered
   -------------------------------------------------------------------------
   The library ships the hubb.digital credit faded by default, brightening on
   hover, which is the conventional treatment for a builder's credit. The
   brief here is the opposite: show it at full strength and let the hover be
   the quiet state.

   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. The hover transition uses the same
   350ms ease as every other hover on the site. */
.site-footer__bottom a[href*="hubb.digital"],
.site-footer__bottom a[href*="hubb.digital"] img {
  opacity:    1;
  transition: opacity 350ms ease;
}
.site-footer__bottom a[href*="hubb.digital"]:hover,
.site-footer__bottom a[href*="hubb.digital"]:hover img {
  opacity: 0.6;
}

/* -------------------------------------------------------------------------
   43. v3.6  --  side cards were pinning behind the header
   -------------------------------------------------------------------------
   Section 15 set the side column's sticky offset to a hardcoded 84px, the
   height of the COMPACT header. But the header only compacts after
   max(200px, 40% of the viewport) of scroll, by design. In the zone between
   "scrolled a little" and "compacted", the header is still at full height,
   taller again since the v3.4 logo, so the cards pinned at 84px slid up
   behind it. Keep scrolling, the header compacts, 84px clears it, and the
   fault appears to fix itself, which is exactly how it presented.

   The library already publishes the cure: since v0.167, initStickyHeader
   sets --site-header-height on the root element to the header's real
   rendered height, re-measured on load, resize and each compaction toggle
   (immediately and again after the transition settles), precisely because a
   static offset let the shop sidebar do the same thing. The offset now
   reads that, with the old 84px kept as the no-JS fallback. */
.content-shell__side {
  top: calc(var(--site-header-height, 84px) + var(--space-4));
}

/* -------------------------------------------------------------------------
   44. v3.7  --  smooth header compaction
   -------------------------------------------------------------------------
   The compaction stuttered rather than eased. Two causes, one structural
   and one self-inflicted.

   Structural: the library transitions padding-block, height and font-size,
   all LAYOUT properties, on a position:sticky header that sits in the
   document flow. Every frame of that animation therefore re-lays-out the
   whole document and shifts all following content upward, while the main
   thread is also servicing the scroll. Browsers only animate transform and
   opacity on the compositor; a layout animation of the entire page is
   jank by construction, and no easing curve fixes it.

   Self-inflicted: v3.4 raised the resting logo to 72px, but the library's
   compact state hardcodes .site-logo img to 40px, so the animated height
   delta nearly doubled (roughly 32px to 56px) and the shrink became both
   longer and more violent.

   THE FIX. Desktop only, matching the library's own compaction scope:

   1. The header comes out of the flow (position:fixed). Its height change
      now lays out only the header's own subtree, a few dozen elements,
      which the browser does comfortably inside a frame. The document never
      reflows and the page content never lurches during the animation,
      which was most of the perceived bump.

   2. A body spacer keeps the content's starting offset constant at the
      EXPANDED header height, so nothing overlaps at the top of the page.
      120px = 72px logo (the row's tallest item) + 24px padding above and
      below. This constant must move if the resting logo size ever does;
      it cannot read --site-header-height, because that var tracks the
      live header and shrinks on compaction, which would bounce the page.
      Only the top of the page can tell: by the time the header compacts,
      the visitor is 200px+ down and the spacer is out of view.

   3. The compact logo comes up from the library's 40px to 56px, restoring
      the ~80% compaction ratio the library intended (48 to 40) at our
      larger resting size, so the shrink reads as a settle rather than a
      collapse. Same (0,3,1) specificity as the library's rule; this file
      loads later and wins.

   Mobile (below 1401px) is untouched: no compaction exists there, the
   header stays the library's sticky, and the panel and pill positioning
   assumptions all hold. */
@media (min-width: 1401px) {
  .site-header {
    position: fixed;
    top:      0;
    left:     0;
    right:    0;
  }
  body {
    padding-top: 120px;
  }
  .site-header.is-scrolled .site-header__brand img {
    height: 56px;
  }
}

/* -------------------------------------------------------------------------
   45. v3.8  --  focus rings, reduced-motion cover, header tidy
   -------------------------------------------------------------------------
   Three additions from the cross-site backport list, all additive: nothing
   here changes what a mouse visitor currently sees.

   FOCUS RINGS. Section 24 gave the form controls a focus-visible outline and
   nothing else on the site has one, so a keyboard user gets whatever each
   component happens to inherit, which on the pills is close to nothing. One
   treatment across every interactive element: the deepened ocean, which is
   4.07:1 on white and on brand. The hero call to action gets white instead,
   because it sits over photography where the ocean would vanish.

   No border-radius is set on these rules on purpose. An outline follows the
   element's own curvature in modern browsers, and asserting a radius on the
   focus state would visibly square the pills off at the exact moment a
   keyboard user reaches them.

   REDUCED MOTION. This library build already does the dangerous half: the
   reveal observer bails out under the preference and the library forces
   revealed elements visible, so nothing can fail to appear. What it does not
   cover is this skin's own transitions, the 350ms opacity hovers and the
   colour fades, so those are collapsed to near-zero here. Near-zero rather
   than none, so transitionend still fires for anything listening. The
   reveal force is deliberately NOT restated, because the library owns it on
   this build and duplicating it would be one more thing to keep in step.

   HELP AND LOW GRAPHICS. The menu module renders a second cell in its table
   row holding Help and Low Graphics links. Low Graphics belongs in the
   footer, and Help is a platform page no visitor is looking for. If the
   library on this build already hides the cell, this matches nothing and
   does nothing.
   ------------------------------------------------------------------------- */

/* Focus rings, everywhere a keyboard can land. */
.site-header--split  .header-giving:focus-visible,
.site-header--tworow .header-giving:focus-visible,
.section-link:focus-visible,
.side-card__cta:focus-visible,
.slice--welcome .ArticleBody p > a:focus-visible,
.slice--stats.slice--stats-reasons .ArticleBody > p > a:focus-visible,
.al-cards .articleListTitle a:focus-visible,
.al-cards-feature .articleListTitle a:focus-visible,
.content-shell__main .UML_AL_1 .articleListTitle a:focus-visible,
.content-shell__side .group_nav_selected > a:focus-visible,
.content-shell__side .group_nav_sibling > a:focus-visible,
.content-shell__side .group_nav_child > a:focus-visible,
.site-footer a:focus-visible,
.mobile-nav-link:focus-visible,
.mobile-nav-sublink:focus-visible,
.mobile-menu-toggle:focus-visible,
.mobile-menu-close:focus-visible,
.site-search-toggle:focus-visible {
  outline:        3px solid var(--wds-ocean-deep);
  outline-offset: 3px;
}

/* Over photography the ocean would vanish. */
.hero_slideshow .hero-caption__cta:focus-visible {
  outline:        3px solid #FFFFFF;
  outline-offset: 3px;
}

/* Reduced motion: collapse this skin's own transitions. The library already
   handles the reveal system on this build. */
@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;
  }
}

/* The Help and Low Graphics cell in the nav row. */
.site-header__nav table.menubardiv > tbody > tr > td + td {
  display: none;
}

/* -------------------------------------------------------------------------
   45. v3.8  --  mobile logo: correcting section 41's scope
   -------------------------------------------------------------------------
   Section 41 raised the logo to 60px with no media query, so it applied on
   phones too. The library sizes the mobile logo at 40px inside its
   max-width:720px block, but both rules land at the same specificity
   (0,1,1) and this file loads afterwards, so ours won even inside their
   media query. Media queries add no specificity, which is the trap here.

   Why that broke the header rather than just looking large: this lockup is
   the mark PLUS the wordmark PLUS the strapline, so it is roughly five
   times as wide as it is tall, about 300px at 60px tall. On a 375px phone,
   after gutters, that leaves nothing for the menu button, and because the
   library sets flex-shrink:0 on the brand ("never let brand compress when
   nav grows"), the row cannot compress: it overflows instead. Hence MENU
   sitting off the right edge and the page scrolling sideways.

   The fix restores a sane mobile size and makes it fluid rather than fixed,
   so the narrowest phones get a smaller lockup instead of a broken row:
   40px at 444px wide and above, scaling down to 32px on a 320px screen.
   Width stays auto, so the aspect ratio is never distorted. min-width:0 on
   the brand is a belt-and-braces flexbox guard: without it a flex item can
   refuse to shrink below its content size even when allowed to.

   Section 41's 60px therefore now serves the tablet range (721px to
   1400px), where the nav is already a hamburger and there is width to
   spare, and its 72px still serves full desktop. Left alone deliberately:
   no overflow-x:hidden on html or body. It would mask any future overflow
   rather than reveal it, and an overflow value on an ancestor breaks
   position:sticky, which is exactly how the header still behaves on
   mobile (the fixed-header treatment in section 44 is desktop-only). */
@media (max-width: 720px) {
  .site-header__brand img {
    height: clamp(32px, 9vw, 40px);
    width:  auto;
  }
  .site-header__brand {
    min-width: 0;
  }
}
