/*
Theme Name:   GoodHeart Child
Theme URI:    https://treesforever.org/
Description:  Child theme for Trees Forever. Holds template overrides and CSS so they survive GoodHeart updates. Overrides partials/default/header.php to render the TF-Standard-Logo-Green in the site branding slot, raises every link in the collapsed mobile menu past the 44px minimum tap target, makes every Elementor image frame 3:2 by default while leaving the editor's own Height control able to override it per image, gives all 43 page headers a swoopy bottom edge in place of their Elementor tilt divider, adds a front-end category filter to the Essential Addons Event Calendar widget, and puts Elementor carousel pagination dots back where the editor placed them after Jetpack's Swiper stylesheet drags them to the left edge.
Author:       Trees Forever
Template:     goodheart
Version:      1.6.1
Text Domain:  goodheart-child
*/

/* The parent enqueues its own stylesheet via get_parent_theme_file_uri(), so
   nothing needs re-importing here. Add child-specific rules below. */

/* --------------------------------------------------------------------------
   Mobile menu tap targets

   Links in the collapsed menu rendered 31.2px tall (line-height 25.2px plus
   3px of vertical padding), under the 44px minimum recommended for touch
   targets and small enough to cause mis-taps between adjacent rows.

   11px of vertical padding clears 44px for every link in the menu rather than
   only the top level. Submenu items set a smaller line-height (22.4px against
   the top level's 25.2px), so the 10px that first brought top-level links to
   45.2px left submenu links at 42.4px - still 1.6px short of the minimum, on
   both phone and tablet widths. At 11px the top level renders 47.2px and
   submenus 44.4px. The only visible effect is slightly taller rows in the open
   menu.

   The parent sets `padding: 3px 0` via
   `body .site-header--default .site-navigation ul a` (style.css, from
   sass/base/_theme-default.scss:730) inside this same breakpoint. That is
   specificity (0,2,3), so the selector below adds `li` to reach (0,2,4) and
   win outright rather than depending on stylesheet order.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
	body .site-header--default .site-navigation ul li a {
		padding-top: 11px;
		padding-bottom: 11px;
	}
}

/* --------------------------------------------------------------------------
   Header bar - top-level items clipped off the right edge below ~1845px

   The bar does not fit the header it lives in. A WPCode CSS snippet ("Header
   Styles", saved in the WPCode plugin rather than in any theme file) sets a
   blanket `nav { font-size: 30px }`; the theme was drawn for 16px, and still
   carries the commented-out `//font-size: 16px` to prove it
   (sass/base/_theme-default.scss:764). At 30px the seven items measure 1257px,
   and the header's flex row wants 1257 + 200 for the logo + the nav's own 50px
   `margin-left` = 1507px. Its `.container` is `width: 1170px; max-width: 96%`.

   Nothing shrinks, because both flex children are `flex: 1 0 auto`, so the
   menu simply overflows the container to the right and is clipped by the
   window instead. That makes the cutoff depend on raw viewport width rather
   than on the container:

     menu left edge = max(2% of width, (width - 1170) / 2) + 250

   which lands the right edge at width/2 + 922 - so the bar only fits above
   about 1845px. Measured at 1600px, "Events" was entirely gone and "Donate"
   was sliced down the middle. At 1920px it fits with 30px to spare, which is
   why it looks fine on the machine it was built on and broken on a laptop.

   The fix scales the label with the window instead of pinning it at 30px.
   `1.5vw` tracks just under the width the header can actually give the bar
   (measured: 30.3px of font would fit at 1920px, 25.3 at 1600, 22.8 at 1440,
   20.3 at 1280), leaving 60-120px of clearance across that range rather than
   30px at the top and a deficit everywhere below. 30px is kept as the ceiling
   for monitors wider than 2000px, and 16px as the floor - the theme's own
   design size, and low enough to fit at 1025px where the collapsed menu takes
   over.

   Link padding moves from a fixed `10px 20px` to the `em` equivalent so the
   seven gaps shrink with the text; left fixed, padding alone would eat 280px
   of a bar that only has ~720px to work with at 1025px. At 30px the two are
   identical, so this changes nothing at the top of the range.

   The fixed `line-height` means the row height barely moves and the header
   stays 106px tall at every font size, so there is no layout shift against the
   fixed header's spacer (verified 16px through 30px).

   Deliberately scoped to `min-width: 1025px`. Below that the bar is replaced
   by the collapsed menu, which sets its own 14px and is not affected.

   Specificity: the snippet's `nav` selector is only (0,0,1) and the parent's
   padding rule is (0,3,0), so these sit comfortably above both at (0,2,1) and
   (0,3,2) and win regardless of stylesheet order - which matters here, because
   the WPCode snippet is printed inline in the document head and would
   otherwise come after the child stylesheet.
   -------------------------------------------------------------------------- */
@media (min-width: 1025px) {
	body .site-header--default .site-navigation {
		font-size: clamp(16px, 1.5vw, 30px);
	}
	body .site-header--default .site-navigation ul.menu > li > a {
		padding: 0.333em 0.667em;
	}
}

/* Under 1200px the container has stopped being 1170px wide and is tracking 96%
   of the window, so the 50px gap between logo and menu is the last thing left
   to reclaim - without it the bar clears the right edge by 6px at 1025px,
   which is not a margin. */
@media (min-width: 1025px) and (max-width: 1199px) {
	body .site-header--default .site-navigation {
		margin-left: 24px;
	}
}

/* --------------------------------------------------------------------------
   Header dropdowns - menu labels wrapping onto three and four lines

   The parent pins every desktop dropdown panel to a hardcoded `width: 220px`
   (`.site-header--default .site-navigation ul ul`, style.css, from
   sass/base/_theme-default.scss:404). Border-box, minus 10px of panel padding
   and 10px of link padding a side, that leaves 180px for the label - against a
   submenu that renders at 30px. Measured before this fix, six of the thirteen
   items in the three dropdowns wrapped:

     Lead     Trees Forever Project Funding   4 lines (178px tall)
              ReLeaf Cedar Rapids             3 lines
              One Million Trees               2 lines
              Growing Futures                 2 lines
     About Us Board of Directors              2 lines
     Learn    Accelerating Agroforestry       2 lines

   `width: max-content` sizes each panel to its own longest label instead, so
   every item gets one line: About Us needs 299px, Learn 406px, and Lead 465px.
   `min-width` keeps the designed 220px as the floor so a future dropdown of
   short labels still renders a panel rather than a sliver. The `nowrap` is
   belt-and-braces - `max-content` already guarantees no wrapping - but it keeps
   the intent legible if the width is ever overridden again.

   Deliberately scoped to the same `min-width: 1025px` query the parent's 220px
   lives in. Below that the dropdowns are static blocks in the collapsed menu at
   full panel width, and nothing wraps there already (checked at 390px).

   Note this widens panels that are positioned `left: 0` against their parent
   item, so a panel now extends further right than it used to. The widest one
   (Lead) still clears the window at every desktop width, because the rule above
   scales the bar down as the window narrows and the panels shrink with it -
   measured right edges of 1427px at 1920, 1140 at 1600, 996 at 1440, and 762 at
   1025. Both rules are load-bearing here: at a fixed 30px the Lead panel would
   run 100-180px past the right edge anywhere below about 1300px.

   Specificity: the parent rule is (0,3,0), so `body` brings these to (0,3,1)
   and (0,3,2) and they win outright rather than depending on stylesheet order.
   -------------------------------------------------------------------------- */
@media (min-width: 1025px) {
	body .site-header--default .site-navigation ul ul {
		width: max-content;
		min-width: 220px;
	}
	body .site-header--default .site-navigation ul ul a {
		white-space: nowrap;
	}
}

/* --------------------------------------------------------------------------
   Board of Directors - hero photo cropping the board out of frame

   The hero on /board-of-directors/ is an Elementor section (422692aa in
   template 24666) with bod-edit.jpg as a `cover` background. The photo is
   1440x1440 and the section is full-bleed at 550px tall, so `cover` scales the
   image to the viewport width in BOTH dimensions - a 1920px window renders it
   1920px tall behind a 550px window onto it. Only 550/vw of the photo is ever
   visible, and which 550 depends entirely on background-position.

   Elementor had that position hardcoded at `0px -600px`. Because -600px is a
   fixed pixel offset against an image whose height tracks the viewport, the
   crop slid across the photo as the window resized: 600px is 43% of the way
   down a 1400px-tall render but only 23% down a 2560px one. The board members
   occupy 37%-85% of the photo's height, so the window landed somewhere
   different every time - foreheads sliced off at 1400px, nothing but treetops
   on a wide monitor.

   The fix is to express the offset in the same unit the image is scaled by.
   `calc(-34.5vw)` is always 34.5% of the rendered image height, so the top of
   the visible band sits at a constant 34.5% of the photo no matter how wide
   the window is - just above the tallest head at 37.4%, leaving a consistent
   sliver of headroom. Wider windows now show less of the photo from the
   ankles up rather than panning away from the faces:

     1025px -> 35.0%-89.5% of the photo (whole group)
     1440px -> 34.9%-73.5% (heads to mid-thigh)
     1920px -> 34.8%-63.6% (heads to waist)
     2560px -> 34.7%-56.3% (heads to chest)

   Elementor's own tablet and mobile rules reset the position to `0px 0px`,
   which framed the treetops and pushed the group to the bottom edge, so this
   deliberately sits outside any media query and wins at every width. The
   section's shorter min-heights at those breakpoints (350px <=1024px, 300px
   <=767px) shrink the window without moving its top edge, so the framing
   stays put and the crop just gets tighter.

   Below 479px the two stop being compatible: 34.5vw + 300px exceeds the
   rendered image height around 458px wide, which would run past the bottom of
   the photo and leave a bare strip above the green shape divider. Phone
   viewports are short enough relative to their width that the whole group fits
   anyway, so those widths anchor near the bottom instead. At the 479px handoff
   the two rules frame within a percent of each other, so there is no visible
   jump.

   The Elementor rule being beaten is
   `:is(.elementor-24666, .elementor-32088) .elementor-element.elementor-element-422692aa:not(.elementor-motion-effects-element-type-background)`,
   specificity (0,4,0), so `body` brings this to (0,4,1) and wins outright
   rather than depending on stylesheet order - same approach as the flip-box
   rules below. Note that the section carries `transition: background 0.3s`,
   which makes the computed background-position an interpolated calc() for
   300ms after any change; measure it after that settles or the numbers lie.

   Horizontal position moves from `0px` to `center` for robustness only. At
   these proportions `cover` renders the image exactly as wide as the section,
   so there is no horizontal slack to distribute either way.
   -------------------------------------------------------------------------- */

body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-element-422692aa:not(.elementor-motion-effects-element-type-background),
body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-element-422692aa > .elementor-motion-effects-container > .elementor-motion-effects-layer {
	background-position: center calc(-34.5vw);
}

/* Narrow phones: 34.5vw would overrun the bottom of the photo, and the group
   fits in full at these proportions regardless. */
@media (max-width: 479px) {
	body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-element-422692aa:not(.elementor-motion-effects-element-type-background),
	body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-element-422692aa > .elementor-motion-effects-container > .elementor-motion-effects-layer {
		background-position: 50% 95%;
	}
}

/* --------------------------------------------------------------------------
   Staff - hero photo cropping the staff out of frame

   The same bug as the board hero above, in the same shape: the hero on /staff/
   is an Elementor section (3dc620f1 in template 24755), full-bleed at 550px
   tall, with a `cover` background pinned to a fixed pixel offset - here
   `0px -1200px`. Read that comment first; only what differs is repeated below.

   What differs is the photo's proportions, and it matters enough that the
   board's -34.5vw is the wrong number here. The staff photo is 1440x1920
   PORTRAIT, not square, so `cover` scales it to the section width and
   1.333x the width in height - a 1440px window renders it 1920px tall, not
   1440px. A given vw offset therefore lands at a different point in this
   photo than in a square one: -34.5vw would be 34.5/133.3 = 26% down this
   image rather than 34.5%. The offset has to be divided by the aspect ratio,
   so the same 29% anchor needs 29 / 0.75 = 38.7vw.

   The taller render also made the original fixed offset worse than the board's
   rather than better, because 1200px is a long way down a photo that is only
   1.333x the window tall. Measured before this fix, the visible band was:

     1025px -> 89.1%-130.0% of the photo - past the bottom edge, so the group
               was gone and a 403px bare strip sat above the shape divider
     1440px -> 63.2%-92.1% - legs and pavement, no heads at all
     2560px -> 35.4%-51.6% - faces, but the tops of heads clipped

   The staff occupy 31.7%-78.1% of the photo's height (more sky above them and
   more pavement below than the board photo has), so the anchor sits at 29%,
   just above the tallest head with a little sky for headroom. After the fix:

     320px  -> 29.0%-99.3% of the photo (whole group)
     375px  -> 29.0%-89.0% (whole group)
     768px  -> 29.6%-64.5% (heads to knees)
     1025px -> 29.5%-70.3% (heads to shins)
     1440px -> 29.3%-58.3% (heads to waist)
     2560px -> 29.2%-45.4% (heads to chest)

   No narrow-phone exception is needed here, unlike the board hero. That one
   needed one because a square image runs out of height below ~458px; this one
   renders a third taller for the same width, so 38.7vw + 300px still fits
   inside the photo down to about 317px - narrower than any real device. The
   320px row above is the measured floor, with 3px to spare.
   -------------------------------------------------------------------------- */

body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-element-3dc620f1:not(.elementor-motion-effects-element-type-background),
body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-element-3dc620f1 > .elementor-motion-effects-container > .elementor-motion-effects-layer {
	background-position: center calc(-38.7vw);
}

/* --------------------------------------------------------------------------
   Board of Directors - bios spilling out of the flip-box cards

   The 18 cards on /board-of-directors/ are Elementor Flip Box widgets, each
   with a hardcoded `height: 630px` (Elementor's own inline CSS for template
   24666) and no responsive override. The bio on the back face sits in a
   `position: absolute` layer with `overflow: visible`, so as soon as the
   column narrows enough for the text to re-wrap past 630px it spills out of
   the card and lands on top of the names below. Measured before this fix:
   0 of 18 cards overflowed at 1600px (488px-wide cards), 2 at 1366px, 11 at
   1038px, and 16 at 768px - where the longest bio needed 1512px of a 630px
   box.

   Two separate things cause that, so the fix has two parts.

   1. The cards stayed 3-across from 1600px all the way down to 767px, so each
      one shrank to ~210px. A bio needs roughly 460px of card width to fit in
      630px, so the column count now steps down instead: 3-across above
      1299px, 2-across from 900-1299px, and one centred card below that,
      capped at 480px so a phone-width card keeps the proportions the design
      was drawn at. `flex-wrap` has to be set as well - Elementor leaves the
      row `nowrap` above 767px, so full-width columns would otherwise just
      shrink back to a third of the row instead of wrapping onto their own
      line.

   2. Any bio that still does not fit now makes its card taller rather than
      spilling. Both faces move into a single CSS grid cell, which makes the
      card as tall as its tallest face, while `min-height` keeps the designed
      630px as the floor. This is safe here only because all 18 cards use the
      fade effect; the 3D flip effects depend on the layers being absolutely
      positioned, so a flip box using one of those wants the grid rule left
      off.

   After the fix nothing overflows at any width from 360px to 1600px. Worst
   case growth is a 742px card at 1300px wide and 976px on a 360px phone.

   Scoped to `:is(.elementor-24666, .elementor-32088)`, the template that renders the board grid, so
   flip boxes elsewhere on the site are untouched. The Elementor rule being
   beaten is `:is(.elementor-24666, .elementor-32088) .elementor-element.elementor-element-XXXX
   .elementor-flip-box`, specificity (0,4,0), so these selectors add `body` to
   reach (0,4,1) and win outright rather than depending on stylesheet order.
   -------------------------------------------------------------------------- */

/* Elementor only wraps this row below 767px; without this, the widened
   columns below shrink to fit one line instead of moving to the next. */
@media (max-width: 1299px) {
	body :is(.elementor-24666, .elementor-32088) .elementor-inner-section > .elementor-container {
		flex-wrap: wrap;
	}
}

/* `margin-left` is reset because the parent theme spaces columns with
   `[data-elementor-device-mode$="top"] .elementor-column + .elementor-column
   { margin-left: auto }` (style.css), which pushes the third card to the far
   right of the line it wraps onto and leaves a hole on the left. The columns
   tile exactly at these widths, so that auto margin resolves to 0 in the
   3-across layout anyway - the visible gutter between cards comes from the
   column's own padding, not from the margin. */
@media (max-width: 1299px) and (min-width: 900px) {
	body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-inner-column.elementor-col-33 {
		width: 50%;
		margin-left: 0;
	}
}

@media (max-width: 899px) {
	body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-inner-column.elementor-col-33 {
		width: 100%;
		max-width: 480px;
		margin-left: auto;
		margin-right: auto;
	}
}

/* Card height follows its tallest face, with 630px as the floor. */
body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-widget-flip-box .elementor-flip-box {
	display: grid;
	height: auto;
	min-height: 630px;
}

body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-widget-flip-box .elementor-flip-box__layer {
	position: relative;
	grid-area: 1 / 1;
	height: auto;
}

/* --------------------------------------------------------------------------
   Board of Directors - headshots cropped off-centre on the card fronts

   Each card front is the member's photo as a `cover` background. 17 of the 18
   photos are square (Grant 2560x2560, Gale 1500x1500, Nancy only 270x270, and
   so on) while the card is always much taller than it is wide, so `cover`
   scales every photo by its HEIGHT and the surplus width is cropped. At 375px
   the cards are 335 wide and 630-880 tall, which throws away 295-545px of a
   photo that renders 630-880px wide - between 47% and 62% of the image.

   Elementor never emits a background-position for these layers, so they were
   sitting at the CSS initial value of `0% 0%` and that entire surplus came off
   the RIGHT side: the crop kept the leftmost 335px and everything past it -
   usually the subject's face, since headshots are rarely composed hard left -
   fell outside the card. Grant lost the right half of his head at 375px, and
   the same thing happened at desktop widths, just less of it (488-wide cards
   still crop 142px off a 630px render).

   Anchoring the crop at `center` instead takes the surplus evenly off both
   sides, which is where the subject actually is in all 18 photos. The vertical
   value stays `top`, unchanged: `cover` is height-driven here so there is no
   vertical slack to position at these widths, and top-anchoring keeps heads in
   frame on the one portrait-orientation photo (Scott Peters, 1442x1920) where
   a wide card does leave a little.

   This deliberately sits outside any media query - the left-anchored crop is
   wrong at every width, not just on phones.

   `background-size` is pinned to `cover` in the same breath because one card
   was missing it. Barry Sackett's photo is the only one where Elementor never
   wrote a size, so it sat at the initial value of `auto` and rendered at its
   natural 667x667 inside a 335x630 box - the card showed the top of his head
   and nothing else, at every width. The other 17 fronts are already `cover`,
   so this only changes that one card.

   Specificity matches the flip-box rules above: Elementor would write any
   background-position set in the editor as `:is(.elementor-24666, .elementor-32088)
   .elementor-element.elementor-element-XXXX .elementor-flip-box__front`,
   (0,4,0), so `body` brings this to (0,4,1) and wins. That also means a
   per-card position or size set in the editor later will NOT take effect until
   this rule is narrowed or removed.
   -------------------------------------------------------------------------- */

body :is(.elementor-24666, .elementor-32088) .elementor-element.elementor-widget-flip-box .elementor-flip-box__front {
	background-position: center top;
	background-size: cover;
}

/* --------------------------------------------------------------------------
   Donate - height for the embedded Qgiv donation form

   /donate/ used to end its "Give Today" card with a button out to
   secure.qgiv.com. The card now carries the form itself, through Qgiv's own
   embed snippet in an Elementor HTML widget (template 25978, widget e9a4c17):
   a `[data-qgiv-embed]` container plus embed.js, which builds the iframe.

   embed.js gives that iframe a flat `height: 1000px` inline - see
   `initialStandardFormHeight: "1000"` in
   secure.qgiv.com/resources/core/js/embed.js - and never revises it for this
   form. The auto-resizing that its `resize` message handler implements is for
   Qgiv's older form templates; this form is template 8, NEW_STANDARD_FORM,
   whose bundle (donationForm.*.js) contains no resize sender at all. So the
   height is the integrator's to set, and 1000px is short enough to cut the
   first step off mid-checkbox.

   Measured against the live form, `documentElement.scrollHeight` per step:

                       ~760px wide     ~500px wide
     Choose Your Gift      1330            1150
     Your Details          1560            1750

   1350/1280 fits step 1 with a little slack and lets the later steps scroll
   inside the iframe, which is what Qgiv's own 1000px default does anyway - the
   iframe keeps `scrolling="yes"`, so nothing is ever unreachable. Sizing for
   step 2 instead would leave 200-600px of blank white under step 1, and step 1
   is the one every visitor sees. Retune these two numbers if the form's fields
   change.

   `min-height` rather than `height` on purpose: it beats embed.js's inline
   `height` without `!important`, while still letting embed.js grow the iframe
   past it if a future form template does start sending resize messages.

   Worth knowing: the form logs "Attention website owner: The embedded iframe
   for this Qgiv form is not communicating with your website as expected" to
   the console, and will until someone generates an embed for this form in the
   Qgiv control panel and swaps that embed's URL into the widget. The handshake
   it wants gates Qgiv's analytics and Abandoned Gift Reminders; it does not
   affect sizing, and donations go through without it.
   -------------------------------------------------------------------------- */

.tf-qgiv-embed {
	max-width: 720px;
	margin: 0 auto;
}

.tf-qgiv-embed iframe {
	min-height: 1350px;
}

.tf-qgiv-embed__fallback {
	margin: 0;
	text-align: center;
	font-size: 0.85rem;
}

@media (max-width: 767px) {
	.tf-qgiv-embed iframe {
		min-height: 1280px;
	}
}

/* --------------------------------------------------------------------------
   Every image frame is horizontal by default, and the editor can override it

   Elementor's image-widget Height control writes a fixed pixel height with
   `object-fit: cover`. Because the frame's WIDTH tracks the viewport while that
   height never moves, the displayed aspect ratio is pure arithmetic and rolls
   between landscape and portrait as the window resizes. Measured on the home
   page before this fix (width x height, and the ratio):

                        1600px         1024px        767px        390px
     carousel         379x500 0.76   235x500 0.47  351x500 0.70  335x500 0.67
     focus row        356x300 1.19   172x300 0.57  672x300 2.24  275x300 0.92
     featured        1024x700 1.46   949x700 1.36  692x700 0.99      ~0.49

   The carousel was never landscape at any width, and the focus row swung from
   0.57 to 2.24 across a 257px change in window width.

   `width: 100%` plus `aspect-ratio` derives the height from the container
   width, so the frame scales with the window and can only ever be 3:2.
   `height: auto` is load-bearing: without it a px height would still be the
   definite dimension and `aspect-ratio` would be ignored.

   THE SPECIFICITY IS DELIBERATELY LOW, and that is the whole design here.

   Elementor prints per-widget settings at
   `.elementor-<template> .elementor-element.elementor-element-<widget> img`,
   which is (0,3,1). The selector below is (0,1,2) - one class, two elements -
   so it loses to anything set in the editor:

     (0,1,2)  this default                 aspect-ratio: 3 / 2
     (0,2,2)  a .tf-ratio-* escape class    a chosen ratio
     (0,3,1)  a Height set in the editor    wins over both

   That ordering is why the 185 stale height settings had to be deleted from
   the database (_tooling/deploy/strip-image-widget-heights.php, values recorded
   in _tooling/docs/image-widget-heights-removed.md) rather than simply
   out-specified. A rule strong enough to beat an EXISTING per-widget height is
   by definition strong enough to beat the NEXT one, so out-specifying them
   would have permanently disabled the Height control - the earlier version of
   this fix did exactly that, across 118 enumerated selectors. With the stale
   values gone, one short rule covers all 219 image widgets on the site,
   including any added later, and the editor stays in charge.

   So: to give one image a different shape, set a Height on it in Elementor and
   that widget stops being 3:2. To give it a different RATIO rather than a fixed
   height - which is what usually wants doing, since a fixed height brings the
   original drift back for that one frame - add one of the escape classes below
   in Advanced > CSS Classes.

   `object-fit: cover` is set at the same low specificity on purpose, so the 52
   widgets that already carry `object-fit: contain` keep it: the sponsor logo
   rows (ReLeaf template 23578 has 15, Corporate Partnerships 34094 six,
   Community Visioning 24968 three) stay letterboxed rather than being cropped.
   They do lose their old uniform pixel height, which is what used to line every
   logo up at one visual size regardless of its own proportions - so those rows
   now read less evenly. `tf-ratio-natural` on the row puts that back in one
   move, or a Height on the widgets restores the original behaviour exactly.

   Worth expecting elsewhere: the 48 widgets that never had a height were
   rendering at their natural ratio and are now cropped to 3:2 like everything
   else. Most are landscape photos, but 18 are not - three copies of a tall
   204x357 Illinois DNR logo, a 226x226 Candid seal, and eleven square 1080x1080
   infographics (Place / Purpose / Plan and the Graphics-for-Website set). Those
   are the frames most likely to want `tf-ratio-natural`.
   -------------------------------------------------------------------------- */

body .elementor-widget-image img {
	width: 100%;
	height: auto;
	aspect-ratio: 3 / 2;
	object-fit: cover;
}

/* --------------------------------------------------------------------------
   Saved object-fit / object-position, which Elementor stopped printing

   Elementor's `object-fit` and `object-position` controls are CONDITIONAL on a
   custom Height being set. Once strip-image-widget-heights.php removed the
   heights, Elementor stopped emitting either property - even though both values
   are still saved in _elementor_data - so without this block the default's
   `object-fit: cover` above would apply to 53 widgets whose author had
   deliberately chosen `contain`, and the sponsor logos would be CROPPED rather
   than letterboxed. Measured on /corporate-partnerships/ before this block:
   every logo computed `object-fit: cover` and Elementor emitted nothing for
   them but `padding` and `border-radius`.

   The `object-position` half matters just as much and is easier to miss: seven
   frames were anchored `top center` to keep heads and subjects in frame, and
   they had silently fallen back to the CSS initial `50% 50%`.

   Specificity is (0,2,2): above the (0,1,2) default, below the (0,3,1) Elementor
   emits per widget. So setting a Height in the editor restores that widget's
   own height AND its saved object-fit in one move and beats these rules - which
   is why these selectors deliberately omit the template id, since including it
   would reach (0,3,2) and start blocking the editor again. That shorter selector
   assumes the widget ids here are unique site-wide;
   _tooling/generate-objectfit-preserve-css.php verifies that and refuses to emit
   if it ever stops being true. Re-run it after changing an object-fit.
   -------------------------------------------------------------------------- */

/* Saved object-fit: contain (52 widgets) */
body .elementor-widget-image.elementor-element-0401254 img,
body .elementor-widget-image.elementor-element-0b698c1 img,
body .elementor-widget-image.elementor-element-0c83b14 img,
body .elementor-widget-image.elementor-element-1517c60 img,
body .elementor-widget-image.elementor-element-1c2bd88 img,
body .elementor-widget-image.elementor-element-261041b img,
body .elementor-widget-image.elementor-element-2eda457 img,
body .elementor-widget-image.elementor-element-331829d img,
body .elementor-widget-image.elementor-element-3b94720 img,
body .elementor-widget-image.elementor-element-3c1b932 img,
body .elementor-widget-image.elementor-element-3ef9e35 img,
body .elementor-widget-image.elementor-element-3fd2023 img,
body .elementor-widget-image.elementor-element-46a51ae img,
body .elementor-widget-image.elementor-element-4ddc0d3 img,
body .elementor-widget-image.elementor-element-51f685c img,
body .elementor-widget-image.elementor-element-53a242c img,
body .elementor-widget-image.elementor-element-669ff9e img,
body .elementor-widget-image.elementor-element-6b9dd4a img,
body .elementor-widget-image.elementor-element-70f6983 img,
body .elementor-widget-image.elementor-element-75c7c13 img,
body .elementor-widget-image.elementor-element-76cfa4f img,
body .elementor-widget-image.elementor-element-79b7c91 img,
body .elementor-widget-image.elementor-element-7d1a81e img,
body .elementor-widget-image.elementor-element-81677d8 img,
body .elementor-widget-image.elementor-element-85dcd62 img,
body .elementor-widget-image.elementor-element-8adc834 img,
body .elementor-widget-image.elementor-element-8ec5845 img,
body .elementor-widget-image.elementor-element-8f65f2e img,
body .elementor-widget-image.elementor-element-931ae26 img,
body .elementor-widget-image.elementor-element-9598a20 img,
body .elementor-widget-image.elementor-element-95ff5f4 img,
body .elementor-widget-image.elementor-element-9b49855 img,
body .elementor-widget-image.elementor-element-9f5aa86 img,
body .elementor-widget-image.elementor-element-9f85e3b img,
body .elementor-widget-image.elementor-element-a154d84 img,
body .elementor-widget-image.elementor-element-a2a5e4c img,
body .elementor-widget-image.elementor-element-ab20e4f img,
body .elementor-widget-image.elementor-element-ad6e3f1 img,
body .elementor-widget-image.elementor-element-b269755 img,
body .elementor-widget-image.elementor-element-b6c18db img,
body .elementor-widget-image.elementor-element-bb468e9 img,
body .elementor-widget-image.elementor-element-bdd5212 img,
body .elementor-widget-image.elementor-element-ca85866 img,
body .elementor-widget-image.elementor-element-d1add13 img,
body .elementor-widget-image.elementor-element-d21e253 img,
body .elementor-widget-image.elementor-element-d42a980 img,
body .elementor-widget-image.elementor-element-dac3414 img,
body .elementor-widget-image.elementor-element-e63f451 img,
body .elementor-widget-image.elementor-element-e7c882f img,
body .elementor-widget-image.elementor-element-f1adffa img,
body .elementor-widget-image.elementor-element-fdc2f36 img,
body .elementor-widget-image.elementor-element-fe052b3 img {
	object-fit: contain;
}

/* Saved object-fit: scale-down (1 widgets) */
body .elementor-widget-image.elementor-element-36e61a6 img {
	object-fit: scale-down;
}

/* Saved object-position: bottom center (1 widgets) */
body .elementor-widget-image.elementor-element-c2b41ca img {
	object-position: bottom center;
}

/* Saved object-position: bottom right (1 widgets) */
body .elementor-widget-image.elementor-element-0b40654 img {
	object-position: bottom right;
}

/* Saved object-position: top center (7 widgets) */
body .elementor-widget-image.elementor-element-2664eee4 img,
body .elementor-widget-image.elementor-element-36213141 img,
body .elementor-widget-image.elementor-element-56fd5f43 img,
body .elementor-widget-image.elementor-element-5ed42e86 img,
body .elementor-widget-image.elementor-element-ea32cbb img,
body .elementor-widget-image.elementor-element-eb8c934 img,
body .elementor-widget-image.elementor-element-f0fcb10 img {
	object-position: top center;
}

/* --------------------------------------------------------------------------
   Escape classes - Elementor > Advanced > CSS Classes

   Each pair matches the class on the image widget itself and on any ancestor,
   because Elementor puts a CSS class on the `.elementor-element` wrapper either
   way. Putting one on a section or container therefore retags every image
   inside it - one entry fixes a whole logo row.

   Both selectors are (0,2,2): above the default, below anything set in the
   editor, so a Height still wins over a class.

     tf-ratio-natural   the image's own shape, never cropped or stretched
     tf-ratio-square    1:1
     tf-ratio-portrait  3:4
     tf-ratio-wide      16:9
   -------------------------------------------------------------------------- */

body .elementor-widget-image.tf-ratio-natural img,
body .tf-ratio-natural .elementor-widget-image img {
	width: auto;
	max-width: 100%;
	height: auto;
	aspect-ratio: auto;
	object-fit: fill;
}

body .elementor-widget-image.tf-ratio-square img,
body .tf-ratio-square .elementor-widget-image img {
	aspect-ratio: 1 / 1;
}

body .elementor-widget-image.tf-ratio-portrait img,
body .tf-ratio-portrait .elementor-widget-image img {
	aspect-ratio: 3 / 4;
}

body .elementor-widget-image.tf-ratio-wide img,
body .tf-ratio-wide .elementor-widget-image img {
	aspect-ratio: 16 / 9;
}

/* The one hand-written exception, and only to a width rather than a ratio. The
   home page featured image has no `width` setting, so before this fix it sized
   itself FROM its height - 700px x its 1.46 native ratio, which is the 1024px
   it rendered at - and sat centred in a 1525px container. Left to the default's
   `width: 100%` it would fill that container and stand 1016px tall, so this
   pins it at the width it already had: 1024x683 now against 1024x700 before.
   Delete this and set a Width in the editor instead if that is easier to find
   later; the Width control works, this only pre-empts it. */
body :is(.elementor-24657, .elementor-32115) .elementor-element.elementor-element-58ea5cd0 img {
	max-width: 1024px;
}

/* --------------------------------------------------------------------------
   Event Calendar category filter

   Styles the chip row that assets/js/event-calendar-category-filter.js inserts
   above an Essential Addons "Event Calendar" widget. Lives here rather than in
   its own stylesheet because the script is enqueued from
   elementor/frontend/before_render, by which point <head> has already been
   sent - a stylesheet enqueued that late never prints.

   The chips borrow the calendar's own toolbar button states so the filter
   reads as part of the widget rather than bolted above it. Those states come
   from the widget's saved settings on the VOLUNTEER template (Elementor global
   colors): text #000000 at rest, primary #00853F on hover, and white on
   #365B6D when active. Contrast is 21:1, 4.7:1 and 7.3:1 against their own
   backgrounds, so every state clears AA - which the widget's own hover state
   does not, at 4.3:1 against the #F6F6F6 calendar background.

   Two things about this site make the obvious version of these rules fail, and
   both were measured on the rendered page rather than guessed:

   1. Every declaration is doubled up on `.gh-ec-filter` for specificity. The
      Elementor kit styles `.elementor-kit-29188 button` - specificity (0,1,1),
      accent yellow #FFC107 on secondary #0D1F0E with `border-radius: 0` - and
      a lone `.gh-ec-filter__chip` at (0,1,0) loses to it. Chaining the parent
      reaches (0,2,0) and wins without hard-coding the kit ID, which changes
      whenever the kit is rebuilt. Padding was the only property that came
      through before, because the kit rule does not set it.

   2. Sizes are in px, not rem. The parent theme sets `html { font-size: 62.5% }`
      (style.min.css, minified from sass), so 1rem is 10px here and the 0.875rem
      that reads as 14px anywhere else rendered at 8.75px. The 14px below sits
      deliberately just under the 16px the calendar's own controls use, which
      are set in px by the widget and so are unaffected by the same trap.

   Elementor globals are used with hard-coded fallbacks: the widget renders
   inside Elementor so the variables are always defined in practice, but the
   fallbacks keep the chips legible if the kit is ever swapped out.
   -------------------------------------------------------------------------- */

.gh-ec-filter {
	margin: 0 0 20px;
}

.gh-ec-filter .gh-ec-filter__label {
	display: block;
	margin-bottom: 8px;
	font-family: var(--e-global-typography-accent-font-family, "Montserrat"), sans-serif;
	font-size: 14px;
	font-weight: 600;
	color: var(--e-global-color-text, #000000);
}

.gh-ec-filter .gh-ec-filter__chips {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.gh-ec-filter .gh-ec-filter__chip {
	padding: 7px 14px;
	border: 1px solid var(--e-global-color-9eaa092, #d9d9d9);
	border-radius: 999px;
	background-color: transparent;
	font-family: var(--e-global-typography-accent-font-family, "Montserrat"), sans-serif;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.3;
	/* The kit's button rule also sets `text-transform: capitalize`, which
	   renders the "In person" term as "In Person". Chips carry taxonomy term
	   names, so they have to read exactly as they were entered. */
	text-transform: none;
	color: var(--e-global-color-text, #000000);
	cursor: pointer;
	transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.gh-ec-filter .gh-ec-filter__chip:hover,
.gh-ec-filter .gh-ec-filter__chip:focus-visible {
	border-color: var(--e-global-color-primary, #00853f);
	background-color: transparent;
	color: var(--e-global-color-primary, #00853f);
}

.gh-ec-filter .gh-ec-filter__chip:focus-visible {
	outline: 2px solid var(--e-global-color-primary, #00853f);
	outline-offset: 2px;
}

/* Both the class and aria-pressed are styled, so the pressed state survives if
   the class is ever renamed and stays visible to assistive tech either way. */
.gh-ec-filter .gh-ec-filter__chip.is-active,
.gh-ec-filter .gh-ec-filter__chip[aria-pressed="true"] {
	border-color: var(--e-global-color-f7c6188, #365b6d);
	background-color: var(--e-global-color-f7c6188, #365b6d);
	color: var(--e-global-color-651faef, #ffffff);
}

.gh-ec-filter .gh-ec-filter__status {
	margin: 8px 0 0;
	font-family: var(--e-global-typography-accent-font-family, "Montserrat"), sans-serif;
	font-size: 13px;
	color: var(--e-global-color-text, #000000);
	opacity: 0.75;
}

/* Same 44px tap target and the same breakpoint as the mobile menu rule at the
   top of this file. At 14px/1.3 the chips measure 34.2px with the 7px padding
   they use on desktop; 12px brings them to 44.2px. Measured at 390px wide the
   six chips wrap to two rows inside a 295px column with no overflow, so the
   extra height is the only change - and the wider gap keeps the two rows from
   reading as one block. */
@media (max-width: 1024px) {
	.gh-ec-filter .gh-ec-filter__chips {
		gap: 10px;
	}

	.gh-ec-filter .gh-ec-filter__chip {
		padding-top: 12px;
		padding-bottom: 12px;
	}
}

/* --------------------------------------------------------------------------
   Event Calendar table - stop it overrunning the viewport on phones

   The Essential Addons "Event Calendar" in its table layout put the whole page
   into horizontal scroll on phones. Measured at a 500px viewport: the wrapper
   is 405px wide, the table rendered 490px, and it hung 45px past the body. The
   page scrolled sideways rather than the table, because `overflow-x: hidden` is
   set on `body` but not on `html`, and in that combination the overflow still
   propagates to the viewport.

   A `table-layout: auto` table cannot be squeezed below its own min-content
   width, and that min-content was 490px against a 405px container. Two things
   made it up, both measured with `width: min-content` forced on the table,
   which is the only way to read the intrinsic size rather than the used one:

   - The header font, worth 78px. The widget's header typography is 40px with no
     responsive values set, so phones get the desktop size, and "Description"
     alone measures 232px at 40px + 20px cell padding - exactly the 253px
     min-content the description column had.
   - Long unbreakable tokens in the descriptions for the rest. The Events
     Calendar strips tags out of event copy, which runs words together
     ("VolunteersWe", "spaces)Runners"), and real entries hold things like
     "Ulteig/ITC/Alliant" at 168px on its own.

   Scrolling the table inside its own wrapper is the fix rather than making it
   narrower, because at 405px there is no honest way to fit three columns: both
   alternatives were built and measured first.

   - Shrinking the columns to fit works - `table-layout: fixed` with 30/25/45
     percentages landed the table exactly on the container at every width down
     to 240px - but it reads badly. The description column falls to 182px, event
     titles run to four lines, and "October" splits mid-word in the date column.
   - `overflow-wrap: anywhere` on the cells is the only thing that lowers the
     intrinsic width (`break-word`, which these cells already have, lets a long
     word spill but leaves min-content at the full word width - it took the
     table from 490px to 157px). Same problem though: once min-content is that
     low the browser hands each column less room than its longest word needs, so
     "Volunteer" and the "Description" heading break mid-word too.

   So the table keeps its natural column widths and the wrapper scrolls - the
   standard responsive-table pattern, and the only option here that costs
   nothing. Sorting, search and pagination are untouched, no text is squeezed or
   broken, and it holds for any future event copy however long. The cost is an
   85px sideways swipe to reach the end of the descriptions.

   `overflow-y` is deliberately left alone. Per spec, setting one axis to
   something other than `visible` forces the other to `auto`, but the wrapper's
   height is content-driven so nothing overflows vertically and no vertical
   scrollbar appears - verified on the page.

   Worth knowing: a stacked "card" layout would read better than either of
   these on a phone - title, date and description each on their own full-width
   line. It is viable here, which is not obvious: fancyTable only ever writes
   `display: none` onto hidden rows and an empty style onto shown ones (checked
   across pagination, sorting and search), never `display: table-row`, so a
   plain `tbody tr { display: block }` restacks the visible rows while leaving
   pagination's hiding intact. It is not done here because it is a redesign of
   the mobile presentation rather than a fix, and it would drop sorting with the
   header row.
   -------------------------------------------------------------------------- */

.eael-event-calendar-wrapper.layout-table {
	overflow-x: auto;
}

/* --------------------------------------------------------------------------
   Page headers - swoopy bottom edge

   Replaces the Elementor "opacity-tilt" shape divider at the bottom of every
   page banner - three flat diagonals in primary green - with an S-curve that
   lets the white page sweep up into the banner image, plus a tapering accent
   stripe along its edge. Modelled on treesatlanta.org, whose hero uses the
   same construction (a body path in the page background colour with two accent
   crescents peeking above it), redrawn here in the TF palette: accent yellow
   #FFC107 on the left half, light green #A0CB67 on the right, meeting at a
   point in the middle.

   Most of it is applied off the divider's own presence rather than by listing
   template and element ids, which the audit says is exactly equivalent and far
   less to maintain. `_tooling/audit-shape-dividers.php` walked every published
   template: 38 carry a bottom divider, every one of them is `opacity-tilt`,
   every one of them is on the first top-level element of its page - and there
   are no bottom dividers anywhere else, so "top-level section or container with
   a bottom divider" *is* the set of dividered page headers. All 38 are followed
   by a section with no background of its own, so the body path can be a flat
   `#ffffff` for the page showing through rather than needing to be themed per
   page.

   Six of the 38 (ABOUT US 27621 and its three copies, RELEAF 23578, ONE
   MILLION TREES 24698) also carry a *top* divider, tucked under the fixed
   header where it is barely visible. Those are deliberately left alone: this
   is about the edge between the banner and the page.

   Drawn on the banner's own ::after rather than by restyling
   `.elementor-shape-bottom`, so the shape survives someone switching the
   divider off in Elementor. The divider element is hidden instead. The `:has()`
   in the selector is what keeps the two in step - a banner only gets a swoop
   automatically if it had a divider to replace.

   FIVE HEADERS ARE OPTED IN BY HAND. They are the same 550px full-bleed photo
   banner as the other 38 but were never given a divider, so `:has()` cannot
   find them and they ended in a hard straight edge:

     /cedar-rapids-adopt/      29186  288919c2   550px
     /des-moines-adopt/        33320  288919c2   550px
     /honor-roll-of-donors/    24908  5e50f21c   550px
     /stewards/                24747  39688cae   550px  title 183px clear
     /roadside-plans/          25141  1bee196f   256px  title  93px clear

   Both templates use element `288919c2` - element ids are NOT unique across
   duplicated templates - so every selector is scoped by template id as well.

   /roadside-plans/ gets a shorter swoop of its own. Its banner is 256px rather
   than 550px and its title is vertically centred, leaving only 93px between the
   title's baseline and the bottom edge; the standard 108px would run straight
   through the words. 4.5vw lands it at 65px at 1440 and caps at 72px, keeping
   at least 21px of clearance.

   Five things about these banners make the obvious version fail, all measured
   on the rendered page rather than guessed:

   1. `z-index: 4` is load-bearing on the home page. The hero headline block
      that mu-plugins/tf-ux.php injects (`.tf-hero-copy`) sits at z-index 3
      with a full-bleed black scrim for text contrast, ending at
      rgba(0,0,0,.5) along the bottom edge. At any lower z-index the scrim
      paints over the swoop and renders the white as mid-grey - which is what
      the green tilt it replaces was suffering from.

   2. `isolation: isolate` on the section is what makes that 4 safe on the
      other 37. Elementor leaves these sections `position: relative` with no
      z-index, so they are not stacking contexts and a z-index of 4 would
      otherwise compete with the whole page - painting the swoop over anything
      later that happens to overlap its box. Isolating confines the 4 to inside
      the banner, where the only thing it has to beat is that scrim.

   3. `pointer-events: none`, because on the home page that z-index does put
      the box over the bottom of the hero's Donate/Volunteer buttons. There is
      no ink up there - the white edge starts 50% down the box and the accents
      taper to nothing at the horizontal centre - but the box itself would
      still swallow clicks.

   4. `background-size: 100% 100%` with `preserveAspectRatio='none'` on the
      SVG, so the curve stretches to the window instead of scaling with it.
      Elementor's own dividers behave the same way; without both halves of that
      pair the shape letterboxes and the seam at the bottom opens up.

   5. The height has to be set here. Elementor gives the divider no height of
      its own - the container tracks the height it puts on the inner <svg> - so
      an ::after with no height renders nothing.

   `bottom: -1px` (matching `.elementor-shape-bottom`) overlaps the section
   below by a pixel to hide the subpixel seam at fractional zoom levels. The
   body path is `#ffffff` rather than a kit global because the swoop has to
   agree with `body`, not with the kit.

   Two of the 38 headers are Elementor *containers* rather than sections -
   VOUCHER 23917 and its copy 33268, element cd4a5f6, rendering /voucher-program/
   and /tree-certificates/ - so both element types are matched below. Containers
   need the same treatment and no more: Elementor leaves them `position:
   relative` too, and reserves only `::before` on them (it is the background
   overlay, and is already `position: absolute`), which is why the swoop is on
   ::after and not ::before.

   Not covered, by design: any page whose header comes from the theme rather
   than an Elementor template - single blog posts and single events, which have
   no banner to put an edge on.

   Note the seven banners that set `overflow: hidden` (/blog/, /campus-canopy-10/,
   /ecofest-vendor-fair/, /fund/, /growingfutures/,
   /illinois-community-forestry/, /treekeepers/) clip the `bottom: -1px` back to
   flush. That loses the anti-seam pixel but cannot open a gap, and Elementor's
   own divider had exactly the same constraint on those pages.
   -------------------------------------------------------------------------- */

body .elementor-section.elementor-top-section > .elementor-shape-bottom,
body .e-con.e-parent > .elementor-shape-bottom {
	display: none;
}

/* The banners that get a swoop: any top-level section or container that had a
   divider, plus the five hand-listed above. `:is()` takes the specificity of
   its most specific branch, so this whole list sits at (0,3,1) - the same place
   the two-selector version did. */
body :is(
	.elementor-section.elementor-top-section:has(> .elementor-shape-bottom),
	.e-con.e-parent:has(> .elementor-shape-bottom),
	:is(.elementor-29186, .elementor-32069) .elementor-element-288919c2,
	:is(.elementor-33320, .elementor-32070) .elementor-element-288919c2,
	:is(.elementor-24908, .elementor-32103) .elementor-element-5e50f21c,
	:is(.elementor-24747, .elementor-32111) .elementor-element-39688cae,
	:is(.elementor-25141, .elementor-32098) .elementor-element-1bee196f
) {
	isolation: isolate;
}

body :is(
	.elementor-section.elementor-top-section:has(> .elementor-shape-bottom),
	.e-con.e-parent:has(> .elementor-shape-bottom),
	:is(.elementor-29186, .elementor-32069) .elementor-element-288919c2,
	:is(.elementor-33320, .elementor-32070) .elementor-element-288919c2,
	:is(.elementor-24908, .elementor-32103) .elementor-element-5e50f21c,
	:is(.elementor-24747, .elementor-32111) .elementor-element-39688cae,
	:is(.elementor-25141, .elementor-32098) .elementor-element-1bee196f
)::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: -1px;
	z-index: 4;
	/* 7.5vw tracks the 1440px design at 108px and flattens on the way down,
	   floored at 60px so the curve stays readable on a phone and capped at
	   118px so it does not eat the shortest banner on an ultrawide. */
	height: clamp(60px, 7.5vw, 118px);
	pointer-events: none;
	background-repeat: no-repeat;
	background-position: bottom center;
	background-size: 100% 100%;
	/* viewBox 1440x140. The two accent paths share the body path's curve as
	   their lower edge, so they can only ever read as a stripe on top of it:
	   each is the same curve offset upward, by 16 units at the outer edge
	   tapering to 0 where they meet at x=720. */
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 140' preserveAspectRatio='none'%3E%3Cpath fill='%23ffc107' d='M0,54 C240,18 480,24 720,86 C480,40 240,34 0,70 Z'/%3E%3Cpath fill='%23a0cb67' d='M720,86 C1000,130 1290,100 1440,10 L1440,26 C1290,116 1000,132 720,86 Z'/%3E%3Cpath fill='%23ffffff' d='M0,70 C240,34 480,40 720,86 C1000,132 1290,116 1440,26 L1440,140 L0,140 Z'/%3E%3C/svg%3E");
}

/* /roadside-plans/ - a 256px banner with its title centred, so only 93px of the
   bottom edge is free. See the note above; the standard 108px cuts the words.

   The extra classes are for specificity, not for matching: the rule above sits
   at (0,3,1) because `:is()` inherits the (0,3,0) of its most specific branch,
   so the obvious `body :is(.elementor-25141, .elementor-32098) .elementor-element-1bee196f` at (0,2,1)
   loses to it and the height never changes. This reaches (0,4,1).

   The 30px floor is lower than the 60px the other banners use because this one
   shrinks with the window while they do not: 256px at 1440 but 185px at 768 and
   148px at 390, where a 48px swoop left 1px under the title. Measured
   clearances with the values below: 23px at 1920, 30 at 1440, 41 at 1200, 47 at
   1025, 31 at 768 and 19 at 390. */
body :is(.elementor-25141, .elementor-32098) .elementor-section.elementor-top-section.elementor-element-1bee196f::after {
	height: clamp(30px, 4.5vw, 72px);
}

/* --------------------------------------------------------------------------
   /events/ - category filter chips

   Styles the chip row that tribe/events/v2/components/filter-bar.php renders
   inside The Events Calendar's own Views v2 container, between the events bar
   and the month grid.

   These read visually the same as the Essential Addons chip row further up this
   file, but they cannot share its rules, because the two live on pages built
   very differently:

   1. The Elementor kit stylesheet is NOT enqueued on /events/. It is a plugin
      archive template, not an Elementor page - the stylesheets it loads are
      goodheart-theme, goodheart-child and The Events Calendar's own, with no
      elementor-post-<kit id>.css among them, and `--e-global` appears nowhere
      in the markup. So every `var(--e-global-color-*)` the other chips use
      would silently fall through to its hard-coded fallback here.

   2. Every selector is prefixed with `.tribe-common` for specificity, for the
      same reason the other chips are doubled up on their own parent - only the
      rule doing the overriding is a different one. The Elementor kit is absent
      here, but The Events Calendar ships its own reset,

        .tribe-common a, .tribe-common abbr, ... { border: 0; margin: 0;
        padding: 0 }

      at specificity (0,1,1), and a lone `.gh-events-cat-filter__chip` at
      (0,1,0) loses to it. Measured before the prefix was added: padding
      computed to 0 and the border to `0px none`, so the chips rendered as 18px
      of bare text with a border-radius on nothing. The prefix reaches (0,2,0)
      and wins. It catches the same reset on `nav`, `h2` and `ul`, which is why
      the wrapper and label margins need it too. Only the active state came
      through unprefixed, because that reset does not set a background.

   So the colours come from The Events Calendar's own `--tec-*` variables
   instead, which has a real advantage: the Customizer (Appearance > Customize >
   Events) writes its accent colour into `--tec-color-accent-primary` on :root,
   so the chips follow the calendar whenever those settings change rather than
   drifting out of step with it. Accent is #365b6d there today. Fallbacks are
   the plugin's own defaults, for the case where only the skeleton stylesheet
   loads.

   Note the Customizer's block is inline in the FOOTER and also targets :root -
   it would win the tie against anything declared on :root in this file. That is
   only a problem for rules that try to override a `--tec-*` value; consuming
   one, as below, resolves at use time and is unaffected.

   Contrast, against each state's own background: 16.9:1 at rest (#141827 on
   white), 7.3:1 on hover (#365b6d on white) and 7.3:1 when active (white on
   #365b6d). All three clear AA.

   Sizes are in px. The parent theme sets `html { font-size: 62.5% }`, so 1rem
   is 10px on this site - see the note on the Essential Addons chips above. The
   calendar's own controls are unaffected because The Events Calendar sets its
   `--tec-font-size-*` scale in px too.
   -------------------------------------------------------------------------- */

.tribe-common .gh-events-cat-filter {
	margin: 0 0 var(--tec-spacer-5, 24px);
}

.tribe-common .gh-events-cat-filter__label {
	margin: 0 0 var(--tec-spacer-1, 8px);
	font-family: var(--tec-font-family-sans-serif, inherit);
	font-size: 14px;
	font-weight: var(--tec-font-weight-bold, 700);
	line-height: 1.4;
	color: var(--tec-color-text-primary, #141827);
}

.tribe-common .gh-events-cat-filter__chips {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.tribe-common .gh-events-cat-filter__chips-item {
	margin: 0;
	list-style: none;
}

.tribe-common .gh-events-cat-filter__chip {
	display: inline-block;
	padding: 7px 14px;
	border: 1px solid var(--tec-color-border-default, #d5d5d5);
	border-radius: 999px;
	background-color: transparent;
	font-family: var(--tec-font-family-sans-serif, inherit);
	font-size: 14px;
	font-weight: var(--tec-font-weight-bold, 700);
	line-height: 1.3;
	/* Chips carry taxonomy term names, so they have to read exactly as they
	   were entered - "In person", not "In Person". */
	text-transform: none;
	text-decoration: none;
	color: var(--tec-color-text-primary, #141827);
	transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.tribe-common .gh-events-cat-filter__chip:hover,
.tribe-common .gh-events-cat-filter__chip:focus-visible {
	border-color: var(--tec-color-accent-primary, #334aff);
	background-color: var(--tec-color-accent-primary-background, #334aff12);
	text-decoration: none;
	color: var(--tec-color-accent-primary, #334aff);
}

.tribe-common .gh-events-cat-filter__chip:focus-visible {
	outline: 2px solid var(--tec-color-accent-primary, #334aff);
	outline-offset: 2px;
}

/* The class and aria-current are both styled, so the state stays visible to
   assistive tech and on screen even if one of the two is ever dropped. */
.tribe-common .gh-events-cat-filter__chip--active,
.tribe-common .gh-events-cat-filter__chip[aria-current="true"],
.tribe-common .gh-events-cat-filter__chip--active:hover,
.tribe-common .gh-events-cat-filter__chip[aria-current="true"]:hover {
	border-color: var(--tec-color-accent-primary, #334aff);
	background-color: var(--tec-color-accent-primary, #334aff);
	color: var(--tec-color-background, #ffffff);
}

/* Same 44px tap target and breakpoint as the other chip row: 14px/1.3 measures
   34px with the desktop padding, and 12px brings it to 44px. */
@media (max-width: 1024px) {
	.tribe-common .gh-events-cat-filter__chips {
		gap: 10px;
	}

	.tribe-common .gh-events-cat-filter__chip {
		padding-top: 12px;
		padding-bottom: 12px;
	}
}

/* --------------------------------------------------------------------------
   Donate - the text column beside the Qgiv form

   The "Give Today" row used to be a single 100%-wide inner column: heading,
   then form, with the form shoved right by a hand-set `margin-left: 600px` on
   the HTML widget to clear space on its left. That space is now a real 40%
   text column, and the row is 40/60 - see
   `_tooling/deploy/apply-donate-text-column.php`, which is also what re-applies
   the structural half of this on production.

   Two things that change need CSS, because neither is an Elementor control.

   1. THE ROW CAP. The section is `layout: full_width`, so Elementor sets no
      max-width on its container at all - only
      `.elementor-section-boxed > .elementor-container` carries one, and this
      section is not boxed. Left alone the row therefore tracks the viewport,
      and at 2560px the text column alone is over 950px wide - an unreadable
      measure. Capping the CONTAINER rather than the section keeps the grey
      band (which is the section's own background) running edge to edge while
      the content inside it stays contained.

      1360px is chosen off the form's own width, not picked for looks:
      `.tf-qgiv-embed` below caps the form at 720px, and 60% of 1360 is 816px,
      less the 10px default column-gap padding each side, leaves 796px - so the
      form renders at its full 720px with a little slack rather than being
      squeezed narrower. The text column's 40% then measures ~515px of text,
      around 75 characters. Retune the two together: dropping this below
      ~1250px starts shrinking the form.

   2. THE TABLET STACK. The columns are set to 100% at tablet in Elementor, but
      that alone does nothing here. Elementor leaves legacy section rows
      `flex-wrap: nowrap` above 767px, so two 100%-wide columns just shrink to
      share one line instead of stacking - `flex-wrap: wrap` has to be set on
      the container too. And once the row does wrap, GoodHeart's
      `.elementor-column + .elementor-column { margin-left: auto }` (parent
      `style.min.css`) throws the second column to the far right of its new
      line, leaving a hole on the left, so that needs resetting as well. Same
      pair of gotchas as the Board of Directors flip-box fix further up this
      file. Mobile (<=767px) is handled by Elementor natively and needs
      neither.

   The heading is deliberately NOT inside this section any more - it sits in the
   parent column so it centres over both columns - which is why it carries the
   grey band colour itself as a widget background. Both use Elementor global
   `a954db2`, so the two stay in step.
   -------------------------------------------------------------------------- */

.tf-donate-give > .elementor-container {
	max-width: 1360px;
	margin-right: auto;
	margin-left: auto;
}

@media (max-width: 1024px) {
	.tf-donate-give > .elementor-container {
		flex-wrap: wrap;
	}

	.tf-donate-give > .elementor-container > .elementor-column {
		margin-left: 0;
	}
}

/* --------------------------------------------------------------------------
   Elementor carousel pagination dots - undoing Jetpack's Swiper stylesheet

   On the home page the "What's Happening" carousel's dots sat jammed against
   the far left edge of the page with the first two bullets clipped off-screen
   entirely, instead of centred under the slides.

   It is a collision between two copies of Swiper's CSS. Elementor positions
   nested-carousel dots off its own custom properties:

       .elementor-widget-n-carousel .swiper-pagination-bullets {      (0,2,0)
           inset-inline-start: calc(--...-horizontal-position + --...-offset);
           top:                calc(--...-vertical-position   + --...-offset);
           width: max-content;
           transform: translate(--...-horizontal-transform,
                                --...-vertical-transform);
       }

   Jetpack ships its own Swiper build for its slideshow block, and because the
   home page uses one, `jetpack/_inc/blocks/swiper.css` is enqueued there and
   flattens three of those four declarations:

       .swiper-pagination-bullets.swiper-pagination-horizontal {      (0,2,0)
           left: 0; top: auto; width: 100%;
       }

   Same specificity, later in the cascade, so Jetpack wins. What survives is
   the `transform` - and `translateX(-50%)` against a box that is now the full
   1545px width of the carousel rather than the 120px the dots actually need
   shifts it 772px to the left, which is exactly how far off the dots were.

   The three lines below restore Elementor's own values at (0,3,0) so they beat
   Jetpack's without touching anything else. They deliberately re-state the
   custom properties rather than hard-coding a position: the Dots > Position
   controls in the editor are what set those variables per widget, so they stay
   authoritative and this stays a fix for the collision rather than a lock on
   one layout. `bottom` needs no reset - with `top` and `height` both non-auto
   the box is over-constrained and CSS drops `bottom` on its own.

   This affects every page carrying both an Elementor carousel and a Jetpack
   slideshow. On pages without the Jetpack stylesheet it changes nothing, since
   it only repeats what Elementor already declares.

   The dots are also 350px off centre in the saved widget settings, from a
   `dots_horizontal_offset` that could never have had a visible effect while
   Jetpack's `left: 0` was overriding it. That is data, not CSS, so it is
   cleared by `_tooling/deploy/center-carousel-dots.php` - which is also what
   re-applies that half of this on production.

   NOT FIXED HERE: the same widget's prev/next arrows are stacked on top of one
   another in the middle of the carousel, and are #eee at 90% opacity on a
   white background, so they are invisible either way. Separate issue.
   -------------------------------------------------------------------------- */

.elementor-widget-n-carousel .swiper-pagination-bullets.swiper-pagination-horizontal {
	inset-inline-start: calc(var(--e-n-carousel-dots-horizontal-position) + var(--e-n-carousel-dots-horizontal-offset));
	top: calc(var(--e-n-carousel-dots-vertical-position) + var(--e-n-carousel-dots-vertical-offset));
	width: max-content;
}

/* --------------------------------------------------------------------------
   Single events - bringing the type and colour into line with the site

   Single events render through The Events Calendar's own
   `tribe-events-single` template, not through Elementor, so they were sitting
   entirely outside the site's design language. Measured before this block, a
   single event page used the theme's `body` font (Avenir) at 18px/27px in
   #5c5c5c, against #000 Poppins 20px/30px everywhere else, and scattered nine
   unrelated greys and blues through one screen: #5c5c5c body copy, #141827
   links and dates, #171717 headings, #b9bec2 date separators, #365b6d on the
   subscribe button, #3a87ad on a Bootstrap-blue notice, #5d5d5d on the
   prev/next nav and #23282d on the venue.

   THE TOKENS BELOW ARE HARD-CODED ON PURPOSE. `var(--e-global-color-*)` cannot
   be used here: Elementor prints the kit stylesheet only on pages holding one
   of its documents, and although `<body>` still carries the
   `elementor-kit-29188` class on an event page, no `elementor-post-29188`
   style tag is emitted and `--e-global-color-primary` resolves to empty. Same
   trap as the /events/ chips above. `--tec-*` is not an option either, since
   these are site colours rather than calendar ones. The values are the kit's
   own globals, so they must be re-checked against Site Settings > Global
   Colors if the brand palette ever moves:

     #00853F  primary green      #0D1F0E  secondary (near-black)
     #265828  a291686, button hover
     #365B6D  f7c6188, used for the date line
     #D9D9D9  9eaa092, borders   #F6F6F6  a954db2, tinted panels

   Type follows what the rest of the site actually RENDERS, not what the kit
   declares as its defaults - the two disagree. The kit's h1-h6 globals say
   Roboto Slab and its body global says Inter, but every page overrides those
   per widget: sampling /, /about-us/, /donate/, /volunteer/ and /blog/,
   Poppins outnumbers everything else 109:29:7 over Montserrat and Inter, and
   the Inter and Roboto Slab counts of 6-7 per page are just the kit's variable
   declarations rather than any element using them. Body copy on /about-us/
   measures Poppins 400 20px/30px #000; section headings measure Poppins 600
   with letter-spacing -0.5px; and real buttons measure Poppins 600 20px, white
   on #00853F, with a 25px pill radius - the kit's yellow #FFC107 button only
   ever shows up on controls nobody styled, such as the nav toggle.

   Poppins has to be ENQUEUED as well as asked for. Nothing loads a webfont on
   these pages, so the declarations below would otherwise fall back to whatever
   the visitor has installed - see the `Google_Font::enqueue()` call in
   functions.php for why that fails silently while developing.

   Sizes are in px throughout. The parent theme sets `html { font-size: 62.5% }`
   so 1rem is 10px here, which is also why the kit's own `clamp()` sizes cannot
   be borrowed: `clamp(2rem, ..., 3.0625rem)` for an h1 is 20-30px on this site,
   not 32-49px.

   Line heights are all unitless. `body` resolves to a literal `line-height:
   15px` on this site, so anything that inherits it and then sets a larger
   font-size draws its wrapped lines on top of each other - a bad line-height
   here could not be fixed by removing the declaration, because the inherited
   fallback is just as broken.

   `text-transform: none` is set explicitly on the title, the headings and the
   venue name. The site's heading and button globals both say `capitalize`, and
   these three carry author-entered text where that would corrupt real words -
   an event called "Tree ID and Fall Color Hike" would render as "Tree ID And
   Fall Color Hike". Buttons keep `capitalize` to match the site, since those
   are short labels.

   Selectors are scoped `.single-tribe_events .tribe-events-single` and use no
   IDs. The wrapper element has both an id and a class
   (`<div id="tribe-events-content" class="tribe-events-single ...">`); matching
   the class keeps every rule here in the same specificity band, so the
   overrides further down can win on class count alone. Prefixing with
   `#tribe-events-content` instead would put the general prose rule at (1,1,1)
   and no amount of classes could then override it.

   The subscribe dropdown is the one part that needs `.tribe-common` in the
   selector: it renders inside `<div class="tribe-events tribe-common">`, where
   the plugin's own reset `.tribe-common a, .tribe-common abbr, ... { border: 0;
   margin: 0; padding: 0 }` sits at (0,1,1) and beats a lone class.

   This is deliberately a stylesheet rather than an Elementor template.
   Production renders single events from this same TEC template, so everything
   here deploys and takes effect there as-is. There WAS an Elementor
   theme-builder template for single events (elementor_library 27741) but it is
   in the trash, which is why these pages fall back to the plugin template;
   restoring it would bypass this block entirely.
   -------------------------------------------------------------------------- */

.single-tribe_events .tribe-events-single {
	--gh-event-green: #00853f;
	--gh-event-green-dark: #265828;
	--gh-event-ink: #0d1f0e;
	--gh-event-teal: #365b6d;
	--gh-event-border: #d9d9d9;
	--gh-event-panel: #f6f6f6;

	font-family: "Poppins", sans-serif;
	color: #000000;
}

/* Body copy, matching the 20px/1.5 measured on /about-us/. `li` is included
   because 15 published events use list blocks. */
.single-tribe_events .tribe-events-single p,
.single-tribe_events .tribe-events-single li,
.single-tribe_events .tribe-events-single dd,
.single-tribe_events .tribe-events-single dt {
	font-family: "Poppins", sans-serif;
	font-size: 20px;
	font-weight: 400;
	line-height: 1.5;
	color: #000000;
}

/* Prose links. The kit's global link colour is the near-black secondary, which
   inside a #000 paragraph would leave links indistinguishable from the text
   around them, so the primary green is used instead - 4.7:1 on white, and the
   same colour event titles already use on the /events/ archive. The underline
   is always on rather than hover-only, so colour is not the only signal. */
.single-tribe_events .tribe-events-single p a,
.single-tribe_events .tribe-events-single li a {
	color: var(--gh-event-green);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.single-tribe_events .tribe-events-single p a:hover,
.single-tribe_events .tribe-events-single p a:focus-visible,
.single-tribe_events .tribe-events-single li a:hover,
.single-tribe_events .tribe-events-single li a:focus-visible {
	color: var(--gh-event-green-dark);
}

/* The event title. Sized off the site's 40px section headings rather than the
   kit's h1 global, which resolves to 20-30px here. The clamp only comes into
   play below ~800px; 28px is the floor so a long title still fits a 360px
   phone in a few lines. Green rather than the kit's near-black h1, matching
   the titles on the /events/ archive. */
/* The `h1` in the selector is load-bearing, not decoration. The plugin ships

     .single-tribe_events .tribe-blocks-editor .tribe-events-single-event-title
       { font-size: revert; line-height: revert }                       (0,3,0)

   in tribe-events-single-blocks.css, and `revert` rolls the two properties
   back to the theme level, where GoodHeart's `h1 { font-size:
   var(--theme-h1-font-size) }` and `h1 { line-height:
   var(--theme-heading-font-line-height) }` take over. That is exactly the
   36px/54px this rule was measured rendering before the qualifier was added -
   the colour, weight and letter-spacing all applied, because the plugin does
   not set those, which made it look like the rule was working. Adding the
   element takes this to (0,3,1) and it wins outright rather than on load
   order. */
.single-tribe_events .tribe-events-single h1.tribe-events-single-event-title {
	font-family: "Poppins", sans-serif;
	font-size: clamp(28px, 5vw, 40px);
	font-weight: 600;
	line-height: 1.15;
	letter-spacing: -0.5px;
	text-transform: none;
	color: var(--gh-event-green);
}

/* Headings inside the description - 132 published events use heading blocks.
   Scaled down from the title so the hierarchy still reads. */
.single-tribe_events .tribe-events-single h2,
.single-tribe_events .tribe-events-single h3,
.single-tribe_events .tribe-events-single h4,
.single-tribe_events .tribe-events-single h5,
.single-tribe_events .tribe-events-single h6 {
	font-family: "Poppins", sans-serif;
	font-weight: 600;
	line-height: 1.2;
	letter-spacing: -0.5px;
	text-transform: none;
	color: var(--gh-event-ink);
}

.single-tribe_events .tribe-events-single h2 {
	font-size: 30px;
}

.single-tribe_events .tribe-events-single h3 {
	font-size: 24px;
}

.single-tribe_events .tribe-events-single h4,
.single-tribe_events .tribe-events-single h5,
.single-tribe_events .tribe-events-single h6 {
	font-size: 20px;
}

/* The date line under the title.

   The plugin renders it as five spans at three different sizes with the
   separators in a pale grey - 21px for the date, 19px for the times and 15px
   #b9bec2 for the "@" and the dash - which read as three fragments rather than
   one line. All five are reset to inherit so the whole line is one size and
   one colour. The teal is the same one the /events/ archive uses for its
   day-of-week row (7.3:1 on white), which keeps the date clearly secondary to
   the green title while staying legible.

   Two things have to be outranked here, which is why the selector is as long
   as it is. The datetime element is itself a `p`, so this must beat the
   body-copy rule above; and the plugin sets it again from
   tribe-events-single-blocks.css at

     .single-tribe_events .tribe-blocks-editor .tribe-events-schedule
     .tribe-events-schedule__datetime { font-size: var(--tec-font-size-3) }

   which is (0,4,0). Four classes plus the element reaches (0,4,1) and clears
   both. Without it the line stayed at the plugin's 16px #141827 while the
   font-family from this rule applied, which is a confusing way to fail. */
.single-tribe_events .tribe-events-single .tribe-events-schedule p.tribe-events-schedule__datetime {
	font-family: "Poppins", sans-serif;
	font-size: 18px;
	font-weight: 500;
	line-height: 1.5;
	color: var(--gh-event-teal);
}

/* The five spans inside it only need to stop overriding their parent - the
   plugin sizes them individually (`.tribe-events-schedule__date` is 21px,
   `__time` 19px, `__separator` 15px in #b9bec2) at (0,1,0), so inheriting is
   enough to collapse them onto one consistent line. */
.single-tribe_events .tribe-events-single .tribe-events-schedule__datetime span {
	font-size: inherit;
	font-weight: inherit;
	color: inherit;
}

/* "All Events" back link. Also a `p`, so it needs the same class count as the
   rule above to beat the body-copy size. */
.single-tribe_events .tribe-events-single .tribe-events-back a {
	font-family: "Poppins", sans-serif;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.4;
	color: var(--gh-event-green);
	text-decoration: none;
}

.single-tribe_events .tribe-events-single .tribe-events-back a:hover,
.single-tribe_events .tribe-events-single .tribe-events-back a:focus-visible {
	color: var(--gh-event-green-dark);
	text-decoration: underline;
}

/* The "This event has passed" notice, which ships in Bootstrap's blue
   (#3a87ad on #d9edf7) and was the loudest off-brand thing on the page. Now a
   tinted panel with a green rule down its leading edge. `border: 0` clears the
   plugin's own all-round border so only the accent edge shows. */
.single-tribe_events .tribe-events-notices {
	border: 0;
	border-inline-start: 4px solid var(--gh-event-green);
	border-radius: 0;
	background: var(--gh-event-panel);
	box-shadow: none;
	text-shadow: none;
}

.single-tribe_events .tribe-events-notices li {
	font-family: "Poppins", sans-serif;
	font-size: 16px;
	font-weight: 500;
	line-height: 1.5;
	color: var(--gh-event-ink);
}

/* Venue, organizer and price blocks - 182, 156 and 115 published events use
   them respectively. The venue name arrives as a 14px `h3`, which is smaller
   than the address beneath it on some events, so it is set explicitly rather
   than left to the heading rules above. */
.single-tribe_events .tribe-events-single .tribe-block__venue,
.single-tribe_events .tribe-events-single .tribe-block__organizer__details {
	border-color: var(--gh-event-border);
}

/* The organizer name is an unclassed `h3` nested inside `__title`, so it has
   to be named here as well as the wrapper - left to the heading rules above it
   came out at 24px, larger than the venue name it sits next to. */
.single-tribe_events .tribe-events-single .tribe-block__venue__name h3,
.single-tribe_events .tribe-events-single .tribe-block__organizer__title,
.single-tribe_events .tribe-events-single .tribe-block__organizer__title h3 {
	font-family: "Poppins", sans-serif;
	font-size: 20px;
	font-weight: 600;
	line-height: 1.3;
	letter-spacing: -0.5px;
	text-transform: none;
	color: var(--gh-event-ink);
}

/* The organizer's phone and email are `p` elements, so they are matched
   directly rather than left to inherit from `__details` - inheritance loses to
   any rule that matches the element itself, and the body-copy rule above does,
   which had them rendering at 20px beside a 16px venue address. */
.single-tribe_events .tribe-events-single .tribe-block__venue__address,
.single-tribe_events .tribe-events-single .tribe-block__venue__address span,
.single-tribe_events .tribe-events-single .tribe-block__venue__address abbr,
.single-tribe_events .tribe-events-single .tribe-block__organizer__details,
.single-tribe_events .tribe-events-single .tribe-block__organizer__details span,
.single-tribe_events .tribe-events-single .tribe-block__organizer__phone,
.single-tribe_events .tribe-events-single .tribe-block__organizer__email,
.single-tribe_events .tribe-events-single .tribe-block__organizer__website {
	font-family: "Poppins", sans-serif;
	font-size: 16px;
	font-weight: 400;
	line-height: 1.5;
	color: #000000;
}

.single-tribe_events .tribe-events-single .tribe-events-gmap,
.single-tribe_events .tribe-events-single .tribe-block__organizer__email a,
.single-tribe_events .tribe-events-single .tribe-block__organizer__phone a,
.single-tribe_events .tribe-events-single .tribe-block__organizer__website a {
	font-family: "Poppins", sans-serif;
	font-size: 16px;
	font-weight: 600;
	color: var(--gh-event-green);
	text-decoration: none;
}

.single-tribe_events .tribe-events-single .tribe-events-gmap:hover,
.single-tribe_events .tribe-events-single .tribe-block__organizer__email a:hover,
.single-tribe_events .tribe-events-single .tribe-block__organizer__phone a:hover,
.single-tribe_events .tribe-events-single .tribe-block__organizer__website a:hover {
	color: var(--gh-event-green-dark);
	text-decoration: underline;
}

.single-tribe_events .tribe-events-single .tribe-block__event-price__cost {
	font-family: "Poppins", sans-serif;
	font-size: 20px;
	font-weight: 600;
	line-height: 1.4;
	color: var(--gh-event-ink);
}

/* The event website block is a filled BUTTON, not a link - the plugin gives
   it a background, 12px/20px padding and a 4px radius, and its label is white.
   So it gets the site's button treatment rather than the green link treatment
   above, which would have painted white-on-white text.

   This is the one rule in this section that needs an ID to win, because the
   plugin uses one itself:

     #tribe-events-content .tribe-block__event-website a
       { color: var(--tec-color-background) }                          (1,1,1)

   in tribe-events-single-blocks.css. No class-only selector can outrank that,
   so `.single-tribe_events` is stacked on the same ID to reach (1,2,1). The
   background comes from a separate (0,1,1) rule set to
   `--tec-color-accent-primary`, which is why the button was rendering in the
   Events Customizer's #365b6d rather than a brand colour.

   Buttons in the description get the same treatment - 21 published events use
   core button blocks. Both match the site's real buttons (Poppins 600 20px,
   white on green, 25px pill, 12px/24px padding) rather than the kit's
   unstyled-button default, which is the yellow accent and appears nowhere a
   designer put it. */
.single-tribe_events #tribe-events-content .tribe-block__event-website a,
.single-tribe_events .tribe-events-single .wp-block-button__link {
	padding: 12px 24px;
	border: 0;
	border-radius: 25px;
	background-color: var(--gh-event-green);
	font-family: "Poppins", sans-serif;
	font-size: 20px;
	font-weight: 600;
	line-height: 1.4;
	text-transform: capitalize;
	text-decoration: none;
	color: #ffffff;
}

.single-tribe_events #tribe-events-content .tribe-block__event-website a:hover,
.single-tribe_events #tribe-events-content .tribe-block__event-website a:focus-visible,
.single-tribe_events .tribe-events-single .wp-block-button__link:hover,
.single-tribe_events .tribe-events-single .wp-block-button__link:focus-visible {
	background-color: var(--gh-event-green-dark);
	color: #ffffff;
}

/* The Add to calendar control. Deliberately a green outline rather than a
   filled pill: it is a utility next to whatever call to action the description
   carries, and two filled green buttons would compete. The pill radius still
   ties it to the site's button language. `.tribe-common` is in the selector to
   clear the plugin reset described at the top of this section. */
/* `color` is set on the wrapper as well as the label because the plugin styles
   the label `color: currentcolor` - so the wrapper is what actually decides
   the colour of the label and of the chevron icon beside it, which would
   otherwise stay on the plugin's #365b6d. */
.single-tribe_events .tribe-common .tribe-events-c-subscribe-dropdown__button {
	border: 1px solid var(--gh-event-green);
	border-radius: 25px;
	color: var(--gh-event-green);
}

/* The extra `.tribe-events-c-subscribe-dropdown` class is needed to outrank
   views-full.css, which sets `font-size: inherit; color: currentcolor` on this
   label at (0,3,0) - a tie with the shorter selector, which then came down to
   stylesheet order. (0,4,0) settles it. */
.single-tribe_events .tribe-common .tribe-events-c-subscribe-dropdown .tribe-events-c-subscribe-dropdown__button-text,
.single-tribe_events .tribe-common .tribe-events-c-subscribe-dropdown .tribe-events-c-subscribe-dropdown__button-text:hover {
	font-family: "Poppins", sans-serif;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.4;
	color: var(--gh-event-green);
}

.single-tribe_events .tribe-common .tribe-events-c-subscribe-dropdown__list-item a {
	font-family: "Poppins", sans-serif;
	font-size: 16px;
	font-weight: 400;
	line-height: 1.5;
	color: var(--gh-event-ink);
}

.single-tribe_events .tribe-common .tribe-events-c-subscribe-dropdown__list-item a:hover,
.single-tribe_events .tribe-common .tribe-events-c-subscribe-dropdown__list-item a:focus-visible {
	color: var(--gh-event-green);
}

/* Previous/next event navigation. */
.single-tribe_events .tribe-events-single .tribe-events-sub-nav a {
	font-family: "Poppins", sans-serif;
	font-size: 16px;
	font-weight: 600;
	line-height: 1.4;
	color: var(--gh-event-green);
	text-decoration: none;
}

.single-tribe_events .tribe-events-single .tribe-events-sub-nav a:hover,
.single-tribe_events .tribe-events-single .tribe-events-sub-nav a:focus-visible {
	color: var(--gh-event-green-dark);
	text-decoration: underline;
}

/* --------------------------------------------------------------------------
   Staff - bios spilling out of the flip-box cards

   The 29 cards on /staff/ are Elementor Flip Box widgets (template 24755),
   each with a hardcoded `height: 500px` and no responsive override, and the
   bio sits on the back face inside a `position: absolute` layer with
   `overflow: visible`. As soon as a column narrows enough for the bio to
   re-wrap past 500px, the surplus leaves the panel as bare text on the page
   and lands on top of the name and title underneath. Measured before this
   fix, bios overflowing out of 29 and the worst overflow:

     1920px   1 card,   30px of bare text below the panel
     1600px   6 cards, 181px
     1440px  12 cards, 333px
     1300px  17 cards, 486px
     1024px  26 cards, 1269px
      768px  29 cards, 4239px - the grid is still 3-across at exactly 768px,
              which leaves cards 114-228px wide
      360px  28 cards, 908px

   Only at 1920px and up did the page hold together, and even there one bio
   was already over the edge.

   That is the same defect as the Board of Directors one above, but NOT the
   same fix, and the difference is the point of this comment. The board's
   cards were allowed to grow taller than 630px instead of spilling. Here
   that reads badly, because the two pages put different things on the front
   face: the board shows a `cover` photo that fills the card, while a staff
   front is one small headshot at `background-size: 50%`, centred in the card.
   Growing the card therefore adds empty space around a floating headshot,
   and since each card grows by a different amount, the photos and the names
   in a row stop lining up. Measured with that version in place at 1440px,
   one card went to 903px while its neighbour stayed at 500px - 330px of
   stagger inside a single row of three.

   So the cards stay 500px and the panel does the moving. Two parts.

   1. The columns step down, so most bios have the width to fit 500px in the
      first place. Measured need against card width (Christi Smeed, the
      longest bio, in a 500px box):

        210px card -> 3317px  |  450px card ->  732px
        250px card -> 2122px  |  490px card ->  656px
        290px card -> 1517px  |  550px card ->  555px
        330px card -> 1187px  |  610px card ->  505px
        370px card ->  986px  |  690px card ->  455px
        410px card ->  833px  |  790px card ->  404px

      Fitting the worst bio outright takes a 610px card, which this grid only
      hands that column at around 2000px wide - the one bio still 30px over
      at 1920px in the table above is this same card - so stepping alone
      cannot carry this. What it does do is keep the panel's growth small,
      and keep the cards from getting absurd at tablet widths. 3-across
      above 1299px, 2-across from 1024-1299px, and one centred card below
      that, capped at 480px so a phone-width card keeps the proportions the
      design was drawn at. `flex-wrap` has to be set as well: Elementor
      leaves the row `nowrap` above 767px, so widened columns would otherwise
      shrink back to a third of the row instead of wrapping onto their own
      line.

      The single-column step is at 1024px rather than the board's 900px
      because 2-across gets narrower than the 480px cap before then - at
      1024px the cards measure 341-475px against 380-470px for a capped
      single column, and by 900px they are down to 279px. 1024px is also
      where Elementor switches the flip boxes to tap-to-open (its own
      `@media (max-width: 1024px)` rule gives them `cursor: pointer`).

   2. Any bio that still does not fit makes the back face taller than the
      card instead of spilling out of it - `height: auto` with the 500px kept
      as a floor. The panel carries the #F6F6F6 background and 25px radius
      that Elementor already puts on `__back`, so the part that hangs below
      the card reads as a panel opening over the page rather than as loose
      text. Every card stays 500px, so the grid stays aligned whether a
      panel is open or not.

   Two things are needed to make an oversized panel behave, and neither is
   obvious from the markup:

   z-index, because Elementor gives every widget `position: relative` with
   `z-index: auto`, so widgets paint in tree order and the name heading -
   later in the tree - painted straight over the panel. A z-index on the
   layer cannot fix that on its own: `.elementor-flip-box` sets
   `perspective: 1000px` (widget-flip-box.min.css) and is therefore a
   stacking context, so anything inside it paints as one unit at the flip
   box's own level. The z-index has to go on the flip box itself, and only
   while it is open, or a later row's cards would paint over a panel from the
   row above. 10 clears every z-index the page uses in content (the highest
   is 9) and stays under the fixed site header at 170 and the skip link at
   200.

   `pointer-events: none` on the closed panel, because the fade effect hides
   the back face with `opacity: 0` and nothing else - an invisible panel
   hanging 400px below its card would still be the topmost hit target over
   the cards underneath and would swallow their hover and clicks. It comes
   back on hover, focus-within and the tapped `--flipped` state, which also
   keeps an open panel open while the pointer is in the part that hangs below
   the card: `:hover` matches the ancestors of whatever the pointer is over,
   and the panel is a descendant of the flip box, so the geometry does not
   matter.

   After the fix nothing spills at any width from 360px to 2560px, and every
   card is 500px at every width. Tallest panel, and how far it reaches past
   the bottom of its own column while open:

     2560px  3-across, 711-1113px cards  no panel grows at all
     1920px  3-across,  498-793px cards  600px tall, inside its column
     1600px  3-across,  391-633px cards  751px tall, 121px past
     1440px  3-across,  338-553px cards  903px tall, 233px past
     1300px  3-across,  291-483px cards  1056px tall, 356px past
     1299px  2-across,  478-612px cards  625px tall, inside its column
     1024px  2-across,  341-475px cards  877px tall, 207px past
     1023px  1 card,    380-470px cards  726px tall, 96px past
      768px  1 card,    380-470px cards  726px tall, 96px past
      375px  1 card,    235-325px cards  1327px tall, 639px past
      360px  1 card,    220-310px cards  1478px tall, 790px past

   356px is the worst of the desktop cases, and it covers the card's own name
   and title plus about 100px of the row below. Phones are the outlier, for a
   reason that is not this fix's to solve: the inner sections keep enough
   horizontal padding at that width to leave a 375px phone with 235-325px
   cards, so the longest bio wraps to 1327px and has to be scrolled to be
   read. Trimming that padding would shorten every panel on mobile.

   Scoped to `:is(.elementor-24755, .elementor-32089)`, the template that renders the staff grid.
   Safe only because all 29 cards use the fade effect: the slide and push
   effects put `overflow: hidden` on the flip box, which would clip an
   oversized panel back to the card, and the 3D flip effects need both layers
   the same size. The Elementor rules being beaten are
   `:is(.elementor-24755, .elementor-32089) .elementor-element.elementor-element-XXXX
   .elementor-flip-box` and Elementor Pro's own `.elementor-flip-box__layer`,
   so these selectors add `body` and the widget class to reach (0,4,1) and
   win outright rather than depending on stylesheet order.
   -------------------------------------------------------------------------- */

/* Elementor only wraps this row below 767px; without this, the widened
   columns below shrink to fit one line instead of moving to the next. */
@media (max-width: 1299px) {
	body :is(.elementor-24755, .elementor-32089) .elementor-inner-section > .elementor-container {
		flex-wrap: wrap;
	}
}

/* `margin-left` is reset because the parent theme spaces columns with
   `[data-elementor-device-mode$="top"] .elementor-column + .elementor-column
   { margin-left: auto }` (style.css), which pushes the third card to the far
   right of the line it wraps onto and leaves a hole on the left. */
@media (max-width: 1299px) and (min-width: 1024px) {
	body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-inner-column.elementor-col-33 {
		width: 50%;
		margin-left: 0;
	}
}

/* The last row of the grid is a two-column section rather than a three, so it
   needs naming separately to collapse with the rest. */
@media (max-width: 1023px) {
	body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-inner-column.elementor-col-33,
	body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-inner-column.elementor-col-50 {
		width: 100%;
		max-width: 480px;
		margin-left: auto;
		margin-right: auto;
	}
}

/* The bio panel grows past the card instead of spilling out of it. */
body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-widget-flip-box .elementor-flip-box__back {
	height: auto;
	min-height: 100%;
	pointer-events: none;
}

body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-widget-flip-box .elementor-flip-box:hover .elementor-flip-box__back,
body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-widget-flip-box .elementor-flip-box:focus-within .elementor-flip-box__back,
body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-widget-flip-box.elementor-flip-box--flipped .elementor-flip-box .elementor-flip-box__back {
	pointer-events: auto;
}

/* Lifts the whole card above the page while its panel is open - see the
   stacking-context note above for why this cannot go on the layer. */
body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-widget-flip-box .elementor-flip-box:hover,
body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-widget-flip-box .elementor-flip-box:focus-within,
body :is(.elementor-24755, .elementor-32089) .elementor-element.elementor-widget-flip-box.elementor-flip-box--flipped .elementor-flip-box {
	z-index: 10;
}
