/*
 * SP Summit redesign — reusable header/page-type classes + color/type tokens.
 *
 * Namespaced "esc-" (Evenz Summit Child) deliberately, to avoid Evenz's
 * own generic classes (.container, .btn, .card, .header, .nav, .hero),
 * which carry heavy !important usage in the parent theme's compiled CSS.
 *
 * Apply via WPBakery's "Extra class name" field on the outer vc_row for
 * header classes, and on vc_row/vc_column/vc_column_text for the color
 * utilities below. Do NOT apply classes via vc_single_image's "Extra
 * class name" field — confirmed unreliable on this theme/WPBakery stack
 * (silently doesn't reach the rendered <a> wrapper). Wrap a classed image
 * in a vc_column instead and target it with a descendant selector.
 *
 * Exact spacing/sizing below are reasonable starting values, not measured
 * from the XD file (no XD connector available) — verify and adjust against
 * the XD Specs link during build/QA, section by section.
 */

/* ---------- Color tokens (real XD values, confirmed 2026-09-02) ---------- */

:root {
	--esc-navy: #032954;
	--esc-cyan: #03A1DF;
	--esc-white: #FFFFFF;
	--esc-dark: #06090D;
	--esc-darknavy: #0A1420;
	/* IEEE SPS Summit brand colors -- sourced from the official production
	   wordmark (25-TA-1-032_SPS-Summit-WM_Color_RGB.svg), not part of the
	   original 5-token set, needed for nav/button parity with the real
	   design. --esc-sps-blue/-green are the wordmark's solid-fill values
	   (used for most lettering); --esc-sps-blue-grad/-green-grad are its
	   own gradient stop colors (a deliberately different, lighter blue --
	   #00609c vs #00629b -- used only where the wordmark itself gradients). */
	--esc-sps-blue: #00629B;
	--esc-sps-green: #78BE20;
	--esc-sps-blue-grad: #00609C;
	--esc-sps-green-grad: #76BC21;
	/* "Light blue" -- Jonathan's own term, confirmed 2026-09-07 as this
	   exact hex (first given for card headings/the plus-icon color, now
	   also a named button color in its own right) -- genuinely distinct
	   from --esc-cyan (#03A1DF), not a synonym for it. */
	--esc-lightblue: #00A0DE;
	/* The site's real content-grid left/right inset, verified live 2026-09-07
	   against the nav logo's actual position (see the hero rowcontent rule
	   below for the full derivation) -- not a guess. Reuse this everywhere
	   a section needs to align with the nav/logo's true left edge instead
	   of bleeding to the viewport edge. */
	--esc-grid-inset: max(30px, calc((100vw - 1670px) / 2 + 30px));
}

/* ---------- Generic content-grid constraint ---------- */
.esc-grid-x {
	padding-left: var(--esc-grid-inset) !important;
	padding-right: var(--esc-grid-inset) !important;
}

/* ---------- Site-wide container width/alignment fix -- 2026-09-08 ------- */
/* Real template-level bug, not a per-page one: Evenz's own base class for
   every plain evenz_container="1" row -- .evenz-container.evenz-rowcontainer-vc
   (confirmed live: appears ONLY on top-level vc_row containers, never on
   vc_row_inner, so safe to target globally) -- still uses the ORIGINAL
   wrong assumption this project already disproved on day one: a fixed
   max-width:1170px, centered via auto margins. The real grid (traced from
   the nav logo's own DOM chain) is 1670px wide with a 30px left-aligned
   inset, not a centered 1170px box -- confirmed live 2026-09-08: a plain
   content paragraph's own left edge sat at 47px while the nav/hero text
   next to it sat at 30px, a real ~17px misalignment invisible on centered
   text but obvious on any left-aligned body copy. Pages 1 & 2 mostly
   dodged this because most of their content sits inside specifically-
   classed rows (esc-icon-feature, esc-summit-cards, esc-text-cta, etc.)
   that already had their own grid-inset override; pages 3-6 lean much
   more on plain unclassed evenz_container="1" rows for body content, so
   the misalignment shows up there directly. Fixing the BASE class itself
   (rather than adding yet another per-section override) makes this
   correct for every current page automatically and for any future page
   built with a plain content row, with no extra el_class required. */
.evenz-container.evenz-rowcontainer-vc {
	max-width: 1670px !important;
	/* +15px compensates for the parent vc_row's own ambient -15px negative
	   margin (Evenz's Bootstrap-style row/col gutter technique, confirmed
	   live: the row is consistently 15px wider than its own visible area
	   and shifted 15px left) -- without this the container's content
	   still lands 15px left of the true grid line. */
	padding-left: calc(var(--esc-grid-inset) + 15px) !important;
	padding-right: calc(var(--esc-grid-inset) + 15px) !important;
	margin-left: auto !important;
	margin-right: auto !important;
}
/* WPBakery's own column-inner ships a default 15px left gutter (the same
   gotcha already documented and fixed for the hero content column above)
   -- only zero it on the FIRST column so multi-column rows inside a plain
   container keep their own internal gutters between columns. */
.evenz-container.evenz-rowcontainer-vc .wpb_column:first-child > .vc_column-inner {
	padding-left: 0 !important;
}

/* ---------- Logo breathing room ---------- */
/* Evenz's own .evenz-logo_header caps the logo at max-height:90px inside a
   fixed 100px-tall h3 (line-height:100px, vertically centered) -- only
   5px of clearance top and bottom combined. Confirmed live 2026-09-07
   (Jonathan flagged the logo as cramped). Reducing the image's own max-
   height (rather than touching the fixed 100px h3/nav-bar height, which
   would ripple into other nav layout) lets the existing vertical-centering
   do the rest automatically. */
.evenz-logo_header {
	max-height: 70px !important;
}

/* ---------- Nav bar (main menu) background ---------- */
/* XD design uses a navy-to-green gradient nav bar, matching the exact
   gradient already defined in the official wordmark SVG. Scoped ONLY to
   the actual nav bar element -- NOT via Kirki's evenz_menu_bg field,
   which was tried first and reverted: that field's own Evenz-defined
   output also targets the white logo strap above the nav AND submenu
   link backgrounds, so setting it here broke both of those. This
   selector avoids that entirely. */
.evenz-menu.evenz-paper {
	background: linear-gradient(to right, var(--esc-sps-blue-grad) 0%, var(--esc-sps-green-grad) 100%) !important;
}

.esc-bg-navy     { background-color: var(--esc-navy); }
.esc-bg-cyan     { background-color: var(--esc-cyan); }
.esc-bg-white    { background-color: var(--esc-white); }
.esc-bg-dark     { background-color: var(--esc-dark); }
.esc-bg-darknavy { background-color: var(--esc-darknavy); }

.esc-text-navy  { color: var(--esc-navy); }
.esc-text-cyan  { color: var(--esc-cyan); }
.esc-text-white { color: var(--esc-white); }

/* ---------- Typography ---------- */
/* Font-family is no longer set here -- retired in favor of Kirki's own
   typography fields (evenz_typography_text/_headings/_captions/etc.,
   seeded to Carlito in functions.php), which give an admin a real
   Customizer UI to change fonts per text-role. A CSS !important rule here
   would permanently override whatever an admin picks, defeating that. */

/* Evenz's own ".evenz-caption" class (rendered by the qt-caption and
   qt-section-caption shortcodes, and reused internally for a lot of
   theme-native "eyebrow" text) carries a hardcoded underline by default --
   a ::after pseudo-element with a 2px border-bottom, unconditional,
   affecting every element that gets this class regardless of context.
   No heading, caption, or eyebrow-style text in the XD design uses an
   underline anywhere (confirmed across all 6 screens) -- removed
   site-wide here rather than overridden per instance, since it's a
   single shared mechanism and any future qt-caption/qt-section-caption
   use (ours or an editor's) would otherwise silently reintroduce it.
   Found via page 1742 ("About Singapore"), which uses
   [qt-caption title="Welcome to Singapore"] -- confirmed 2026-09-04 this
   is the actual source of the underlined section headings Jonathan
   flagged, not something in our own CSS. */
.evenz-caption::after {
	display: none !important;
}

/* ---------- Content-page headings (H2/H3 within body content) ---------- */
/* The "content" heading treatment confirmed across the XD screens that
   use article/detail-style layouts (Patrons & Exhibitors, About SP
   Summit, Contact Us, and -- per this page's own qt-caption headings --
   About Singapore too): left-aligned, Title Case (never all-caps), bold,
   navy, no underline. Distinct from the OTHER "eyebrow" heading treatment
   used on landing-style pages (Home, Event Home): centered, ALL-CAPS,
   color adapting to background -- that one already has a home via
   Evenz's own .evenz-caption/.qt-section-caption (color hooked in the
   hero rules above; underline now removed globally above too).

   Apply via vc_custom_heading's own "Extra class name" field (a real
   heading element, el_class is reliable there) -- never qt-caption, which
   pulls in Evenz's whole .evenz-caption apparatus (now-disabled underline
   aside, still carries animation classes and other baggage this plain
   heading treatment doesn't need). evenz_titles (Kirki, seeded
   #032954 as of 2026-09-04) already colors any real hN tag navy
   automatically -- ".esc-heading"/".esc-subheading" below only need to
   handle alignment and size, not color, so an editor who later changes
   evenz_titles via the Customizer still gets a consistent result here. */
.esc-heading {
	text-align: left;
	font-weight: 700;
}

.esc-subheading {
	text-align: left;
	font-weight: 700;
}

/* ---------- Header / page-type classes ---------- */
/* Apply to the page's outer vc_row (the header/hero section).

   Architecture (esc-header-hero / -2btn / -1btn / -banner — all four,
   confirmed 2026-09-04: XD's banner-height pages use the exact same
   two-column bleed layout, just shorter, not a separate single-column
   treatment as first assumed): two real WPBakery columns, both
   width="1/1", as genuine flex siblings —
   [vc_column el_class="esc-header-content"] FIRST (text/button), then
   [vc_column el_class="esc-header-bgcol"][vc_single_image ...] SECOND.
   DOM order matters: it drives both the desktop left/right placement
   (flex-direction:row) and the mobile stacking order (flex-direction:
   column) for free, with no explicit `order` property needed anywhere.

   Content column: fixed comfortable reading width, left-aligned,
   heading/copy/button stacked as separate blocks. Image column: takes
   all remaining space (flex:1) and keeps extending to the true viewport
   edge on wide screens rather than stopping at a fixed pixel value —
   XD only mocks one width, but a fixed-width image column would either
   look cramped on a laptop or leave a dead gap on an ultrawide monitor;
   letting it flex-grow means object-fit:cover just reveals more image on
   wider screens, which always looks intentional regardless of viewport.
   The row's own left inset replicates ".evenz-container"'s real max-width
   (1170px, confirmed live 2026-09-04 via getComputedStyle) so the content
   column's left edge lines up with the rest of the page's contained
   content at any screen width, while the image column is free to bleed
   past that on the right with nothing capping it.

   Superseded a first attempt at this (see project memory) that made the
   image column position:absolute, overlaid on top of a single shared
   column with the text -- worked, but fought CSS the whole way (percentage-
   height circular dependencies, containing-block conflicts with the text's
   own z-index lift) and couldn't do the true edge-to-viewport bleed this
   version does. Two real flex siblings is simpler AND meets the actual
   design requirement -- prefer this pattern for every future hero page. */

.esc-header-hero,
.esc-header-hero-2btn,
.esc-header-hero-1btn {
	position: relative;
	overflow: hidden;
	background-color: var(--esc-navy); /* fallback if a hero ever ships with no image */
	/* Reduced from 520px per Jonathan 2026-09-04: also shrinks the image
	   column's box (it stretches to match), which reduces how much
	   object-fit:cover has to crop off the sides of a photo that's much
	   wider than tall -- less height needed to fill means less width
	   needs to be sacrificed. */
	min-height: 460px;
	/* display:flex on the ROW too (in addition to .evenz-rowcontent further
	   down, which handles the actual two-column layout) -- the row's only
	   direct child (.evenz-rowcontainer-vc) is a normal block element, and
	   normal block children do NOT stretch to fill a taller parent just
	   because the parent has min-height -- only flex/grid children with
	   stretch alignment do. Without this, the row's content sizes itself
	   to its own natural (shorter) height and the extra space from
	   min-height:520px sits unfilled above and below it instead of the
	   columns actually reaching the row's true top/bottom edges. Confirmed
	   live 2026-09-04 (80px gap top and bottom, matching the difference
	   between the row's rendered height and its content's natural height). */
	display: flex;
	align-items: stretch;
	/* Evenz's own ".evenz-section.evenz-paper" content wrapper (every page's
	   main content area) has a built-in 75px top padding meant for normal
	   body copy spacing below the nav. Hero rows are always a page's
	   opening element and are meant to sit flush against the nav instead --
	   this cancels that inherited padding rather than editing the shared
	   wrapper itself, which other (non-hero) content on the same page still
	   needs untouched. Confirmed exact value live 2026-09-04. */
	margin-top: 42px;
	/* Recalibrated 2026-09-08 (was -75px, confirmed live 2026-09-04): the
	   value had gone stale after several days of unrelated nav-height
	   changes (the logo max-height fix, etc.) and was letting the sticky
	   nav bar overlap the top of every hero/banner row, hiding the first
	   line of any 2-line H1 -- confirmed live on both Program at a Glance
	   and Contact Us, cut off identically despite Contact Us never being
	   touched today. Recomputed empirically, not guessed: with margin-top
	   temporarily zeroed, the row's natural top sat at 148px while the
	   real nav bottom was 190px, so +42px is what makes them flush right
	   now. This is a plain hardcoded value, not derived from the nav's
	   real height at compute time -- re-verify if the nav's own height
	   changes again. */
	/* WPBakery's own full_width="stretch_row" JS adds inline
	   padding-left/padding-right to the row itself (~32-33px at typical
	   desktop widths, confirmed live) to visually align stretch-row content
	   with the theme's normal container edge. Correct default behavior for
	   a contained row, but it stops the image column ~33px short of the
	   true viewport edge here -- overridden to 0 with !important (the only
	   thing that beats a plain inline style) so the image genuinely reaches
	   the edge on any screen width. The content column's own left inset is
	   handled separately, by its own padding-left formula, not by this --
	   zeroing padding-left here too, so the content column's own formula is
	   the ONLY thing controlling that inset instead of stacking on top of
	   WPBakery's own approximation of it. */
	padding-left: 0 !important;
	padding-right: 0 !important;
	/* padding-top/bottom on the row also confirmed live at 80px each
	   (exactly matching the visible gap between the row's edges and the
	   content/image columns), despite an exhaustive check finding NO
	   matching rule in any loaded stylesheet and no inline style setting
	   it -- iterated every stylesheet's cssRules, checked el.matches() on
	   each selector, dumped full cssText for every actual match (8 total),
	   and checked the raw style attribute directly. None of them account
	   for it. Root cause not identified. Forcing it to 0 regardless, since
	   the two columns (both align-self:stretch) need the row's full box,
	   uncontaminated by any padding from whatever source this is. */
	padding-top: 0 !important;
	padding-bottom: 0 !important;
}

/* ---------- Banner (redesigned 2026-09-08) ---------- */
/* Separated from the three hero variants above -- height and image
   treatment diverge; vertical offset uses the SAME 42px value the hero
   variants use above (not a separate template-specific override -- an
   earlier version of this fix split -75px/0 by page template, which was
   correct at the time but is now redundant: whatever changed the nav's
   real height today made a single 42px value flush on both the
   `nosidebar` template, e.g. Program at a Glance, AND the `fullwidth`
   template, e.g. Contact Us -- confirmed live 2026-09-08, both land at
   the identical row.top=190px with zero nav overlap. Kept banner as its
   own rule anyway since min-height/image treatment still genuinely
   diverge from the hero variants). */
.esc-header-banner {
	position: relative;
	overflow: hidden;
	background-color: var(--esc-navy);
	display: flex;
	align-items: stretch;
	/* Floor only, not a fixed height -- a longer title (3+ lines) is meant
	   to grow the whole row taller (both columns stretch together via
	   align-items:stretch above), not get compressed into 220px. */
	min-height: 220px;
	margin-top: 42px;
	padding-left: 0 !important;
	padding-right: 0 !important;
	padding-top: 0 !important;
	padding-bottom: 0 !important;
}

/* The actual flex container for the two columns is NOT the row itself --
   Evenz's vc_row template nests columns three levels deep:
   .vc_row > div.evenz-rowcontainer-vc > div.evenz-rowcontent > columns
   (".evenz-container" is only added when the row carries evenz_container="1",
   which this row deliberately does NOT use -- see the content-column note
   on the responsive left-inset formula above). Styling display:flex on
   ".vc_row" itself only affects that ONE wrapper div as a flex item; it
   does nothing for the columns underneath. Confirmed live 2026-09-04 --
   the columns rendered stacked instead of side-by-side despite the row
   correctly showing flex-direction:row, because flex properties on the
   wrong element don't cascade to non-direct descendants. Both intermediate
   wrapper needs align-self:stretch (NOT height:100%) so the row's
   min-height actually reaches down to where the columns can stretch to
   fill it -- explicit height:100% was tried first and reverted: the row
   only has min-height, not a definite height, so height:100% on the
   child hits the exact same percentage-height-needs-a-definite-ancestor
   problem already documented twice elsewhere in this file, and appears to
   take CSS priority over the flex align-self:stretch default even when it
   fails to resolve cleanly, silently cancelling the stretch behavior.
   Confirmed live 2026-09-04 (columns started flush with the row's top
   once align-self:stretch was added, but still fell ~82px short of the
   row's bottom edge until height:100% was removed here). align-self
   computes from the flex container's actual USED height, not a CSS
   percentage, so it doesn't have this problem. */
.esc-header-hero > .evenz-rowcontainer-vc,
.esc-header-hero-2btn > .evenz-rowcontainer-vc,
.esc-header-hero-1btn > .evenz-rowcontainer-vc,
.esc-header-banner > .evenz-rowcontainer-vc {
	/* Evenz's own CSS sets align-items:center on ".vc_row" with higher
	   specificity/later cascade position than our align-items:stretch
	   above (confirmed live 2026-09-04 -- row's computed align-items
	   stayed "center" despite our rule). align-self on the child wins
	   over the parent's align-items regardless of that fight, so this is
	   more reliable than chasing specificity on the row itself. */
	align-self: stretch;
}

.esc-header-hero > .evenz-rowcontainer-vc > .evenz-rowcontent,
.esc-header-hero-2btn > .evenz-rowcontainer-vc > .evenz-rowcontent,
.esc-header-hero-1btn > .evenz-rowcontainer-vc > .evenz-rowcontent,
.esc-header-banner > .evenz-rowcontainer-vc > .evenz-rowcontent {
	/* Direct-child chain, not a bare descendant selector: Evenz reuses the
	   same "evenz-rowcontent" class name on the wrapper it generates for a
	   nested vc_row_inner (e.g. the two-button row inside the content
	   column). A descendant selector here was also matching that inner
	   wrapper, putting it into flex/stacking mode and squeezing its two
	   button columns down to their shrink-wrapped content width (~63px --
	   confirmed live 2026-09-04 on the future-spsummit page, buttons
	   collapsed to vertical letter-wrapped columns). Scoping to exactly the
	   row's own rowcontent leaves nested rows alone. */
	display: flex;
	align-items: stretch;
	height: 100%;
	/* The site-grid-alignment left inset lives HERE, on the flex container
	   itself -- not inside the content column's own padding. Confirmed
	   live 2026-09-04: with it inside a column capped at width:min(560px,
	   46%), the padding value grows unbounded with viewport width (it's
	   ~375px at a 1920px-wide window) while the column's total box stays
	   capped at 560px -- past a certain width the padding alone exceeds
	   the box, leaving negative room for the heading text and causing it
	   to wrap to almost one word per line. Putting it on the flex
	   container instead shifts BOTH columns together as a unit; the
	   content column keeps only its own fixed internal padding, with no
	   competing width constraint.

	   Formula corrected 2026-09-07 (Jonathan flagged the hero text not
	   lining up with the logo/nav's own left edge): the original max(16px,
	   calc((100vw-1170px)/2)) was never verified against the real nav
	   container -- it assumed a 1170px content max-width that doesn't
	   actually exist on this theme. Traced the nav logo's real position
	   live instead (DOM chain: img -> a.evenz-logolink -> h3.evenz-menu__
	   logo [margin-left:15px] -> .logo-container-flex -> .evenz-menu__cont
	   .logo-container [max-width:1670px, centered, padding-left:15px]) --
	   confirmed at both 1440px (inset 30px, container narrower than max-
	   width so no centering margin yet) and 1920px (inset ~148px, centering
	   margin kicks in) viewports. Real formula: half the space beyond a
	   1670px centered container, plus the nav's own 30px (15+15) padding/
	   margin, floored at 30px on narrow viewports. */
	padding-left: var(--esc-grid-inset);
}

/* ---------- Content column ---------- */
.esc-header-hero .esc-header-content,
.esc-header-hero-2btn .esc-header-content,
.esc-header-hero-1btn .esc-header-content,
.esc-header-banner .esc-header-content {
	position: relative;
	z-index: 2;
	flex: 0 0 auto;
	width: min(560px, 46%);
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: flex-start;
	text-align: left;
	/* Just the column's own internal breathing room now -- the
	   responsive site-grid left inset lives on .evenz-rowcontent instead
	   (see its rule above for why). Top/bottom reduced from 80px per
	   Jonathan 2026-09-04. Left set to 0 2026-09-07: this 40px left value
	   was stacking on TOP of .evenz-rowcontent's own grid-matching inset,
	   pushing the heading ~40px past the real left margin (on top of the
	   ~55-60px the outer formula itself was off by before that same-day
	   fix) -- confirmed live, heading/body/buttons must sit flush with the
	   grid's true left edge, with zero of their own extra left padding. */
	padding: 55px 40px 55px 0;
	gap: 20px;
}

.esc-header-hero .esc-header-content h1,
.esc-header-hero-2btn .esc-header-content h1,
.esc-header-hero-1btn .esc-header-content h1,
.esc-header-banner .esc-header-content h1,
.esc-header-hero .esc-header-content p,
.esc-header-hero-2btn .esc-header-content p,
.esc-header-hero-1btn .esc-header-content p,
.esc-header-banner .esc-header-content p {
	text-align: left;
	margin: 0;
}

.esc-header-hero .esc-header-content p,
.esc-header-hero-2btn .esc-header-content p,
.esc-header-hero-1btn .esc-header-content p,
.esc-header-banner .esc-header-content p {
	color: var(--esc-white);
}

/* Revised 2026-09-08: 60/40 split (col 2 favored) -- replacing the
   min(420px,38%) !important cap. Two rounds of live measurement drove
   this: Jonathan's spec started at 50/50, but the source banner images
   are natively very wide (1707x216) with their actual graphic content
   concentrated in roughly the right 43% of the file -- at 50/50 columns
   the graphic never reached into view at any common desktop width.
   Widened toward the image column so it does. No !important -- that was
   the actual cause of the mobile width:100% override never applying
   (see the mobile section for the fix on that side too). Padding bumped
   24px->32px top/bottom the same day for breathing room on multi-line
   titles. */
.esc-header-banner .esc-header-content {
	padding: 32px 40px 32px 0;
	width: 40%;
}
.esc-header-banner .esc-header-bgcol {
	width: 60%;
}

/* ---------- Countdown timer "D/H/M/S" unit labels (Event Home Page) ---- */
/* [qt-countdown]'s numbers get their color from the shortcode's own
   color_text attribute (set to white in the page content), but the small
   unit labels ("D"/"H"/"M"/"S", rendered as Evenz's own generic ".l" class
   via labels="inline") have no equivalent shortcode attribute -- they
   inherit color_text too, same white as the numbers. Jonathan asked for
   these to be a different color per XD. Confirmed live 2026-09-07: not
   actually tricky, just needed the real class name from Evenz's main.css
   (.evenz-countdown-shortcode--labels-inline .l). Scoped to the hero
   specifically (".l" is a very generic class name) so it doesn't affect
   any other countdown instance elsewhere on the site. */
.esc-header-content .evenz-countdown-shortcode--labels-inline .l {
	color: var(--esc-sps-green) !important; /* changed cyan -> green 2026-09-07 per Jonathan */
}

/* WPBakery's own vc_column-inner ships a default 15px left/right gutter,
   separate from (and stacking on top of) esc-header-content's own padding
   above -- confirmed live 2026-09-07, this was the last 15px of the
   heading/logo misalignment after zeroing esc-header-content's own left
   padding. Zero it only on the content column (the image column is
   absolutely positioned via inset:0 and doesn't depend on this gutter). */
.esc-header-hero .esc-header-content > .vc_column-inner,
.esc-header-hero-2btn .esc-header-content > .vc_column-inner,
.esc-header-hero-1btn .esc-header-content > .vc_column-inner,
.esc-header-banner .esc-header-content > .vc_column-inner {
	padding-left: 0 !important;
}

/* ---------- Image column ---------- */
/* Background image: add a plain WPBakery "Single Image" element inside
   this column -- no Extra Class Name needed on the image itself. Targets
   vc_single_image's own stock wrapper/img classes directly (confirmed
   2026-09-04: Evenz does NOT override vc_single_image the way it overrides
   vc_row/vc_column, so these are real, stable WPBakery core class names).
   Deliberately NOT using Evenz's native "Parallax Image" row setting (the
   $parallax_image row attribute) -- tried first and reverted. That renders
   via a jQuery "Stellar.js" library which converts the image to
   position:fixed for scroll-based movement, computed against assumptions
   that don't hold in this layout: confirmed broken live 2026-09-04
   (Stellar set the image to position:fixed sized 1264x900 positioned at
   the page's top-left origin, instead of filling the hero row it was
   supposed to sit inside). This CSS-only approach sidesteps that JS
   entirely, and also sidesteps the already-documented "vc_single_image's
   own el_class is unreliable on this stack" gotcha -- targeting core
   WPBakery classes by descendant selector needs no el_class on the image
   at all. */
.esc-header-hero .esc-header-bgcol,
.esc-header-hero-2btn .esc-header-bgcol,
.esc-header-hero-1btn .esc-header-bgcol {
	position: relative;
	z-index: 1;
	/* flex-grow alone proved unreliable on this stack for this column --
	   both "auto" and "0%" flex-basis were tried and confirmed live
	   2026-09-04 to produce wrong results (roughly half the remaining
	   space, then literal 0 width), despite every computed flex property
	   individually reporting correct textbook values. Root cause not fully
	   isolated (Evenz's ".vc_col-sm-12" grid class -- present on every
	   WPBakery column regardless of el_class -- is the leading suspect,
	   likely some interaction not visible in getComputedStyle alone).
	   Sidestepped entirely with an explicit calc() width instead of
	   flex-grow -- deterministic, and easy to verify/debug by eye. Must
	   match the content column's own width formula exactly (currently
	   "min(560px, 46%)") so the two always sum to 100% with nothing left
	   over or overlapping. */
	flex: 0 0 auto;
	width: calc(100% - min(560px, 46%));
	min-width: 0;
}

/* Banner's image column: 60% width (set above), and -- unlike the hero
   variants -- overflow:hidden here specifically, because the image inside
   is now shown at its own native pixel size (see the img rule below) and
   will very often be wider than this column; anything past the column's
   right edge just needs to disappear rather than push layout around. */
.esc-header-banner .esc-header-bgcol {
	position: relative;
	z-index: 1;
	flex: 0 0 auto;
	overflow: hidden;
	min-width: 0;
}

.esc-header-hero .esc-header-bgcol .vc_column-inner,
.esc-header-hero-2btn .esc-header-bgcol .vc_column-inner,
.esc-header-hero-1btn .esc-header-bgcol .vc_column-inner,
.esc-header-banner .esc-header-bgcol .vc_column-inner,
.esc-header-hero .esc-header-bgcol .wpb_wrapper,
.esc-header-hero-2btn .esc-header-bgcol .wpb_wrapper,
.esc-header-hero-1btn .esc-header-bgcol .wpb_wrapper,
.esc-header-banner .esc-header-bgcol .wpb_wrapper,
.esc-header-hero .esc-header-bgcol .wpb_single_image,
.esc-header-hero-2btn .esc-header-bgcol .wpb_single_image,
.esc-header-hero-1btn .esc-header-bgcol .wpb_single_image,
.esc-header-banner .esc-header-bgcol .wpb_single_image,
.esc-header-hero .esc-header-bgcol .vc_figure,
.esc-header-hero-2btn .esc-header-bgcol .vc_figure,
.esc-header-hero-1btn .esc-header-bgcol .vc_figure,
.esc-header-banner .esc-header-bgcol .vc_figure,
.esc-header-hero .esc-header-bgcol .vc_single_image-wrapper,
.esc-header-hero-2btn .esc-header-bgcol .vc_single_image-wrapper,
.esc-header-hero-1btn .esc-header-bgcol .vc_single_image-wrapper,
.esc-header-banner .esc-header-bgcol .vc_single_image-wrapper {
	height: 100%;
	margin: 0;
}

.esc-header-hero .vc_single_image-img,
.esc-header-hero-2btn .vc_single_image-img,
.esc-header-hero-1btn .vc_single_image-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* Biased right-of-center rather than plain "center" -- the box is
	   narrower than the source photo's own wide aspect ratio, so cover
	   crops equal amounts off both sides by default. The left side is
	   already the part getting tinted by the seam-blend gradient above,
	   so there's nothing lost preserving less of it in exchange for
	   showing more of the right. Per Jonathan 2026-09-04. Revisit per
	   image if a future photo's important content sits elsewhere. */
	object-position: 65% center;
	display: block;
}

/* Banner's own image treatment -- deliberately NOT object-fit:cover like
   the hero variants. Per Jonathan's direct instruction (2026-09-08): show
   the image at its own true native size -- not scaled down, not cropped
   -- positioned flush against col 2's left edge. Absolute positioning
   (anchored to .esc-header-bgcol, which has position:relative above)
   rather than a flow-based width:auto sidesteps the wrapper chain's own
   height:100% rules below, which would otherwise stretch/distort a
   naturally-sized image. Vertically centered via the standard
   top:50%/translateY(-50%) technique since the image's native height
   (216px) rarely matches the row's own (title-driven) height exactly.
   Whatever the image's true width doesn't cover, and whatever it
   overflows past the column's own right edge, is simply the row's plain
   navy background-color (set above) showing through -- no gradient
   needed, confirmed via canvas alpha sampling 2026-09-08 that the source
   PNGs are already genuinely transparent on their own left portion, by
   design, specifically so this works. The uploaded assets were also
   cropped 2026-09-08 to trim that transparent dead zone off entirely
   (e.g. attachment 2027 replaces 1898 on the About Singapore page) --
   native-size display only works at typical desktop widths once that
   dead zone is gone; the untrimmed originals needed an unrealistically
   wide column to ever show their actual graphic content. */
.esc-header-banner .vc_single_image-img {
	position: absolute;
	left: 0;
	top: 50%;
	transform: translateY(-50%);
	width: auto;
	height: auto;
	max-width: none;
	display: block;
}

/* Left-to-right navy fade on the image column only -- blends its left
   edge into the content column's solid navy so the two columns read as
   one continuous surface rather than two visibly separate boxes stitched
   together ("continue the navy background", per Jonathan 2026-09-04).
   Text no longer sits on top of the photo in this two-column layout, so
   unlike the old single-column overlay this isn't doing readability work
   -- it's purely the seam blend. Tightened 2026-09-04: the wider/heavier
   original version was washing out the photo's own decorative "+" cross
   marks, which sit close to its left edge -- now fades out fast enough to
   only soften the immediate seam. */
.esc-header-hero .esc-header-bgcol::before,
.esc-header-hero-2btn .esc-header-bgcol::before,
.esc-header-hero-1btn .esc-header-bgcol::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(to right, var(--esc-navy) 0%, rgba(3,41,84,0.3) 5%, rgba(3,41,84,0) 14%);
	pointer-events: none;
}
/* Banner deliberately has no seam-blend gradient -- per Jonathan
   2026-09-08, the source images' own genuine transparency already reveals
   the row's plain navy background wherever there's no graphic, so a
   gradient overlay would be redundant (there's no hard seam to soften). */

.esc-header-hero .evenz-caption,
.esc-header-hero-2btn .evenz-caption,
.esc-header-hero-1btn .evenz-caption,
.esc-header-hero .qt-section-caption,
.esc-header-hero-2btn .qt-section-caption,
.esc-header-hero-1btn .qt-section-caption {
	color: var(--esc-white);
}

.esc-header-banner .evenz-caption,
.esc-header-banner .qt-section-caption {
	color: var(--esc-white);
	font-size: 1.5rem;
}

/* Button row layout only (spacing/wrap for the two-button hero variant) --
   COLOR now comes from the global .esc-btn-* system below, applied per
   button via el_class/css_class, not from this hero-scoped descendant
   selector anymore (that approach only ever worked inside a hero row --
   see the button system's own comment block for why it was replaced). */
.esc-header-hero-2btn .esc-btn-row {
	display: flex;
	gap: 20px;
	justify-content: flex-start;
	flex-wrap: wrap;
}

/* ---------- Global button system ---------- */
/* Confirmed across all 6 XD screens 2026-09-04 (see the component-system
   plan doc): every button in the design is one of these four variants,
   always pill-shaped. Apply via qt-button's own "css_class" attribute or
   vc_btn3/vc_btn's "Extra class name" field, ALONGSIDE Evenz's own base
   button class (style="evenz-btn__white" is a safe structural default --
   pill shape, padding, sizing already correct from Evenz's own CSS; these
   modifier classes only override color/border, not structure or shape).
   Usable anywhere on the site, not just inside a hero row -- this is the
   actual "bake it into the theme" piece: one el_class instead of
   hand-picking colors per instance.

   No hover states exist anywhere in the XD file -- it's a static design
   export, not an interactive prototype, so there was nothing to extract.
   The hover treatment here is our own design decision (confirmed with
   Jonathan 2026-09-04): each variant flips fill+text on hover, one
   consistent "reverse" pattern across all four rather than four different
   hover ideas.

   Selectors are deliberately doubled (".esc-btn--outline.esc-btn--outline")
   -- a same-specificity !important fight against Evenz's own
   ".evenz-negative .evenz-btn { border-color:#fff !important }" rule
   (main.css) was LOSING on a real hero button, because that rule's
   two-class descendant selector out-specifies a single class selector
   even with matching !important. Doubling a class is a standard way to
   add specificity without an ID or depending on the button's surrounding
   context (works whether or not the button happens to sit inside
   .evenz-negative). Also sets border-width/style explicitly -- Evenz's
   base ".evenz-btn" ships with border-width:0, so a border-color alone
   is invisible regardless of which value wins. Confirmed live 2026-09-04
   on page 1515's hero button (rendered as plain text, no visible pill,
   until both of these were fixed). */

/* ---------- Button structural fixes (2026-09-07 QA) ---------- */
/* (1) Evenz's parent-theme main.css gives EVERY button a large animated
   "wave" blob via ::before/::after (a 50em circle drifting on a 20s+
   infinite loop, sped up on hover) -- confirmed live 2026-09-07 as the
   distracting hover effect Jonathan asked to remove. First pass scoped
   this to just our esc-btn--* classes; Jonathan asked the same day to
   suppress it globally, on every button in the theme, not just ours --
   widened to the same selector list the wave rule itself uses in Evenz's
   main.css (.evenz-btn, .evenz-btn-primary, button[type="submit"],
   input[type="submit"], the WPBakery/Gutenberg button-block links, and
   plain button/button.button) so nothing with the effect is missed,
   including native Evenz UI we haven't touched directly. (2) Evenz's base
   .evenz-btn already sets text-transform:uppercase, but something in this
   specific render chain was overriding it back to normal case -- forced
   explicitly on our own esc-btn--* classes rather than chasing the exact
   source (left scoped to our classes only -- Jonathan confirmed only the
   wave needed to go global, not uppercase/nowrap). (3) white-space:nowrap
   + width:auto so a button's own text never wraps -- the button grows to
   fit instead, which is what "don't wrap, allow buttons to grow
   horizontally" means in practice for an inline-flex element like this. */
.evenz-btn::before, .evenz-btn::after,
.evenz-btn-primary::before, .evenz-btn-primary::after,
button[type="submit"]::before, button[type="submit"]::after,
input[type="submit"]::before, input[type="submit"]::after,
.evenz-entrycontent .wp-block-button__link::before, .evenz-entrycontent .wp-block-button__link::after,
.evenz-entrycontent .wp-block-file__button::before, .evenz-entrycontent .wp-block-file__button::after,
button::before, button::after,
button.button::before, button.button::after {
	display: none !important;
	content: none !important;
	animation: none !important;
}

/* ---------- Button color system v3 -- rebuilt 2026-09-07, replaces v1 AND
   v2 in one pass. Real bug found doing this: v2 (built earlier the same
   day) only ADDED new rules for blue/cyan/navy -- it never removed v1's
   original .esc-btn--green/.esc-btn--outline rules, which is exactly why
   Jonathan's live hover test found "only cyan working": whichever buttons
   happened to be green/outline that day were silently still running the
   old matching-border definition with no white-border update, while
   cyan/navy/blue had genuinely been rebuilt. Rewritten as ONE single
   source of truth for every class, no leftover duplicates from earlier
   passes. Per Jonathan's own corrected audit table (2026-09-07,
   afternoon): cyan and green are now the swapping pair (cyan<->green
   hover), not cyan<->light-blue -- light-blue moves to a border-only role
   (navy's hover border) plus its own still-standing "esc-btn--blue" class
   (unchanged, not part of this table, still used on the Elevate CTA
   button -- left as-is since Jonathan didn't call it out either way).
   "esc-btn--green-border" is a NEW, separate class preserving this
   morning's original green treatment (matching green border, not white)
   under its own name, rather than overwriting what "green" means --
   nothing currently in page content uses it yet. */
.esc-btn--outline, .esc-btn--green, .esc-btn--green-border, .esc-btn--cyan, .esc-btn--navy, .esc-btn--blue {
	text-transform: uppercase !important;
	white-space: nowrap !important;
	width: auto !important;
}

.esc-btn--outline.esc-btn--outline {
	background-color: transparent !important;
	border-width: 2px !important;
	border-style: solid !important;
	border-color: var(--esc-cyan) !important;
	color: var(--esc-cyan) !important;
}
.esc-btn--outline.esc-btn--outline:hover {
	background-color: var(--esc-sps-green) !important;
	border-color: var(--esc-sps-green) !important;
	color: var(--esc-white) !important;
}

.esc-btn--green.esc-btn--green {
	background-color: var(--esc-sps-green) !important;
	border-width: 2px !important;
	border-style: solid !important;
	border-color: var(--esc-white) !important;
	color: var(--esc-white) !important;
}
.esc-btn--green.esc-btn--green:hover {
	background-color: var(--esc-cyan) !important;
	border-color: var(--esc-white) !important;
	color: var(--esc-white) !important;
}

.esc-btn--green-border.esc-btn--green-border {
	background-color: var(--esc-sps-green) !important;
	border-width: 2px !important;
	border-style: solid !important;
	border-color: var(--esc-sps-green) !important;
	color: var(--esc-white) !important;
}
.esc-btn--green-border.esc-btn--green-border:hover {
	background-color: var(--esc-cyan) !important;
	border-color: var(--esc-cyan) !important;
	color: var(--esc-white) !important;
}

.esc-btn--cyan.esc-btn--cyan {
	background-color: var(--esc-cyan) !important;
	border-width: 2px !important;
	border-style: solid !important;
	border-color: var(--esc-white) !important;
	color: var(--esc-white) !important;
}
.esc-btn--cyan.esc-btn--cyan:hover {
	background-color: var(--esc-sps-green) !important;
	border-color: var(--esc-white) !important;
	color: var(--esc-white) !important;
}

.esc-btn--navy.esc-btn--navy {
	background-color: var(--esc-navy) !important;
	border-width: 2px !important;
	border-style: solid !important;
	border-color: var(--esc-white) !important;
	color: var(--esc-white) !important;
}
.esc-btn--navy.esc-btn--navy:hover {
	background-color: var(--esc-white) !important;
	border-color: var(--esc-lightblue) !important;
	color: var(--esc-navy) !important;
}

.esc-btn--blue.esc-btn--blue {
	background-color: var(--esc-lightblue) !important;
	border-width: 2px !important;
	border-style: solid !important;
	border-color: var(--esc-white) !important;
	color: var(--esc-white) !important;
}
.esc-btn--blue.esc-btn--blue:hover {
	/* Changed cyan -> green 2026-09-07 -- Jonathan reported this button's
	   hover "doesn't seem to be working." The CSS itself was correct and
	   the only matching rule (confirmed via a full CSSOM search, no
	   conflicting/duplicate rule found) -- the real problem is that
	   light-blue (#00A0DE) and cyan (#03A1DF) are close enough in hue/
	   brightness that swapping between them reads as no visible change at
	   all. Green is a clean, unambiguous jump, consistent with the same
	   "swap to a clearly distinct color" pattern Jonathan's own corrected
	   table already uses for cyan<->green. */
	background-color: var(--esc-sps-green) !important;
	border-color: var(--esc-white) !important;
	color: var(--esc-white) !important;
}

/* ---------- Mobile: stack instead of side-by-side ---------- */
/* No mobile treatment exists in the XD file (it only mocks one desktop
   width) -- this is a judgment call for the best mobile UX rather than a
   recreation of any design, per Jonathan 2026-09-04: text first (heading,
   copy, button), image second below it, cropped to a controlled banner-
   like ratio rather than carrying its full desktop height down the page. */
@media only screen and (max-width: 992px) {
	.esc-header-hero,
	.esc-header-hero-2btn,
	.esc-header-hero-1btn {
		min-height: 0;
	}

	.esc-header-banner {
		min-height: 0;
	}

	/* Real bug found 2026-09-07 (Jonathan, mobile check): the -75px
	   margin-top used to pull the hero flush with the nav on desktop
	   (cancelling Evenz's own 75px top padding on body content) was
	   confirmed live to overlap the mobile nav/logo strap by exactly
	   75px, hiding the first line of the H1 underneath it. Desktop's own
	   nav height/padding doesn't carry over unchanged to mobile. Reduced
	   to a small positive buffer instead of 0 -- Jonathan asked for "an
	   appropriate buffer between the header block and the image", not a
	   flush fit. Later dropped from 20px to 10px (still 2026-09-07) after
	   Jonathan removed the second mobile logo from the Customizer -- that
	   shrank the nav bar itself, so the same fixed 20px buffer below it
	   started reading as 10px too much; this is a plain margin value, not
	   derived from nav height, so it has to be re-checked by eye any time
	   the Customizer's mobile logo/nav setup changes again. */
	.esc-header-hero,
	.esc-header-hero-2btn,
	.esc-header-hero-1btn,
	.esc-header-banner {
		margin-top: 5px !important; /* tightened from 10px 2026-09-07, Jonathan: still ~4-5px too much */
	}

	.esc-header-hero > .evenz-rowcontainer-vc > .evenz-rowcontent,
	.esc-header-hero-2btn > .evenz-rowcontainer-vc > .evenz-rowcontent,
	.esc-header-hero-1btn > .evenz-rowcontainer-vc > .evenz-rowcontent,
	.esc-header-banner > .evenz-rowcontainer-vc > .evenz-rowcontent {
		/* column-reverse, not column: Jonathan corrected the original
		   text-first-on-mobile decision after seeing it live -- image
		   needs to stack first (on top), text second, on every header
		   type. This flips the VISUAL order without touching DOM order in
		   every page's content (DOM stays content-column-first, image-
		   column-second everywhere, which is still what desktop and the
		   grid-alignment logic depend on). */
		flex-direction: column-reverse;
	}

	.esc-header-hero .esc-header-content,
	.esc-header-hero-2btn .esc-header-content,
	.esc-header-hero-1btn .esc-header-content {
		width: 100% !important;
		padding: 48px 6% 40px;
	}

	/* No !important needed here (unlike the hero rules above, which still
	   need it) -- the desktop banner-content-width fix dropped its own
	   !important the same day (see that rule's own comment), so this
	   plain-weight mobile override wins on source order alone now. */
	.esc-header-banner .esc-header-content {
		width: 100%;
		padding: 24px 6%;
	}

	.esc-header-hero .esc-header-bgcol,
	.esc-header-hero-2btn .esc-header-bgcol,
	.esc-header-hero-1btn .esc-header-bgcol {
		flex: 0 0 auto;
		width: 100% !important;
		aspect-ratio: 16 / 9;
		min-height: 220px;
	}

	/* Banner's image column on mobile: the desktop native-size/absolute
	   treatment doesn't work here -- even with the dead zone trimmed off
	   the source PNGs (2026-09-08), a ~375px-wide mobile column is still
	   narrower than the cropped graphic's own native width. Instead let
	   the whole image scale down to fit the full mobile width
	   proportionally -- shows the real graphic in full, just smaller. */
	.esc-header-banner .esc-header-bgcol {
		flex: 0 0 auto;
		width: 100%;
		overflow: visible;
	}
	.esc-header-banner .vc_single_image-img {
		position: static;
		transform: none;
		width: 100%;
		height: auto;
		max-width: 100%;
	}

	/* The left-to-right seam blend doesn't apply once the columns stack --
	   swap it for a light top-down fade instead, purely decorative here
	   since text no longer sits on the image either way. */
	.esc-header-hero .esc-header-bgcol::before,
	.esc-header-hero-2btn .esc-header-bgcol::before,
	.esc-header-hero-1btn .esc-header-bgcol::before {
		background: linear-gradient(180deg, rgba(3,41,84,0.35) 0%, rgba(3,41,84,0) 25%);
	}

	.esc-header-hero-2btn .esc-btn-row {
		justify-content: flex-start;
		flex-wrap: wrap;
	}
}

/* ---------- Submenu (dropdown) fix ---------- */
/* evenz-child's own blanket ".evenz-menubar li a { color: #fff }" rule
   (correct for the top-level navy nav bar) also catches nested submenu
   links. Kirki's "Paper" background (white, evenz_paper) is what those
   submenu panels sit on, so the text goes invisible: white on white.
   Kirki's own menu-specific color fields (evenz_menu_l/_t) are lower
   specificity than evenz-child's rule and can't win the cascade to fix
   this from the Customizer alone -- this is a selector-specificity bug,
   not a color choice, so it's fixed here structurally regardless of
   whatever palette is active.
   Also re-colors the hover fill-slide effect, which is hardcoded red
   (#f00) in the Evenz PARENT theme's main.css (not Kirki-configurable,
   parent theme never touched) -- overridden here to the accent cyan so
   it matches the redesign palette instead of Evenz's demo red. */
.evenz-menu-horizontal .evenz-menubar > li ul li a {
	color: var(--esc-navy) !important;
	background-image: linear-gradient(to right, var(--esc-cyan) 100%, var(--esc-white) 100%) !important;
}
.evenz-menu-horizontal .evenz-menubar > li ul li:hover > a {
	color: var(--esc-white) !important;
}

/* ---------- vc_cta / vc_row closing bar (per-page usage, e.g. page 1515,
   the future-spsummit and homepage patronage CTAs) ---------- */
/* WPBakery's vc_cta only supports a solid inline custom_background via
   style="custom" -- no gradient option in the shortcode itself. Applied via
   el_class="esc-cta-summit" on the shortcode; custom_background="#032954"
   stays in the shortcode attributes as a solid fallback if this stylesheet
   ever fails to load. Reuses the same nav-bar gradient tokens for visual
   consistency with the header, matching the XD reference for this section
   (a green-leaning gradient bar with a navy button, not WPBakery's stock
   flat blue). Button color is set directly via the shortcode's own
   btn_custom_background attribute -- no CSS override needed there.
   Also matches a plain .vc_row carrying el_class="esc-cta-summit" (the
   future-spsummit/homepage pattern, a heading+text+button CTA block rather
   than a vc_cta shortcode) -- the padding lives here instead of the row's
   own inline css=".vc_custom_XXXX{padding:...}" attribute because that
   attribute's CSS is only ever generated by WPBakery's save_post hook when
   a page is saved through the real visual editor; content pushed via
   wp_update_post()/wp-cli (as this page was) never triggers it, so the rule
   silently never exists and the row renders with no background/padding at
   all. Confirmed live 2026-09-04 on both the future-spsummit and homepage
   patronage CTAs. Never rely on a shortcode's own css="..." attribute for
   content pushed outside the editor -- use el_class against this
   stylesheet instead. */
.vc_cta3.esc-cta-summit,
.vc_row.esc-cta-summit {
	background: linear-gradient(to right, var(--esc-sps-blue-grad) 0%, var(--esc-sps-green-grad) 100%) !important;
	border-color: transparent !important;
}
.vc_row.esc-cta-summit {
	padding: 40px !important;
}

/* ---------- Repeatable CTA block (bordered card, gradient heading strap,
   white centered body) -- added 2026-09-08 per Jonathan's reference image.
   A real component now, not a one-off: use el_class="esc-cta-block" on any
   future [vc_cta] that needs this exact treatment. Real DOM (vc_cta always
   renders through the vc_cta3 engine, confirmed via the existing
   esc-cta-summit rule above): .vc_cta3.esc-cta-block >
   .vc_cta3_content-container > .vc_cta3-content > (header[h2] + p*).
   Two-part design, unlike esc-cta-summit's single whole-card gradient:
   only the header strap (h2) gets the gradient + white text; everything
   below it is plain white with centered text, matching the reference
   image exactly (bounding border, white body, centered button on its own
   last row). */
.vc_cta3.esc-cta-block {
	background-color: var(--esc-white) !important;
	border: 1px solid var(--esc-lightblue) !important;
	border-radius: 12px !important;
	overflow: hidden; /* clips the header strap's corners to the card's own radius */
	padding: 0 !important;
}
.vc_cta3.esc-cta-block .vc_cta3-content-header {
	background: linear-gradient(to right, var(--esc-sps-blue-grad) 0%, var(--esc-sps-green-grad) 100%) !important;
	margin: 0 !important;
	padding: 24px 40px !important;
	text-align: center !important;
}
.vc_cta3.esc-cta-block .vc_cta3-content-header h2 {
	color: var(--esc-white) !important;
	margin: 0 !important;
}
.vc_cta3.esc-cta-block .vc_cta3-content {
	padding: 30px 40px !important;
	text-align: center !important;
	align-items: center !important; /* centers the button's own p.aligncenter as a flex item too, not just its text */
}
.vc_cta3.esc-cta-block .vc_cta3-content > p {
	text-align: center !important;
}

/* ---------- One-off decorative section background (homepage "Three
   Events. One Technical Forum." row) ---------- */
/* Same wp-cli-vs-editor custom-css gap as esc-cta-summit above -- this row's
   background-image lived in an inline css="..." attribute that never got
   its stylesheet generated. Baked in directly since it's a one-time image
   for this specific section, not a reusable token. */
.esc-events-bg {
	background-image: url(https://training.ieeespsummit.org/wp-content/uploads/sites/8/2025/03/abstract-blurred-people-in-exhibition-hall-event-2021-08-26-23-02-53-utc.jpg);
	background-size: cover;
	background-position: center;
}

/* ---------- Card component (event cards, conference cards) ---------- */
/* Applied via el_class="esc-card" on a vc_cta shortcode (style="flat"
   color="grey" btn_style="custom" -- the grey/flat combo gives a stable,
   already-rounded (5px) base to override rather than fighting WPBakery's
   other color presets). Confirmed live 2026-09-04 default render: light
   grey background (#EBEBEB), grey heading/meta text (#666/#9D9D9E) -- this
   class swaps those for a white card with navy heading text, matching the
   XD's event/conference card pattern. Real DOM (read live, not guessed):
   .vc_cta3.vc_cta3-color-grey > .vc_cta3_content-container >
   .vc_cta3-content > header.vc_cta3-content-header > h2/h4, then a
   sibling p for meta/description text, then .vc_cta3-actions >
   .vc_btn3-container > a.vc_btn3 for the button. Button color is already
   set correctly per-instance via the shortcode's own btn_custom_background
   attribute (green for conference cards, navy for event cards) -- no CSS
   override needed for the button itself. */
.vc_cta3.esc-card {
	background-color: var(--esc-white) !important;
	border-radius: 24px !important; /* bumped again 2026-09-07 (was 16px) per Jonathan, closer match to XD -- needs !important, WPBakery's own .vc_cta3-shape-rounded sets 5px at equal-or-higher specificity */
	border: 2px solid var(--esc-lightblue) !important; /* light-blue border added 2026-09-07 */
	box-shadow: 0 4px 20px rgba(3, 41, 84, 0.10);
	display: flex !important;
	flex-direction: column !important;
}
/* Equal-height/button-alignment fix, added 2026-09-07: the card's real DOM is
   .vc_cta3.esc-card > .vc_cta3_content-container > .vc_cta3-content > (header + text node + p.aligncenter[button]).
   height:100% cascaded down through the wrapper chain was tried first (and
   the row/column itself IS correctly equal-height via the CSS Grid fix
   further down) but confirmed live, repeatedly, across three different
   layout mechanisms (table-cell, flexbox, grid): the card whose own content
   is naturally longer (ASLI's 2-line h4) never actually inherits the
   percentage height, staying at its own natural ~308px while its siblings
   correctly stretch to 343px -- a genuine, reproducible browser quirk with
   percentage height resolved through several nested wrapper levels when
   sibling content lengths differ, not a selector mistake (every ancestor in
   the chain up to the card's own immediate parent confirmed correctly
   resolving to the equal 342.6px in devtools). A hardcoded min-height
   sidesteps the cascade entirely -- no percentage resolution involved, so no
   opportunity for this quirk. Values below are the known content's natural
   height rounded up with a small safety margin (3-card row's tallest
   natural content ~308px; 2-card row's ~296px, already equal but the rule
   is harmless there) -- re-check by eye if a future copy edit changes how
   many lines any card's heading or description wraps to. Below the
   min-height, the card is made a flex column itself (above) and its two
   inner wrappers use flex:1 (grow to fill available space) rather than
   height:100% -- percentage heights need an explicit (non-min) height on
   the parent to resolve against, which min-height alone doesn't provide,
   but flex-grow distributes a flex container's actual rendered size
   (however it was reached -- content, min-height, whichever is larger)
   with no such requirement, so the button paragraph's margin-top:auto
   further down correctly reaches the true bottom of the min-height box. */
.esc-summit-cards .evenz-rowcontent:has(> .wpb_column:nth-child(5)) .vc_cta3.esc-card {
	min-height: 340px;
}
.esc-summit-cards .evenz-rowcontent:has(> .wpb_column:nth-child(3)):not(:has(> .wpb_column:nth-child(4))) .vc_cta3.esc-card {
	min-height: 300px;
}
.vc_cta3.esc-card .vc_cta3_content-container,
.vc_cta3.esc-card .vc_cta3-content {
	display: flex !important;
	flex-direction: column !important;
	flex: 1 1 auto !important;
}
.vc_cta3.esc-card .vc_cta3-content > p.aligncenter {
	margin-top: auto !important;
	padding-top: 20px;
}
/* equal_height="yes" on vc_row_inner is a no-op on this theme (confirmed
   live: the row_inner's real wrapper -- a .evenz-rowcontent div that is the
   direct parent of the .wpb_column cells -- renders as display:table with
   its columns as display:table; float:left, never table-cell). Two more
   approaches were tried and rejected before this one, both confirmed live:
   (1) forcing real display:table-cell hit a genuine browser quirk where a
   cell whose content wraps onto more lines (the ASLI card's longer h4)
   resolved height:100% inconsistently vs. its siblings despite an identical
   class and an identical already-equal parent height; (2) switching to
   flexbox with align-items:stretch -- normally the standard fix for exactly
   this pattern -- mysteriously never stretched the shorter items at all,
   even with align-self:stretch forced inline with !important (tested
   directly in devtools, ruled out every plausible cause). CSS Grid's default
   stretch behavior is what actually works (confirmed live: all 5 cells
   equal height on the very first test). Selector needs care: the SAME
   single "evenz-rowcontent" class is reused for an outer, unrelated full-
   width wrapper elsewhere in this same section, so a plain :has(> .wpb_column)
   check matches that wrong ancestor too -- the deep :has() chain checks for
   a .vc_cta3.esc-card at the exact known depth to uniquely identify the real
   card row (confirmed live: matches exactly 1 element). grid-template-
   columns uses fr units matching each layout's own WPBakery column-width
   fractions (3/12+1/12+3/12+1/12+3/12 for the 3-card row on Page 2;
   5/12+2/12+5/12 for the 2-card row on Pages 1 & 5) rather than a fixed
   value, since the two summit-cards layouts have a different column count;
   width:100% on the columns is required too -- without it, a grid item with
   its own explicit width (from the existing .vc_col-sm-* class) sizes to
   that width instead of stretching to fill its track (confirmed live). */
.esc-summit-cards .evenz-rowcontent:has(> .wpb_column > .vc_column-inner > .wpb_wrapper > .vc_cta3-container > .vc_cta3.esc-card) {
	display: grid !important;
	align-items: stretch !important;
	column-gap: 16px;
}
/* Cards widened and dividers narrowed 2026-09-07 per Jonathan ("cards
   slightly wider", spacing called out as important) -- was 3fr/1fr (3-card)
   and 5fr/2fr (2-card), now 5fr/1fr both rows, plus a real column-gap above
   for consistent breathing room instead of relying solely on the divider
   column's own width. minmax(0, Nfr) instead of a bare Nfr matters, not
   just style: a bare `Nfr` track defaults to `minmax(auto, Nfr)`, where
   "auto" is the item's own min-content size -- confirmed live this was
   silently preventing any track from shrinking below its card's natural
   content width, which is exactly why mobile stacking below wasn't taking
   effect (the grid was refusing to shrink and overflowing horizontally
   instead of the single column taking over). */
.esc-summit-cards .evenz-rowcontent:has(> .wpb_column:nth-child(5)) {
	grid-template-columns: minmax(0, 5fr) minmax(0, 1fr) minmax(0, 5fr) minmax(0, 1fr) minmax(0, 5fr) !important; /* 3-card row: Page 2 */
}
.esc-summit-cards .evenz-rowcontent:has(> .wpb_column:nth-child(3)):not(:has(> .wpb_column:nth-child(4))) {
	grid-template-columns: minmax(0, 5fr) minmax(0, 1fr) minmax(0, 5fr) !important; /* 2-card row: Pages 1 & 5 */
}
.esc-summit-cards .evenz-rowcontent:has(> .wpb_column > .vc_column-inner > .wpb_wrapper > .vc_cta3-container > .vc_cta3.esc-card) > .wpb_column.vc_column_container {
	width: 100% !important;
	float: none !important;
	min-width: 0;
}
/* Mobile: stack every card/divider into its own full-width row instead of
   forcing them to keep competing for horizontal space. Needed even with the
   minmax(0, ...) fix above -- that fix stops the grid from overflowing, but
   3-5 narrow side-by-side columns still isn't a usable mobile layout;
   Jonathan explicitly asked for a vertical stack here, matching the
   existing hero/banner mobile stacking pattern elsewhere in this file. */
@media only screen and (max-width: 992px) {
	.esc-summit-cards .evenz-rowcontent:has(> .wpb_column > .vc_column-inner > .wpb_wrapper > .vc_cta3-container > .vc_cta3.esc-card) {
		grid-template-columns: minmax(0, 1fr) !important;
		row-gap: 20px;
	}
	.esc-summit-cards .wpb_column.vc_column_container:has(.esc-plus-icon) {
		padding: 4px 0 !important;
	}
	/* The 340px/300px min-height further down exists purely to keep cards
	   equal height when they sit SIDE BY SIDE on desktop -- once stacked
	   single-column here, there's no sibling to match and the fixed
	   min-height just wastes vertical space (confirmed 2026-09-07, Jonathan
	   flagged mobile cards as "unnecessarily high"). Let each card size to
	   its own real content on mobile instead. */
	.esc-summit-cards .vc_cta3.esc-card {
		min-height: 0 !important;
	}
}
/* Percentage height doesn't reach through an auto-height ancestor -- every
   wrapper between the now grid-stretched column and .vc_cta3.esc-card itself
   (confirmed live chain: wpb_column > vc_column-inner > wpb_wrapper >
   SECTION.vc_cta3-container > .esc-card) needs its own height:100% or the
   card still shrinks to its own content height regardless of the column. */
.esc-summit-cards .wpb_column.vc_column_container > .vc_column-inner,
.esc-summit-cards .vc_column-inner > .wpb_wrapper,
.esc-summit-cards .wpb_wrapper > .vc_cta3-container {
	height: 100% !important;
}
.esc-summit-cards .wpb_column.vc_column_container:has(.esc-plus-icon) {
	display: flex !important;
	align-items: center !important;
	justify-content: center !important;
}
.vc_cta3.esc-card .vc_cta3-content-header h2 {
	color: #00A0DE !important; /* corrected 2026-09-07 per Jonathan's exact hex, not the --esc-cyan token (#03A1DF -- close but not identical) */
}
.vc_cta3.esc-card .vc_cta3-content-header h4 {
	color: var(--esc-sps-blue) !important;
	font-weight: 500;
	opacity: 0.85;
}
.vc_cta3.esc-card .vc_cta3-content p {
	color: var(--esc-darknavy) !important;
}

/* ---------- "Join Us at the Next Summit" cards section (Home Page) ----- */
/* Applied via el_class="esc-summit-cards" on the row, added 2026-09-07 per
   Jonathan's QA. Background image itself still pending (needs the actual
   file -- not guessed/invented here); heading color, grid alignment, and
   the FontAwesome "+" divider are ready now regardless. Font Awesome is
   already loaded on this stack (Evenz's own vc_text_separator instances
   use i_icon_fontawesome elsewhere in the real source content), so no new
   asset dependency here. */
.esc-summit-cards {
	/* padding-left/right removed 2026-09-08: now double-applying on top of
	   the new global .evenz-container fix (this row is itself an
	   evenz_container="1" row, so it already gets the correct grid-inset
	   padding on its own .evenz-container child) -- confirmed live this
	   was pushing the heading 45px further right than the nav/hero grid
	   line instead of matching it. */
	/* Background image comes from each page's own inline vc_custom_XXXXX
	   class (WPBakery's row "css" attribute), which only sets
	   background-image -- no repeat/size -- so it was using the CSS
	   default (repeat, auto), tiling vertically once the section grew
	   taller than the image's own natural height (confirmed 2026-09-07:
	   this is exactly what happened once mobile card-stacking below made
	   the section much taller). Force no-repeat + cover here instead so
	   it always fills the section as one image regardless of how tall the
	   section ends up being on any given viewport. */
	background-repeat: no-repeat !important;
	background-size: cover !important;
	background-position: center !important;
}
.esc-summit-cards > .vc_column_text h2.esc-heading,
.esc-summit-cards h2.esc-heading {
	color: var(--esc-white) !important;
	text-transform: uppercase !important; /* added 2026-09-07 */
}
.esc-summit-cards .vc_column_inner:has(.esc-plus-icon) .wpb_wrapper {
	text-align: center;
}

/* ---------- Shared "+" divider icon (real XD asset) --------------------- */
/* Replaces the earlier FontAwesome fa-plus placeholder -- Jonathan supplied
   the actual XD asset (icon-plus-symbol.svg) 2026-09-07, used here between
   both the icon-feature cards (Innovation/Interaction/Impact) and the
   "Join Us" event cards (SP Summit 2027/2028). One shared class since it's
   the same asset/treatment in both places.

   Recolored to cyan via a CSS mask rather than an <img> tag: the SVG file
   itself has its own fill baked in (renders green, confirmed live and
   flagged by Jonathan), and a plain <img> can't be recolored with CSS --
   only an element's own background/text can. mask-image uses the SVG
   purely as a shape (its alpha channel), painted with whatever
   background-color we set, guaranteed to match our color tokens exactly
   regardless of the source file's own fill. Content markup changed from
   <img class="esc-plus-icon" src="..."> to an empty <span
   class="esc-plus-icon">. Same technique Evenz's own main.css already uses
   for the Google Calendar icon (i.evenz-gc), not a new pattern for this
   codebase. */
.esc-plus-icon {
	width: 32px;
	height: 32px;
	display: inline-block;
	background-color: var(--esc-cyan);
	-webkit-mask-image: url(https://training.ieeespsummit.org/wp-content/uploads/sites/10/2026/09/icon-plus-symbol.svg);
	mask-image: url(https://training.ieeespsummit.org/wp-content/uploads/sites/10/2026/09/icon-plus-symbol.svg);
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-position: center;
	mask-position: center;
	-webkit-mask-size: contain;
	mask-size: contain;
}
.esc-icon-feature .esc-plus-icon {
	/* Reverted 2026-09-07 -- the margin-top:190px offset (an attempt to
	   match Jonathan's annotated screenshot) didn't read correctly live;
	   back to the original row-vertical-center placement from
	   content_placement="middle" on the row_inner. Color (cyan, via the
	   mask-image technique above) stays as-is -- only position reverted. */
	align-self: center;
}

/* ---------- Patronage text-box CTA (Home Page) -- 2026-09-07 rebuild --- */
/* Replaces the qt-cardshorizontal version -- Jonathan asked to "convert
   section to text box" (plain white section, heading in normal/navy
   color, no split-image card layout). esc-heading already gives navy;
   this class only adds the white background + grid alignment. */
.esc-text-cta {
	background-color: var(--esc-white) !important;
	/* padding-left/right removed 2026-09-08, same reason as .esc-summit-cards
	   above -- double-applying on top of the new global .evenz-container fix. */
}

/* ---------- Stats bar (Home Page: "2,000+ Global Attendees" etc.) ------- */
/* Applied via el_class="esc-stats-bar" on the row (replaces the plain
   esc-bg-navy placeholder). Reuses the same nav-bar gradient tokens as
   esc-cta-summit for visual consistency, per the XD reference showing a
   navy-to-green gradient here rather than flat navy. Only adds
   typography/gradient on top of the row's existing plain
   vc_column_text h3/p structure -- no markup change needed, the
   placeholder's inline color:#fff on both tags is already correct and
   left as-is (no specificity conflict, since font-size/weight/spacing
   aren't set inline). */
.esc-stats-bar {
	background: linear-gradient(to right, var(--esc-sps-blue-grad) 0%, var(--esc-sps-green-grad) 100%) !important;
	/* Equal top/bottom padding added 2026-09-07 -- the content's own
	   [qt-spacer] shortcodes (identical, one before/one after the numbers
	   row) were rendering asymmetric (confirmed live: 140px above the
	   numbers, 70px below, root cause not fully isolated). Moved spacing
	   into CSS instead of chasing the spacer discrepancy further -- the
	   [qt-spacer] instances were removed from this row's content. */
	padding-top: 70px !important;
	padding-bottom: 70px !important;
}
.esc-stats-bar h3 {
	font-size: 64px; /* bumped from 44px per Jonathan 2026-09-07 QA */
	font-weight: 700;
	margin-bottom: 6px;
}
.esc-stats-bar p {
	text-transform: uppercase;
	letter-spacing: 0.06em;
	font-size: 14px;
	opacity: 0.9;
}

/* ---------- Icon-feature row (Home Page: Innovation/Interaction/Impact) - */
/* Applied via el_class="esc-icon-feature". Targets the actual current
   markup -- a vc_single_image icon at img_size "100x100" followed by an
   h4.esc-subheading and a plain p -- rather than requiring any new
   component structure. The "+" decorative divider motif catalogued in the
   component plan is deliberately not built here (lower priority, flagged
   as its own follow-up, not needed for this row to read correctly).

   2026-09-07 QA (Jonathan): background corrected from esc-bg-dark
   (#06090D, near-black) to the lighter navy token (#032954) -- this class
   now owns its own background directly rather than depending on a
   separate esc-bg-dark class in the row's el_class, and the icon
   heading/paragraph text switched from centered to left-aligned within
   the real page grid (--esc-grid-inset, verified against the nav's own
   position). text-align:left here needs !important to beat the content's
   own inline style="text-align:center" (an !important external rule
   always beats a plain inline style, regardless of specificity) -- this
   was deliberately done in CSS instead of editing every instance's inline
   style in the page content.

   Follow-up same day: the section's own caption ("The Summit Experience",
   rendered by qt-caption as .evenz-caption) was mistakenly swept into that
   same left-align rule and also inherited its own default navy color
   (#032954, Evenz's normal on-white caption color) -- both wrong now that
   the section sits on a navy background: navy text on navy background is
   effectively invisible, and Jonathan wants this specific caption back to
   centered regardless of the icon content's own left-alignment. Caption
   is now handled by its own rule, separate from the icon heading/paragraph
   rule below. */
.esc-icon-feature {
	background-color: var(--esc-navy) !important;
	padding-left: var(--esc-grid-inset) !important;
	padding-right: var(--esc-grid-inset) !important;
}
.esc-icon-feature .evenz-caption {
	color: var(--esc-white) !important;
	text-align: center !important;
}
.esc-icon-feature h4.esc-subheading,
.esc-icon-feature p {
	text-align: left !important;
}
/* 2026-09-07 QA: two real bugs found in the icon row.
   (1) H4 headings ("Innovation"/"Interaction"/"Impact") had no color rule
   of their own at all -- they were inheriting the page's normal navy
   heading color (evenz_titles), invisible on this section's navy
   background. Confirmed live: all three were navy, not just some.
   (2) Icons 2 and 3 (icon-interaction.svg, icon-impact.svg -- swapped in
   live by someone after this build's first pass, replacing the original
   PNGs) rendered at 0x0. Root cause: max-width alone gives no width to an
   element with no other sizing information, and these SVG files apparently
   don't declare their own width/height/viewBox in a way the browser can
   fall back on (unlike icon 1, still the original PNG, which has real
   pixel dimensions to shrink from). A plain "width" (not just max-width)
   forces real sizing regardless of the source file's own declarations. */
.esc-icon-feature h4.esc-subheading {
	color: var(--esc-white) !important;
	margin-bottom: 12px;
}
.esc-icon-feature .vc_single_image-img {
	/* Bumped 60px -> 100px 2026-09-07 -- Jonathan flagged the icons as hard
	   to see. img_size in the shortcode itself also changed from "100x100"
	   to "full" in the page content so the source file isn't upscaled past
	   its own generated resolution. */
	width: 100px;
	max-width: 100px;
	height: 100px;
	display: block;
}
/* White ellipse behind each icon, per Jonathan's QA -- a fixed-size circle
   (equal width/height) rather than a shape tied to each icon's own
   natural aspect ratio, so all three read as the same badge size
   regardless of the source icon's own dimensions. Bumped 110px -> 150px
   alongside the icon size increase above, to keep proportional padding
   inside the circle. */
.esc-icon-feature .vc_single_image-wrapper {
	width: 150px;
	height: 150px;
	background-color: var(--esc-white);
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	margin: 0 0 22px;
}
.esc-icon-feature .wpb_column .wpb_wrapper > .wpb_text_column p {
	max-width: 320px;
	line-height: 1.6;
}

/* ---------- Contact form box (Contact Us page) ---------- */
/* Same wp-cli-vs-editor custom-css gap as esc-cta-summit/esc-events-bg above
   -- hub's original markup applied this box's light-grey background and
   padding via an inline css="..." attribute (on a column carrying
   el_class="page-boxcard") that never got its stylesheet generated for
   wp-cli-pushed content. Baked in directly via a real class instead.
   Fixed px padding, not hub's original 7.5% -- confirmed live 2026-09-04
   that percentage padding here resolves against a much narrower
   containing-block width than the visible column (computed to ~2px, not
   the ~45-60px a 7.5% figure implies), the same class of fragile
   percentage-on-an-unexpected-ancestor problem hit earlier in this build. */
.esc-contact-box {
	/* Changed from a boxed #f5f5f5 gray card to a plain white, uncontained
	   block 2026-09-08 per Jonathan: XD shows this as left-aligned body
	   content, not a centered gray card -- matches the page bg now so it
	   no longer reads as its own "box" at all. Content-side fix (removing
	   the 1/6+2/3+1/6 centering columns) is the other half of this, in
	   page 1473's own post_content, not here. */
	background-color: var(--esc-white) !important;
	padding: 0 !important;
}

/* ---------- Patronage CTA (qt-cardshorizontal, Home Page + Event Home Page) */
/* Confirmed against XD 2026-09-04: this exact CTA ("Learn About Patronage
   Opportunities") is a white background with a navy heading, dark body
   text, and a solid cyan button -- structurally different from Patrons &
   Exhibitors' own closing CTA (esc-cta-summit, a navy-green gradient bar),
   which stays correct as-is for that page. Both hub's and 2027's real raw
   source use the native qt-cardshorizontal shortcode here (theme-function-
   cards-horizontal.php), not a hand-built vc_row -- but its default look
   is Evenz's own dark "gradprimary" gradient (#111618->#353535) with a
   completely unstyled button (transparent bg, no border -- effectively
   invisible chrome, just white text sitting on the dark card). Confirmed
   live 2026-09-04. No el_class/css_class param exists on this shortcode to
   scope an override to specific instances, but this is currently the only
   place qt-cardshorizontal is used anywhere in the redesign, so a global
   override is safe. */
.evenz-cards__horizontal .evenz-cards__content.evenz-gradprimary {
	background: var(--esc-white) !important;
}
.evenz-cards__horizontal h2.evenz-caption {
	color: var(--esc-navy) !important;
}
.evenz-cards__horizontal .evenz-cards__horizontal__pad p {
	color: var(--esc-darknavy) !important;
}
.evenz-cards__horizontal a.evenz-btn {
	background-color: var(--esc-cyan) !important;
	border: none !important;
	color: var(--esc-white) !important;
}
.evenz-cards__horizontal a.evenz-btn:hover {
	background-color: var(--esc-navy) !important;
}
