/* ---------------------------------------------------------------------------
 * public.css — defined visual style for tenant public pages.
 *
 * Layers a cohesive, editorial design language on top of the design-system
 * Bootstrap build. Everything here consumes the per-tenant theme.css custom
 * properties (--bs-primary, --hubbub-accent, fonts) so each tenant stays on
 * brand — we shape rhythm, elevation and component detail, not the palette.
 * ------------------------------------------------------------------------- */

:root {
  --bs-body-font-size: 1.0625rem;
}

body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* The tenant's own background colour (theme.css), over the DS base.css's fixed --surface-page. */
  background-color: var(--bs-body-bg);
}

/* --- Typography -------------------------------------------------------- */

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  letter-spacing: -0.018em;
  line-height: 1.12;
}

.display-1, .display-2, .display-3, .display-4, .display-5, .display-6 {
  letter-spacing: -0.03em;
  font-weight: 800;
  line-height: 1.04;
}

.lead {
  font-weight: 400;
  line-height: 1.55;
  color: var(--bs-secondary-color);
}

a {
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  transition: color 0.15s ease;
}

/* The design system drops link underlines, which leaves a link in running text told apart from the
   words around it by colour alone, at well under 3:1 (WCAG 1.4.1). Underline those. A classed link
   is a component (button, nav item, card title) and keeps its own styling. The line is faded so it
   reads as a hint rather than a rule (65% keeps it 3:1 against the background for any link colour),
   and goes solid on hover and focus. */
:where(p, li, dd, td, blockquote, figcaption, .cms-block-richtext) a:not([class]) {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 65%, transparent);
}
:where(p, li, dd, td, blockquote, figcaption, .cms-block-richtext) a:not([class]):is(:hover, :focus-visible) {
  text-decoration-color: currentColor;
}

/* --- Navbar ------------------------------------------------------------ */

.navbar {
  box-shadow: 0 1px 0 var(--bs-border-color);
  transition: box-shadow 0.2s ease;
}

/* A translucent bar over the page, for every nav but the default theme's solid brand-coloured one.
   !important to beat the banner theme's own bg-body utility. */
.navbar:not(.gv-nav) {
  backdrop-filter: saturate(1.4) blur(10px);
  background-color: rgba(var(--bs-body-bg-rgb, 255, 255, 255), 0.88) !important;
}

.navbar.sticky-top {
  box-shadow: var(--shadow-xs);
}

.navbar-brand {
  font-weight: 800;
  letter-spacing: -0.02em;
  font-size: 1.35rem;
}

.navbar .nav-link {
  font-weight: 500;
  position: relative;
  padding-block: 0.4rem;
}

/* Animated underline that grows from the centre on hover/active. Excludes
   dropdown toggles so Bootstrap's caret (its own ::after) stays intact, and the
   default theme's nav, which signals state with a wash instead. */
.navbar:not(.gv-nav) .nav-link:not(.dropdown-toggle)::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0.1rem;
  width: 0;
  height: 2px;
  border-radius: 2px;
  background-color: var(--hubbub-accent);
  transform: translateX(-50%);
  transition: width 0.22s ease;
}

.navbar:not(.gv-nav) .nav-link:not(.dropdown-toggle):hover::after,
.navbar:not(.gv-nav) .nav-link:not(.dropdown-toggle):focus-visible::after,
.navbar:not(.gv-nav) .nav-link:not(.dropdown-toggle).active::after {
  width: 70%;
}

/* Submenu disclosure toggle: restore a sensible hit area (the markup pins it
   to p-0) and align the caret with the adjacent link. */
.navbar .dropdown-toggle-split {
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.6rem !important;
  border-radius: var(--radius-sm);
}

.navbar .dropdown-toggle-split::after {
  margin: 0;
}

.navbar .dropdown-toggle-split:hover,
.navbar .dropdown-toggle-split:focus-visible {
  color: var(--hubbub-accent);
  background-color: rgba(var(--hubbub-accent-rgb, var(--bs-primary-rgb)), 0.1);
}

.navbar .dropdown-menu {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 0.5rem;
  margin-top: 0.5rem;
}

.navbar .dropdown-item {
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.75rem;
  font-weight: 500;
}

/* --- Buttons ----------------------------------------------------------- */

.btn {
  letter-spacing: 0.005em;
  padding-inline: 1.15rem;
  transition: transform 0.12s ease, box-shadow 0.18s ease, background-color 0.15s ease, border-color 0.15s ease;
}

.btn-lg {
  padding: 0.8rem 1.6rem;
  font-size: 1.05rem;
}

.btn:active {
  transform: translateY(0);
}

/* base.css's global `a:hover { text-decoration: underline }` (feat-083) outranks
   Bootstrap's own un-hovered rules by specificity, so a CTA or nav item
   rendered as an `<a>` (most of them, across every page) gained an underline
   on hover. Buttons and nav links never underline, link or not — matches the
   design system's own Navbar, which sets text-decoration:none unconditionally. */
.btn:hover,
.btn:focus,
.nav-link:hover,
.nav-link:focus {
  text-decoration: none;
}

.btn-primary {
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.12);
}

.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}

/* Brand buttons — token-driven pills usable anywhere (nav CTAs, hero, cards).
   They take the design system's ink (--on-primary flips to near-black on a light
   brand) rather than white, which fails 4.5:1 on a light brand fill. */
.btn-brand {
  --bs-btn-color: var(--on-primary);
  --bs-btn-bg: var(--color-primary);
  --bs-btn-border-color: var(--color-primary);
  --bs-btn-hover-color: var(--on-primary);
  --bs-btn-hover-bg: var(--color-primary-hover);
  --bs-btn-hover-border-color: var(--color-primary-hover);
  --bs-btn-active-color: var(--on-primary);
  --bs-btn-active-bg: var(--color-primary-active);
  --bs-btn-active-border-color: var(--color-primary-active);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
  box-shadow: inset 0 0 0 1px var(--fill-edge-primary);
}
/* The hover and active shades are darker than the fill, so on a light brand the near-black ink
   drops under 4.5:1 on them. Flip each state's ink at the design system's own 0.57 split. */
@supports (color: oklch(from red l c h)) {
  .btn-brand {
    --bs-btn-hover-color: oklch(from var(--color-primary-hover) clamp(0, (0.57 - l) * 1000, 1) 0 h);
    --bs-btn-active-color: oklch(from var(--color-primary-active) clamp(0, (0.57 - l) * 1000, 1) 0 h);
  }
}

/* The brand fill is kept light enough to sit on a white card, which is too light for text on one.
   Brand-coloured text caps the fill's lightness instead: a dark brand passes as it is and keeps its
   exact colour, a light one is darkened only as far as 4.5:1 needs. The design system's link shade
   is the fallback, which darkens every brand. The vendored outline button sets the fill colour, so it
   is overridden here rather than edited in design-system/. */
:root { --brand-text: var(--text-link); }
@supports (color: oklch(from red l c h)) {
  :root { --brand-text: oklch(from var(--color-primary) min(l, 0.48) c h); }
}
.btn-outline-primary { --bs-btn-color: var(--brand-text); }

/* The vendored muted text (--neutral-500, #7c746a) is 4.37:1 on the page and 4.0:1 on the donor map's
   card. This step between neutral-500 and -600 clears 4.5:1 on every surface muted text sits on
   (card, page, subtle, map card) while staying lighter than body text. Drop it once the vendored
   design system ships an AA-passing muted text. */
:root { --text-muted: #6f675d; }

.btn-ghost-brand {
  --bs-btn-color: var(--bs-secondary);
  --bs-btn-border-color: color-mix(in srgb, var(--bs-secondary) 35%, transparent);
  --bs-btn-bg: transparent;
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--bs-secondary);
  --bs-btn-hover-border-color: var(--bs-secondary);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--bs-secondary);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

/* --- Cards ------------------------------------------------------------- */

.card {
  --bs-card-inner-border-radius: calc(var(--radius-lg) - 1px);
  overflow: hidden;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.card-img-top {
  transition: transform 0.4s ease;
}

/* Interactive cards (project / cause tiles) lift on hover. */
a.card,
.card-hover {
  text-decoration: none;
  color: inherit;
}

a.card:hover,
.card-hover:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

a.card:hover .card-img-top,
.card-hover:hover .card-img-top {
  transform: scale(1.04);
}

.card-title {
  font-weight: 700;
}

/* --- Forms ------------------------------------------------------------- */

/* The design system's FormField label. bootstrap-forms renders .col-form-label, which would otherwise
   be body-size with input-height padding above it. */
.form-label,
.col-form-label {
  font-weight: 600;
  font-size: 0.875rem;
  letter-spacing: 0.01em;
  margin-bottom: 0.4rem;
  color: var(--bs-emphasis-color);
}

.col-form-label {
  padding-block: 0 0.4rem;
}

.form-control,
.form-select {
  padding: 0.6rem 0.85rem;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* A button in a row of form fields (a search or filter bar): the fields' vertical padding and the
   bridge's 1.5px border, so the button and the fields beside it are the same height. */
.btn-field {
  --bs-btn-padding-y: 0.6rem;
  --bs-btn-border-width: 1.5px;
}

/* The shorthand above would otherwise take back the right padding Bootstrap reserves for the
   select's arrow, and a longer option runs underneath it. */
.form-select {
  padding-right: 2.25rem;
}

/* feat-083: .form-control:focus/.form-select:focus's border-color + box-shadow
   are now the bridge's shared focus-ring rule too — see the note on
   .btn:focus-visible above. */

/* --- Progress & badges (donation / matched-funding patterns) ---------- */

.badge {
  letter-spacing: 0.02em;
  padding: 0.45em 0.7em;
}

/* --- CMS blocks -------------------------------------------------------- */

.cms-page {
  padding-block: clamp(2rem, 4vw, 3.5rem);
}

.cms-page > h1 {
  font-size: clamp(2.25rem, 5vw, 3.25rem);
  margin-bottom: 1.5rem;
}

/* Vertical rhythm between stacked content blocks. */
.cms-block + .cms-block {
  margin-top: clamp(3rem, 6vw, 6rem);
}

/* Keep long-form prose to a comfortable reading measure. */
.cms-block-richtext {
  max-width: 68ch;
  font-size: 1.075rem;
  line-height: 1.7;
}

.cms-block-richtext h2,
.cms-block-richtext h3 {
  margin-top: 2rem;
}

.cms-block-richtext img {
  border-radius: var(--radius-lg);
}

/* A challenge card's rich-text description: no trailing gap above the progress bar. */
.challenge-blurb > :last-child {
  margin-bottom: 0;
}

/* Rich-text quotes are stored with Bootstrap's .blockquote, which sets no padding. */
.blockquote {
  padding: 0.78125rem 1.5625rem;
}

/* Hero — full-bleed image with a legibility scrim and centred content. */
.cms-block-hero {
  position: relative;
  display: flex;
  align-items: center;
  min-height: clamp(22rem, 52vh, 34rem);
  border-radius: var(--radius-xl);
  overflow: hidden;
  color: #fff;
  isolation: isolate;
}

.cms-block-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    105deg,
    rgba(var(--bs-primary-rgb), 0.82) 0%,
    rgba(16, 24, 40, 0.55) 55%,
    rgba(16, 24, 40, 0.25) 100%
  );
}

.cms-block-hero .container {
  position: relative;
  z-index: 1;
}

.cms-block-hero h1 {
  font-size: clamp(2.5rem, 6vw, 4rem);
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.35);
  max-width: 16ch;
}

.cms-block-hero .lead {
  color: rgba(255, 255, 255, 0.92);
  max-width: 46ch;
  font-size: clamp(1.1rem, 2vw, 1.4rem);
}

.cms-block-hero .btn-primary {
  --bs-btn-bg: #fff;
  --bs-btn-border-color: #fff;
  --bs-btn-color: var(--bs-primary);
  --bs-btn-hover-bg: rgba(255, 255, 255, 0.9);
  --bs-btn-hover-border-color: rgba(255, 255, 255, 0.9);
  --bs-btn-hover-color: var(--bs-primary);
  margin-top: 1.5rem;
}

/* Image blocks. */
.cms-block-image img,
.cms-block-image picture {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
}

.cms-block-image figcaption,
.cms-block-gallery figcaption {
  margin-top: 0.6rem;
  font-size: 0.875rem;
  color: var(--bs-secondary-color);
}

.cms-block-gallery img {
  border-radius: var(--radius-lg);
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.cms-block-gallery > h2 {
  margin-bottom: 1.5rem;
}

/* --- Header / announcement (banner theme) ------------------------------ */

.text-bg-primary.py-2 {
  letter-spacing: 0.02em;
}

/* --- Footer ------------------------------------------------------------ */

.footer {
  --bs-link-color-rgb: var(--bs-secondary-color-rgb, 108, 117, 125);
}

.footer a {
  color: var(--bs-secondary-color);
  text-decoration: none;
  font-weight: 500;
}

.footer a:hover {
  color: var(--bs-emphasis-color);
  text-decoration: underline;
}

.footer .list-inline-item a {
  font-weight: 600;
}

/* --- Auth / entrance cards --------------------------------------------- */

.cover-container .card {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
}

.cover-container .card-body {
  padding: clamp(1.5rem, 4vw, 2.5rem);
}

/* --- Default theme: editorial nav + footer ----------------------------- */
/* Scoped classes used only by the `default` theme base.html, so they layer on
   the brand tokens without affecting other themes or the dashboard chrome. */

/* feat-084: matches the design system's Navbar `tone="primary"` exactly — a
   brand-coloured bar with white text/logo, used on every one of its templates
   (homepage, challenges, pledge, project), not just an editorial white strip. */
/* A solid brand-coloured bar (DS Navbar tone="primary"). Everything on it is drawn in --on-primary, the
   design system's readable ink for the tenant's primary: white on a dark primary, near-black on a light
   one. The DS's own primary tone hardcodes white, which fails AA on a light primary. */
.gv-nav {
  min-height: 72px;
  background: var(--color-primary);
}

/* The mobile toggler's bars, painted in --on-primary through Bootstrap's icon as a mask: a data-URI
   image cannot take a custom property, and Bootstrap's default dark-grey bars all but vanish. */
.gv-nav .navbar-toggler-icon {
  background: var(--on-primary);
  mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23000' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") center / 100% no-repeat;
}

.gv-nav .navbar-brand {
  font-family: var(--font-heading, inherit);
  font-weight: 700;
  font-size: 1.18rem;
  color: var(--on-primary);
}

/* Bootstrap zeroes a link's inline padding until the nav expands at lg, so in the mobile menu the
   hover box hugged the label. From lg the expanded nav's own padding takes over. */
.gv-nav .navbar-nav {
  --bs-nav-link-padding-x: 0.75rem;
}

.gv-nav .nav-link {
  color: var(--on-primary);
  border-radius: var(--radius-sm, 0.375rem);
  transition: background-color 0.15s ease, color 0.15s ease;
}

/* DS primary-tone nav hover: a faint wash of the ink behind the link — the colour itself does not
   change, so the wash is what marks it. */
.gv-nav .nav-link:hover,
.gv-nav .nav-link.active {
  color: var(--on-primary);
  background-color: color-mix(in oklab, var(--on-primary), transparent 86%);
}

/* The Donate CTAs on the brand bar and the hero are one white pill lettered in the brand. A brand-filled
   one vanished into either: the bar is the brand colour, and the hero's overlay darkens the brand or
   photo behind it (a deep plum's button was 1.15:1). The label is what identifies the button, and
   --brand-text is at least 4.5:1 on white for any brand. The ring takes whatever contrasts with what is
   behind it: the bar's own ink, or white on the always-dark hero. */
.gv-nav .btn-brand,
.gv-hero .btn-brand {
  --bs-btn-bg: #fff;
  --bs-btn-border-color: #fff;
  --bs-btn-color: var(--brand-text);
  --bs-btn-hover-bg: var(--neutral-50);
  --bs-btn-hover-border-color: var(--neutral-50);
  --bs-btn-hover-color: var(--brand-text);
  --bs-btn-active-bg: var(--neutral-50);
  --bs-btn-active-border-color: var(--neutral-50);
  --bs-btn-active-color: var(--brand-text);
  box-shadow: none;
}
/* White to off-white barely moves, so hover lifts instead, as .btn-primary's does. */
.gv-nav .btn-brand:hover,
.gv-hero .btn-brand:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.gv-nav .btn-brand:focus-visible { box-shadow: none; outline: 3px solid var(--on-primary); outline-offset: 3px; }
.gv-hero .btn-brand:focus-visible { box-shadow: none; outline: 3px solid #fff; outline-offset: 3px; }

.gv-nav__crest {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: color-mix(in oklab, var(--on-primary), transparent 85%);
  color: var(--on-primary);
  font-family: var(--font-heading, inherit);
  font-weight: 700;
}

.gv-nav__logo {
  max-height: 40px;
  width: auto;
}

/* feat-084: matches the design system's Footer exactly — a fixed near-black
   (--surface-inverse), not a tenant-tinted dark. */
.gv-footer {
  /* The secondary at a fixed high lightness, keeping its hue: plain --color-secondary is a fill colour
     and can be dark (an indigo was unreadable on the near-black footer). At L 0.82 any hue clears AA
     against --surface-inverse. Browsers without relative colour syntax get white. */
  --gv-footer-highlight: #fff;
  background: var(--surface-inverse, #212529);
  color: var(--text-on-inverse, #f8f9fa);
}

@supports (color: oklch(from red l c h)) {
  .gv-footer {
    --gv-footer-highlight: oklch(from var(--color-secondary) 0.82 c h);
  }
}

/* Flex, not a Bootstrap grid split: the brand block grows to fill the row
   (pushing columns to the right edge), rather than columns centring inside a
   fixed 8-col half. */
.gv-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem 3rem;
  align-items: flex-start;
}
.gv-footer__brand {
  flex: 1 1 280px;
  min-width: 240px;
}
.gv-footer__cols {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem 3rem;
  /* No shrink: the group only sits beside the brand at its full width, else the whole group wraps
     beneath it. max-width lets it wrap internally once it has a row of its own on a narrow screen. */
  flex: 0 0 auto;
  max-width: 100%;
}
.gv-footer__col {
  flex: 0 0 auto;
  min-width: 140px;
}

.gv-footer__name {
  font-family: var(--font-heading, inherit);
  font-size: 1.4rem;
  color: #fff;
}

.gv-footer__col h4 {
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide, 0.12em);
  font-size: var(--text-xs, 0.76rem);
  font-weight: var(--weight-bold, 700);
  color: color-mix(in oklab, var(--text-on-inverse, #fff), transparent 45%);
}

.gv-footer__col h4 a {
  color: inherit;
}

/* The footer reuses the header's logo, which the tenant chose to sit on their brand-coloured bar; on the
   near-black footer a dark logo vanished. Panelling it in the bar's colour shows it on the background it
   was made for, whatever its colours. */
.gv-footer__logo {
  max-height: 56px;
  width: auto;
  box-sizing: content-box;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-primary);
}

.gv-footer a {
  color: var(--text-on-inverse, #fff);
  text-decoration: none;
  font-weight: 500;
  transition: color 0.2s ease;
}

.gv-footer a:hover {
  color: var(--gv-footer-highlight);
}

.gv-footer__social {
  list-style: none;
}

/* Opens the cookie preferences modal. Sits below the footer, outside its theme block, so it is
   styled for the page background rather than the footer's. */
.cookie-prefs__button {
  background: none;
  border: 0;
  padding: 0;
  min-height: 24px;
  color: var(--bs-secondary-color);
  text-decoration: underline;
}

.cookie-prefs__button:hover {
  color: var(--bs-body-color);
}

.cookie-prefs__button:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.gv-footer__bar {
  border-top: 1px solid color-mix(in oklab, var(--text-on-inverse, #fff), transparent 82%);
  color: color-mix(in oklab, var(--text-on-inverse, #fff), transparent 35%);
}

.gv-footer__hubbub {
  color: inherit;
  font-weight: 600;
}

/* The wordmark sits in the line of text as the word it replaces. */
.gv-footer__hubbub-logo {
  height: 1.25em;
  width: auto;
  vertical-align: -0.2em;
}

/* --- Shared payment page (feat-031) + gift summary (feat-075) ----------- */
/* Moved here from payment_page.html's inline <style>: the two-column layout and
   the mobile bar are past what an inline block should carry. */

:root {
  /* How far down the viewport a sticky element must start to clear the theme's
     navbar. Only the `default` theme sticks its nav; `banner` does not, so this
     cannot be a constant. */
  --gp-sticky-offset: 0px;
  /* Height of the mobile gift bar, measured by payment_page.js. The fallback
     covers the collapsed bar if the script never runs. */
  --gp-gift-bar-height: 8rem;
}

body:has(.navbar.sticky-top) {
  --gp-sticky-offset: 72px;
}

.payment {
  padding-block: 2rem;
}

/* The design system's pledge page: each block a Card, its legend a numbered SectionHeading. A CSS
   counter numbers them, so a block the page leaves out or hides (`hidden` is display:none, which
   does not count) never leaves a gap in the sequence. */
#payment-form {
  counter-reset: payment-block;
}

.payment-block {
  counter-increment: payment-block;
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-6);
  margin-bottom: 1.75rem;
}

.payment-block > legend {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 1rem;
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--text-strong);
}

.payment-block > legend::before {
  content: counter(payment-block);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-pill);
  background: var(--color-primary-subtle);
  color: var(--color-primary-active);
  font-size: var(--text-sm);
}

/* The design system's GiftAidField header: the Gift Aid mark, the offer, and the uplift on the right. */
.gift-aid-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 1rem;
  padding: 0.85rem 1rem;
  border-radius: var(--radius-md);
  background: var(--success-surface);
  border: 1px solid color-mix(in oklab, var(--success), transparent 82%);
}

.gift-aid-head__mark {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  background: var(--success);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  font-size: 10px;
  line-height: 1.05;
  text-align: center;
  text-transform: uppercase;
}

.gift-aid-head__text {
  flex: 1;
  min-width: 0;
}

.gift-aid-head__title {
  margin: 0;
  font-weight: var(--weight-semibold);
  color: var(--success);
  line-height: 1.2;
}

.gift-aid-head__sub {
  margin: 0.2rem 0 0;
  font-size: var(--text-sm);
  color: var(--text-body);
}

.gift-aid-head__uplift {
  flex: 0 0 auto;
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  color: var(--success);
  white-space: nowrap;
}

/* The design system's two-up rows: first and last name, and the short address lines. bootstrap-forms
   renders one .row per field, so they are paired by field name; everything else spans the block. */
@media (min-width: 576px) {
  .payment-block,
  [id^="address-components-"] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 1rem;
  }

  .payment-block > *,
  [id^="address-components-"] > * {
    grid-column: 1 / -1;
  }

  .payment-block .row:has(:is([name="first_name"], [name="last_name"], [name$="_locality"], [name$="_administrative_area"], [name$="_postal_code"])) {
    grid-column: auto;
  }
}

.amount-presets {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* The design system's AmountButton. payment_page.js presses the chip matching the amount field. */
.amount-preset {
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  color: var(--text-strong);
  background: var(--surface-card);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-md);
  min-width: 84px;
  padding: 0.75rem 1rem;
  transition: border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.amount-preset:hover {
  border-color: var(--color-primary);
  color: var(--brand-text);
}
.amount-preset:focus-visible {
  outline: none;
  box-shadow: var(--shadow-ring);
}
.amount-preset[aria-pressed="true"] {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--on-primary);
  box-shadow: inset 0 0 0 1px var(--fill-edge-primary);
}

/* The tip options reuse .amount-presets so they sit on the same grid as the amount shortcuts above.
   They are radios behind Bootstrap's .btn-check, so the checked and focus-visible states come from
   the design-system button styles — nothing here may suppress the focus ring. */
.tip-block__ask {
  margin-bottom: 0.5rem;
}

.tip-block__cash {
  margin-left: 0.35rem;
  font-variant-numeric: tabular-nums;
  /* Deliberately NOT dimmed. An earlier opacity: 0.75 here dropped this to 3.13:1 against the
     button and failed WCAG AA — caught by the live-server axe scan, which is the only one that
     loads theme CSS. It is also the wrong instinct: this figure is what the option actually costs
     and is part of the option's accessible name, so it is content, not decoration. */
}

.tip-block__cash:empty {
  margin-left: 0;
}

.tip-block__note {
  font-size: 0.875rem;
  margin-bottom: 0;
}

#payment-error {
  color: var(--bs-danger, #dc3545);
  font-size: 0.875rem;
  margin-top: 0.5rem;
}

.gift-summary {
  position: sticky;
  top: calc(var(--gp-sticky-offset) + 1rem);
  background-color: var(--surface-card);
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: var(--space-6);
}

.gift-summary__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}

.gift-summary__title {
  font-size: var(--text-xl);
  margin: 0 0 1rem;
}

.gift-summary__toggle {
  display: none;
}

.gift-summary__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.gift-summary__label {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.gift-summary__figure {
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  font-size: var(--text-md);
  color: var(--text-strong);
  text-align: right;
}

.gift-summary__rule {
  border-top: 1px solid var(--bs-border-color, #dee2e6);
  opacity: 1;
  margin: 0.875rem 0;
}

.gift-summary__row--total {
  margin-bottom: 0;
}

.gift-summary__row--total .gift-summary__label {
  font-size: var(--text-base);
}

.gift-summary__figure--total {
  font-size: var(--text-2xl);
}

/* The prompt before there is an amount: body text, so it reads as guidance rather than a figure. */
.gift-summary__figure--total.gift-summary__figure--empty {
  font-family: var(--font-body);
  font-weight: var(--weight-regular);
  font-size: var(--text-base);
  color: var(--text-muted);
}

.gift-summary__uplift {
  margin-top: 0.875rem;
}

.gift-summary__figure--uplift {
  color: var(--brand-text);
}

.gift-summary__match-note {
  margin: 0.25rem 0 0;
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.gift-summary__submit {
  margin-top: 1.25rem;
}

.gift-summary__submit[aria-disabled="true"] {
  opacity: 0.65;
}

.gift-summary__hint {
  color: var(--bs-secondary-color, #6c757d);
  font-size: 0.8125rem;
  margin: 0.5rem 0 0;
}

.gift-summary__secure {
  color: var(--bs-secondary-color, #6c757d);
  font-size: 0.75rem;
  text-align: center;
  margin: 0.75rem 0 0;
}

/* Below lg the panel becomes a bar fixed to the bottom of the viewport, carrying
   the total and the Give button, with the rows behind a disclosure. */
@media (max-width: 991.98px) {
  /* The bar covers the bottom of the viewport, so the page ends with room for it. The room goes after
     the Cookie preferences button, which every theme base renders last and outside the footer block,
     so it scrolls clear of the bar whether or not a tenant has hidden the footer. */
  body:has(.gift-summary) .cookie-prefs::after {
    content: "";
    display: block;
    height: var(--gp-gift-bar-height);
  }

  .gift-summary {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    top: auto;
    z-index: 1030;
    border-inline: 0;
    border-bottom: 0;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: var(--shadow-lg);
    padding: 0.75rem 1rem 1rem;
  }

  .gift-summary__title {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .gift-summary__toggle {
    display: block;
    width: 100%;
    text-align: start;
    background: none;
    border: 0;
    padding: 0 0 0.5rem;
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.8125rem;
  }

  .gift-summary__toggle::after {
    content: " \25BE";
  }

  .gift-summary[data-expanded="true"] .gift-summary__toggle::after {
    content: " \25B4";
  }

  .gift-summary:not([data-expanded="true"]) .gift-summary__rows,
  .gift-summary:not([data-expanded="true"]) .gift-summary__uplift {
    display: none;
  }

  .gift-summary__submit {
    margin-top: 0.75rem;
  }

  .gift-summary__secure {
    display: none;
  }

  /* feat-028's consent modal sits bottom-left. The bar stands down until it is
     answered rather than fighting it for the same corner. */
  html.show--consent .gift-summary {
    display: none;
  }

  html.show--consent .cookie-prefs::after {
    content: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}

/* ---- Thank-you page (feat-085) — shared by every product. A full-bleed hero in the primary, the
   facts card lifted over its bottom edge, then the module's content in the same column. The hero is
   drawn in --on-primary because its ground IS the primary; the soft glows are decoration only. ---- */
.thank-you { --ty-overlap: clamp(3.5rem, 8vw, 5rem); padding: 0; }
.thank-you-hero {
  position: relative; isolation: isolate; overflow: hidden; text-align: center;
  padding: clamp(3rem, 8vw, 5rem) 16px calc(var(--ty-overlap) + clamp(2rem, 5vw, 3rem));
  background: var(--color-primary); color: var(--on-primary);
}
.thank-you-hero::before, .thank-you-hero::after {
  content: ""; position: absolute; z-index: -1; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, color-mix(in oklab, var(--color-secondary), transparent 55%), transparent 70%);
}
.thank-you-hero::before { width: 34rem; height: 34rem; top: -16rem; left: -10rem; }
.thank-you-hero::after {
  width: 28rem; height: 28rem; bottom: -14rem; right: -8rem;
  background: radial-gradient(circle, color-mix(in oklab, var(--on-primary), transparent 86%), transparent 70%);
}
.thank-you-hero__inner { max-width: 40rem; margin-inline: auto; }
.thank-you-hero__badge {
  position: relative; display: inline-grid; place-items: center; width: 6rem; height: 6rem;
  margin-bottom: var(--space-4); border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--on-primary), transparent 86%);
  box-shadow: 0 0 0 10px color-mix(in oklab, var(--on-primary), transparent 93%);
  animation: ty-pop .5s var(--ease-out) both;
}
.thank-you-hero__ring { stroke-dasharray: 145; stroke-dashoffset: 145; animation: ty-draw .6s var(--ease-out) .15s forwards; }
.thank-you-hero__tick { stroke-dasharray: 32; stroke-dashoffset: 32; animation: ty-draw .35s var(--ease-out) .6s forwards; }
.thank-you-hero__spark {
  position: absolute; width: 8px; height: 8px; border-radius: var(--radius-pill);
  background: var(--color-secondary); box-shadow: 0 0 0 2px color-mix(in oklab, var(--on-primary), transparent 60%);
  opacity: 0; animation: ty-spark .7s var(--ease-out) .55s forwards;
}
.thank-you-hero__spark:nth-child(1) { --x: -3.6rem; --y: -1.6rem; }
.thank-you-hero__spark:nth-child(2) { --x: 3.8rem; --y: -2.2rem; width: 6px; height: 6px; }
.thank-you-hero__spark:nth-child(3) { --x: -4.2rem; --y: 1.8rem; width: 5px; height: 5px; }
.thank-you-hero__spark:nth-child(4) { --x: 4.1rem; --y: 1.5rem; }
.thank-you-hero__spark:nth-child(5) { --x: -1.2rem; --y: -4rem; width: 5px; height: 5px; }
.thank-you-hero__spark:nth-child(6) { --x: 1.6rem; --y: 3.9rem; width: 6px; height: 6px; }
@keyframes ty-pop { from { transform: scale(.6); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes ty-draw { to { stroke-dashoffset: 0; } }
@keyframes ty-spark {
  from { opacity: 0; transform: translate(0, 0) scale(.3); }
  to { opacity: 1; transform: translate(var(--x), var(--y)) scale(1); }
}
.thank-you-hero__title {
  margin: 0; font-family: var(--font-heading); font-weight: var(--weight-bold);
  font-size: clamp(2.5rem, 6vw, 3.75rem); line-height: 1.05; letter-spacing: var(--tracking-tight); color: inherit;
}
.thank-you-hero__lead { margin: var(--space-3) auto 0; max-width: 32rem; font-size: var(--text-lg); opacity: .92; }
.thank-you-hero__lead strong { opacity: 1; font-weight: var(--weight-bold); }

.thank-you__body {
  position: relative; max-width: 40rem; margin: calc(-1 * var(--ty-overlap)) auto 0;
  padding: 0 16px var(--space-8);
}
.thank-you__body > * + * { margin-top: var(--space-5); }
.thank-you-card { padding: var(--space-6); border-radius: var(--radius-xl); }
.thank-you__body > .thank-you-card:first-child { box-shadow: var(--shadow-lg); }
.thank-you-card > * + * { margin-top: var(--space-4); }
.thank-you-card__title {
  margin: 0; font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-muted);
}
.thank-you-card__title-name {
  margin-right: .15em; font-family: var(--font-heading); font-size: var(--text-xl); font-weight: var(--weight-bold);
  letter-spacing: normal; text-transform: none; color: var(--text-strong);
}
a.thank-you-card__title-name { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .15em; }
a.thank-you-card__title-name:hover { text-decoration-thickness: 2px; }
.thank-you-card__lead { margin: 0; font-size: var(--text-md); color: var(--text-strong); }
.thank-you-card__note { margin-bottom: 0; font-size: var(--text-sm); color: var(--text-muted); }
.thank-you-card .alert { margin-bottom: 0; }
.thank-you-facts { margin: 0; }
.thank-you-facts > div {
  display: flex; justify-content: space-between; gap: var(--space-4);
  padding-block: var(--space-3); border-bottom: 1px solid var(--border);
}
.thank-you-facts > div:last-child { border-bottom: 0; padding-bottom: 0; }
.thank-you-facts dt { font-weight: var(--weight-regular); color: var(--text-muted); }
.thank-you-facts dd { margin: 0; text-align: right; font-weight: var(--weight-semibold); color: var(--text-strong); overflow-wrap: anywhere; }
.thank-you-gift-aid { margin-bottom: 0; }
.thank-you-gift-aid__text { margin: 0; font-size: var(--text-base); }
@media (max-width: 36rem) {
  .thank-you-card { padding: var(--space-5); }
  /* The sentence already carries the figure; on a phone the repeat only squeezes it. */
  .thank-you-gift-aid .gift-aid-head__uplift { display: none; }
  /* The badge becomes the box's header strip rather than a pill floating above the text. */
  .gv-match.thank-you-match { flex-direction: column; gap: 0; padding: 0; overflow: hidden; }
  .thank-you-match .gv-match__badge { align-self: stretch; border-radius: 0; padding: var(--space-2) var(--space-4); }
  .thank-you-match .gv-match__title { padding: var(--space-3) var(--space-4); }
}

/* Module pieces any product can use in its slot. */
.thank-you-progress__stats { display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); gap: var(--space-6) var(--space-8); }
.thank-you-progress__value {
  margin: 0; font-family: var(--font-heading); font-size: clamp(1.75rem, 7vw, var(--text-3xl)); font-weight: var(--weight-bold);
  line-height: 1; letter-spacing: var(--tracking-tight); color: var(--brand-text);
}
.thank-you-progress__label {
  margin: var(--space-1) 0 0; font-size: var(--text-sm); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-muted);
}
.thank-you-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.thank-you-actions .btn { flex: 1 1 14rem; }
.gv-match.thank-you-match { align-items: flex-start; }
.thank-you-match .gv-match__title { font-weight: var(--weight-regular); }

.thank-you-share {
  padding: clamp(3rem, 7vw, 4.5rem) 16px; text-align: center;
  background: var(--color-primary-subtle, var(--primary-50)); border-top: 1px solid var(--border-brand);
}
.thank-you-share__inner { max-width: 40rem; margin-inline: auto; }
.thank-you-share__title { margin: 0; font-family: var(--font-heading); font-size: var(--text-2xl); }
.thank-you-share__lead { margin: var(--space-2) 0 var(--space-5); color: var(--text-muted); }
.thank-you-share [data-share] { justify-content: center; }

@media (prefers-reduced-motion: reduce) {
  .thank-you-hero__badge, .thank-you-hero__spark { animation: none; opacity: 1; }
  .thank-you-hero__spark { transform: translate(var(--x), var(--y)); }
  .thank-you-hero__ring, .thank-you-hero__tick { animation: none; stroke-dashoffset: 0; }
}

/* Pagination in the brand rather than the design system's default indigo (donor wall, project picker). */
.pagination {
  --bs-pagination-color: var(--brand-text);
  --bs-pagination-hover-color: var(--brand-text);
  --bs-pagination-focus-color: var(--brand-text);
  --bs-pagination-active-bg: var(--color-primary);
  --bs-pagination-active-border-color: var(--color-primary);
  --bs-pagination-active-color: var(--on-primary);
}
