/* ===========================================================================
 * giving-day.css — visual layer for the Giving Day module.
 *
 * Loaded only by giving pages (see themes/default/giving/page.html and the
 * giving_day module's own templates), never the global shell. Everything is
 * scoped under `.gv-page` so it cannot leak into other public pages or themes.
 *
 * feat-084: rewritten to use the design system's actual component recipes
 * (colors.css / spacing.css / typography.css, vendored under
 * themes/static/themes/design-system/ by feat-083) rather than bespoke
 * pre-design-system values. Structural things (shadows, borders, muted text)
 * now use the design system's fixed neutrals; only genuine brand/accent
 * moments (buttons, badges, the hero, decorative glyphs) use the brand
 * colors. Everything still derives from --brand-primary/--brand-secondary
 * (branding/css.py, per-tenant) and --hubbub-heading-font/--bs-body-font-family
 * (also per-tenant) — dashboard customisation is unaffected; only the fixed
 * shape/neutral layer changed.
 * ------------------------------------------------------------------------- */

.gv-page {
  /* A tiny warm-neutral wash, not the tenant's raw background colour: cards
     (--surface-card, always a fixed white) need to read as lifted off the
     page even when the tenant's own background_colour is plain white too —
     otherwise card and page are the same white and the card shadow is the
     only separation. Derived from the tenant colour, not a fixed value, so a
     tenant with a non-white background still sees their own colour, just
     very slightly deepened. */
  background: color-mix(in oklab, var(--bs-body-bg, #fff), #71717a 4%);
}

/* ---- Button flourishes (colours come from public.css .btn-brand) -------- */
.gv-page .btn-brand,
.gv-page .btn-ghost-brand {
  transition: transform var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.gv-page .btn-brand:hover,
.gv-page .btn-ghost-brand:hover { transform: translateY(-2px); }

/* ---- Progress bars — the design system's ProgressMeter, value for value:
   14px track (20px at --lg), gradient fill with an 8px floor and the
   brand-edge inset, and the "onbrand" tone used on the hero's dark band. --- */
.gv-page .progress { --bs-progress-height: 14px; }
.gv-page .progress-bar {
  width: 0;
  min-width: 8px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--color-primary), color-mix(in oklab, var(--color-primary), var(--color-secondary) 40%));
  box-shadow: inset 0 0 0 1px var(--fill-edge-primary);
  transition: width var(--dur-slow) var(--ease-out);
}
.gv-page .progress-bar.is-filled { width: var(--to, 0); }
.gv-page .progress--lg { --bs-progress-height: 20px; }
/* On the hero's always-dark band a brand fill ran to 1.1:1 for a dark brand and 2.4:1 for a light one
   against a lightened track. The track darkens the hero instead, and the fill is the brand at a fixed
   light lightness with its own hue and saturation, so it stays vivid and clears 3:1 whatever the brand
   or photo. The palest tint is the fallback where relative colour is missing. */
.gv-page .progress--onbrand { --bs-progress-bg: color-mix(in oklab, #000, transparent 50%); }
.gv-page .progress--onbrand .progress-bar { background: var(--primary-100); box-shadow: none; }
@supports (color: oklch(from red l c h)) {
  .gv-page .progress--onbrand .progress-bar { background: oklch(from var(--color-primary) 0.68 c h); }
}

/* The design system's ProgressMeter head, in its lg + onbrand form (the hero's). */
.gv-meter__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .5rem; }
.gv-meter__raised { font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: var(--text-3xl); line-height: 1; color: #fff; }
.gv-meter__goal { font-size: var(--text-sm); color: color-mix(in oklab, #fff, transparent 28%); }
.gv-meter__gift-aid { display: inline-block; margin-left: .75rem; vertical-align: .35em; }
.gv-meter__pct { font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: var(--text-lg); color: #fff; }

/* The design system's IconButton, "outline" variant — a 40px circle, not a
   text-label pill. Used for share links wherever they appear (project page,
   the homepage's "Spread the word" section). */
.gv-share-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--radius-pill);
  border: 1.5px solid var(--border-strong); background: transparent; color: var(--text-body);
  font-size: 1.1rem; line-height: 0; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.gv-share-btn:hover, .gv-share-btn:focus-visible {
  border-color: var(--color-primary); color: var(--brand-text);
}

/* ---- Badges — the design system's Badge shape, in the tenant's accent (challenge status) or
   success ("goal met"). --------------------------------------------------------------------- */
.badge.gv-chip {
  padding: .35em .7em;
  font-size: var(--text-xs);
}
.badge.gv-chip--accent {
  background: var(--color-accent); color: var(--on-accent);
  box-shadow: inset 0 0 0 1px var(--fill-edge-accent);
}
.badge.gv-chip--done { background: var(--success-surface); color: var(--success); }

/* ---- Match funding — the design system's MatchFundBanner (themes/components/match_banner.html),
   value for value. The panel keeps the secondary tint; the multiplier pill and the pot meter take the
   accent, the design system's colour for matched flags, so they read as a highlight distinct from the
   primary progress meter. ------------------------------------------------------------------------- */
.gv-match {
  display: flex; align-items: center; gap: .9rem;
  padding: .85rem 1.1rem; border-radius: var(--radius-lg);
  background: var(--color-secondary-subtle); border: 1.5px solid var(--border-brand);
  color: var(--text-body);
}
.gv-match__badge {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: .4rem;
  background: var(--color-accent); color: var(--on-accent); box-shadow: inset 0 0 0 1px var(--fill-edge-accent);
  font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: var(--text-sm);
  padding: .3rem .7rem; border-radius: var(--radius-pill); white-space: nowrap;
}
.gv-challenge__icon {
  flex: none; display: inline-grid; place-items: center; width: 2rem; height: 2rem; border-radius: var(--radius-pill);
  background: var(--color-primary); color: var(--on-primary); box-shadow: inset 0 0 0 1px var(--fill-edge-primary);
}
.gv-match__body { flex: 1; min-width: 0; line-height: 1.4; }
.gv-match__title { margin: 0; font-weight: var(--weight-semibold); color: var(--text-strong); }
.gv-match__sub { margin: 0; font-size: var(--text-sm); color: var(--text-muted); }
.gv-match__meter {
  margin-top: .55rem; height: 8px; border-radius: var(--radius-pill); overflow: hidden;
  background: color-mix(in oklab, var(--color-accent), transparent 78%);
}
.gv-match__fill {
  height: 100%; border-radius: var(--radius-pill);
  background: var(--color-accent); box-shadow: inset 0 0 0 1px var(--fill-edge-accent);
}
/* Stacked, the body stretches to the box so the pot meter spans it; the badge keeps its own width. */
.gv-match--banner { flex-direction: column; align-items: stretch; }
.gv-match--banner .gv-match__badge { align-self: flex-start; }

/* ---- Section utilities ------------------------------------------------- */
.gv-page .section { padding-block: var(--space-8); }
/* Cooler, more neutral grey than the design system's own warm --surface-subtle
   (per explicit preference) — mixed against the tenant's own page background,
   same reasoning as .gv-page's wash above. */
.gv-page .section--tint,
.gv-page--alternate > section:nth-of-type(even) { background: color-mix(in oklab, var(--bs-body-bg, #fff), #71717a 8%); border-block: 1px solid var(--border); }
/* The design system's homepage tells its sections apart by alternating plain and tinted bands. By
   position rather than per fragment, so the rhythm holds whichever optional sections render. */
.gv-page--alternate > section:nth-of-type(odd) { background: none; border-block: 0; }
.gv-page .text-ink-soft { color: var(--text-muted) !important; }
.gv-page .text-brand { color: var(--brand-text) !important; }
.gv-page .font-display { font-family: var(--font-heading) !important; }

.gv-page .gv-section-title {
  font-size: clamp(var(--text-2xl), 4.5vw, var(--text-3xl));
  color: var(--text-strong);
}

/* ---- Hero --------------------------------------------------------------
   The design system sanctions a full-bleed photo + dark scrim only for the
   flagship giving-day homepage (readme: "VISUAL FOUNDATIONS"). The scrim is
   a fixed dark neutral, not brand-tinted, so it reads legibly over any
   photo regardless of the tenant's brand hue; the no-photo fallback keeps a
   brand gradient since there's no image to protect text against. -------- */
.gv-hero {
  position: relative; isolation: isolate; overflow: hidden; color: #fff; text-align: left;
  padding-block: clamp(3.5rem, 9vw, 6.5rem) clamp(3rem, 7vw, 5rem);
  background:
    linear-gradient(180deg,
      color-mix(in oklab, var(--neutral-900) 62%, transparent),
      color-mix(in oklab, var(--neutral-900) 78%, transparent)),
    var(--hero-img, linear-gradient(135deg, var(--color-primary), var(--primary-700)));
  background-size: cover; background-position: center;
}
.gv-hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(ellipse at 50% 0%, transparent 30%, color-mix(in oklab, var(--neutral-900) 45%, transparent) 100%);
  mix-blend-mode: multiply;
}
.gv-badge-live {
  display: inline-flex; align-items: center; gap: .4em;
  background: var(--danger); color: #fff;
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  padding: .35em .7em; border-radius: var(--radius-pill);
}
.gv-badge-live__dot {
  width: 7px; height: 7px; border-radius: var(--radius-pill); background: #fff;
  animation: gv-pulse 1.4s var(--ease-standard) infinite;
}
@keyframes gv-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }

.gv-hero__eyebrow {
  text-transform: uppercase; letter-spacing: var(--tracking-wide); font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  display: inline-block; padding-bottom: var(--space-3);
  border-bottom: 1px solid color-mix(in oklab, #fff, transparent 65%);
}
.gv-hero__title {
  /* base.css sets a direct `color` on every h1-h6, which wins over the
     inherited white from .gv-hero's `color:#fff` — a direct declaration on
     the element always beats an inherited one, regardless of selector
     specificity. Must be set explicitly here, not assumed from the parent. */
  font-family: var(--font-heading); font-size: clamp(2.2rem, 4.5vw, 3.4rem); font-weight: var(--weight-bold);
  text-shadow: 0 2px 30px rgba(0,0,0,.25);
  color: #fff;
}
.gv-hero__lead {
  font-size: var(--text-md); color: rgba(255,255,255,.92); max-width: 46ch;
  text-shadow: 0 1px 8px rgba(0,0,0,.3);
}
.gv-hero__media {
  border-radius: var(--radius-xl); overflow: hidden; box-shadow: var(--shadow-xl); aspect-ratio: 16/9;
}
.gv-hero__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- Stat figures — the design system's StatBlock, "lg". Always white, not --on-primary: they sit on
   the hero's dark scrim, which stays dark whatever the brand colour, and --on-primary turns black for a
   light primary. --------------------------------------------------------- */
.gv-figure__value {
  font-family: var(--font-heading); font-size: var(--text-4xl);
  font-weight: var(--weight-bold); line-height: 1; letter-spacing: var(--tracking-tight);
  color: #fff;
}
.gv-figure__label {
  font-size: var(--text-sm); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: rgba(255,255,255,.75);
}

/* The hero's figures on one line while they fit the column, else 2×2 — never 3 + 1. They wrap in
   pairs, so the only break left is between the pairs. */
.gv-hero__figures { display: flex; flex-wrap: wrap; gap: var(--space-5) 3rem; }
.gv-hero__pair { display: flex; gap: 3rem; }
/* The raised figure and its Gift Aid pill share a line, the pill wrapping under only when the column runs out. */
.gv-figure__row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; }
/* Phones: one step down, so two figures still share a row at 390px. */
@media (max-width: 575.98px) {
  .gv-figure__value { font-size: var(--text-3xl); }
  .gv-meter__raised { font-size: var(--text-2xl); }
}

/* The results page's headline figure — the day's final total, set larger than any live figure. */
.gv-results__raised { font-size: clamp(3.5rem, 10vw, 6.5rem); }

/* The pause switch, drawn only in white and the hero's dark scrim — the pair the hero's text already
   relies on. Bootstrap's draws its checked fill in the primary, which may be near the scrim, and its
   knob is an SVG that cannot read a custom property, so the knob here is a gradient sized to
   Bootstrap's (75% of the height) that its background-position rules still slide. */
.gv-hero .form-switch .form-check-input {
  --gv-switch-knob: #fff;
  background-color: transparent;
  border-color: #fff;
  background-image: radial-gradient(circle closest-side, var(--gv-switch-knob) 75%, transparent calc(75% + 1px));
  background-size: 50% 100%;
}
.gv-hero .form-switch .form-check-input:checked {
  --gv-switch-knob: var(--neutral-900);
  background-color: #fff;
}
.gv-hero .form-switch .form-check-input:focus { border-color: #fff; box-shadow: none; }
.gv-hero .form-switch .form-check-input:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

/* ---- Countdown — the design system's CountdownTimer, "onbrand" tone: a
   translucent dark tile (the number only — the label sits outside it, as its
   own line below), with a light hairline, no blur, no shadow. ------------ */
.gv-countdown__label {
  text-transform: uppercase; letter-spacing: var(--tracking-wide); font-size: var(--text-sm);
  font-weight: var(--weight-bold); color: color-mix(in oklab, #fff, transparent 10%);
}
/* Four equal tile columns filling the progress bar's width, so the tiles' outer edges line up with the
   bar whatever the tenant's font or screen width. The DS sizes tiles by content (min-width 2.6em), which
   comes out wider than the bar. */
.gv-countdown__row {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr) auto) minmax(0, 1fr);
  align-items: start; gap: .6rem;
}
/* The DS's 440px cap, on the countdown only and only beside the image; stacked below lg it runs full
   width. The figures and meter fill the column as the match banner does. */
@media (min-width: 992px) {
  .gv-countdown__row { max-width: 440px; }
}
.gv-countdown__unit { display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.gv-cd {
  display: block; width: 100%; padding: .4em .3em; text-align: center;
  background: color-mix(in oklab, #000, transparent 78%);
  border: 1px solid color-mix(in oklab, #fff, transparent 78%);
  border-radius: var(--radius-md);
  font-family: var(--font-heading); font-size: var(--text-2xl); font-weight: var(--weight-bold);
  line-height: 1; font-variant-numeric: tabular-nums; color: #fff;
}
.gv-countdown__unit small {
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  color: color-mix(in oklab, #fff, transparent 25%);
}
.gv-countdown__sep { font-size: var(--text-2xl); font-weight: var(--weight-bold); color: color-mix(in oklab, #fff, transparent 55%); line-height: 1; padding-top: .35em; }
/* Phones: one step down so the numbers sit comfortably in ~50-60px tiles. */
@media (max-width: 575.98px) {
  .gv-cd, .gv-countdown__sep { font-size: var(--text-xl); }
  .gv-countdown__row { gap: .35rem; }
}

/* ---- Cards: causes / challenges / voices / features — the design
   system's NonprofitCard recipe (radius-lg, shadow-sm resting, shadow-lg +
   -4px lift on hover, 1.04 image zoom). ----------------------------------- */
.gv-card {
  --bs-card-inner-border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.gv-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }

.gv-cause__media { position: relative; aspect-ratio: 16/10; overflow: hidden; background: var(--surface-subtle); }
.gv-cause__media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-slow) var(--ease-out); }
.gv-cause__link { display: block; height: 100%; }
/* ---- Project page (NEX-6163): the key panel, the story column and its supporters. ---- */
/* The project's image in one frame whatever was uploaded: uncropped, a portrait ran three screens tall
   and a panorama to a sliver. In the panel it fills its half, so the two halves always match. */
.gv-project__image { display: block; width: 100%; height: 100%; aspect-ratio: 16/9; object-fit: cover; }
/* Static, so it does not lift on hover as a clickable card does. */
.gv-project-panel { overflow: hidden; border-radius: var(--radius-xl); box-shadow: var(--shadow-md); }
.gv-project-panel:hover { transform: none; box-shadow: var(--shadow-md); }
.gv-project-panel__body { height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; padding: var(--space-6); }
.gv-project-panel__raised {
  font-family: var(--font-heading); font-size: var(--text-4xl); font-weight: var(--weight-bold);
  line-height: 1; letter-spacing: var(--tracking-tight); color: var(--text-strong);
}
.gv-project-panel__logo { display: block; max-height: 3rem; max-width: 10rem; width: auto; margin-bottom: var(--space-4); }
.gv-project-panel__label { font-size: var(--text-md); color: var(--text-body); }
.gv-project-panel__body .gv-match { align-self: stretch; }
@media (max-width: 575.98px) {
  .gv-project-panel__body { padding: var(--space-5); }
  .gv-project-panel__raised { font-size: var(--text-3xl); }
}

/* The story at a readable measure (~68 characters a line) in body text, not small muted copy. */
.gv-prose { max-width: 68ch; font-size: var(--text-md); line-height: 1.7; color: var(--text-body); }
.gv-prose h2, .gv-prose h3, .gv-prose h4 { margin-top: var(--space-6); margin-bottom: var(--space-3); color: var(--text-strong); }
.gv-prose h3 { font-size: var(--text-xl); }
.gv-prose ul, .gv-prose ol { padding-left: 1.25rem; }
.gv-prose li { margin-bottom: var(--space-2); }
/* A pull quote, set apart from the copy; a last paragraph after the quote reads as its attribution. */
.gv-prose blockquote {
  margin: var(--space-6) 0; padding: var(--space-2) 0 var(--space-2) var(--space-5);
  border-left: 4px solid var(--brand-text);
  font-family: var(--font-heading); font-size: var(--text-xl); line-height: 1.4; color: var(--text-strong);
}
.gv-prose blockquote p { margin-bottom: var(--space-2); }
.gv-prose blockquote p:last-child:not(:first-child) {
  font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text-muted);
}

/* The latest supporters beside the story: name and amount, then when. */
.gv-supporters__item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; border-bottom: 1px solid var(--border); }
.gv-supporters__who { display: flex; flex-direction: column; min-width: 0; }
.gv-supporters__name { font-weight: var(--weight-semibold); color: var(--text-strong); overflow-wrap: anywhere; }
.gv-supporters__amount { color: var(--brand-text); margin-left: var(--space-1); }
.gv-supporters__when { font-size: var(--text-sm); color: var(--text-muted); }
.gv-card:hover .gv-cause__media img { transform: scale(1.04); }
.gv-cause__flag {
  position: absolute; top: var(--space-3); left: var(--space-3);
  background: var(--color-secondary); color: var(--on-secondary);
  font-weight: var(--weight-bold); font-size: var(--text-xs); letter-spacing: var(--tracking-wide);
  text-transform: uppercase; padding: .35rem .7rem; border-radius: var(--radius-pill);
}
.gv-cause__title { color: var(--text-strong); }
/* Card titles double as links (click-through to the project page, not just the
   Donate button) — the design system's own NonprofitCard title isn't a link at
   all, so without this override base.css's global `a` colour (--text-link)
   wins over the neutral heading colour intended here. */
.gv-cause__title a { color: inherit; text-decoration: none; }
.gv-cause__title a:hover { text-decoration: underline; }
.gv-cause__more { display: block; padding-block: .25rem; color: var(--brand-text); font-weight: var(--weight-semibold); }
.gv-fig-amount { font-family: var(--font-heading); font-size: var(--text-lg); font-weight: var(--weight-bold); color: var(--text-strong); }
.gv-fig-pct { font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: var(--text-lg); color: var(--brand-text); white-space: nowrap; }

/* ---- Leaderboards — the design system's LeaderboardRow (feat-063, feat-084). The podium's rank is a
   filled secondary disc; every other rank is a muted numeral. The rank is written either way, so
   the disc is never the only signal. ------------------------------------------------------------- */
.gv-board-card { padding: var(--space-5); }
.gv-board { width: 100%; border-collapse: collapse; }
.gv-board td, .gv-board th { padding-block: var(--space-2); vertical-align: middle; }
.gv-board__place { width: 2.75rem; }
.gv-board__name { font-weight: var(--weight-semibold); color: var(--text-strong); padding-inline: var(--space-3); }
.gv-board__name a { color: inherit; text-decoration: none; }
.gv-board__name a:hover { text-decoration: underline; }
.gv-board__figure {
  text-align: right; white-space: nowrap;
  font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: var(--text-md); color: var(--text-strong);
}
.gv-rank {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: var(--radius-pill);
  font-family: var(--font-heading); font-weight: var(--weight-bold); color: var(--text-muted);
}
/* Gold, silver and bronze for the podium (feat-063), fixed rather than brand colours so first, second
   and third read the same for every tenant; a brand's own shades run together when the brand is dark.
   The place is written on every disc, so the colour is never the only sign of it. The darker edge keeps
   the pale discs discs on a white card. */
.gv-rank--podium { --rank-bg: #c9ced6; background: var(--rank-bg); color: var(--neutral-900); box-shadow: inset 0 0 0 1.5px color-mix(in oklab, var(--rank-bg), black 35%); }
.gv-rank--gold { --rank-bg: #f2c94c; }
.gv-rank--silver { --rank-bg: #c9ced6; }
.gv-rank--bronze { --rank-bg: #d9965b; }

/* ---- Momentum charts (feat-063) — no chart component in the design system, so drawn in its tokens:
   one series, one hue. The primary carries the line (and so needs its contrast), and a fading tint of
   it fills the area and rings the latest point; a second hue in the fill read as mud on a dark brand.
   Text never takes it. Thin marks, a hairline grid,
   strokes that keep their width however the SVG scales. ------------------------------------------ */
.gv-chart {
  padding: var(--space-5);
  --gv-chart-line: var(--color-primary);
}
.gv-chart__title { font-family: var(--font-heading); font-size: var(--text-lg); font-weight: var(--weight-bold); color: var(--text-strong); }
.gv-chart svg { display: block; width: 100%; height: auto; overflow: visible; }
.gv-chart__line {
  fill: none; stroke: var(--gv-chart-line); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke;
}
.gv-chart__area { fill: var(--gv-chart-line); opacity: 0.1; }
.gv-chart__grid { stroke: var(--border); stroke-width: 1; vector-effect: non-scaling-stroke; }
.gv-chart__tick { fill: var(--text-muted); font-size: 13px; }
.gv-chart__hit { fill: transparent; }
.gv-chart__hit:hover { fill: color-mix(in srgb, var(--gv-chart-line) 8%, transparent); }
.gv-chart__table { --bs-table-bg: transparent; }

/* The d3 redraw (themes/static_src/momentum_chart.js), drawn at its real size. */
.gv-chart__plot { position: relative; border-radius: var(--radius-md); }
.gv-chart__plot:focus-visible { outline: none; box-shadow: var(--shadow-ring); }
.gv-chart__svg { display: block; overflow: visible; }
.gv-chart__stop--top { stop-color: var(--gv-chart-line); stop-opacity: 0.2; }
.gv-chart__stop--bottom { stop-color: var(--gv-chart-line); stop-opacity: 0; }
/* d3 sets font-family="sans-serif" on each axis group; the page's own face should win. */
.gv-chart__axis { font-family: inherit; }
.gv-chart__axis text { fill: var(--text-muted); font-size: var(--text-xs); }
.gv-chart__axis line,
.gv-chart__axis path { stroke: var(--border); }
.gv-chart__axis--x .tick line { display: none; }
.gv-chart__line--draw { stroke-dasharray: 1; stroke-dashoffset: 1; animation: gv-chart-draw 1.2s var(--ease-standard) forwards; }
@keyframes gv-chart-draw { to { stroke-dashoffset: 0; } }
/* A surface-coloured ring keeps the marker legible where it sits on the line. */
.gv-chart__dot,
.gv-chart__crosshair circle { fill: var(--gv-chart-line); stroke: var(--surface-card); stroke-width: 2; }
.gv-chart__halo { fill: var(--gv-chart-line); opacity: 0.25; }
.gv-chart__end-label { fill: var(--text-strong); font-family: var(--font-heading); font-size: var(--text-sm); font-weight: var(--weight-bold); }
.gv-chart__crosshair line { stroke: var(--text-muted); stroke-width: 1; }
.gv-chart__overlay { fill: transparent; cursor: crosshair; }
.gv-chart__tooltip {
  position: absolute; transform: translate(-50%, -100%);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  background: var(--surface-card); border: 1px solid var(--border); box-shadow: var(--shadow-md);
  color: var(--text-body); font-size: var(--text-sm); line-height: 1.3;
  pointer-events: none; white-space: nowrap;
}
.gv-chart__tooltip-time { display: block; color: var(--text-muted); font-size: var(--text-xs); }
.gv-chart__tooltip strong { font-family: var(--font-heading); color: var(--text-strong); }
@media (prefers-reduced-motion: reduce) {
  .gv-chart__line--draw { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; }
}

/* Donor wall, activity feed and donor map (feat-065), ported from giving.css onto design-system
   tokens. */
/* ---- Donor wall ------------------------------------------------------ */
/* The page renders inside the theme's container (themes differ in their `main`), so the band bleeds to
   the viewport edges with a spread shadow clipped to the sides — no 100vw, which overflows by the
   scrollbar's width and scrolls the page sideways. */
/* The wall's figures — the design system's StatBlock row, the heading font on the figures. */
.gv-wall-stats { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-4) var(--space-8); margin-bottom: var(--space-6); text-align: center; }
.gv-wall-stats p { display: flex; flex-direction: column; }
.gv-wall-stats__value { font-family: var(--font-heading); font-size: var(--text-3xl); font-weight: var(--weight-bold); line-height: 1.1; color: var(--text-strong); }
.gv-wall-stats__label { font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-muted); }

/* Columns rather than a grid, so cards of different heights pack without gaps. DOM order still runs
   newest first, and columns flow top to bottom, so reading and focus order follow each column. */
/* A page turn scrolls the results to the top of the viewport; clear the sticky nav so it does not cover the focused entry. */
.gv-wall-results { scroll-margin-top: calc(var(--gp-sticky-offset) + 1rem); }
.gv-wall { columns: 1; column-gap: 1.25rem; }
@media (min-width: 576px) { .gv-wall { columns: 2; } }
@media (min-width: 992px) { .gv-wall { columns: 3; } }
/* No fourth column: a card carries a project, a time and an amount, and at a quarter width they wrap. */
.gv-wall__item { break-inside: avoid; margin-bottom: 1.25rem; }

/* A donor card: the donor row, and above it any message as a speech bubble whose tail points down at
   the avatar. The tint is the bubble's alone, so it always means "this donor's words". */
.gv-donor { background: var(--surface-card); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-xs); padding: var(--space-4); }
.gv-donor:focus-visible, .gv-donor:focus { outline: none; box-shadow: var(--shadow-ring); }
.gv-donor__message {
  --tail: 0.5rem;
  /* A light brand tint behind dark text, so the bubble takes the brand without costing contrast. */
  --gv-message-bg: color-mix(in oklab, var(--color-primary) 12%, #fff);
  position: relative; margin: 0 0 calc(var(--space-3) + var(--tail));
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  background: var(--gv-message-bg); color: var(--text-strong); overflow-wrap: anywhere;
}
/* Centred over the 2.25rem avatar, which starts where the bubble does. */
.gv-donor__message::after {
  content: ""; position: absolute; top: 100%; left: calc(1.125rem - var(--tail));
  border: var(--tail) solid transparent; border-bottom: 0; border-top-color: var(--gv-message-bg);
}
.gv-donor__by { display: flex; align-items: flex-start; gap: var(--space-3); }
/* A solid brand fill: the subtle tint all but disappears for a dark brand, which left the wall grey. */
.gv-donor__avatar {
  flex: none; display: grid; place-items: center; width: 2.25rem; height: 2.25rem; border-radius: var(--radius-pill);
  background: var(--color-primary); color: var(--on-primary); box-shadow: inset 0 0 0 1px var(--fill-edge-primary);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
}
.gv-donor__who { flex: 1; min-width: 0; }
.gv-donor__name { margin: 0; font-weight: var(--weight-semibold); color: var(--text-strong); overflow-wrap: anywhere; }
.gv-donor__meta { margin: 0; font-size: var(--text-xs); color: var(--text-muted); overflow-wrap: anywhere; }
.gv-donor__meta a { color: var(--text-body); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .15em; text-decoration-color: color-mix(in srgb, currentColor 65%, transparent); }
.gv-donor__meta a:is(:hover, :focus-visible) { color: var(--brand-text); text-decoration-color: currentColor; }
.gv-donor__amount { flex: none; margin: 0; font-family: var(--font-heading); font-weight: var(--weight-bold); color: var(--brand-text); }

/* ---- Live activity feed: the design system's floating "Live donations" panel ----
   A <details>: closed it is the design system's small primary Button with a dot; open, a raised Card
   whose summary is the header (title, Live badge, minimise). Rows are its ActivityFeedItem. */
:root {
  /* The open panel's height is capped so focus can be scrolled exactly clear of it. */
  --gv-live-panel-height: min(30rem, calc(100vh - 40px));
}
/* WCAG 2.4.11: the panel covers the bottom-right corner, so focus moving to an element scrolls it
   clear — of the open card, or of the minimised button. Nothing below the end of the page can scroll
   clear, so there activity_feed.js minimises the panel and docks it above the footer instead. */
html:has(.gv-live-panel) { scroll-padding-bottom: 4rem; }
html:has(.gv-live-panel__details[open]) { scroll-padding-bottom: calc(var(--gv-live-panel-height) + 20px); }

.gv-live-panel {
  position: fixed; right: 20px; bottom: 20px; z-index: 1020;
  width: 360px; max-width: calc(100vw - 40px);
  display: flex; justify-content: flex-end;
}
.gv-live-panel__details { width: 100%; }
.gv-live-panel__details:not([open]) { width: auto; }
.gv-live-panel__summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: var(--space-2);
}
.gv-live-panel__summary::-webkit-details-marker { display: none; }
.gv-live-panel__summary:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

.gv-live-panel__details:not([open]) .gv-live-panel__summary {
  padding: .45rem .9rem; border-radius: var(--radius-pill);
  background: var(--color-primary); color: var(--on-primary);
  font-size: var(--text-sm); font-weight: var(--weight-semibold); box-shadow: var(--shadow-lg);
}
.gv-live-panel__dot { width: 8px; height: 8px; border-radius: var(--radius-pill); background: currentColor; }
.gv-live-panel__details:not([open]) :is(.gv-badge-live, .gv-live-panel__minimise) { display: none; }

.gv-live-panel__details[open] {
  background: var(--surface-card); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl); padding: var(--space-4);
  max-height: var(--gv-live-panel-height); display: flex; flex-direction: column;
}
.gv-live-panel__details[open] > .gv-live-panel__body { display: flex; flex-direction: column; min-height: 0; }
.gv-live-panel__details[open] .gv-live-panel__summary { margin: calc(-1 * var(--space-2)) 0 var(--space-3); }
.gv-live-panel__details[open] .gv-live-panel__dot { display: none; }
.gv-live-panel__details[open] .gv-live-panel__title {
  flex: 1; font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: var(--text-lg); color: var(--text-strong);
}
.gv-live-panel__minimise {
  display: inline-grid; place-items: center; width: 32px; height: 32px; border-radius: var(--radius-md);
  color: var(--text-body);
}
.gv-live-panel__summary:hover .gv-live-panel__minimise { background: var(--surface-subtle); }
.gv-live-panel__body .form-check { font-size: var(--text-sm); }
.gv-live-panel__wall { margin-top: var(--space-3); font-size: var(--text-sm); font-weight: var(--weight-semibold); }

#activity-feed { flex: 0 1 auto; min-height: 0; max-height: 320px; overflow: auto; }
.gv-feed__line {
  display: flex; align-items: center; gap: .75rem; padding: .7rem .25rem; border-bottom: 1px solid var(--border);
}
.gv-feed__line:last-child { border-bottom: 0; }
.gv-avatar--sm { width: 32px; height: 32px; font-size: 12px; }
.gv-feed__goal { background: var(--primary-700); color: #fff; }
.gv-feed__text { flex: 1; min-width: 0; line-height: 1.35; font-size: var(--text-sm); color: var(--text-body); overflow-wrap: anywhere; }
.gv-feed__name { font-weight: var(--weight-semibold); color: var(--text-strong); }
.gv-feed__text a { color: var(--primary-700); }
.gv-feed__message { font-style: italic; margin-top: .1rem; }
.gv-feed__time { display: block; font-size: var(--text-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.gv-feed__amount { flex: none; font-family: var(--font-heading); font-weight: var(--weight-bold); color: var(--brand-text); font-size: var(--text-md); }
.gv-feed__empty { padding: var(--space-3) .25rem; font-size: var(--text-sm); color: var(--text-body); }

/* ---- Donor map ------------------------------------------------------- */
/* Beside the map, the list is held to the map card's height and scrolls inside its card: the card is
   taken out of the flow so only the map sets the row's height. Stacked below lg, it runs full length. */
@media (min-width: 992px) {
  .gv-map-list { position: relative; height: 100%; }
  .gv-map-list > .card { position: absolute; inset: 0 0 auto; max-height: 100%; display: flex; flex-direction: column; }
  .gv-map-list__scroll { min-height: 0; overflow-y: auto; }
}
.gv-map-list__scroll:focus-visible { outline: none; box-shadow: var(--shadow-ring); border-radius: var(--radius-sm); }
/* Neutral, so the brand-tinted shades read against it; the same grey as the tinted sections. */
.gv-map-card { padding: var(--space-5); background: color-mix(in oklab, var(--bs-body-bg, #fff), #71717a 8%); border: 0; }
/* Capped well under the screen, so a tall map (the UK, with Shetland) narrows and centres rather than
   pushing everything below it out of view. */
.gv-map { width: 100%; height: auto; max-height: min(60vh, 30rem); display: block; }
/* A tall map (the UK) is held to the screen; its viewBox keeps the shape centred in the space. */
/* The outline is what shows every region, donors or none: neutral-500 is about 4:1 on the map card, where
   the card-coloured stroke it replaces was invisible (1.1:1). */
.gv-map__region { stroke: var(--neutral-500); stroke-width: .6; vector-effect: non-scaling-stroke; }
/* One colour per shade, read by both the map's fill and the legend's swatch, so the two cannot drift. */
/* A neutral for no donors, never a tenant colour, so colour only ever means "has donors" and the empty
   map reads the same for every tenant. Light on purpose: the outline marks the shape, and a darker fill
   would read as more donors than shade 1. */
.gv-map__shade--0 { --shade: var(--neutral-200); }
.gv-map__shade--1 { --shade: color-mix(in srgb, var(--color-primary) 32%, var(--surface-card)); }
.gv-map__shade--2 { --shade: color-mix(in srgb, var(--color-primary) 48%, var(--surface-card)); }
.gv-map__shade--3 { --shade: color-mix(in srgb, var(--color-primary) 64%, var(--surface-card)); }
.gv-map__shade--4 { --shade: color-mix(in srgb, var(--color-primary) 80%, var(--surface-card)); }
.gv-map__shade--5 { --shade: var(--primary-700); }
.gv-map__region { fill: var(--shade); }
/* The pointed-at region (donor_map.js), filled in the secondary at full strength, which no shade uses. A fill, not an
   outline: a neighbour drawn after it paints over half of any stroke, so an outline looks uneven. The
   card names its count meanwhile. */
.gv-map__region.is-hovered { fill: var(--color-secondary); }
/* donor_map.js's hover card, positioned at the pointer: a small design-system Card. */
.gv-map-tip {
  position: fixed; z-index: 1080; pointer-events: none; max-width: 16rem;
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  background: var(--surface-card); border: 1px solid var(--border); box-shadow: var(--shadow-md); color: var(--text-body);
  font-size: var(--text-sm); line-height: 1.35;
}
.gv-map-tip__name { display: block; font-weight: var(--weight-semibold); color: var(--text-strong); }
.gv-map-tip__donors { display: block; color: var(--text-muted); }
.gv-map-legend__swatch { display: inline-block; width: 1.25rem; height: 1.25rem; border-radius: 4px; vertical-align: -.2rem; background: var(--shade); border: 1px solid var(--border); }

/* ---- Challenges — the design system's PageHead, Tabs, Avatar and challenge card. ---- */
.gv-page-head {
  background: linear-gradient(160deg, var(--surface-brand-band), var(--surface-brand-subtle));
  border-bottom: 1px solid var(--border);
}
.gv-tabs { min-width: 0; max-width: 100%; overflow-x: auto; }
.gv-tabs__list { display: flex; gap: .25rem; border-bottom: 1.5px solid var(--border); }
/* Spanning the row, and its rule under every tab even once they scroll: the list is as wide as its
   tabs or the row, whichever is wider. */
.gv-tabs--full .gv-tabs__list { width: max-content; min-width: 100%; }
/* Phones: wrap rather than scroll. A scrolling strip clipped its last tab out of sight and still pushed
   the page sideways (WCAG 1.4.10); wrapped, every tab is visible and nothing is wider than the screen. */
@media (max-width: 575.98px) {
  .gv-tabs { overflow-x: visible; }
  .gv-tabs__list, .gv-tabs--full .gv-tabs__list { flex-wrap: wrap; width: auto; }
}
.gv-tab {
  display: inline-block; padding: .7rem 1rem; margin-bottom: -1.5px;
  font-weight: var(--weight-semibold); color: var(--text-muted); text-decoration: none; white-space: nowrap;
  border-bottom: 2.5px solid transparent;
  transition: color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.gv-tab:hover { color: var(--text-strong); }
.gv-tab[aria-current="page"] { color: var(--brand-text); border-bottom-color: var(--color-primary); }
.gv-tab:focus-visible { outline: none; box-shadow: var(--shadow-ring); border-radius: var(--radius-sm); }
.gv-tab__count {
  margin-left: .4rem; font-size: var(--text-xs); font-weight: var(--weight-semibold);
  background: var(--surface-subtle); color: var(--text-muted); border-radius: var(--radius-pill); padding: .1rem .45rem;
}
.gv-tab[aria-current="page"] .gv-tab__count { background: var(--color-primary-subtle); color: var(--color-primary-active); }
/* Label above the select on phones, beside it from md up — inline, the select was truncated. */
.gv-filter { flex-wrap: wrap; width: 100%; }
.gv-filter label { flex-basis: 100%; }
.gv-filter .form-select { width: auto; min-width: 0; }
@media (min-width: 768px) {
  .gv-filter { flex-wrap: nowrap; width: auto; }
  .gv-filter label { flex-basis: auto; }
}
.gv-avatar {
  display: inline-grid; place-items: center; flex: 0 0 auto; width: 36px; height: 36px;
  border-radius: var(--radius-pill); background: var(--color-primary-subtle); color: var(--color-primary-active);
  font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: 14px; line-height: 1;
}
.gv-challenge__pledge { font-family: var(--font-heading); font-weight: var(--weight-bold); font-size: var(--text-lg); color: var(--brand-text); }
.gv-challenge__done { text-align: center; padding: .5rem; border-radius: var(--radius-pill); background: var(--success-surface); color: var(--success); font-weight: var(--weight-semibold); }

/* ---- Scroll-reveal ------------------------------------------------------ */
.gv-page [data-reveal] { opacity: 0; transform: translateY(22px); transition: opacity .7s ease, transform .7s var(--ease-out); transition-delay: calc(var(--i, 0) * 80ms); }
.gv-page [data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .gv-page [data-reveal] { opacity: 1; transform: none; transition: none; }
  .gv-page .progress-bar { transition: none; }
  .gv-page .btn-brand:hover, .gv-page .btn-ghost-brand:hover, .gv-card:hover { transform: none; }
}

/* Section and page headings carry the brand colour the eyebrows above them used to (NEX-6163). */
.gv-page .gv-section-title, .gv-page-head h1 { color: var(--brand-text); }
