/*
Theme Name: Einar & Partners Theme
Theme URI: https://einar.partners
Author: Einar & Partners
Author URI: https://einar.partners
Description: Custom WordPress theme for Einar & Partners.
Version: 0.0.9
Requires at least: 6.0
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: einar-partners
*/

/* -----------------------------------------------------------------------
   Reset & base
----------------------------------------------------------------------- */
*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	-webkit-text-size-adjust: 100%;
}

body {
	margin: 0;
	font-family: var(--font-body);
	font-weight: var(--fw-body);
	font-size: var(--fs-body);
	line-height: var(--lh-body);
	color: var(--text);
	background: var(--body-bg);
}

img {
	max-width: 100%;
	height: auto;
	display: block;
}

a {
	color: var(--link);
	text-decoration: none;
}

ul,
ol {
	margin: 0 0 1.5em;
}

h1,
h2,
h3,
h4,
h5,
h6 {
	margin: 0 0 0.5em;
	font-family: var(--font-head);
	font-weight: var(--fw-heading);
	line-height: var(--lh-heading);
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }
h5 { font-size: var(--fs-h5); }
h6 { font-size: var(--fs-h6); }

p {
	margin: 0 0 1.5em;
}

/* -----------------------------------------------------------------------
   Layout
----------------------------------------------------------------------- */
.site {
	display: flex;
	flex-direction: column;
	min-height: 100vh;
}

.container {
	width: 100%;
	max-width: var(--container-max);
	margin: 0 auto;
	padding: 0 1.5rem;
}

.site-content {
	flex: 1 0 auto;
	/* Clears the fixed #main-nav (height: 70px, see .nav-inner in
	   assets/css/site-header-footer.css) so page content isn't hidden
	   underneath it. The front page skips this wrapper entirely and
	   handles its own nav clearance per-section instead. */
	padding-top: 70px;
}

.content-area {
	width: 100%;
}

/* Templates that use a sidebar wrap #primary (.content-area) and #secondary
   (.widget-area) in a ".content-sidebar" div — see search.php/single.php/
   page.php (archive.php/index.php dropped their sidebar — see
   template-parts/post-grid.php). Anything that needs to span the FULL
   container width (e.g. the archive/blog page-header) stays OUTSIDE that
   wrapper, as a sibling before it, so it isn't squeezed into the left
   column alongside the sidebar. */
.has-sidebar .content-sidebar {
	display: grid;
	grid-template-columns: 1fr 320px;
	align-items: start;
	gap: 2.5rem;
}

@media (max-width: 782px) {
	.has-sidebar .content-sidebar {
		grid-template-columns: 1fr;
	}
}

/* Header (#main-nav) and footer styles live in
   assets/css/site-header-footer.css, loaded site-wide via functions.php. */

/* -----------------------------------------------------------------------
   Buttons

   All button styles (.btn-underline, .btn-base, .einar-base-btn) are
   defined once in assets/css/global-styles.css — a file loaded on every
   page — instead of here, so there's a single source of truth. See that
   file's "BUTTON COMPONENTS" section.
----------------------------------------------------------------------- */

/* -----------------------------------------------------------------------
   Widgets & sidebar
----------------------------------------------------------------------- */
.widget {
	margin-bottom: 2rem;
}

.widget-title {
	font-size: 1.1rem;
	margin-bottom: 0.75rem;
}

/* -----------------------------------------------------------------------
   Forms
----------------------------------------------------------------------- */
input,
textarea,
select,
button {
	font-family: inherit;
	font-size: 1rem;
}

.search-form {
	display: flex;
	gap: 0.5rem;
}

/* -----------------------------------------------------------------------
   Utilities
----------------------------------------------------------------------- */
.screen-reader-text {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute !important;
	width: 1px;
	word-wrap: normal !important;
}

.screen-reader-text:focus {
	background-color: var(--focus-bg);
	border-radius: 3px;
	box-shadow: 0 0 2px 2px var(--focus-shadow);
	clip: auto !important;
	clip-path: none;
	color: var(--focus-text);
	display: block;
	font-size: 0.875rem;
	font-weight: 700;
	height: auto;
	left: 5px;
	line-height: normal;
	padding: 15px 23px 14px;
	text-decoration: none;
	top: 5px;
	width: auto;
	z-index: 100000;
}

.alignfull {
	width: 100%;
}

.alignwide {
	max-width: var(--container-wide);
}

/* ===== BEGIN COMPILED — generated by bin/build-css.js. Do not edit below by hand: edit the source files listed here and re-run the script instead. ===== */

/* ---------------------------------------------------------------
   Source: assets/css/variables.css
--------------------------------------------------------------- */
/* ===================================================================
   Einar Design Tokens — SINGLE SOURCE OF TRUTH for color/font values.
   Loaded first, before every other stylesheet. No selectors other than
   :root live here — just custom properties.

   To change a brand color everywhere it's used, edit it here ONCE.
   Every other CSS file references these variables instead of repeating
   hex/rgba literals — see README.md for the full list of what uses what.

   A few variables intentionally share the same value as another one
   today but are NOT merged — each has a comment explaining why (usually:
   one of them is overridden by the light/dark header theme switch in
   assets/css/site-header-footer.css and the other must never move).
   =================================================================== */

:root {
  /* ---- Brand navy ---- */
  --bg:            #121F36; /* primary dark navy: page/section backgrounds (swaps to white in the light header theme) */
  --bg-rgb:        18 31 54; /* same navy as r g b (space-separated for rgb(var(--bg-rgb) / alpha)) — for one-off alpha variants not covered by --dark-sl */
  --ink-900:       #121F36; /* near-black navy text/buttons on light homepage sections — distinct value from --bg, don't merge 0f172a*/

  /* ---- White / off-white ---- */
  --white:         #ffffff; /* swaps to dark navy in the light header theme (footer hover text, etc.) */
  --white-rgb:     255 255 255;
  --off-white:     #fdfeff; /* barely-off-white card background (who-we-are stat cards) */
  --ghostwhite:    #f8f8ff; /* very pale off-white, almost white */
  --body-bg:       #ffffff; /* fixed — plain page background (style.css); never swaps with header theme */

  /* ---- Slate / muted text ---- */
  --slate-000:         #cbd3de;
  --slate:         #94a3b8;
  --muted:         #64748b; /* swaps in the light header theme */
  --slate-600:     #475569;
  --slate-700:     #334155;

  /* ---- Violet (brand accent) ---- */
  --violet:        #857ada; /* muted violet — eyebrows, section tags, icons */
  --violet-lt:     #c9c7de; /* light violet — accent gradient text, dividers */
  --violet-dim:    #675eaf; /* dim violet (kept for completeness; not currently used) */
  --violet-shimmer:#a78bfa; /* lighter periwinkle — card top-edge shimmer */
  --accent-rgb:    124 58 237; /* saturated CTA violet — used only as rgb(var(--accent-rgb) / alpha) glows/grids */
  --gradient-accent: linear-gradient(135deg, var(--violet-shimmer) 0%, var(--violet-lt) 100%); /* the standard "accent" gradient — see .accent in global-styles.css for the gradient-text technique */

  /* ---- Sky / cyan ---- */
  --cyan:          #9ebaee; /* soft, desaturated cyan — accent gradient text (distinct from --sky) */
  --sky:           #38bdf8; /* saturated sky-blue — icons, gradient bars */
  --sky-rgb:       56 189 248;
  --ice-blue:      #a4def9; /* pale ice-cyan — templates/governance-operating-models.php's Iceberg section only */
  --gradient-iceberg: linear-gradient(165deg, var(--violet-shimmer) 0%, var(--violet-lt) 45%, var(--ice-blue) 100%); /* the standard accent gradient (see --gradient-accent) extended with --ice-blue for the Iceberg section's tip */

  /* ---- Indigo (gradient stops only, no solid use) ---- */
  --indigo-rgb:    99 102 241;

  /* ---- Green ---- */
  --green:         #4ade80;
  --green-rgb:     74 222 128;

  /* ---- Borders / soft overlays ---- */
  --border:        rgba(255, 255, 255, .1); /* swaps in the light header theme */
  --border2:       rgba(255, 255, 255, .7); /* kept for completeness; not currently used */
  --border3:       rgba(0, 0, 0, .05);

  /* ---- Custom/interior page pattern ----
     Light lavender page background + white "block" cards, used by
     custom page templates (digital-resilience, style-guide) instead of
     the homepage's dark-navy sections. */
  --page-bg-custom: #FBF8FF;
  --block-bg:       #ffffff;
  --block-border:   #f3f3f3; /* rgba(198, 198, 203, .4) #C6C6CB at 40% */
  --block-shadow:   0 1px 2px 0 rgba(0, 0, 0, .05);

  /* ---- Fixed nav & footer "ink" — see assets/css/site-header-footer.css ----
     The nav is always a light bar regardless of header theme, so these
     must NEVER swap with --bg/--white. They hold the same value as --bg/
     --white today purely by coincidence. */
  --nav-ink:       #121f36;
  --nav-ink-soft:  #555555; /* mobile dropdown secondary text/hover */
  --nav-surface:   #ffffff;
  --nav-bg:        rgba(255, 255, 255, .95); /* the nav bar's own frosted background */
  --nav-hover-bg:  #f6f6f6; /* top-level nav item hover pill — always a light bar, so this never swaps with the header theme */
  --bar-color:     #121f36; /* hamburger icon bars; has its own OS-dark-mode override */
  --nav-dropdown-shadow:    rgba(43, 52, 59, .1);
  --nav-mobile-border:      rgba(9, 8, 64, .15);
  --nav-mobile-border-soft: rgba(9, 8, 64, .1);
  --nav-mobile-shadow:      rgba(0, 0, 0, .12);

  /* ---- "Dark on light" / "light on dark" shades ----
     --dark-sl and --dark-menu hold the same value today but must stay
     separate: --dark-menu is overridden by the light header theme below,
     --dark-sl (the nav's own default link color) is not and never should
     be — the nav stays a light bar in both themes. */
  --white-sl:      rgba(255, 255, 255, .8);
  --dark-sl:       rgba(18, 31, 54, .8);  /* fixed — nav link color */
  --dark-menu:     rgba(18, 31, 54, .8);  /* swaps in the light header theme */

  /* ---- Fonts ---- */
  --font-head:     'Inter Tight', sans-serif;
  --font-body:     'Inter', sans-serif;
  --fs-small:     13px;

  /* ---- Typography scale ----
     The base scale used for plain post/page content (style.css: body,
     headings, paragraphs, links) — roughly a 1.25 (major third) ratio.
     homepage.css and digital-resilience.css each set their own heading
     sizes for their marketing context instead of using this scale — see
     the comments in those files. Treat those as intentionally separate,
     not as a second place the same tokens should have been used. */
  --fs-h1:        2.441rem;
  --fs-h2:        2.25rem;
  --fs-h3:        1.45rem;
  --fs-h4:        1.25rem;
  --fs-h5:        1rem;
  --fs-h6:        0.9rem;
  --fs-body:      15px;
  --fs-small:     13px;

  --fw-heading:   500;
  --fw-body:      300;

  --lh-heading:   1.25;
  --lh-body:      1.6;

  /* ---- Plain content-area colors (style.css: post/page body copy) ---- */
  --text:          #121F36;
  --link:          #0a4a8f;
  --focus-bg:      #f1f1f1; /* skip-link focus background */
  --focus-text:    #21759b; /* skip-link focus text */
  --focus-shadow:  rgba(0, 0, 0, .6);

  /* ---- Spacing scale ----
     Use these for new/refactored section and component spacing instead of
     one-off rem values. Existing files keep their original hand-tuned
     numbers where migrating them would risk a visual diff; treat this
     scale as the standard for anything new. */
  --space-1:  0.25rem;
  --space-2:  0.5rem;
  --space-3:  0.75rem;
  --space-4:  1rem;
  --space-5:  1.5rem;
  --space-6:  2rem;
  --space-7:  2.5rem;
  --space-8:  3rem;
  --space-9:  4rem;
  --space-10: 6rem;
  --space-11: 7rem;
  --space-12: 9rem; /* large section vertical padding, e.g. homepage/digital-resilience hero sections */

  /* ---- Border radius scale ---- */
  --radius-xs:   2px;  /* smallest chips — blog entry-footer tags/cat/comment links */
  --radius-sm:   4px;  /* cards (digital-resilience feature/impact/research cards) */
  --radius-md:   8px;
  --radius-lg:   10px; /* homepage cards */
  --radius-xl:   14px;
  --radius-pill: 999px; /* badges/pills */
  --radius-round: 50%;  /* circular icons/dots */

  /* ---- Letter-spacing (tracking) scale ---- */
  --tracking-tight:   -0.02em; /* headings */
  --tracking-normal:  0;
  --tracking-wide:    0.06em; /* .btn-underline / button labels */
  --tracking-wider:   0.1em;
  --tracking-eyebrow: 0.14em; /* uppercase eyebrows/badges/tags */
  --tracking-widest:  0.18em;

  /* ---- Layout ---- */
  --container-max:  1330px; /* .container — see style.css, homepage.css, digital-resilience.css */
  --container-wide: 1400px; /* .alignwide, style.css */

  /* ---- Breakpoints (reference only — CSS custom properties can't be used
     inside @media queries, so these document the values actually used in
     @media rules across the theme; keep new breakpoints aligned to these
     instead of picking new numbers ---
     mobile:  max-width: 640px
     tablet:  max-width: 782px  (WordPress admin-bar/mobile-menu breakpoint)
     laptop:  max-width: 1024px */
}

/* ═══════════════════════════════════════════════
   LIGHT HEADER THEME — overrides for body.header-theme-light
   (see assets/css/site-header-footer.css for what actually uses these)
═══════════════════════════════════════════════ */
body.header-theme-light {
  --bg:         #ffffff;
  --white:      #121F36;
  --white-sl:   rgba(20, 33, 61, .85);
  --muted:      #5b6472;
  --border:     rgba(15, 23, 42, .08);
  --dark-menu:  rgba(255, 255, 255, .95);
}
/* ---------------------------------------------------------------
   Source: assets/css/global-styles.css
--------------------------------------------------------------- */
/* ===================================================================
   Einar Global Utility Styles  v1.0.0
   Loaded on every page.

   ── Background class ────────────────────────────────────────────
   ep-cta-bg
     Apply as a CSS class to any Elementor section (or any element).
     Adds the purple-gradient background + animated globe canvas
     (canvas is injected automatically by global-scripts.js).
   =================================================================== */


/* ===================================================================
   INLINE EMPHASIS:  strong
   Sitewide default for inline-bolded copy on light backgrounds — darker
   than surrounding muted/slate text but not full-black, medium weight
   rather than the browser's bold 700. Dark-background sections (e.g.
   the homepage hero's .hero-sub, homepage.css) override this locally
   to a light color instead, since --slate-700 on a dark navy bg would
   be unreadable.
   =================================================================== */
strong {
    color: var(--slate-700);
    font-weight: 500;
}


/* ===================================================================
   BACKGROUND:  .ep-cta-bg
   =================================================================== */
.ep-cta-bg {
    position: relative;
    overflow: hidden;
    background:
        linear-gradient(
            90deg,
            rgb(var(--accent-rgb) / 0)    0%,
            rgb(var(--accent-rgb) / 0.12) 50%,
            rgb(var(--accent-rgb) / 0)    100%
        ),
        var(--bg) !important; /* !important so it overrides Elementor's section background */
}

/* Canvas injected by global-scripts.js */
.ep-cta-bg > .ep-bg-canvas {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 600px;
    height: 600px;
    opacity: 0.35;
    pointer-events: none;
    z-index: 0;
}

/* Elementor containers inside ep-cta-bg must sit above the canvas */
.ep-cta-bg > .elementor-container,
.ep-cta-bg > .e-con-inner,
.ep-cta-bg > .elementor-row {
    position: relative;
    z-index: 1;
}


/* ===================================================================
   BUTTON COMPONENTS  — single source of truth for every button in the
   theme. Two variants share the same underline (dim at rest, full
   strength on hover) + arrow-icon mechanic, grouped together below
   instead of each re-declaring it:

     .einar-base-btn / .btn-base — the larger CTA link. Shared by
     Elementor's "Base Button" widget and PHP templates
     (inc/cta-section.php, templates/*.php, front-page.php). Color
     comes from --white/--bg, which swap with the page's header theme.

     .btn-underline — the smaller, uppercase text-link (searchform.php,
     template-parts/content*.php "read more" links, comments.php).
     Color comes from currentColor instead, since it's used in contexts
     that don't swap --white/--bg.

   .btn-base used to be defined only in homepage.css, so any page that
   doesn't enqueue homepage.css (e.g. templates/digital-resilience.php,
   which reuses inc/cta-section.php) rendered it as a plain unstyled
   link. .btn-underline used to carry its own full copy of the slide/
   arrow mechanic in style.css. Both are defined once here now, in a
   file loaded on every page.

   .elementor-kit-839 a forces global link styling that breaks
   .einar-base-btn/.btn-base, hence the !important on their
   font/text-decoration/padding below — .btn-underline isn't used
   inside Elementor content, so it doesn't need the same guard.
   =================================================================== */
.einar-base-btn,
.btn-base,
.btn-underline {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    border-radius: 0;
    position: relative;
    cursor: pointer;
}

/* -- Sizing / typography -------------------------------------------- */
.einar-base-btn,
.btn-base {
    text-decoration: none !important;
    padding: 0 !important;
    font-family: var(--font-body) !important;
    font-size: 15px !important;
    font-weight: 500 !important;
}
.btn-base { font-size: 14px !important; }

.btn-underline {
    border: 0;
    background: none;
    padding: 0.5rem 0;
    font-family: var(--font-body);
    font-size: 0.85rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    text-decoration: none;
}

.einar-base-btn { line-height: 1; transition: color 0.3s; }
.btn-base       { color: var(--white); transition: background 0.3s, color 0.3s; }
.btn-underline  { color: inherit; transition: color 0.3s; }

/* -- Colors -------------------------------------------------------- */
.einar-base-btn--light       { color: var(--white) !important; }
.einar-base-btn--dark        { color: var(--bg) !important; }
.einar-base-btn--light:hover { color: var(--white) !important; }
.einar-base-btn--dark:hover  { color: var(--bg) !important; }
.btn-base:hover               { color: var(--white); }

/* -- Underline — two stacked 1px lines, same position/thickness, so
   they always read as one line:
     ::before — always on, dim (0.2 opacity), full width. The "at rest"
                line.
     ::after  — full opacity, width-animated. Sits at 0 width pinned to
                the right edge at rest (invisible); on hover the pin
                flips to the left edge and it grows to full width, so
                it sweeps in left-to-right over the dim ::before line.
                Flipping the pin back to "right" happens instantly (it
                isn't in the transition list) the moment hover ends, so
                the reverse animation reads as the line receding back
                out to the right, not shrinking back into itself.
   bottom: -4px (rather than 0) pulls both off the text baseline. */
.einar-base-btn::before,
.einar-base-btn::after,
.btn-base::before,
.btn-base::after,
.btn-underline::before,
.btn-underline::after {
    content: '';
    position: absolute;
    bottom: -4px;
    left: 0;
    right: 0;
    height: 1px;
    display: block;
}

.einar-base-btn::before,
.btn-base::before,
.btn-underline::before {
    opacity: 0.1;
}

.einar-base-btn::after,
.btn-base::after,
.btn-underline::after {
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.5s;
    /* Keep this hairline on its own compositing layer from the very first
       paint (not only while a transition is running). Without this, the
       browser promotes the element to a GPU layer the moment the hover
       transition starts and demotes it again once the transition settles —
       each promotion/demotion re-rasterizes the 1px line at a slightly
       different subpixel offset, so it reads as a different (thicker)
       thickness once fully grown than it did at rest. Pinning the layer
       up front keeps rasterization identical at 0%, mid-grow, and 100%. */
    will-change: transform;
    backface-visibility: hidden;
}

.einar-base-btn--light::before,
.einar-base-btn--light::after { background: var(--white); }
.einar-base-btn--dark::before,
.einar-base-btn--dark::after  { background: var(--bg); }
.btn-base::before,
.btn-base::after               { background: var(--white); }
.btn-underline::before,
.btn-underline::after          { background: currentColor; }

.einar-base-btn:hover::after,
.btn-base:hover::after,
.btn-underline:hover::after {
    transform: scaleX(1);
    transform-origin: left;
}

/* -- Arrow --------------------------------------------------------- */
/* Icon svg (einar_partners_get_inline_icon( 'arrow-btn_wh' )) already
   points up-right, unlike the old inline path which needed a -45deg
   rotate to turn a plain "→" into that shape. */
.einar-base-btn svg,
.btn-base svg {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}
.btn-underline svg {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
}


/* ===================================================================
   HEADING COLOR OVERRIDES — ensure contrast on dark backgrounds
   
   Elementor's .elementor-kit-839 h{1-6} forces all headings to the 
   primary brand color, which creates poor contrast on dark sections 
   (footer, dark Elementor sections, etc.).
   
   These rules override Elementor's color for headings within dark
   background contexts, ensuring readability while preserving brand
   color on light backgrounds.
   =================================================================== */

/* Footer headings — always light on dark background */
footer h1,
footer h2,
footer h3,
footer h4,
footer h5,
footer h6 {
    color: var(--white) !important;
}

/* Footer links — always white on dark background.
   :not(.entry-footer) excludes <footer class="entry-footer"> — a single
   post's own per-article footer (tags/categories, see blog.css), a light
   background element that also happens to be a <footer> tag. Same
   exclusion as the duplicate rule in site-header-footer.css. */
footer:not(.entry-footer) a {
    color: var(--white) !important;
}
footer:not(.entry-footer) a:hover:not(.einar-base-btn):not(.btn-base) {
    color: var(--white) !important;
}

/* Dark-themed Elementor sections (navy background: #121F36 or rgb(18, 31, 54)) */
[style*="background-color: #121f36"] h1,
[style*="background-color: #121f36"] h2,
[style*="background-color: #121f36"] h3,
[style*="background-color: #121f36"] h4,
[style*="background-color: #121f36"] h5,
[style*="background-color: #121f36"] h6,
[style*="background-color: #121F36"] h1,
[style*="background-color: #121F36"] h2,
[style*="background-color: #121F36"] h3,
[style*="background-color: #121F36"] h4,
[style*="background-color: #121F36"] h5,
[style*="background-color: #121F36"] h6,
[style*="background-color: rgb(18, 31, 54"] h1,
[style*="background-color: rgb(18, 31, 54"] h2,
[style*="background-color: rgb(18, 31, 54"] h3,
[style*="background-color: rgb(18, 31, 54"] h4,
[style*="background-color: rgb(18, 31, 54"] h5,
[style*="background-color: rgb(18, 31, 54"] h6 {
    color: var(--white) !important;
}

/* Dark-themed Elementor section links */
[style*="background-color: #121f36"] a,
[style*="background-color: #121F36"] a,
[style*="background-color: rgb(18, 31, 54"] a {
    color: var(--white) !important;
}
[style*="background-color: #121f36"] a:hover:not(.einar-base-btn):not(.btn-base),
[style*="background-color: #121F36"] a:hover:not(.einar-base-btn):not(.btn-base),
[style*="background-color: rgb(18, 31, 54"] a:hover:not(.einar-base-btn):not(.btn-base) {
    color: var(--violet-lt) !important;
}

/* Utility class: apply to any Elementor section with a dark background */
.ep-dark-section h1,
.ep-dark-section h2,
.ep-dark-section h3,
.ep-dark-section h4,
.ep-dark-section h5,
.ep-dark-section h6 {
    color: var(--white) !important;
}

.ep-dark-section a {
    color: var(--white) !important;
}
.ep-dark-section a:hover:not(.einar-base-btn):not(.btn-base) {
    color: var(--violet-lt) !important;
}

/* ===================================================================
   ANIMATED HERO GRID BACKGROUND  — .ep-hero-grid
   Utility class, same idea as .ep-dark-section above: apply it to any
   Elementor Section/Container (Advanced tab > CSS Classes — no code
   needed) or any PHP-templated wrapper element to get this "synthwave
   terrain" background — a grid tilted back in 3D that recedes toward a
   horizon, tinted with the site's main accent color (var(--accent-rgb)),
   plus a soft accent glow that sweeps across it on a loop, and a
   bottom-edge fade so it always blends cleanly into whatever comes
   after it. Static otherwise (no mouse interaction) — everything here
   is a plain CSS animation. Originally built for assets/css/blog.css's
   .page-header (the blog/archive/search hero) — see that file for a
   real example layering its own min-height/title styles on top of this
   shared class instead of duplicating it.

   USAGE
   - Elementor: select a Section or Container, open Advanced > CSS
     Classes, add "ep-hero-grid". Add your own Heading/Text widgets
     inside as normal — content added inside always renders above the
     background (see .ep-hero-grid > * below). Use the same Advanced
     tab's Min Height / padding controls to resize it; they override
     this class's defaults automatically.
   - PHP templates: add the class to any wrapper element, e.g.
     <header class="ep-hero-grid">...</header>.

   .ep-hero-grid::after carries a large radial vignette (transparent in
   the middle, solid var(--body-bg) white toward its edge) layered UNDER
   the glow but as part of the SAME element so the two share one
   mix-blend-mode against the grid — this is what keeps the color+grid
   contained to a circle in the center and fades it to white on every
   side (not just top/bottom), so the section blends into plain white
   sections above/below/around it instead of reading as a darker
   rectangle. It also carries a bottom-edge fade (in real pixels, not
   percentages) so the grid always finishes fading out before this
   element's own overflow:hidden edge, regardless of how tall or short
   it's set — without it, a short section can clip the grid mid-line
   instead of fading it, since the grid's own mask is tuned in
   percentages relative to its own box and this element's fixed 500px
   perspective. The glow's opacity is NOT animated (that would fade the
   vignette too, since they're the same element) — instead it just
   travels off-screen left/right, so it disappears by leaving the box
   rather than by fading.
   =================================================================== */
.ep-hero-grid {
    position: relative !important;
    overflow: hidden !important;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 24rem;
    padding: 4rem 2rem;
    perspective: 500px;
    /* !important — same reason as .btn-base/.einar-base-btn above: an
       Elementor Section/Container gets its own per-instance class (e.g.
       .elementor-element-abc123) for anything set in its Style/Advanced
       tabs, which combined with Elementor's own base class outranks a
       single plain class here on specificity alone (2 classes > 1), even
       with no Kit-wide rule involved. position/overflow/background are
       pinned because they're load-bearing for the effect itself (a
       transparent overflow would let the grid spill out of the box,
       nothing to override the background WOULD leave). min-height and
       padding are deliberately left alone — those are meant to stay
       adjustable via Elementor's own Advanced tab controls (see the
       usage note above). */
    background: linear-gradient(120deg, rgb(var(--accent-rgb) / 0.07), rgb(var(--accent-rgb) / 0.04) 50%, rgb(var(--accent-rgb) / 0.07)), var(--body-bg) !important;
    text-align: center;
}

.ep-hero-grid::before {
    content: '';
    position: absolute;
    left: -20%;
    right: -20%;
    bottom: -15%;
    height: 170%;
    z-index: 0;
    background-image:
        linear-gradient(rgb(var(--accent-rgb) / 0.4) 1px, transparent 1px),
        linear-gradient(90deg, rgb(var(--accent-rgb) / 0.35) 1px, transparent 1px);
    background-size: 54px 54px;
    transform: rotateX(62deg);
    transform-origin: bottom center;
    mask-image: radial-gradient(ellipse 70% 60% at 50% 92%, black 15%, transparent 88%);
    -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 92%, black 15%, transparent 88%);
    animation: ep-hero-grid-drift 32s linear infinite;
    pointer-events: none;
}

.ep-hero-grid::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background-image:
        linear-gradient(to top, var(--body-bg) 0, transparent 70px),
        radial-gradient(ellipse 55% 65% at 50% 55%, transparent 32%, var(--body-bg) 85%),
        radial-gradient(circle, rgb(var(--accent-rgb) / 0.5), transparent 70%);
    background-repeat: no-repeat, no-repeat, no-repeat;
    background-size: 100% 100%, 100% 100%, 360px 360px;
    background-position: 0 0, 0 0, -30% 85%;
    mix-blend-mode: screen;
    animation: ep-hero-grid-sweep 12s ease-in-out infinite;
    pointer-events: none;
}

@keyframes ep-hero-grid-drift {
    from {
        background-position: 0 0, 0 0;
    }
    to {
        background-position: 54px 54px, 54px 54px;
    }
}

@keyframes ep-hero-grid-sweep {
    0%,
    100% {
        background-position: 0 0, -30% 85%;
    }
    50% {
        background-position: 0 0, 130% 85%;
    }
}

/* Any direct child (Elementor's own inner container div, or a plain
   .container in a PHP template) sits above the grid/vignette/glow. */
.ep-hero-grid > * {
    position: relative;
    z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
    .ep-hero-grid::before,
    .ep-hero-grid::after {
        animation: none;
    }
}

/* Title/description pairing for .ep-hero-grid — same values as
   assets/css/blog.css's .page-title/.archive-description (kept as
   separate, duplicate declarations rather than merged, so the blog
   templates stay untouched and these can be tweaked independently).
   In Elementor: add "ep-hero-title" under CSS Classes on a Heading
   widget, and "ep-hero-description" on a Text widget, the same way you
   added "ep-hero-grid" to the Section/Container.

   Elementor's "CSS Classes" field always lands on the widget's OUTER
   wrapper div, never on the tag it actually renders — a Heading widget
   comes out as:
     <div class="elementor-element ... ep-hero-title elementor-widget-heading">
       <div class="elementor-widget-container">
         <h1 class="elementor-heading-title elementor-size-default">...</h1>
       </div>
     </div>
   font-size/color/etc. do inherit from the wrapper down to that <h1> in
   principle, but Elementor's own .elementor-heading-title.elementor-
   size-default rule sets them explicitly on the <h1> itself — and any
   explicitly-set value, at ANY specificity, beats an inherited one. So
   this has to reach past the wrapper with a descendant selector
   (.ep-hero-title .elementor-heading-title etc.), not just style
   .ep-hero-title itself. !important on top of that for the same reason
   as .ep-dark-section's h1-h6 override and footer h1-h6 above —
   Elementor's Kit CSS (.elementor-kit-839 h1{...}, see
   wp-content/uploads/elementor/css/post-839.css) and its own widget
   classes still have to be out-ranked even once the selector reaches
   the right element. */
.ep-hero-title,
.ep-hero-title .elementor-heading-title,
.ep-hero-title h1,
.ep-hero-title h2,
.ep-hero-title h3 {
    font-family: var(--font-head) !important;
    font-size: clamp(2.25rem, 5vw, 3rem) !important;
    letter-spacing: -0.01em !important;
    color: var(--ink-900) !important;
}

.ep-hero-description,
.ep-hero-description .elementor-widget-container > p,
.ep-hero-description p {
    max-width: 60ch !important;
    margin: 1rem auto 0 !important;
    color: var(--slate-600) !important;
}

/* ===================================================================
   ACCENT GRADIENT TEXT  — .accent
   Used inside headings on the homepage and on templates that reuse its
   markup (e.g. templates/digital-resilience.php's hero and CTA).

   Used to be defined only in homepage.css, so any page that doesn't
   enqueue it rendered .accent as plain, uncolored text. Defined here —
   a file loaded on every page — like the .btn-base fix above.
   =================================================================== */
.accent {
    background: var(--gradient-accent);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* ===================================================================
   SHARED CTA SECTION  — #cta, .cta-*
   Markup rendered by einar_partners_cta_section() (inc/cta-section.php),
   used by the homepage and reused as-is by templates/digital-resilience.php
   (and available anywhere via the [einar_cta] shortcode).

   Used to be defined only in homepage.css, so any page that doesn't
   enqueue it (e.g. the Digital Resilience template) rendered the CTA
   with no layout/colors and its .cta-reveal content stuck at opacity 0
   (nothing added the "visible" class — see assets/js/global-scripts.js,
   which now also drives the reveal + globe canvas sitewide). Defined
   here, a file loaded on every page, like the .btn-base fix above.

   #cta always renders like the homepage's CTA — dark navy background,
   white/light text — regardless of a page's own "Header & Footer Theme"
   (see inc/template-tags.php: einar_partners_get_header_theme() defaults
   to "light" on every page except the front page, and body.header-theme-
   light in variables.css swaps --bg/--white/--muted to their light
   values). Re-pinning those custom properties here, on #cta itself,
   overrides the swap for #cta and everything inside it, since custom
   properties inherit down from the nearest ancestor that sets them.

   The shimmer gradient and the solid navy fill used to be written as
   two separate `background:` declarations — the second fully replaces
   the first (same property), so only the gradient (whose stops are all
   near-transparent) actually applied. That was invisible on the
   homepage only because homepage.css also paints <body> itself navy,
   showing through; on any other page (e.g. digital-resilience's light
   page background) it let the page's own background show through
   instead. Combined into one layered `background` below so #cta is
   opaque on its own regardless of what page it's on.
   =================================================================== */
#cta {
    --bg: #121F36;
    --white: #ffffff;
    --muted: #64748b;
    position: relative;
    overflow: hidden;
    background: radial-gradient(ellipse 70% 55% at 50% 0%, rgb(var(--accent-rgb) / 0.16) 0%, rgb(var(--accent-rgb) / 0) 70%), var(--bg);
}
#cta h1, #cta h2, #cta h3, #cta h4, #cta h5, #cta h6 {
    color: var(--white) !important;
}
#cta a {
    color: var(--white) !important;
}
#cta a:hover:not(.einar-base-btn):not(.btn-base) {
    color: var(--violet-lt) !important;
}
.cta-inner {
    position: relative;
    max-width: var(--container-max);
    margin: 0 auto;
    padding: 14rem 2rem 14rem 2rem;
    text-align: center;
    overflow: hidden;
}
#cta-canvas {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 600px; height: 600px;
    opacity: .35;
    pointer-events: none;
    z-index: 0;
}
.cta-content {
    position: relative;
    z-index: 1;
}
.cta-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    font-size: 11px;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--violet-lt);
    margin-bottom: 1.4rem;
}
.cta-eyebrow::before, .cta-eyebrow::after {
    content: '';
    display: block;
    width: 24px; height: 1px;
    background: var(--violet-lt);
}
.cta-inner h2 {
    margin-bottom: 1.8rem;
}
/* Optional short line under the heading ($args['subtitle'],
   inc/cta-section.php) — e.g. "Behavioral Psychologists, Researchers &
   Creatives" (templates/enterprise-enablement.php). Negative top margin
   pulls it back up into the h2's own 1.8rem bottom margin above, so it
   reads as a supporting line directly under the title rather than a
   second paragraph with the same gap before and after it. Same
   violet-lt accent color as .cta-eyebrow, for the same "supporting
   label" role on this dark section.
   Two classes (.cta-content .cta-subtitle), not just .cta-subtitle
   alone: it's also a direct <p> child of .cta-content, so without the
   extra specificity here the "class + type" .cta-content > p rule right
   below (same specificity family, later in the file) would win the
   color/margin instead. */
.cta-content .cta-subtitle {
    max-width: none;
    margin: -1rem 0 1.5rem;
    color: var(--violet-lt);
    font-family: var(--font-head);
    font-size: 15px;
    font-weight: 500;
}
.cta-content > p {
    color: var(--slate);
    max-width: 80%;
    margin: 0 auto 2rem;
}
.cta-actions {
    display: flex;
    gap: 1.4rem;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 2.8rem;
}
.cta-note {
    color: var(--muted);
    letter-spacing: .04em;
    margin-bottom: 0 !important;
}
/* Staggered reveal, toggled by assets/js/global-scripts.js */
.cta-reveal {
    opacity: 0;
    transform: translateY(26px);
    transition: opacity .7s ease, transform .65s cubic-bezier(.22,1,.36,1);
}
.cta-reveal.visible {
    opacity: 1;
    transform: translateY(0);
}

/* ===================================================================
   SHARED PILL/TAG COMPONENT
   One shape + typography for every small icon+label chip in the theme,
   in three color modes layered on top of the shared base below:

     - default (.dr-research-kind, .entry-footer .cat-links a) — solid
       white fill, dark ink text/icon, no border stroke. This is
       exactly the look .dr-research-kind already had
       (digital-resilience.css's "REPORT"/"VIDEO" chip) — kept as-is
       and reused everywhere else instead of each pill inventing its
       own treatment.
     - dark (.dr-cocreate-pill, .blog-cat-pill.is-active, .dr-hero-pill) —
       solid ink-900 fill, white text/icon, no border stroke.
     - border (.dr-impact-tag, .dr-award-badge, .blog-cat-pill,
       .reading-time-pill, .entry-footer .tags-links a) — the only
       mode with a visible border: transparent fill, dark ink text,
       muted 1px border
       (var(--border3)) — darkened to ink-900 on hover for the linked
       instances (.blog-cat-pill, .tags-links a), text color is the
       only thing that changes on hover for the others.
     - .blog-featured-event-pill only takes the shared SHAPE below, not
       one of these three color modes — its own gradient fill + pulsing
       dot live in blog.css instead (see that file's own comment).

   Shape is shared and NOT the full pill radius — --radius-sm, the same
   small "badge" corner .dr-research-kind always used, not
   var(--radius-pill) (that's for the site's other rounded buttons).

   Icons are inline SVGs with fill="currentColor" (see
   einar_partners_get_inline_icon() in inc/template-tags.php), so they
   pick up each mode's text color automatically — no separate icon-color
   rule needed per component.

   Layout/spacing/type only here; each source file below still owns its
   component's positioning (e.g. .dr-research-kind's absolute placement
   in digital-resilience.css) and any active/hover color overrides.
   =================================================================== */
.dr-research-kind,
.blog-cat-pill,
.dr-impact-tag,
.dr-award-badge,
.dr-cocreate-pill,
.reading-time-pill,
.dr-hero-pill,
.blog-featured-event-pill,
.entry-footer .cat-links a,
.entry-footer .tags-links a,
.share-pill,
.social-share-btn {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem .8rem;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-size: 11px;
    font-weight: 500;
    line-height: 1;
    text-transform: uppercase;
    letter-spacing: var(--tracking-eyebrow);
    white-space: nowrap;
    transition: background .2s ease, border-color .2s ease, color .2s ease;
}
.dr-research-kind svg,
.blog-cat-pill svg,
.dr-impact-tag svg,
.dr-award-badge svg,
.dr-cocreate-pill svg,
.reading-time-pill svg,
.share-pill svg,
.social-share-btn svg {
    width: 13px;
    height: 13px;
    flex-shrink: 0;
}

/* Mode: default — solid white fill, dark ink text/icon, no border */
.dr-research-kind,
.entry-footer .cat-links a {
    background: #fff;
    color: var(--ink-900);
    border-color: transparent;
}
.entry-footer .cat-links a:hover {
    color: var(--violet);
}

/* Mode: dark — solid ink-900 fill, white text/icon, no border */
.dr-cocreate-pill,
.blog-cat-pill.is-active,
.dr-hero-pill,
.share-pill {
    background: var(--ink-900);
    color: #fff;
    border-color: transparent;
}

/* Modifier: white — solid white fill, dark ink text, no border (same look
   as the shared "default" mode above). Opt-in via einar_partners_hero_section()'s
   'pill_white' arg for a hero pill that reads better than the dark mode on
   a given page. Placed after the dark-mode block above so it wins the tie
   (equal specificity, later source order) on .dr-hero-pill--white. */
.dr-hero-pill--white {
    background: #fff;
    color: var(--ink-900);
    border-color: transparent;
}

/* Mode: border — the only mode with a visible border: transparent
   fill, dark ink text, muted border (rest state only — see the hover
   rule below for linked instances like .blog-cat-pill). */
.dr-impact-tag,
.dr-award-badge,
.blog-cat-pill,
.reading-time-pill,
.entry-footer .tags-links a,
.social-share-btn {
    background: transparent;
    color: var(--ink-900);
    border-color: var(--border3);
}
/* Linked instances (.blog-cat-pill, .tags-links a, .social-share-btn):
   hover darkens the border — .dr-impact-tag and .reading-time-pill are
   plain spans (no hover). */
.blog-cat-pill:hover,
.entry-footer .tags-links a:hover,
.social-share-btn:hover {
    border-color: var(--ink-900);
    color: var(--ink-900);
}
/* Higher specificity than the plain :hover above (3 selectors vs 2),
   so an active/dark pill stays solid on hover instead of picking up
   the outline mode's hover border/text color. */
.blog-cat-pill.is-active:hover {
    border-color: transparent;
    color: #fff;
}

/* ===================================================================
   COLUMN GUTTER  — narrow viewports
   Elementor columns lose their inner side padding below 1200px, letting
   widget content run flush to the viewport edge. Re-adds a minimum
   gutter sitewide.
   =================================================================== */
@media (max-width: 1200px) {
    .elementor-section .elementor-column .elementor-widget-wrap {
        padding-left: 15px !important;
        padding-right: 15px !important;
    }
}

/* ===================================================================
   HORIZONTAL-SCROLL GUARD  — html, body
   Sitewide safety net: this file is the one thing enqueued
   unconditionally on every page (see functions.php), so it's the only
   safe place for a rule that must truly apply everywhere — moved here
   from digital-resilience.css, which only loads on a handful of
   templates in dev mode (see that file's own note).

   Needed now that the column-gutter padding above can nudge borderline
   widget content (fixed-width tables/images, unrevealed .dr-reveal-right
   translateX() transforms, etc.) a few px past the viewport edge on
   pages that never had any guard against it — plain Elementor pages
   (elementor_header_footer template) chief among them.

   `clip`, not `hidden`: `overflow-x: hidden` on html/body makes
   Chromium treat <body> as the nearest scroll container for any
   position:sticky descendant — and since <body> itself never actually
   scrolls (documentElement does), every sticky element on the page
   silently stops pinning and just scrolls past with the rest of the
   page. `overflow-x: clip` clips overflow identically but doesn't
   register as a scroll container, so sticky keeps working. Both html
   and body carry it — body's overflow doesn't reliably govern
   document/viewport-level horizontal scroll in every engine; html's
   does, and removing either risks reopening a case the other was
   covering. */
html,
body {
    overflow-x: clip;
}
/* ---------------------------------------------------------------
   Source: assets/css/site-header-footer.css
--------------------------------------------------------------- */
/* ===================================================================
   Einar Site Header & Footer  v1.0.0
   Loaded on every page. Powers the shared #main-nav header and the
   5-column footer used by header.php / footer.php.

   The nav renders with a light (white/frosted) bar + dark text on every
   page regardless of theme. The footer is what actually switches: dark
   navy by default (matches the homepage), or light when the current
   page/post has "Light" selected under the "Header & Footer Theme" meta
   box (see inc/meta-boxes.php), toggled via the .header-theme-light
   class on <body> (see einar_partners_body_classes()).
   =================================================================== */

/* All custom properties (colors, fonts) now live in
   assets/css/variables.css, loaded before this file — single source of
   truth for the whole theme. This file only adds the light-header-theme
   override (also in variables.css) and consumes the variables below. */

/* ═══════════════════════════════════════════════
   NAV
═══════════════════════════════════════════════ */

.headerlogo {
  height: 60px !important;
}

#main-nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 200;
  transition: background .3s;
  background: var(--nav-bg);
  /* Always Inter, regardless of the page's own body font — the nav must
     look identical everywhere, not inherit whatever font style.css or
     homepage.css happens to set on <body>. */
  font-family: var(--font-body);
}
#main-nav.scrolled {
  background: rgb(var(--white-rgb) / 0.95);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px); /* for Safari */
  border-bottom: 1px solid var(--block-border);
}
.nav-inner {
  height:70px;
  display:flex;
  align-items:center;
  justify-content:space-between;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 2rem;
}
.tm-hide {
  display: none;
}
/* Logo */
.nav-logo a,
.nav-logo img {
  display: block;
  width: auto;
}

/* Top-level menu list */
ul.nav-menu {
  display: flex;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Top-level items */
ul.nav-menu > li {
  position: relative;
}
/* font-weight/color/hover-color are !important — Elementor's global
   kit CSS (e.g. .elementor-kit-839 a) applies to every <a> on the page,
   including this nav, and otherwise thins the font and recolors it
   on hover. */
ul.nav-menu > li > a {
  position: relative;
  display: flex;
  align-items: center;
  height: 70px;
  color: var(--nav-ink) !important;
  font-weight: 400 !important;
  font-size: 14px;
  padding: 0 17px;
  text-decoration: none;
  white-space: nowrap;
}
ul.nav-menu > li > a:hover,
ul.nav-menu > li:hover > a {
  color: var(--nav-ink) !important;
}
/* Hover pill — sized to the text (14px around it), not the full nav
   height, and grows outward from the center in every direction rather
   than just appearing. */
ul.nav-menu > li > a::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 3px;
  right: 3px;
  height: calc(1em + 28px);
  background: var(--nav-hover-bg);
  border-radius: 5px;
  transform: translate(0, -50%) scale(0);
  transition: transform .25s ease;
  z-index: -1;
}
ul.nav-menu > li > a:hover::before,
ul.nav-menu > li:hover > a::before {
  transform: translate(0, -50%) scale(1);
}

/* Grid icon for a top-level item with a sub-menu (desktop only — the
   mobile media query below forces this ::after hidden). Rotates 180deg
   from its own center while the item is hovered (sub-menu shown), and
   back when the hover ends. Dark-muted to match the nav's one
   consistent light/white-bar look on every page. */
ul.nav-menu > li.menu-item-has-children > a::after {
  content: '';
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-left: 10px;
  flex-shrink: 0;
  background-color: var(--nav-ink-soft);
  -webkit-mask: url('assets/icons/grid-menu.svg') center / contain no-repeat;
  mask: url('assets/icons/grid-menu.svg') center / contain no-repeat;
  transform: rotate(0deg);
  transition: transform .25s ease;
}
ul.nav-menu > li.menu-item-has-children:hover > a::after {
  transform: rotate(180deg);
}

/* Dropdown — level 1 (opens downward) */
ul.nav-menu .sub-menu {
  position: absolute;
  list-style: none;
  margin: 0;
  padding: 8px;
  width: max-content;
  min-width: 200px;
  max-width: 400px;
  background: var(--nav-surface);
  border-radius: 5px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
  z-index: 300;
  box-shadow: 0 3px 25px 0px var(--nav-dropdown-shadow), 0 0 0 var(--nav-dropdown-shadow) inset;
}

/* Level 1 — below the top-level item */
ul.nav-menu > li > .sub-menu {
  top: 100%;
  left: 0;
  transform: translateY(6px);
}

/* Level 2 — to the right of the level-1 item */
ul.nav-menu .sub-menu li > .sub-menu {
  top: 0;
  left: 100%;
  transform: translateX(6px);
}

/* Show level 1 on hover */
ul.nav-menu > li:hover > .sub-menu,
ul.nav-menu > li:focus-within > .sub-menu {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

/* Show level 2 on hover */
ul.nav-menu .sub-menu li:hover > .sub-menu,
ul.nav-menu .sub-menu li:focus-within > .sub-menu {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(0);
}

/* Dropdown items — an optional icon column on the left (see
   inc/nav-menu-icons.php) and a text column on the right holding the
   title + optional description, stacked (see Einar_Partners_Nav_Walker
   in inc/class-nav-walker.php, which wraps both in a ".sub-menu-row" only
   when an item has an icon and/or a description; plain items keep the
   simple <li><a> markup). The row is its own element — not the <li>
   itself — so a nested <ul class="sub-menu"> (a third-level dropdown)
   still stacks below it instead of joining it as a flex item. */
ul.nav-menu .sub-menu li:not(:last-child) {
  border-bottom: 1px solid var(--border3);
}
ul.nav-menu .sub-menu li {
  position: relative;
  cursor: pointer;
  border-radius: 5px;
  padding: 15px;
}
ul.nav-menu .sub-menu-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
ul.nav-menu .sub-menu li a {
  display: block;
  padding: 0;
  font-size: 14px;
  font-weight: 400 !important;
  color: var(--nav-ink) !important;
  text-decoration: none;
  white-space: nowrap;
}
/* Stretch the link's clickable area over the whole row — the icon and
   description (when present) sit next to the <a>, not inside it (see
   Einar_Partners_Nav_Walker in inc/class-nav-walker.php), so without this
   only the title text itself was clickable/tappable. Anchored against the
   <li> (above) rather than a wrapper, since plain items with no icon/
   description have no wrapper around the <a> at all. Uses ::before (not
   ::after, already used for the has-children chevron below). */
ul.nav-menu .sub-menu li a::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* Icon column, chosen via the item's "Icon" field in Appearance > Menus.
   The icon file is set inline as the mask-image; this only sizes,
   positions and colors it — same masked-icon technique as the chevron
   grid icon above. Dark on the dropdown's light background by default,
   flipped to white on the li's dark hover background below. */
ul.nav-menu .sub-menu-icon {
  display: inline-block;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  background-color: var(--nav-ink);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  transition: background-color .2s;
}

/* Text column — title, then the optional description below it. */
ul.nav-menu .sub-menu-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
ul.nav-menu .sub-menu-description {
  display: block;
  font-size: 12px;
  line-height: 1.4;
  color: var(--nav-ink-soft);
  white-space: normal;
  transition: color .2s;
}

/* Dropdown hover — triggered on the li; the icon, link and description
   change color together, in sync with the li's own background change. */
ul.nav-menu .sub-menu li,
ul.nav-menu .sub-menu li a {
  transition: background-color .2s, color .2s;
}
ul.nav-menu .sub-menu li:hover {
  background: var(--nav-ink);
}
ul.nav-menu .sub-menu li:hover a {
  color: var(--nav-surface) !important;
}
ul.nav-menu .sub-menu li:hover .sub-menu-description {
  color: var(--muted);
}
ul.nav-menu .sub-menu li:hover .sub-menu-icon {
  background-color: var(--nav-surface);
}

/* Indicate sub-menu exists (chevron) */
ul.nav-menu .sub-menu li.menu-item-has-children a::after {
  content: '›';
  margin-left: auto;
  opacity: .5;
}
ul.nav-menu .sub-menu li.menu-item-has-children a {
  display: flex;
  align-items: center;
}

/* Logo switching — the dark (navy) logo shows by default, since the nav
   bar itself is always a light/white bar. The white logo only takes
   over on pages using the dark-section nav variant (body.header-theme
   -dark, see the NAV — dark-section variant rules below) — a per-page
   choice, not tied to the visitor's OS color-scheme — and flips back
   for the dark page's own #main-nav.flash-mode moments, same as the
   rest of that variant. #main-nav.on-dark-section gets the same swap,
   for the same reason as its other rules above. */
.logo-light { display: none !important; }
.logo-dark  { display: block !important; }

body.header-theme-dark #main-nav:not(.flash-mode) .logo-dark,
#main-nav.on-dark-section .logo-dark { display: none !important; }
body.header-theme-dark #main-nav:not(.flash-mode) .logo-light,
#main-nav.on-dark-section .logo-light { display: block !important; }

/* ═══════════════════════════════════════════════
   HAMBURGER TOGGLE BUTTON
═══════════════════════════════════════════════ */

.menu-toggle {
  display: none; /* hidden on desktop */
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 !important;
  margin-left: auto;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 50px !important;
  height: 50px !important;
  z-index: 201;
}

/* The <i> element acts as the middle bar. font-size/line-height are
   zeroed here — the top/bottom bars are plain ::before/::after boxes
   with no text content to size against, but this one is a real <i>
   element, which otherwise renders its explicit height:1px against
   whatever font-size/line-height it inherits (icon-font base styles
   elsewhere in the page can set both), making it visibly thicker/
   blurrier than the other two even though the box height is the same
   1px on all three. display:block (not inline-block) for the same
   reason — it removes any inline-box baseline/vertical-align nuance
   that inline-block is otherwise subject to. */
.menu-toggle .tm-labpeak-icon-bars {
  display: block;
  width: 27px;
  height: 1px;
  font-size: 0;
  line-height: 0;
  background: var(--bar-color);
  border-radius: 0px;
  transition: 0.3s;
  position: relative;
  font-style: normal; /* override <i> italic */
}
#main-nav.scrolled .menu-toggle .tm-labpeak-icon-bars {
  background: var(--bar-color);
}

/* Top bar */
.menu-toggle .tm-labpeak-icon-bars::before {
  content: '';
  display: inline-block;
  width: 27px;
  height: 1px;
  background: inherit;
  border-radius: 0px;
  transition: 0.3s;
  position: absolute;
  left: 0;
  top: -8px;
}

/* Bottom bar — same 1px height as the top/middle bars (only the width
   differs, on purpose: half their 27px, a common hamburger-icon accent).
   right:0 (not left:0) so the shorter bar hugs the right edge, with the
   gap it leaves on the left instead of the right. */
.menu-toggle .tm-labpeak-icon-bars::after {
  content: '';
  display: inline-block;
  width: 13.5px;
  height: 1px;
  background: inherit;
  border-radius: 0px;
  transition: 0.3s;
  position: absolute;
  right: 0;
  top: 8px;
}

/* Open state — animate to X */
#main-nav.nav-open .menu-toggle .tm-labpeak-icon-bars {
  background: transparent;
}
#main-nav.nav-open .menu-toggle .tm-labpeak-icon-bars::before {
  top: 0;
  transform: rotate(45deg);
  background: var(--bar-color);
}
#main-nav.nav-open .menu-toggle .tm-labpeak-icon-bars::after {
  top: 0;
  /* Widen back to the top bar's full 27px so the two lines forming the
     X are the same length — only the resting 3-bar icon keeps the
     shorter bottom bar. */
  width: 27px;
  transform: rotate(-45deg);
  background: var(--bar-color);
}

/* ═══════════════════════════════════════════════
   MOBILE MENU DRAWER
   One flat, always-expanded, full-screen menu. No shadow, no hover
   state, no per-level color — every row is identical except for the
   left padding used to indent nested items.
═══════════════════════════════════════════════ */

@media (max-width: 1024px) {

  /* Show the toggle button */
  .menu-toggle {
    display: flex;
  }

  /* Consistent 1.5rem left/right padding on mobile, matching every other
     mobile container in the theme (.container in style.css,
     .footer-inner above) instead of the desktop bar's wider 2rem. */
  .nav-inner {
    padding: 0 1.5rem;
  }

  /* Lock the page underneath while the drawer is open (class toggled on
     <body> by navigation.js alongside #main-nav.nav-open). The drawer
     itself keeps its own overflow-y: auto above, so it can still be
     scrolled internally. */
  body.nav-scroll-lock {
    overflow: hidden;
    height: 100%;
  }

  /* The outer nav-menu wrapper */
  .nav-inner > .nav-menu {
    display: flex;
    align-items: center;
  }

  /* The inner nav-menu div — the drawer itself. Fills the full screen
     below the header and scrolls internally. */
  .nav-inner > .nav-menu > .nav-menu {
    position: absolute;
    top: 70px;
    left: 0;
    width: 100%;
    height: calc(100vh - 70px);
    background-color: var(--nav-surface);
    box-shadow: none;
    z-index: 100;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* Hidden by default. visibility is the actual guard against stray
       taps elsewhere on the page (e.g. in a hero section) registering
       against this drawer's links while it's meant to be closed —
       opacity:0 + pointer-events:none alone should already prevent
       that, but some mobile browsers (WebKit in particular) can
       briefly repaint/hit-test a position:fixed-ancestored, opacity
       -transitioning layer as if it were still there right as a touch
       lands. visibility:hidden removes it from hit-testing outright,
       independent of any opacity/paint timing; the transition delay
       holds it "visible" until the opacity fade-out finishes, so the
       closing animation itself is unaffected. */
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .25s ease, visibility 0s linear .25s;
  }
  #main-nav.nav-open .nav-inner > .nav-menu > .nav-menu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity .25s ease, visibility 0s linear 0s;
  }

  /* Every level — top menu and every sub-menu — is a single flat,
     full-width column. !important throughout this block because the
     desktop dropdown rules above (scoped to the same .sub-menu/li/a/
     span selectors) would otherwise win on specificity and bleed into
     this view.

     NOTE: position/opacity/pointer-events are NOT touched here for
     ul.nav-menu itself — that's the same element that must stay
     position:absolute + opacity/pointer-events-toggled to work as the
     full-screen drawer (.nav-inner > .nav-menu > .nav-menu below).
     Only nested .sub-menu elements get flattened to static/visible.

     Background is NOT set here either — ul.nav-menu is also the drawer
     itself (.nav-inner > .nav-menu > .nav-menu above already gives it
     its solid white background-color); only the nested .sub-menu boxes
     need their own background stripped so they don't sit as separate
     white cards on top of the (already white) drawer. */
  ul.nav-menu,
  ul.nav-menu .sub-menu {
    display: flex !important;
    flex-direction: column !important;
    width: 100% !important;
    margin: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    max-height: none !important;
    transform: none !important;
    padding: 0 !important;
  }

  /* Extra space below the last (flattened) item so it isn't flush
     against the bottom edge of the viewport once the drawer is
     scrolled all the way down. Only the top-level drawer needs this,
     not each nested .sub-menu, so it's set after (and overrides) the
     shared padding:0 rule above rather than folded into it. */
  ul.nav-menu {
    padding-bottom: 3rem !important;
  }
  ul.nav-menu .sub-menu {
    position: static !important;
    opacity: 1 !important;
    pointer-events: auto !important;
    background: transparent !important;
    border-left: 1px solid var(--nav-mobile-border) !important;
  }

  /* The <li> — not the row or the <a> — owns the row's height and
     indentation, exactly as before icons existed. An optional icon
     column (see inc/nav-menu-icons.php) sits to the left of the text
     column inside its own ".sub-menu-row" wrapper (see
     Einar_Partners_Nav_Walker in inc/class-nav-walker.php); that row is
     not the <li> itself, so a nested <ul class="sub-menu"> — a
     third-level dropdown, appended after the row as a sibling — still
     stacks below it in normal flow instead of joining it as a flex item.
     Plain items (no icon/description) keep the simple <li><a> markup. */
  ul.nav-menu li {
    position: relative !important;
    width: 100% !important;
    border-bottom: none !important;
    border-radius: 0 !important;
    background: transparent !important;
  }
  ul.nav-menu > li {
    padding: .85rem 1.5rem !important;
  }
  ul.nav-menu .sub-menu li {
    padding: .85rem 1.25rem .85rem 2.25rem !important;
  }
  ul.nav-menu .sub-menu .sub-menu li {
    padding-left: 3.25rem !important;
  }
  ul.nav-menu .sub-menu-row {
    display: flex !important;
    align-items: center !important;
    gap: 12px !important;
    width: 100% !important;
  }

  /* Text column — grows to fill the row; the <a> and description <span>
     inside it carry no padding/margin of their own. */
  ul.nav-menu .sub-menu-body {
    display: flex !important;
    flex-direction: column !important;
    gap: .25rem !important;
    min-width: 0 !important;
    flex: 1 !important;
  }
  ul.nav-menu li > a,
  ul.nav-menu .sub-menu-body a,
  ul.nav-menu .sub-menu-description {
    display: block !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  ul.nav-menu li > a,
  ul.nav-menu .sub-menu-body a {
    height: auto !important;
    font-size: 14px !important;
    font-weight: 400 !important;
    color: var(--nav-ink) !important;
    text-decoration: none !important;
    white-space: normal !important;
  }
  ul.nav-menu li:hover > a,
  ul.nav-menu li:hover .sub-menu-body a,
  ul.nav-menu li:hover .sub-menu-description {
    color: var(--nav-ink) !important;
  }
  /* Matches the desktop dropdown's .sub-menu li:hover a selector
     specificity (which flips text/icon color for its dark hover
     background) so it doesn't win here and leave content invisible
     against the mobile drawer's white background/no hover bg. */
  ul.nav-menu .sub-menu li:hover a {
    color: var(--nav-ink) !important;
  }
  ul.nav-menu .sub-menu li:hover .sub-menu-icon {
    background-color: var(--nav-ink-soft) !important;
  }
  ul.nav-menu .sub-menu-description {
    font-size: 12px !important;
    line-height: 1.4 !important;
    color: var(--nav-ink-soft) !important;
  }

  /* No chevron on mobile rows */
  ul.nav-menu li.menu-item-has-children > a {
    display: block !important;
    margin-bottom: .5rem !important;
  }
  ul.nav-menu li.menu-item-has-children a::after {
    display: none !important;
  }

  /* Open animation — each row fades in from the left, cascading from
     the first item to the last. --i (set in navigation.js) holds each
     <li>'s position in the flattened, always-expanded list. */
  ul.nav-menu li {
    opacity: 0;
  }
  #main-nav.nav-open ul.nav-menu li {
    animation: einarMobileItemIn .35s ease forwards;
    animation-delay: calc(var(--i, 0) * 40ms);
  }
}

@keyframes einarMobileItemIn {
  from {
    opacity: 0;
    transform: translateX(-16px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* ═══════════════════════════════════════════════
   NAV — hamburger-only header variant (body.header-style-hamburger)
   Selectable per-page via the "Header & Footer Theme" meta box (see
   inc/meta-boxes.php / einar_partners_get_header_style()). Below the
   1024px breakpoint above, this variant looks identical to every other
   page — the mobile drawer already applies unconditionally. Everything
   here only matters above that breakpoint, where the default header
   normally shows the full horizontal menu with no toggle at all.

   Mechanism: the real <ul class="nav-menu"> (the top-level horizontal
   list) is pulled out of flow entirely (position:absolute, anchored
   top/right against the wrapping <div class="nav-menu"> from
   header.php, which becomes position:relative here) — so at rest it
   never affects that div's width. The div's only flow child is then
   #menu-toggle, which is why margin-left:auto (already set inline in
   header.php) still pins the toggle flush-right in the bar exactly as
   in the plain mobile view, with no extra positioning rule needed for
   it here.

   visibility (not just opacity) is toggled on the absolutely-positioned
   ul itself so its links aren't keyboard-tabbable while closed — an
   opacity-only hide would still leave them focusable, same reasoning
   as the mobile drawer's own visibility toggle above. Unlike that
   drawer, no transition-delay is needed on it here: there's no "fade
   out first, then remove from hit-testing" case to cover, since closing
   this variant has no closing animation of its own (see below).

   Each top-level <li> then handles its own reveal — see
   @keyframes einarHamburgerItemIn below — fading and sliding in from
   the right, staggered via the --hi custom property (set per nth-child
   just below; a separate, small, fixed sequence rather than reusing
   navigation.js's own --i, which indexes every item at every depth
   flattened together for the mobile drawer and would produce uneven
   gaps between top-level items here). Delay order matches the mobile
   drawer's own convention (first DOM item animates first) for
   consistency between the two, even though this variant's motion
   direction is mirrored (right instead of left). Closing has no
   matching reverse animation — removing .nav-open simply drops the
   items back to the base opacity:0 rule instantly; only opening was
   asked for. */
@media (min-width: 1025px) {
  body.header-style-hamburger .nav-inner > .nav-menu {
    position: relative;
  }

  body.header-style-hamburger .menu-toggle {
    display: flex;
    transition: opacity .2s ease;
  }
  body.header-style-hamburger #main-nav.nav-open .menu-toggle {
    opacity: 0;
    pointer-events: none;
  }

  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    visibility: hidden;
    pointer-events: none;
  }
  body.header-style-hamburger #main-nav.nav-open .nav-inner > .nav-menu > .nav-menu {
    visibility: visible;
    pointer-events: auto;
  }

  /* Staggered index, top-level items only — see the comment above. */
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(1) { --hi: 0; }
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(2) { --hi: 1; }
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(3) { --hi: 2; }
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(4) { --hi: 3; }
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(5) { --hi: 4; }
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(6) { --hi: 5; }
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(7) { --hi: 6; }
  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li:nth-child(8) { --hi: 7; }

  body.header-style-hamburger .nav-inner > .nav-menu > .nav-menu > li {
    opacity: 0;
  }
  body.header-style-hamburger #main-nav.nav-open .nav-inner > .nav-menu > .nav-menu > li {
    animation: einarHamburgerItemIn .45s cubic-bezier(.22, 1, .36, 1) forwards;
    animation-delay: calc(var(--hi, 0) * 70ms);
  }
}

/* Fade in from the right — mirrors @keyframes einarMobileItemIn above,
   same easing family used elsewhere for reveal animations (see
   .cta-reveal in global-styles.css), just the opposite direction since
   this menu is right-anchored instead of a left-reading drawer list. */
@keyframes einarHamburgerItemIn {
  from {
    opacity: 0;
    transform: translateX(24px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* ═══════════════════════════════════════════════
   NAV — dark-section variant (transparent bar, light-colored items)
   Set per-page via body.header-theme-dark (the "Header & Footer Theme"
   meta box, see inc/meta-boxes.php — defaults to dark on the front
   page). This is a per-page editorial choice, NOT tied to the
   visitor's OS/browser color-scheme, so any page or custom template
   that sits over a dark section can opt in the same way.

   #main-nav.on-dark-section is the same treatment, triggered by a
   plain class instead of the page-level theme — for a page whose
   default theme is light but that crosses one specific dark section
   mid-page (see templates/governance-operating-models.php's Iceberg
   section / assets/js/governance-operating-models.js) rather than
   being dark throughout. Toggle it from that section's own scroll
   logic; no page-level theme change needed.

   Kept as its OWN rule set below rather than joined into the
   body.header-theme-dark selectors above: --white / --white-sl /
   --dark-menu / --border all swap to different values under
   body.header-theme-light (see variables.css) — meaning on exactly the
   kind of light-themed page .on-dark-section exists for, those
   variables resolve to their light-theme meaning, not "white"/"dark
   navy". .on-dark-section uses the fixed rgb()-space forms instead
   (--white-rgb, --bg-rgb) so it renders correctly regardless of the
   page's own theme.

   Every selector here is guarded with :not(.flash-mode): a
   dark-themed page can still need the nav to flip back to the default
   light look temporarily where it crosses a lighter sub-section (e.g.
   the homepage's "why now" and "who" sections) — toggle #main-nav
   .flash-mode for that (further below) and these rules simply stop
   matching, so flash-mode doesn't need to re-declare every property
   back to its default value. See assets/js/homepage.js for a working
   scroll-driven example other templates can copy. .on-dark-section
   isn't guarded by :not(.flash-mode) since nothing toggles both at
   once today — add the guard if a future page ever needs to.

   The dropdown and the mobile drawer are NOT re-themed here — both
   already default to the white-bg/dark-text look unconditionally, so
   there's nothing for this dark variant to override on them. */
body.header-theme-dark #main-nav:not(.flash-mode) {
  background: transparent;
}
body.header-theme-dark #main-nav.scrolled:not(.flash-mode) {
  background: var(--dark-menu);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px); /* for Safari */
  border-bottom: 0 !important;
}

body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li > a {
  color: var(--white-sl) !important;
}
body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li > a:hover,
body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li:hover > a {
  color: var(--white) !important;
}
body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li > a::before {
  background: var(--border);
}
body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li.menu-item-has-children > a::after {
  background-color: rgb(var(--white-rgb) / .5);
}

/* .on-dark-section — same look, fixed (non-swapping) color forms. */
#main-nav.on-dark-section {
  background: transparent;
}
#main-nav.on-dark-section.scrolled {
  background: rgb(var(--bg-rgb) / .8);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px); /* for Safari */
  border-bottom: 0 !important;
}
#main-nav.on-dark-section ul.nav-menu > li > a {
  color: rgb(var(--white-rgb) / .8) !important;
}
#main-nav.on-dark-section ul.nav-menu > li > a:hover,
#main-nav.on-dark-section ul.nav-menu > li:hover > a {
  color: rgb(var(--white-rgb)) !important;
}
#main-nav.on-dark-section ul.nav-menu > li > a::before {
  background: rgb(var(--white-rgb) / .1);
}
#main-nav.on-dark-section ul.nav-menu > li.menu-item-has-children > a::after {
  background-color: rgb(var(--white-rgb) / .5);
}

/* The mobile drawer renders the very same top-level <a> elements
   (ul.nav-menu > li > a) as the desktop bar, just relaid-out — so on a
   dark-section page the light-on-transparent overrides above still
   match inside the drawer too, leaving items either near-invisible
   (light text on the drawer's solid white background) or ringed with
   a hover pill. Placed after the dark-section block above so it wins
   the (otherwise tied) specificity fight on #main-nav pages that are
   both dark-themed and at mobile width. */
@media (max-width: 1024px) {
  ul.nav-menu > li > a,
  ul.nav-menu > li > a:hover,
  ul.nav-menu > li:hover > a,
  body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li > a,
  body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li > a:hover,
  body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li:hover > a,
  #main-nav.on-dark-section ul.nav-menu > li > a,
  #main-nav.on-dark-section ul.nav-menu > li > a:hover,
  #main-nav.on-dark-section ul.nav-menu > li:hover > a {
    color: var(--nav-ink) !important;
  }
  /* No hover pill behind top-level items in the drawer, light or dark
     section — this is the same pill used by the desktop bar. */
  ul.nav-menu > li > a::before,
  body.header-theme-dark #main-nav:not(.flash-mode) ul.nav-menu > li > a::before,
  #main-nav.on-dark-section ul.nav-menu > li > a::before {
    content: none !important;
  }
}

/* Hamburger bars — set on #main-nav (not body) so the :not(.flash-mode)
   guard applies here too; flash-mode then needs no bar-color override
   of its own, it just falls through to the --bar-color default. */
body.header-theme-dark #main-nav:not(.flash-mode) { --bar-color: var(--white); }
#main-nav.on-dark-section { --bar-color: rgb(var(--white-rgb)); }

/* ═══════════════════════════════════════════════
   NAV — #main-nav.flash-mode: temporary "light section" override
   Toggle this class on #main-nav (from any page's own scroll/section
   logic — see assets/js/homepage.js for the homepage's implementation)
   to flip a dark-section nav back to the default light/white-bar look
   while it crosses a lighter sub-section. Lives here (not in a
   per-page CSS file) so any current or future template can reuse it
   for free by just toggling the class — no CSS duplication needed.

   Only the background needs overriding: every dark-section rule above
   is already guarded with :not(.flash-mode), so the moment this class
   is added, the text color, hover pill, icon, logo and hamburger bars
   all simply fall through to their normal default-light values. */
#main-nav.flash-mode,
#main-nav.flash-mode.scrolled {
  background: rgb(var(--white-rgb) / 0.90) !important;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px); /* for Safari */
}

/* ═══════════════════════════════════════════════
   FOOTER
═══════════════════════════════════════════════ */
footer {
  /* Always dark navy, like the homepage — regardless of this page's own
     "Header & Footer Theme" (see inc/meta-boxes.php / einar_partners_get_
     header_theme()), which used to swap the footer to a light theme via
     body.header-theme-light's --bg/--white/--muted/--white-sl/--border
     overrides in variables.css. Re-pinning those here, on footer itself,
     overrides the swap for the footer and everything inside it, since
     custom properties inherit down from the nearest ancestor that sets
     them (same technique used for #cta in global-styles.css). */
  --bg: #121F36;
  --white: #ffffff;
  --white-sl: rgba(255, 255, 255, .8);
  --muted: #64748b;
  --border: rgba(255, 255, 255, .1);
  position: relative;
  background: radial-gradient(ellipse 70% 55% at 50% 100%, rgb(var(--accent-rgb) / 0.16) 0%, rgb(var(--accent-rgb) / 0) 70%), var(--bg);
  padding: 8rem 0 2rem;
  overflow: hidden;
  /* Always Inter, regardless of the page's own body font (see #main-nav). */
  font-family: var(--font-body);
}
.footer-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 2rem;
}
.footer-top {
  display: grid;
  grid-template-columns: 14fr 24fr 24fr 24fr 14fr;
  gap: 2.5rem;
  align-items: flex-start;
  margin-bottom: 6rem;
}
.foot-col h4 {
  font-family: var(--font-head);
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--white-sl);
  margin-bottom: 1rem;
}
.foot-col ul { list-style: none; display: flex; flex-direction: column; gap: 1rem; margin: 0; padding: 0; }
.foot-col a {
  color: var(--muted);
  /* !important: Elementor's global kit (.elementor-kit-839 a) has the same
     specificity and loads after this stylesheet, so it silently overrides
     font-size/line-height unless pinned here (matches the color fix below). */
  font-size: 15px !important;
  line-height: 1.6 !important;
  font-weight: 300;
  text-decoration: none;
  transition: color .25s;
}
.foot-col a:hover { color: var(--white); }

/* Footer link color fix — ensure links are readable on dark background.
   :not(.entry-footer) excludes <footer class="entry-footer"> — a single
   post's own per-article footer (tags/categories, see blog.css), a light
   background element that also happens to be a <footer> tag. Without the
   exclusion this !important rule reached into it too and forced its tag/
   category pill links to solid white on hover — invisible against their
   own light pill background. */
footer:not(.entry-footer) a {
  color: var(--muted) !important;
}
footer:not(.entry-footer) a:hover:not(.einar-base-btn):not(.btn-base) {
  color: var(--white) !important;
}

/* 5th column — logo + contact */
.foot-col--brand {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.foot-logo-img {
  display: block;
}
.foot-logo-img img {
  height: 50px;
  width: auto;
  display: block;
  opacity: .85;
}
/* Light/dark footer logo swap, mirrors .logo-light/.logo-dark in the nav */
.foot-logo-img .foot-logo-dark { display: none; }
.foot-logo-img .foot-logo-light { display: block; }

.foot-contact {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.foot-contact-group {
  display: flex;
  flex-direction: column;
  gap: .2rem;
}
.foot-contact-label {
  font-family: var(--font-head);
  text-transform: uppercase;
  color: var(--muted);
}
.foot-contact-group a,
.foot-contact-group span:not(.foot-contact-label) {
  font-size: 15px;
  color: var(--muted);
  text-decoration: none;
  transition: color .25s;
  line-height: 1.5;
}
.foot-contact-group a:hover { color: var(--white); }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}
.footer-bottom p { color: var(--muted); margin: 0; }
.foot-bottom-right { display: flex; align-items: center; gap: 2rem; }
.foot-legal { display: flex; gap: 1.5rem; margin: 0; padding: 0; }
.foot-legal a { color: var(--muted); font-size: 15px; text-decoration: none; transition: color .25s; }
.foot-legal a:hover { color: var(--white); }
.foot-social { display: flex; align-items: center; gap: 1rem; }
.foot-social a { display: flex; align-items: center; opacity: .45; transition: opacity .25s; }
.foot-social a:hover { opacity: 1; }
.foot-social img { width: 18px; height: 18px; filter: invert(1); }

@media (max-width: 1024px) {
  .footer-inner { padding: 0 1.5rem; }
  .footer-top { grid-template-columns: repeat(2, 1fr); gap: 2rem; }
  .foot-col--brand { grid-column: 1 / -1; flex-direction: row; align-items: flex-start; gap: 3rem; }
}
@media (max-width: 767px) {
  .footer-inner { padding: 0 1.5rem; }
  .footer-top { grid-template-columns: 1fr; gap: 2.5rem; margin-bottom: 3rem; }
  .foot-col--brand { grid-column: 1 / -1; flex-direction: column; gap: 1.5rem; }

  /* Bottom bar: one column — privacy, then social icons, then copyright last,
     each its own row (reordered with flex `order`, markup unchanged). */
  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 1.25rem;
  }
  .footer-bottom p { order: 2; }
  .foot-bottom-right {
    order: 1;
    flex-direction: column;
    align-items: flex-start;
    gap: 1.25rem;
    width: 100%;
  }
}

/* ═══════════════════════════════════════════════
   CONTENT THEME — .header-theme-light on <body>
   The footer no longer switches to a light theme (see the footer's own
   dark-navy variable pins above) — it always matches the homepage now,
   so the light-footer logo/social icon variants below are unused.
═══════════════════════════════════════════════ */
/* ---------------------------------------------------------------
   Source: assets/css/homepage.css
--------------------------------------------------------------- */
/* ═══════════════════════════════════════════════
       RESET
    ═══════════════════════════════════════════════ */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    /* All custom properties (colors, fonts) now live in
       assets/css/variables.css, loaded before this file — single source
       of truth for the whole theme. */

    p {
      font-size: 15px;
      font-family: var(--font-body);
      font-weight: 300;
    }
    h1, h2, h3, h4, h5, h6 {
      font-family: var(--font-head);
      font-weight: 500;
      line-height: 1.2;
      letter-spacing: -.02em;
    }
    h1 { font-size: 40px; margin-bottom: 1.2rem; }
    h2 { font-size: 36px; }
    h3 { font-size: 33px; }
    h4 { font-size: 23px; }
    h5 { font-size: 19px; }
    h6 { font-size: 17px; }

    /* ═══════════════════════════════════════════════
       ACCENT GRADIENT TEXT
       .accent is now defined once, sitewide, in
       assets/css/global-styles.css.
    ═══════════════════════════════════════════════ */

    /* ═══════════════════════════════════════════════
       HEADING COLOR OVERRIDES — Dark sections
       Ensure headings in dark-background sections have
       white color to override Elementor's global rules
    ═══════════════════════════════════════════════ */
    /* Hero section — dark background */
    #hero h1, #hero h2, #hero h3, #hero h4, #hero h5, #hero h6 {
      color: var(--white) !important;
    }
    #hero h1 {
      font-size: 48px;
    }

    /* Why Now section — dark background */
    #why-now h1, #why-now h2, #why-now h3, #why-now h4, #why-now h5, #why-now h6,
    #wn-imperative h1, #wn-imperative h2, #wn-imperative h3, #wn-imperative h4, #wn-imperative h5, #wn-imperative h6 {
      color: var(--white) !important;
    }

    /* Morph section — dark background */
    #morph-wrapper h1, #morph-wrapper h2, #morph-wrapper h3, #morph-wrapper h4, #morph-wrapper h5, #morph-wrapper h6 {
      color: var(--white) !important;
    }

    /* Research section — dark background */
    #research h1, #research h2, #research h3, #research h4, #research h5, #research h6 {
      color: var(--white) !important;
    }

    /* CTA section — dark background heading/link colors now live in
       assets/css/global-styles.css alongside the rest of the shared
       #cta / .cta-* styles (see below). */

    /* Europe section — dark background */
    #europe-scroll-wrapper h1, #europe-scroll-wrapper h2, #europe-scroll-wrapper h3, #europe-scroll-wrapper h4, #europe-scroll-wrapper h5, #europe-scroll-wrapper h6 {
      color: var(--white) !important;
    }

    /* ═══════════════════════════════════════════════
       LINK COLOR OVERRIDES — Dark sections
       Ensure links in dark-background sections are white
    ═══════════════════════════════════════════════ */
    /* Hero section — dark background links */
    #hero a {
      color: var(--white) !important;
    }
    #hero a:hover:not(.einar-base-btn):not(.btn-base) {
      color: var(--violet-lt) !important;
    }

    /* Why Now section — dark background links */
    #why-now a {
      color: var(--white) !important;
    }
    #why-now a:hover:not(.einar-base-btn):not(.btn-base) {
      color: var(--violet-lt) !important;
    }

    /* Morph section — dark background links */
    #morph-wrapper a {
      color: var(--white) !important;
    }
    #morph-wrapper a:hover:not(.einar-base-btn):not(.btn-base) {
      color: var(--violet-lt) !important;
    }

    /* Research section — dark background links */
    #research a {
      color: var(--white) !important;
    }
    #research a:hover:not(.einar-base-btn):not(.btn-base) {
      color: var(--violet-lt) !important;
    }

    /* Europe section — dark background links */
    #europe-scroll-wrapper a {
      color: var(--white) !important;
    }
    #europe-scroll-wrapper a:hover:not(.einar-base-btn):not(.btn-base) {
      color: var(--violet-lt) !important;
    }

    /* ═══════════════════════════════════════════════
        BASE BUTTON
        .btn-base is now defined once, sitewide, in
        assets/css/global-styles.css (alongside .einar-base-btn,
        the same component under its Elementor-widget name).
    ═══════════════════════════════════════════════ */

    /* ═══════════════════════════════════════════════
       CONTAINER
    ═══════════════════════════════════════════════ */
    .container {
      width: 100%;
      max-width: var(--container-max);
      margin-left: auto;
      margin-right: auto;
      padding-left: 1.5rem;
      padding-right: 1.5rem;
    }

    html { scroll-behavior: smooth; }
    body {
      background: var(--bg);
      color: var(--white);
      font-family: var(--font-body);
      font-size: 15px;
      /* clip, not hidden — hidden makes body count as position:sticky's
         scroll container, which breaks every pinned section below
         (#methodology-scroll-sticky, #who-scroll-sticky, #ai-scroll-sticky);
         see the matching html/body rule in digital-resilience.css for the
         full explanation. */
      overflow-x: clip;
      -webkit-font-smoothing: antialiased;
    }

    /* NAV, hamburger toggle, and mobile drawer styles now live in
       assets/css/site-header-footer.css (loaded site-wide via header.php). */

    /* ═══════════════════════════════════════════════
       PERSISTENT GLOBE CANVAS (fixed overlay)
    ═══════════════════════════════════════════════ */
    #globe-canvas {
      position: fixed;
      top: 0; left: 0;
      width: 100vw; height: 100vh;
      pointer-events: none;
      z-index: 1;
    }

    /* White transition overlay — full-screen fade from dark to white */
    #wn-flash {
      position: fixed;
      inset: 0;
      background: var(--white);
      pointer-events: none;
      z-index: 160;
      opacity: 0;
      will-change: opacity;
    }

    /* Morph section flash transition dot — same mechanics as wn-flash, originates from canvas center */
    #morph-flash {
      position: fixed;
      width: 300px; height: 300px;
      margin-left: -150px; margin-top: -150px;
      border-radius: 50%;
      background: var(--white);
      pointer-events: none;
      z-index: 160;
      opacity: 0;
      transform: scale(0);
      will-change: transform, opacity;
    }

    /* Nav flash-mode (light-section override) now lives in
       assets/css/site-header-footer.css — shared with every page/
       template, not just the homepage. See that file's "NAV —
       #main-nav.flash-mode" section. */

    /* ═══════════════════════════════════════════════
       HERO
    ═══════════════════════════════════════════════ */
    #hero {
      position: relative;
      min-height: 100vh;
      display: flex;
      align-items: center;
      overflow: hidden;
    }
    #hero::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgb(var(--accent-rgb) / .06) 1px, transparent 1px),
        linear-gradient(90deg, rgb(var(--accent-rgb) / .06) 1px, transparent 1px);
      background-size: 60px 60px;
      mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
    }
    #hero::after {
      content: '';
      position: absolute;
      inset: 0;
      pointer-events: none;
      background-image:
        radial-gradient(ellipse 30% 40% at 18% 30%, rgb(var(--accent-rgb) / .18) 0%, transparent 65%);
    }
    .globe-glow { display: none; }
    #hero-canvas { display: none; }

    /* ─── Scroll progress bar ─── */
    #scroll-progress {
      position: fixed;
      top: 0; left: 0;
      width: 100%;
      height: 2px;
      z-index: 9999;
      pointer-events: none;
    }
    #scroll-progress-bar {
      height: 100%;
      width: 0%;
      background: linear-gradient(90deg, rgb(var(--indigo-rgb) / .0) 0%, rgb(var(--accent-rgb) / .7) 40%, rgb(var(--sky-rgb) / .9) 100%);
      transition: width .08s linear;
      position: relative;
    }
    #scroll-progress-bar::after {
      content: '';
      position: absolute;
      right: -2px;
      top: 50%;
      transform: translateY(-50%);
      width: 4px;
      height: 4px;
      border-radius: 50%;
      background: rgb(var(--sky-rgb) / .95);
      box-shadow: 0 0 4px 2px rgb(var(--sky-rgb) / .6), 0 0 8px 3px rgb(var(--accent-rgb) / .35);
      opacity: 0;
      transition: opacity .15s ease;
    }
    #scroll-progress-bar.active::after {
      opacity: 1;
    }

    .scroll-hint {
      position: absolute;
      bottom: 2.2rem; left: 50%;
      transform: translateX(-50%);
      display: flex; flex-direction: column; align-items: center; gap: .5rem;
      opacity: 0;
      animation: fadeUp .9s 1.3s cubic-bezier(.22,1,.36,1) forwards;
      pointer-events: none;
    }
    .scroll-hint span { font-size: .66rem; letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }
    .scroll-line {
      width: 1px; height: 36px;
      background: linear-gradient(to bottom, var(--violet-lt), transparent);
      animation: scrollPulse 1.8s ease-in-out infinite;
    }
    .hero-inner {
      display: flex;
      flex-direction: column;
      justify-content: center;
      min-height: 100vh;
      width: 100%;
      max-width: var(--container-max);
      margin: 0 auto;
      padding-left: 2rem;
      padding-right: 2rem;
    }
    .hero-content {
      position: relative;
      z-index: 2;
      max-width: 600px;
    }

    #hero-title {
      font-size: 56px;
      /* More than the inherited h1 default (0.5em, style.css) — a
         clearer gap before .hero-sub below. */
      margin-bottom: 2rem;
    }

    /* Word-by-word title reveal */
    .hero-word {
      display: inline-block;
      opacity: 0;
      transform: translateY(22px);
      transition: opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1);
    }
    .hero-word.visible {
      opacity: 1;
      transform: translateY(0);
    }

    /* Global `strong` color (global-styles.css) is dark slate for light
       backgrounds — unreadable on this dark navy hero, so pinned back
       to white here, same pattern as .float-text/.why-line strong below. */
    .hero-sub strong { color: var(--slate-000); font-weight: 400; }

    .hero-sub {
      /* Reduced from the body default (15px, variables.css) — this now
         holds 4 short paragraphs instead of 2 sentences, and at the
         default size that ran noticeably tall/dense in this column
         next to the rest of the hero. */
      font-size: 14px;
      line-height: 1.6;
      color: var(--slate);
      margin-bottom: 2rem;
      opacity: 0;
      transform: translateY(18px);
      transition: opacity .6s cubic-bezier(.22,1,.36,1), transform .6s cubic-bezier(.22,1,.36,1);
    }
    .hero-sub.visible {
      opacity: 1;
      transform: translateY(0);
    }
    /* .hero-sub is now a <div> of several <p>s (was a single <p>) — the
       fade-up reveal above still applies to it as one block (JS toggles
       .visible on the single .hero-sub element, see homepage.js), these
       just handle spacing between the paragraphs inside it. */
    .hero-sub p {
      margin: 0 0 1em;
    }
    .hero-sub p:last-child {
      margin-bottom: 0;
    }
    .hero-resilience {
      font-family: var(--font-head);
      font-weight: 600;
      color: var(--violet-lt);
      letter-spacing: .02em;
      margin-bottom: 2.4rem;
      opacity: .9;
    }
    .hero-actions {
      display: flex;
      gap: 1rem;
      align-items: center;
      opacity: 0;
      transform: translateY(16px);
      transition: opacity .6s cubic-bezier(.22,1,.36,1), transform .6s cubic-bezier(.22,1,.36,1);
    }
    .hero-actions.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .hero-stats {
      position: relative; z-index: 2;
      display: flex; gap: 2.8rem;
      padding: 1.6rem 0 3rem;
      opacity: 0;
      animation: fadeUp .9s .65s cubic-bezier(.22,1,.36,1) forwards;
    }
    .stat-num {
      font-family: var(--font-head);
      line-height: 1;
    }
    .stat-num span { color: var(--violet-lt); }
    .stat-label {
      letter-spacing: .15em;
      text-transform: uppercase;
      color: var(--muted);
      margin-top: .35rem;
    }
    .stat-divider { width: 1px; background: rgb(var(--white-rgb) / .08); align-self: stretch; }

    .float-badge, .float-badge2 {
      position: absolute;
      z-index: 3;
      background: rgba(8,15,32,.88);
      border-radius: 8px;
      padding: .75rem 1.1rem;
      display: flex;
      align-items: center;
      gap: .7rem;
      backdrop-filter: blur(12px);
      opacity: 0;
    }
    .float-badge {
      right: calc(8% + 200px); top: calc(50% - 160px);
      border: 1px solid rgb(var(--accent-rgb) / .3);
      animation: fadeUp .8s .9s cubic-bezier(.22,1,.36,1) forwards, floatY 4s 1.7s ease-in-out infinite;
    }
    .float-badge2 {
      right: calc(8% + 240px); top: calc(50% + 110px);
      border: 1px solid rgb(var(--sky-rgb) / .25);
      animation: fadeUp .8s 1.1s cubic-bezier(.22,1,.36,1) forwards, floatY 5s 2s ease-in-out infinite reverse;
    }
    .float-dot { width:8px; height:8px; border-radius:50%; background: var(--green); box-shadow:0 0 8px var(--green); }
    .float-text { color: var(--slate); white-space:nowrap; }
    .float-text strong { color: var(--white); font-weight:500; }



    /* ═══════════════════════════════════════════════
       WHY NOW — scroll-driven timeline
    ═══════════════════════════════════════════════ */
    #why-now {
      position: relative;
      height: 750vh;
      background: var(--bg);
    }
    #why-now-sticky {
      position: sticky;
      top: 0;
      height: 100vh;
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      overflow: hidden;
    }
    #why-now-sticky::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgb(var(--accent-rgb) / .06) 1px, transparent 1px),
        linear-gradient(90deg, rgb(var(--accent-rgb) / .06) 1px, transparent 1px);
      background-size: 60px 60px, 60px 60px, 100% 100%;
      pointer-events: none;
    }
    .why-now-left {
      position: relative;
      z-index: 2;
      max-width: 50%;
      padding-right: max(2rem, calc((100vw - var(--container-max)) / 2 + 2rem));
      padding-left: 3rem;
    }

    /* Stage reveal elements */
    .wn-stage {      
      opacity: 0;
      transform: translateY(22px);
      transition: opacity .75s ease, transform .75s ease;
    }
    .wn-stage.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .wn-stage-heading {
      margin-bottom: 1.8rem;
    }
    .wn-stage-heading h2 {
      margin-top: .6rem;
    }
    /* Stages 2 & 3 crossfade in place — .wn-stage-stack is the shared
       positioning box (it collapses to 0 height itself; its absolutely
       positioned children paint from its top edge, same as before). */
    .wn-stage-stack {
      position: relative;
    }
    .wn-stage-paras1,
    .wn-stage-paras2 {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      display: flex;
      flex-direction: column;
      gap: 1.1rem;
      pointer-events: none;
    }
    .wn-stage-paras1.visible,
    .wn-stage-paras2.visible {
      pointer-events: auto;
    }
    .wn-stage-btn {
      margin-top: .5rem;
      align-self: flex-start;
    }

    /* Stage 4 — imperative fullscreen reveal */
    #wn-imperative {
      position: fixed;
      inset: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 5;
      opacity: 0;
      pointer-events: none;
      text-align: center;
    }
    .wn-value {
      max-width: var(--container-max);
      width: 100%;
      padding: 0 3rem;
    }
    .wn-value h3 {
      font-family: var(--font-head);
      font-size: clamp(26px, 3vw, 42px);
      line-height: 1.3;
      letter-spacing: -.025em;
      color: var(--white);
      margin-bottom: 3.5rem;
    }
    .wn-value-list {
      list-style: none;
      display: grid;
      grid-template-columns: repeat(5, 1fr);
      gap: 0;
    }
    .wn-value-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 1rem;
      padding: 2.4rem 1.2rem;
      border: 1px solid rgba(167, 139, 250, .18);
      border-radius: 0;
      background: rgba(167, 139, 250, .05);
      opacity: 0;
      transform: translateY(18px);
      transition: opacity .55s ease, transform .55s ease;
    }
    .wn-value-item:not(:first-child) {
      margin-left: -1px;
    }
    .wn-value-item.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .wn-value-icon {
      width: 60px;
      height: 60px;
      filter: drop-shadow(0 0 12px rgba(167, 139, 250, .5));
    }
    .wn-value-keyword {
      font-size: 13px;
      letter-spacing: .16em;
      text-transform: uppercase;
      font-weight: 500;
      background: linear-gradient(135deg, #a78bfa 0%, #c9c7de 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
    }
    .wn-value-item p {
      font-size: 14px;
      line-height: 1.4;
      font-weight: 300;
      color: var(--white);
      margin: 0;
    }

    /* Stage 4b — client logos, fixed to the bottom of the viewport, covered
       by #wn-flash along with the rest of the stage once it fades in */
    #wn-clients {
      position: fixed;
      left: 0; right: 0; bottom: 0;
      z-index: 5;
      padding: 0 0 2.5rem;
      opacity: 0;
      pointer-events: none;
      text-align: center;
      transition: opacity .6s ease;
    }
    #wn-clients.visible {
      opacity: 1;
    }
    #wn-clients .logo-item {
      width: 135px;
      height: 78px;
      opacity: .55;
    }
    #wn-clients .logo-item:hover {
      opacity: 1;
    }
    #wn-clients .logo-track-wrap::before { background: linear-gradient(to right, var(--bg), transparent); }
    #wn-clients .logo-track-wrap::after  { background: linear-gradient(to left,  var(--bg), transparent); }

    /* Why-line styles — left-aligned, no self-animation (parent .wn-stage handles it) */
    .why-line {
      font-family: var(--font-head);
      font-weight: 400;
      color: var(--slate);
      opacity: 1;
      transform: none;
      transition: none;
    }
    .why-line strong { color: var(--white); font-weight: 600; }
    .why-line.punch {
      color: var(--white);
    }
    .why-line-lg {
      font-size: 21px;
      font-weight: 500;
    }
    .why-line em { color: var(--violet-lt); font-style: italic; }

    /* Legacy — keep .visible harmless for any stray elements */
    .why-line.visible { opacity: 1; transform: none; }
    .why-divider.visible { opacity: 1; }

    /* ═══════════════════════════════════════════════
       METHODOLOGY & FRAMEWORK — sticky scroll zone, same structure as
       #ai-scroll-wrapper/#ai-scroll-sticky: header + card grid pinned
       together inside one full-screen white viewport.
    ═══════════════════════════════════════════════ */
    #methodology-scroll-wrapper {
      position: relative;
      height: 240vh;
      background: var(--white);
    }
    #methodology-scroll-sticky {
      position: sticky;
      top: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      overflow: hidden;
      background: var(--white);
      /* Above #wn-imperative/#wn-clients (z-index: 5, fixed, and never fully
         reset once the why-now value cascade has fired) so the pinned white
         viewport actually covers the client-logo strip instead of it bleeding
         through on top; below #wn-flash (160) so that crossfade still plays. */
      z-index: 10;
    }
    #methodology {
      position: relative;
      width: 100%;
      padding: 0;
      background: transparent;
    }
    .methodology-inner {
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 2rem;
    }
    .methodology-header {
      text-align: center;
      max-width: 620px;
      margin: 0 auto 2.5rem;
      opacity: 0;
      transform: translateY(24px);
      transition: opacity 0.8s ease, transform 0.8s ease;
    }
    .methodology-header.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .methodology-header h2 {
      font-family: var(--font-head);
      line-height: 1.2;
      letter-spacing: -.02em;
      color: var(--ink-900);
      margin-bottom: .8rem;
    }
    .methodology-header p {
      color: var(--slate-600);
      line-height: 1.7;
      font-size: 1rem;
    }

    /* Card grid — same recipe as .ai-pillars-grid / .ai-pillar-card */
    .methodology-grid {
      display: grid;
      grid-template-columns: repeat(5, 1fr);
      gap: 0;
    }
    .method-card {
      padding: 2rem 1.6rem;
      border: 1px solid rgb(var(--bg-rgb) / .1);
      border-radius: 0;
      background: var(--white);
      position: relative;
      display: flex;
      flex-direction: column;
      height: auto;
      overflow: hidden;
      transition: border-color .3s, background .35s, box-shadow .35s, opacity .6s ease, transform .6s ease;
      opacity: 0;
      transform: translateX(40px);
    }
    .method-card + .method-card {
      margin-left: -1px;
    }
    /* Mouse-tracking glow layer, tinted with each card's own --card-accent (set inline in front-page.php) */
    .method-card::before {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: 0;
      background: radial-gradient(
        500px circle at var(--mx, 50%) var(--my, 50%),
        color-mix(in srgb, var(--card-accent, var(--violet)) 18%, transparent) 0%,
        transparent 70%
      );
      opacity: 0;
      transition: opacity .4s ease;
      pointer-events: none;
      z-index: 0;
    }
    .method-card > * { position: relative; z-index: 2; }
    .method-card.visible {
      opacity: 1;
      transform: translateX(0);
    }
    .method-card:hover {
      border-color: color-mix(in srgb, var(--card-accent, var(--violet)) 45%, transparent);
      background: rgb(var(--white-rgb) / .92);
      backdrop-filter: blur(2px);
      z-index: 3;
    }
    .method-card:hover::before { opacity: 1; }
    .method-card-num {
      font-family: var(--font-head);
      font-weight: 600;
      color: var(--card-accent, var(--violet));
      margin-bottom: 1.2rem;
      font-size: 0.8rem;
      letter-spacing: .06em;
      opacity: .8;
    }
    .method-card h3 {
      font-family: var(--font-head);
      font-size: 22px;
      margin-bottom: .6rem;
      color: var(--bg);
    }
    .method-card p {
      font-size: 0.9rem;
      color: var(--slate-600);
      line-height: 1.6;
      margin-bottom: .6rem;
    }
    .method-card p:last-child {
      margin-bottom: 0;
    }

    /* ═══════════════════════════════════════════════
       WHO WE ARE
    ═══════════════════════════════════════════════ */

    /* Sticky scroll zone — 300vh: flash fades first, then reveals, then dwell */
    #who-scroll-wrapper {
      position: relative;
      height: 300vh;
      background: var(--white);
    }
    #who-scroll-sticky {
      position: sticky;
      top: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      overflow: hidden;
      background: var(--white);
    }

    /* 3-col x 3-row mosaic — same bordered-box technique as .dr-impact-grid
       in templates/digital-resilience.php: .who-left spans the left 2
       columns across all 3 rows, and the 3 .wsb-card boxes each take
       column 3, one per row — placed by explicit line numbers (not
       grid-template-areas, which left a stray ~30px gap before column 3
       in testing) below. */
    #who-we-are {
      width: min(100% - 4rem, var(--container-max));
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-template-rows: repeat(3, 1fr);
      gap: 0;
      margin: 0 auto;
    }
    .who-left {
      grid-column: 1 / 3;
      grid-row: 1 / 4;
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      justify-content: center;
      padding: 3rem 3.4rem;
      border: 1px solid rgb(var(--bg-rgb) / .1);
      background: var(--off-white);
      opacity: 0;
      transform: translateY(24px);
      transition: opacity 0.8s ease, transform 0.8s ease;
    }
    .who-left.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .who-left h2 {
      font-family: var(--font-head);
      line-height: 1.2;
      letter-spacing: -.02em;
      margin-bottom: 1rem;
      color: var(--bg);
    }
    .who-left p {
      line-height: 1.8;
      color: var(--slate-600);
      margin-bottom: 2rem;
    }
    /* btn-base on white background needs dark color */
    #who-scroll-wrapper .btn-base { color: var(--ink-900); }
    #who-scroll-wrapper .btn-base::before,
    #who-scroll-wrapper .btn-base::after { background: var(--ink-900); }
    .who-stat {
      padding: 1.4rem 1.8rem;
      border: 1px solid rgb(var(--bg-rgb) / .1);
      border-radius: 8px;
      background: rgb(var(--accent-rgb) / .03);
      opacity: 0;
      transform: translateY(18px);
      transition: opacity 0.6s ease, transform 0.6s ease, border-color .3s;
    }
    .who-stat.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .who-stat:hover { border-color: rgb(var(--accent-rgb) / .3); }
    .who-stat-num {
      font-family: var(--font-head);
      color: var(--violet);
      line-height: 1;
      margin-bottom: .35rem;
    }
    .who-stat-label {
      color: var(--slate-600);
      letter-spacing: .04em;
    }
    .who-sectors {
      display: flex;
      flex-wrap: wrap;
      gap: .5rem;
      margin-top: .2rem;
    }
    .sector-tag {
      letter-spacing: .1em;
      text-transform: uppercase;
      padding: .3rem .75rem;
      border: 1px solid rgb(var(--bg-rgb) / .12);
      border-radius: 20px;
      color: var(--muted);
    }

    /* ── Who We Are: 3 stat boxes — grid-placed directly in #who-we-are
       (siblings of .who-left), one per row of column 3. ─────────────── */
    #who-we-are > .wsb-card:nth-child(2) { grid-column: 3; grid-row: 1; }
    #who-we-are > .wsb-card:nth-child(3) { grid-column: 3; grid-row: 2; }
    #who-we-are > .wsb-card:nth-child(4) { grid-column: 3; grid-row: 3; }
    .wsb-card {
      padding: 2rem 2.2rem;
      border: 1px solid rgb(var(--bg-rgb) / .1);
      border-radius: 0;
      background: var(--off-white);
      position: relative;
      overflow: hidden;
      opacity: 0;
      transform: translateX(32px);
      transition: opacity .65s ease, transform .65s ease, border-color .35s, background .35s, box-shadow .35s;
      margin-left: -1px;
    }
    .wsb-card + .wsb-card {
      margin-top: -1px;
    }
    /* Mouse-tracking glow layer */
    .wsb-card::before {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: 0;
      background: radial-gradient(
        500px circle at var(--mx, 50%) var(--my, 50%),
        rgb(var(--accent-rgb) / .11) 0%,
        rgb(var(--indigo-rgb) / .06) 30%,
        transparent 70%
      );
      opacity: 0;
      transition: opacity .4s ease;
      pointer-events: none;
      z-index: 0;
    }
    /* Glossy top-edge shimmer */
    .wsb-card::after {
      content: '';
      position: absolute;
      top: 0; left: 0; right: 0;
      height: 1px;
      border-radius: 0;
      background: linear-gradient(
        90deg,
        transparent 0%,
        var(--violet-shimmer) 30%,
        rgb(var(--sky-rgb) / .5) 60%,
        transparent 100%
      );
      opacity: 0;
      transition: opacity .35s ease;
      pointer-events: none;
      z-index: 1;
    }
    .wsb-card > * { position: relative; z-index: 2; }
    .wsb-card.visible {
      opacity: 1;
      transform: translateX(0);
    }
    .wsb-card:hover {
      border-color: rgb(var(--accent-rgb) / .22);
      background: rgb(var(--white-rgb) / .92);
      backdrop-filter: blur(2px);
      z-index: 3;
    }
    .wsb-card:hover::before { opacity: 1; }
    .wsb-card:hover::after  { opacity: 1; }
    /* Per-element entrance animations */
    .wsb-num,
    .wsb-label,
    .wsb-industries-label,
    .wsb-industries li {
      opacity: 0;
      transform: translateY(10px);
      transition: opacity .5s ease, transform .5s ease;
    }
    .wsb-card.visible .wsb-num        { opacity: 1; transform: translateY(0); transition-delay: .1s; }
    .wsb-card.visible .wsb-label      { opacity: 1; transform: translateY(0); transition-delay: .2s; }
    .wsb-card.visible .wsb-industries-label { opacity: 1; transform: translateY(0); transition-delay: .1s; }
    .wsb-card.visible .wsb-industries li   { opacity: 1; transform: translateY(0); }
    .wsb-card.visible .wsb-industries li:nth-child(1) { transition-delay: .15s; }
    .wsb-card.visible .wsb-industries li:nth-child(2) { transition-delay: .22s; }
    .wsb-card.visible .wsb-industries li:nth-child(3) { transition-delay: .29s; }
    .wsb-card.visible .wsb-industries li:nth-child(4) { transition-delay: .36s; }
    .wsb-card.visible .wsb-industries li:nth-child(5) { transition-delay: .43s; }
    .wsb-num {
      font-family: var(--font-head);
      font-size: clamp(26px, 2.8vw, 34px);
      font-weight: 600;
      line-height: 1.15;
      color: var(--bg);
      margin-bottom: .4rem;
      letter-spacing: -.02em;
    }
    .wsb-label {
      color: var(--slate-600);
      line-height: 1.55;
      font-size: 14px;
    }
    .wsb-card--industries .wsb-industries-label {
      font-size: 11px;
      letter-spacing: .18em;
      text-transform: uppercase;
      color: var(--violet);
      margin-bottom: .8rem;
    }
    .wsb-industries {
      list-style: none;
      display: flex;
      flex-wrap: wrap;
      gap: .45rem;
    }
    .wsb-industries li {
      font-size: 12px;
      padding: .28rem .8rem;
      border: 1px solid rgb(var(--bg-rgb) / .12);
      border-radius: 20px;
      color: var(--slate-600);
      background: rgb(var(--accent-rgb) / .04);
      transition: border-color .2s, color .2s, opacity .5s ease, transform .5s ease;
    }
    .wsb-card:hover .wsb-industries li {
      border-color: rgb(var(--accent-rgb) / .25);
      color: var(--slate-700);
    }

    /* ═══════════════════════════════════════════════
       MORPH SECTION (sticky scroll — ecosystem)
    ═══════════════════════════════════════════════ */
    #morph-wrapper {
      position: relative;
      height: 780vh;
    }
    #morph-sticky {
      position: sticky;
      top: 0;
      height: 100vh;
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
      background: var(--bg);
    }
    #morph-sticky::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgb(var(--accent-rgb) / .03) 1px, transparent 1px),
        linear-gradient(90deg, rgb(var(--accent-rgb) / .03) 1px, transparent 1px);
      background-size: 60px 60px;
    }
    #morph-canvas {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      width: 600px; height: 600px;
      z-index: 2;
    }
    .morph-section-label {
      position: absolute;
      top: 2rem;
      left: 50%;
      z-index: 3;
      transform: translateX(-50%);
      font-family: var(--font-head);
      font-size: 15px;
      font-weight: 500;
      letter-spacing: .14em;
      text-transform: uppercase;
      color: var(--violet-lt);
      white-space: nowrap;
    }
    .morph-label-letter {
      display: inline-block;
      opacity: 0;
      transform: translateX(12px);
      transition: opacity .45s cubic-bezier(.22,1,.36,1), transform .45s cubic-bezier(.22,1,.36,1);
    }
    .morph-label-letter.visible {
      opacity: 1;
      transform: translateX(0);
    }
.pillar-panel {
      position: absolute;
      top: 50%;
      max-width: 340px;
      font-family: var(--font-body);
      z-index: 10;
      pointer-events: none;
      opacity: 0;
      transition: opacity .5s ease, transform .5s ease;
    }
    /* Pillars 1 & 3 — left side, aligned to container edge */
    #panel-1, #panel-3 {
      left: max(2rem, calc((100vw - var(--container-max)) / 2 + 2rem));
    }
    /* Pillar 2 — right side, aligned to container edge */
    #panel-2 {
      right: max(2rem, calc((100vw - var(--container-max)) / 2 + 2rem));
    }
    .pillar-panel.visible { opacity: 1; pointer-events: auto; }
    .pillar-number {
      font-family: var(--font-head);
      letter-spacing: .22em;
      text-transform: uppercase;
      color: var(--violet-lt);
      margin-bottom: 1rem;
    }
    .pillar-title {
      font-family: var(--font-head);
      font-size: 36px;
      font-weight: 500;
      line-height: 1.2;
      letter-spacing: -.02em;
      margin-bottom: 1rem;
      color: var(--white);
    }
    .pillar-desc {
      font-family: var(--font-body);
      font-size: 15px;
      color: var(--slate);
      line-height: 1.65;
      margin-bottom: 1.1rem;
    }
    .pillar-items {
      list-style: none;
      display: flex;
      flex-direction: column;
      gap: .5rem;
      margin-bottom: 1.4rem;;
    }
    .pillar-items li {
      display: flex;
      align-items: center;
      font-family: var(--font-body);
      font-size: 15px;
      font-weight: 300;
      gap: .65rem;
      color: var(--slate);
      line-height: 1.4;
    }
    .pillar-items li::before {
      content: '';
      width: 4px; height: 4px;
      border-radius: 50%;
      background: var(--violet-lt);
      flex-shrink: 0;
    }

    /* ═══════════════════════════════════════════════
       AI CONTEXT FRAMEWORK SECTION
    ═══════════════════════════════════════════════ */

    /* Sticky scroll zone — 400vh: flash fades first 40vh, content reveals 30-110vh, dwell */
    #ai-scroll-wrapper {
      position: relative;
      height: 400vh;
      background: var(--white);
    }
    #ai-scroll-sticky {
      position: sticky;
      top: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      overflow: hidden;
      background: var(--white);
      z-index: 165;
    }

    #ai-context {
      position: relative;
      padding: 0;
      background: transparent;
      overflow: hidden;
      width: 100%;
    }
    #ai-context::before {
      content: '';
      position: absolute;
      inset: 0;
      background:
        radial-gradient(ellipse 40% 50% at 20% 50%, rgb(var(--accent-rgb) / .07) 0%, transparent 60%),
        radial-gradient(ellipse 30% 40% at 80% 50%, rgb(var(--sky-rgb) / .05) 0%, transparent 60%);
      pointer-events: none;
      opacity: 0;
      transition: opacity 1.2s ease;
    }
    #ai-context:has(.ai-context-header.visible)::before {
      opacity: 1;
    }
    /* Faint grid-line texture, same recipe as #dr-features/#dr-research in
       assets/css/digital-resilience.css — a second pseudo-element since
       ::before above is already used for the accent glow. */
    #ai-context::after {
      content: '';
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgb(var(--accent-rgb) / .05) 1px, transparent 1px),
        linear-gradient(90deg, rgb(var(--accent-rgb) / .05) 1px, transparent 1px);
      background-size: 60px 60px;
      mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
      -webkit-mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
      pointer-events: none;
    }
    .ai-context-inner {
      position: relative;
      z-index: 2;
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 2rem;
    }
    .ai-context-header {
      text-align: center;
      max-width: 620px;
      margin: 0 auto 5rem;
    }

    /* ═══════════════════════════════════════════════
       AI CONTEXT — light mode overrides
       Section uses white background after morph flash
    ═══════════════════════════════════════════════ */
    .ai-context-header {
      opacity: 0;
      transform: translateY(24px);
      transition: opacity 0.8s ease, transform 0.8s ease;
    }
    .ai-context-header.visible {
      opacity: 1;
      transform: translateY(0);
    }
    /* Tighten header bottom margin inside the sticky viewport */
    #ai-scroll-wrapper .ai-context-header {
      margin-bottom: 2.5rem;
    }
    .ai-context-header h2 {
      font-family: var(--font-head);
      line-height: 1.2;
      letter-spacing: -.02em;
      margin-bottom: 1.2rem;
      color: var(--ink-900);
    }
    .ai-context-header p {
      color: var(--slate-600);
      line-height: 1.75;
    }
    .ai-context-subtitle {
      color: var(--violet);
      letter-spacing: .06em;
      font-style: italic;
      margin-top: .8rem;
    }
    .ai-pillars-grid {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 0;
    }
    .ai-pillar-card {
      padding: 2rem 1.6rem;
      border: 1px solid rgb(var(--bg-rgb) / .1);
      border-radius: 0;
      background: var(--white);
      position: relative;
      display: flex;
      flex-direction: column;
      height: auto;
      overflow: hidden;
      transition: border-color .3s, background .35s, box-shadow .35s, opacity .6s ease, transform .6s ease;
      opacity: 0;
      transform: translateX(40px);
    }
    .ai-pillar-card + .ai-pillar-card {
      margin-left: -1px;
    }
    /* Mouse-tracking glow layer, tinted with each card's own --card-accent (set inline in front-page.php) */
    .ai-pillar-card::before {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: 0;
      background: radial-gradient(
        500px circle at var(--mx, 50%) var(--my, 50%),
        color-mix(in srgb, var(--card-accent, var(--violet)) 18%, transparent) 0%,
        transparent 70%
      );
      opacity: 0;
      transition: opacity .4s ease;
      pointer-events: none;
      z-index: 0;
    }
    .ai-pillar-card > * { position: relative; z-index: 2; }
    .ai-pillar-card.visible {
      opacity: 1;
      transform: translateX(0);
    }
    .ai-pillar-card:hover {
      border-color: color-mix(in srgb, var(--card-accent, var(--violet)) 45%, transparent);
      background: rgb(var(--white-rgb) / .92);
      backdrop-filter: blur(2px);
      z-index: 3;
    }
    .ai-pillar-card:hover::before { opacity: 1; }
    .ai-pillar-icon {
      width: 44px; height: 44px;
      background: none;
      display: flex;
      align-items: center;
      justify-content: center;
      margin-bottom: 1.2rem;
    }
    .ai-pillar-icon img {
      width: 44px; height: 44px;
      display: block;
    }
    .ai-pillar-card h3 {
      font-family: var(--font-head);
      font-size: 22px;
      margin-top: 0;
      margin-bottom: .3rem;
      color: var(--bg);
    }
    .ai-pillar-card p {
      color: var(--slate-600);
      line-height: 1.55;
    }
    .ai-pillar-keyword {
      text-transform: uppercase;
      color: var(--violet);
      margin-bottom: 1rem;
      font-size: 0.8rem;
      opacity: .8;
    }
    .ai-pillar-card ul {
      list-style: none;
      display: flex;
      flex-direction: column;
      gap: .4rem;
      font-weight: 300;
      max-height: none;
      overflow: visible;
      opacity: 1;
    }
    .ai-pillar-card ul li {
      font-size: 0.8rem;
      color: var(--dark-menu);
      line-height: 1.45;
      padding-left: .9rem;
      position: relative;
    }
    .ai-pillar-card ul li::before {
      content: '';
      position: absolute;
      left: 0; top: .55em;
      width: 3px; height: 3px;
      border-radius: 50%;
      background: var(--slate);
    }
    .ai-context-cta {
      text-align: center;
      margin-top: 4rem;
      opacity: 0;
      transform: translateY(20px);
      will-change: transform;
      transition: opacity 0.8s ease, transform 0.6s ease;
    }
    .ai-context-cta.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .ai-context-cta p {
      color: var(--muted);
      margin-bottom: 1.2rem;
    }
    #ai-context .btn-base { color: var(--ink-900); }
    #ai-context .btn-base::before,
    #ai-context .btn-base::after { background: var(--ink-900); }

    /* ═══════════════════════════════════════════════
       WHY US
    ═══════════════════════════════════════════════ */
    #why-us {
      padding: 9rem 0;
      background: var(--white);
    }
    .why-us-inner {
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 2rem;
    }
    .why-us-header {
      max-width: 520px;
      margin-bottom: 4rem;
      opacity: 0;
      transform: translateY(24px);
      transition: opacity 0.8s ease, transform 0.8s ease;
    }
    .why-us-header.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .why-us-header h2 {
      font-family: var(--font-head);
      line-height: 1.2;
      letter-spacing: -.02em;
      margin-top: .8rem;
      color: var(--ink-900);
    }
    .differentiators {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 1.5rem;
    }
    .diff-card {
      padding: 2.2rem 2.4rem;
      border: 1px solid rgb(var(--bg-rgb) / .1);
      border-radius: 10px;
      background: var(--off-white);
      position: relative;
      overflow: hidden;
      transition: border-color .35s, background .35s, opacity .6s ease, transform .6s ease, box-shadow .35s;
      opacity: 0;
      transform: translateX(-16px);
    }
    /* Glow layer — radial spotlight that follows the cursor */
    .diff-card::before {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: 10px;
      background: radial-gradient(
        600px circle at var(--mx, 50%) var(--my, 50%),
        rgb(var(--accent-rgb) / .13) 0%,
        rgb(var(--indigo-rgb) / .07) 30%,
        transparent 70%
      );
      opacity: 0;
      transition: opacity .4s ease;
      pointer-events: none;
      z-index: 0;
    }
    /* Glossy top-edge shimmer — always present on hover */
    .diff-card::after {
      content: '';
      position: absolute;
      top: 0; left: 0; right: 0;
      height: 1px;
      border-radius: 10px 10px 0 0;
      background: linear-gradient(
        90deg,
        transparent 0%,
        var(--violet-shimmer) 30%,
        rgb(var(--sky-rgb) / .5) 60%,
        transparent 100%
      );
      opacity: 0;
      transition: opacity .35s ease;
      pointer-events: none;
      z-index: 1;
    }
    .diff-card > * { position: relative; z-index: 2; }
    .diff-card:nth-child(even) { transform: translateX(16px); }
    .diff-card.visible { opacity: 1; transform: translateX(0); }
    .diff-card:hover {
      border-color: rgb(var(--accent-rgb) / .22);
      background: rgb(var(--white-rgb) / .92);
      box-shadow:
        0 0 0 1px rgb(var(--accent-rgb) / .04),
        0 8px 32px rgb(var(--accent-rgb) / .06),
        0 2px 8px rgb(var(--sky-rgb) / .04),
        inset 0 1px 0 rgb(var(--white-rgb) / .6);
      backdrop-filter: blur(2px);
    }
    .diff-card:hover::before { opacity: 1; }
    .diff-card:hover::after  { opacity: 1; }
    .differentiators:has(.diff-card:hover) .diff-card:not(:hover) {
      opacity: 0.75;
    }
    .diff-icon {
      margin-bottom: 1rem;
      color: var(--violet);
    }
    .diff-icon img {
      width: 60px; height: 60px;
      display: block;
    }
    .diff-card h3 {
      font-family: var(--font-head);
      font-size: 23px;
      margin-bottom: .65rem;
      color: var(--ink-900);
    }
    .diff-card p {
      line-height: 1.7;
      color: var(--slate-600);
    }

    /* ═══════════════════════════════════════════════
       CLIENTS
    ═══════════════════════════════════════════════ */
    #clients {
      display: none; /* mobile-only fallback — shown below 1024px; desktop uses #wn-clients */
      padding: 7rem 0;
      background: var(--white);
      overflow: hidden;
    }
    .clients-stats {
      display: flex;
      justify-content: center;
      gap: 4rem;
      margin-bottom: 3rem;
      padding: 0 4rem;
    }
    .cs-item { text-align: center; }
    .cs-num {
      font-family: var(--font-head);
      color: var(--white);
      line-height: 1;
    }
    .cs-num span { color: var(--violet-lt); }
    .cs-label {
      letter-spacing: .14em;
      text-transform: uppercase;
      color: var(--muted);
      margin-top: .4rem;
    }
    .cs-divider { width: 1px; background: var(--border); align-self: stretch; }
    .logo-track-wrap {
      position: relative;
      overflow: hidden;
    }
    .logo-track-wrap::before,
    .logo-track-wrap::after {
      content: '';
      position: absolute;
      top: 0; bottom: 0;
      width: 140px;
      z-index: 2;
      pointer-events: none;
    }
    .logo-track-wrap::before { left: 0; background: linear-gradient(to right, var(--white), transparent); }
    .logo-track-wrap::after  { right:0; background: linear-gradient(to left,  var(--white), transparent); }
    .logo-track {
      display: flex;
      gap: 6rem;
      width: max-content;
      animation: scrollTrack 50s linear infinite;
    }
    .logo-track:hover { animation-play-state: paused; }
    .logo-item {
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      width: 150px;
      height: 100px;
      padding: 0.5rem;
      transition: opacity .3s;
    }
    .logo-item img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
    }

    /* ═══════════════════════════════════════════════
       RESEARCH & BENCHMARK SPOTLIGHT
    ═══════════════════════════════════════════════ */
    #research {
      position: relative;
      padding: 9rem 0;
      background: var(--bg);
      overflow: hidden;
    }
    #research::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgb(var(--accent-rgb) / .06) 1px, transparent 1px),
        linear-gradient(90deg, rgb(var(--accent-rgb) / .06) 1px, transparent 1px);
      background-size: 60px 60px;
      mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
      pointer-events: none;
    }
    .research-inner {
      position: relative;
      z-index: 1;
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 2rem;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 6rem;
      align-items: center;
    }
    .research-left h2 {
      font-family: var(--font-head);
      line-height: 1.2;
      letter-spacing: -.02em;
      margin-bottom: 1.2rem;
      margin-top: .8rem;
    }
    .research-left p {
      line-height: 1.8;
      color: var(--slate);
      margin-bottom: 2rem;
    }
    .research-actions { display: flex; flex-direction: column; gap: .8rem; }
    /* Slide-in animation on scroll */
    .research-left {
      opacity: 0;
      transform: translateX(-60px);
      transition: opacity 0.8s ease, transform 0.8s ease;
    }
    .research-right {
      position: relative;
      opacity: 0;
      transform: translateX(60px);
      transition: opacity 0.8s ease 0.15s, transform 0.8s ease 0.15s;
    }
    .research-inner.visible .research-left,
    .research-inner.visible .research-right {
      opacity: 1;
      transform: translateX(0);
    }
    .research-left > * {
      opacity: 0;
      transform: translateY(18px);
      transition: opacity .55s ease, transform .6s cubic-bezier(.22,1,.36,1);
    }
    .research-inner.visible .research-left > * {
      opacity: 1;
      transform: translateY(0);
    }
    .research-left > :nth-child(1) { transition-delay: .08s; }
    .research-left > :nth-child(2) { transition-delay: .18s; }
    .research-left > :nth-child(3) { transition-delay: .30s; }
    .research-left > :nth-child(4) { transition-delay: .42s; }
    @media (prefers-reduced-motion: reduce) {
      .research-left > * { opacity: 1; transform: none; transition: none; }
    }
    /* ── Platform Advisory: eyebrow + platform lists ── */
    .pa-kicker {
      font-family: var(--font-head);
      font-size: 12px;
      font-weight: 500;
      letter-spacing: .18em;
      text-transform: uppercase;
      color: var(--violet-lt) !important;
      margin-bottom: .9rem;
    }
    .pa-lists {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 2rem;
      border-top: 1px solid var(--border);
      padding-top: 1.6rem;
      margin-top: 2rem;
      max-width: 30rem;
    }
    .pa-list h3 {
      font-family: var(--font-head);
      font-weight: 500;
      font-size: 11px;
      letter-spacing: .1em;
      text-transform: uppercase;
      color: var(--slate);
      margin-bottom: .9rem;
    }
    .pa-list ul {
      list-style: none;
      display: flex;
      flex-direction: column;
      gap: .6rem;
    }
    .pa-list li {
      font-family: var(--font-body);
      font-size: .9rem;
      color: var(--white);
      display: flex;
      align-items: center;
      gap: .6rem;
    }
    .pa-dot {
      width: 6px; height: 6px; border-radius: 50%; flex: none;
    }
    .pa-list.enterprise .pa-dot { background: var(--sky); }
    .pa-list.ai .pa-dot { background: var(--violet-shimmer); }

    /* ── Platform Advisory: hub-and-spoke diagram ── */
    .pa-diagram {
      position: relative;
      width: 100%;
      aspect-ratio: 640 / 560;
      isolation: isolate;
    }
    #platform-network-canvas {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
    }
    .pa-hub-label {
      position: absolute;
      top: 51.8%;
      left: 51.6%;
      z-index: 1;
      display: flex;
      flex-direction: column;
      align-items: center;
      transform: translate(-50%, -50%);
      font-family: var(--font-head);
      font-weight: 600;
      font-size: 14px;
      letter-spacing: .08em;
      text-transform: uppercase;
      fill: var(--white);
      text-anchor: middle;
      opacity: 1;
      color: var(--white);
      line-height: 1.2;
      text-shadow: 0 0 14px rgb(var(--accent-rgb) / .55);
    }
    .pa-hub-label span,
    .pa-hub-label small { color: var(--violet-lt); font-size: 10px; font-weight: 500; letter-spacing: .13em; }
    .pa-network-node {
      position: absolute;
      z-index: 1;
      display: flex;
      align-items: center;
      gap: .75rem;
      color: var(--white);
      font-family: var(--font-body);
      font-size: 13px;
      line-height: 1;
      opacity: .92;
      white-space: nowrap;
      text-shadow: 0 1px 8px rgb(var(--bg-rgb) / .9);
      transform: translateY(-50%);
    }
    .pa-network-node::before {
      content: '';
      width: 8px;
      height: 8px;
      border-radius: 50%;
      box-shadow: 0 0 12px currentColor;
    }
    .pa-network-node--enterprise { color: var(--sky); }
    .pa-network-node--enterprise::before { background: var(--sky); }
    .pa-network-node--ai { color: var(--violet-shimmer); flex-direction: row-reverse; }
    .pa-network-node--ai::before { background: var(--violet-shimmer); }
    .pa-network-node span { color: var(--white); }
    .pa-network-node--service-now { top: 16.1%; left: 17.2%; }
    .pa-network-node--leanix { top: 51.8%; left: 10.9%; }
    .pa-network-node--neo4j { top: 87.5%; left: 17.2%; }
    .pa-network-node--mistral { top: 33.9%; right: 12.5%; }
    .pa-network-node--claude { top: 71.4%; right: 12.5%; }
    /* ── 3-D scene wrapper for stacked benchmark cards ── */
    .bp-scene {
      position: relative;
      perspective: 1000px;
      perspective-origin: center center;
      /* bottom + left padding give the back card room to peek out */
      padding: 0 0 36px 28px;
      cursor: default;
    }

    .benchmark-preview {
      border: 1px solid rgb(var(--accent-rgb) / .2);
      border-radius: 12px;
      background: rgba(7,14,29,.85);
      padding: 2rem;
      backdrop-filter: blur(8px);
    }

    /* Front card — JS drives transform on hover; CSS sets resting tilt */
    .benchmark-preview--front {
      position: relative;
      z-index: 2;
      transform: rotateX(4deg) rotateY(-10deg);
      transform-origin: center center;
      will-change: transform;
    }

    /* Back card — clearly a second chart, not a shadow */
    .benchmark-preview--back {
      position: absolute;
      top: 26px;         /* JS overrides this on hover via inline style */
      left: 0;
      width: calc(100% - 28px);
      z-index: 1;
      opacity: 0.72;
      transform: rotateX(4deg) rotateY(-10deg);
      transform-origin: center center;
      pointer-events: none;
      /* visually distinct: dark navy + cyan border */
      background: rgba(4,10,26,.9);
      border-color: rgb(var(--sky-rgb) / .28);
      will-change: transform;
    }

    /* Back card uses a cyan→green gradient for bars */
    .benchmark-preview--back .bp-bar {
      background: linear-gradient(to right, var(--sky), var(--green));
    }

    /* Back card title in cyan, badge in violet */
    .benchmark-preview--back .bp-title { color: var(--sky); }
    .benchmark-preview--back .bp-badge {
      border-color: rgb(var(--accent-rgb) / .35);
      color: var(--violet-lt);
    }

    /* Back card rows appear instantly — no stagger */
    .benchmark-preview--back .bp-header,
    .benchmark-preview--back .bp-row {
      opacity: 0;
      transform: none;
      transition: opacity 0.3s ease;
    }
    .research-inner.visible .benchmark-preview--back .bp-header,
    .research-inner.visible .benchmark-preview--back .bp-row {
      opacity: 1;
      transform: none;
      transition-delay: 0.45s;
    }
    .bp-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 1.5rem;
      padding-bottom: 1rem;
      border-bottom: 1px solid var(--border);
    }
    .bp-title {
      font-family: var(--font-head);
      letter-spacing: .12em;
      text-transform: uppercase;
      color: var(--violet-lt);
    }
    .bp-badge {
      letter-spacing: .1em;
      text-transform: uppercase;
      padding: .25rem .6rem;
      border: 1px solid rgb(var(--green-rgb) / .25);
      border-radius: 20px;
      color: var(--green);
    }
    .bp-row {
      display: flex;
      font-size: 0.8rem;
      align-items: center;
      justify-content: space-between;
      padding: .7rem 0;
      border-bottom: 1px solid rgb(var(--white-rgb) / .03);
    }
    .bp-row:last-child { border-bottom: none; }
    .bp-metric { color: var(--slate); }
    .bp-bar-wrap { flex: 1; margin: 0 1rem; height: 4px; background: rgb(var(--white-rgb) / .05); border-radius: 2px; }
    .bp-bar {
      height: 100%;
      border-radius: 2px;
      background: linear-gradient(to right, var(--violet), var(--cyan));
      transform: scaleX(0);
      transform-origin: left center;
      transition: transform 0.7s cubic-bezier(.25,.46,.45,.94);
    }
    .bp-val { font-family: var(--font-head); color: var(--white); }

    /* ── Benchmark preview: staggered row reveal + bar fill ── */
    .bp-header,
    .bp-row {
      opacity: 0;
      transform: translateY(8px);
      transition: opacity 0.4s ease, transform 0.4s ease;
    }
    .research-inner.visible .bp-header {
      opacity: 1; transform: translateY(0); transition-delay: 0.45s;
    }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(2) { opacity: 1; transform: translateY(0); transition-delay: 0.6s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(3) { opacity: 1; transform: translateY(0); transition-delay: 0.75s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(4) { opacity: 1; transform: translateY(0); transition-delay: 0.9s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(5) { opacity: 1; transform: translateY(0); transition-delay: 1.05s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(6) { opacity: 1; transform: translateY(0); transition-delay: 1.2s; }
    /* Bar fills after the row fades in */
    .research-inner.visible .benchmark-preview .bp-row:nth-child(2) .bp-bar { transform: scaleX(1); transition-delay: 0.85s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(3) .bp-bar { transform: scaleX(1); transition-delay: 1.0s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(4) .bp-bar { transform: scaleX(1); transition-delay: 1.15s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(5) .bp-bar { transform: scaleX(1); transition-delay: 1.3s; }
    .research-inner.visible .benchmark-preview .bp-row:nth-child(6) .bp-bar { transform: scaleX(1); transition-delay: 1.45s; }

    /* ═══════════════════════════════════════════════
       EUROPE FORWARD
    ═══════════════════════════════════════════════ */
    /* ═══════════════════════════════════════════════
       EUROPE FORWARD
       Sticky 400vh scroll zone — left slides in, right cards stack
    ═══════════════════════════════════════════════ */
    #europe-scroll-wrapper {
      position: relative;
      height: 530vh;
      background: var(--bg);
    }
    #europe-scroll-sticky {
      position: sticky;
      top: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      overflow: hidden;
    }
    #europe {
      position: relative;
      width: 100%;
      background: transparent;
    }
    #europe::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgb(var(--accent-rgb) / .06) 1px, transparent 1px),
        linear-gradient(90deg, rgb(var(--accent-rgb) / .06) 1px, transparent 1px);
      background-size: 60px 60px;
      mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
      pointer-events: none;
      opacity: 0;
      transition: opacity 1s ease;
    }
    #europe::after {
      content: '';
      position: absolute;
      inset: 0;
      pointer-events: none;
      background-image:
        radial-gradient(ellipse 35% 50% at 75% 50%, rgb(var(--accent-rgb) / .15) 0%, transparent 65%);
      opacity: 0;
      transition: opacity 1s ease;
    }
    #europe:has(.europe-item.visible)::before,
    #europe:has(.europe-item.visible)::after {
      opacity: 1;
    }
    .europe-inner {
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 2rem;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 6rem;
      align-items: center;
    }
    .europe-left {
      opacity: 0;
      transform: translateX(-60px);
      transition: opacity 0.8s ease, transform 0.8s ease;
    }
    .europe-inner.visible .europe-left {
      opacity: 1;
      transform: translateX(0);
    }
    .europe-left h2 {
      font-family: var(--font-head);
      line-height: 1.2;
      letter-spacing: -.02em;
      margin-bottom: 1.2rem;
      margin-top: .8rem;
    }
    .europe-left p {
      line-height: 1.8;
      color: var(--slate);
    }
    .europe-right {
      position: relative;
    }
    /* Stack container — height set by JS to the tallest card */
    .europe-stack {
      position: relative;
    }
    .europe-item {
      position: absolute;
      top: 0; left: 0; right: 0;
      display: flex;
      gap: 1.4rem;
      align-items: flex-start;
      padding: 3rem 2rem;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: rgba(5,13,26,.85);
      opacity: 0;
      transform: translateY(50px);
      transition: opacity 0.7s ease, transform 0.7s ease;
    }
    /* Item 1 slides in from the left — matching europe-left direction */
    .europe-item:nth-child(1) { z-index: 1; top: 0;    transform: translate(-60px, 0); }
    .europe-item:nth-child(2) { z-index: 2; top: 24px; }
    .europe-item:nth-child(3) { z-index: 3; top: 48px; }
    .europe-item.visible {
      opacity: 1;
      transform: translate(0, 0);
    }
    /* Stacked (covered) cards scale down slightly so the card behind peeks out */
    .europe-item.stacked {
      transform: scale(0.97) translate(0, 0);
      transform-origin: top center;
      transition: opacity 0.7s ease, transform 0.7s ease;
    }
    /* Top (most recently revealed) card gets an animated gradient border via ::after */
    .europe-item.top-card { border-color: transparent; position: relative; }
    .europe-item.top-card::after {
      content: '';
      position: absolute;
      inset: 0;
      padding: 2px;
      border-radius: 8px;
      background: conic-gradient(from var(--angle), var(--violet), var(--cyan), rgb(var(--sky-rgb) / .2), var(--violet));
      -webkit-mask: linear-gradient(var(--white) 0 0) content-box, linear-gradient(var(--white) 0 0);
      -webkit-mask-composite: xor;
      mask: linear-gradient(var(--white) 0 0) content-box, linear-gradient(var(--white) 0 0);
      mask-composite: exclude;
      animation: rotateBorder 4s linear infinite;
      pointer-events: none;
    }
    .europe-item:hover { border-color: rgb(var(--sky-rgb) / .2); }
    .europe-item-icon {
      width: 70px; height: 70px;
      border-radius: 8px;
      display: flex; align-items: center; justify-content: center;
      flex-shrink: 0;
    }
    .europe-item-icon img { width: 70px; height: 70px; display: block; }
    .europe-item h4 {
      font-family: var(--font-head);
      font-size: 20px;
      margin-bottom: .40rem;
      color: var(--white);
    }
    .europe-item p { color: var(--slate); line-height: 1.55; }

    /* ═══════════════════════════════════════════════
       CTA SECTION
       #cta / .cta-* is now defined once, sitewide, in
       assets/css/global-styles.css, since it's reused as-is by
       templates/digital-resilience.php (and available via the
       [einar_cta] shortcode). The reveal + globe canvas JS that drive
       it now live in assets/js/global-scripts.js for the same reason.
    ═══════════════════════════════════════════════ */

    /* FOOTER styles, and the OS prefers-color-scheme dark-mode overrides for
       #main-nav, now live in assets/css/site-header-footer.css (loaded
       site-wide via header.php/footer.php). */

    /* ═══════════════════════════════════════════════
       KEYFRAMES
    ═══════════════════════════════════════════════ */
    @keyframes fadeUp {
      to { opacity: 1; transform: translateY(0); }
    }
    @keyframes scrollPulse {
      0%,100% { opacity:.4; } 50% { opacity:1; }
    }
    @keyframes floatY {
      0%,100% { transform:translateY(0); } 50% { transform:translateY(-10px); }
    }
    @keyframes scrollTrack {
      from { transform: translateX(0); }
      to   { transform: translateX(-50%); }
    }
    /* Rotating gradient border for europe top card */
    @property --angle {
      syntax: '<angle>';
      initial-value: 0deg;
      inherits: false;
    }
    @keyframes rotateBorder {
      to { --angle: 360deg; }
    }

    /* ═══════════════════════════════════════════════
       RESPONSIVE (mobile/tablet)
    ═══════════════════════════════════════════════ */
    @media (max-width: 1024px) {

      /* 1. Hero — smaller title so it doesn't overwhelm the mobile viewport,
         plus centering the "Scroll" hint text (letter-spacing adds trailing
         space that skews optical centering). Needs the "#hero #hero-title"
         double-ID selector: the base "#hero h1 { font-size: 48px; }" rule
         (ID + element) outranks a plain "#hero-title" (ID only), so a same-
         specificity override here would silently lose regardless of order. */
      #hero #hero-title { font-size: 34px; }
      .scroll-hint span { margin-right: -0.18em; }

      /* 2. Why Now — one full-width block (like hero) instead of a half-width
         column; all 5 value items visible (was overflowing off-screen at 5 cols) */
      #why-now-sticky { padding: 0 1.5rem; }
      .why-now-left { width: 100%; max-width: 100%; padding-left: 0; padding-right: 0; }
      #wn-imperative { align-items: flex-start; padding: 5.5rem 1.25rem 2rem; overflow-y: auto; }
      .wn-value h3 { margin-bottom: 1.75rem; }
      .wn-value-list { grid-template-columns: repeat(2, 1fr); gap: 0; }
      .wn-value-item { padding: 1rem .9rem; gap: .6rem; margin-left: 0; }
      .wn-value-item:nth-child(2n) { margin-left: -1px; }
      .wn-value-item:nth-child(n+3) { margin-top: -1px; }
      .wn-value-item:last-child { grid-column: 1 / -1; max-width: calc(50% - .375rem); margin-left: auto; margin-right: auto; }
      .wn-value-icon { width: 42px; height: 42px; }
      .wn-value-keyword { font-size: 11px; }
      .wn-value-item p { font-size: 12.5px; }

      /* 4. Methodology — no pin/scroll-jack on mobile: plain document flow
         (title, then cards stacked 1/row, then the next section), header and
         cards always visible — no JS-driven reveal to fight the layout. */
      #methodology-scroll-wrapper { height: auto; }
      /* position: relative (not static) so the base rule's z-index: 10 still
         applies — keeps this white block painting above the fixed, z-index: 1
         #globe-canvas instead of the moving globe showing through on top of it
         the instant this section reaches the top of the viewport. */
      #methodology-scroll-sticky { position: relative; height: auto; display: block; overflow: visible; padding: 5rem 0 4rem; }
      .methodology-inner { padding: 0 1.5rem; }
      .methodology-header { opacity: 1; transform: none; }
      .methodology-grid { grid-template-columns: 1fr; gap: 0; }
      .method-card { padding: 1.6rem 1.4rem; opacity: 1; transform: none; }
      .method-card + .method-card { margin-left: 0; margin-top: -1px; }

      /* 5. Who We Are — same treatment: no pin, plain flow, always visible.
         Grid collapses to one column: text box, then the 3 stat cards
         stacked below it (still seamlessly touching each other via the
         existing -1px overlap, just with a gap above the first one). */
      #who-scroll-wrapper { height: auto; }
      #who-scroll-sticky { position: static; height: auto; display: block; overflow: visible; padding: 5rem 0; }
      #who-we-are {
        width: min(100% - 3rem, var(--container-max));
        grid-template-columns: 1fr;
        grid-template-rows: auto;
      }
      .who-left, #who-we-are > .wsb-card { grid-column: 1; grid-row: auto; }
      .who-left { width: 100%; max-width: 100%; padding: 2rem; opacity: 1; transform: none; }
      .who-stat, .wsb-card { opacity: 1; transform: none; margin-left: 0; }
      /* The base rules place each card with #who-we-are > .wsb-card:nth-child(n)
         (grid-column: 3), which outranks the plain ".wsb-card" reset above on
         specificity alone regardless of source order — so the 3 stat cards kept
         sitting in their own column next to who-left instead of stacking below
         it. Re-declare the same nth-child selectors here to actually win. */
      #who-we-are > .wsb-card:nth-child(2),
      #who-we-are > .wsb-card:nth-child(3),
      #who-we-are > .wsb-card:nth-child(4) { grid-column: 1; grid-row: auto; }
      .wsb-num, .wsb-label, .wsb-industries-label, .wsb-industries li { opacity: 1; transform: none; }

      /* 6. Morph — globe upper viewport, pillar copy lower viewport */
      #morph-canvas { top: 26%; width: 260px; height: 260px; transition: top .2s ease-out, width .2s ease-out, height .2s ease-out; }
      .pillar-panel { top: auto; bottom: 5%; left: 1.25rem; right: 1.25rem; max-width: none; text-align: left; }
      #panel-1, #panel-2, #panel-3 { left: 1.25rem; right: 1.25rem; }

      /* 7. AI Context — same treatment: no pin, plain flow, always visible.
         Header, then cards stacked 1/row. */
      #ai-scroll-wrapper { height: auto; }
      /* Top padding matches the morph-flash fade-out distance (~1.15 viewport heights,
         see homepage.js) so the header is freshly revealed as the flash clears instead
         of already scrolled past while still hidden behind it. */
      #ai-scroll-sticky { position: static; height: auto; display: block; overflow: visible; padding: 110vh 0 4rem; z-index: auto; }
      .ai-context-inner { padding: 0 1.5rem; }
      .ai-context-header, .ai-pillar-card, .ai-context-cta { opacity: 1; transform: none; }
      .ai-pillars-grid { grid-template-columns: 1fr; gap: 0; }
      .ai-pillar-card + .ai-pillar-card { margin-left: 0; margin-top: -1px; }

      /* 8. Research — animated diagram on top, content below, both full width */
      .research-inner { grid-template-columns: 1fr; gap: 3rem; }
      .research-right { order: -1; }
      .pa-lists { max-width: none; }
    }
/* ---------------------------------------------------------------
   Source: assets/css/blog.css
--------------------------------------------------------------- */
/* ===================================================================
   Einar Blog Styles  v1.0.0
   Loaded on the blog index, category/tag/date archives, single posts,
   and search results (see functions.php for the exact conditions).

   Cards here are intentionally sharp-edged (border-radius: 0) — a
   deliberate contrast with the rounded 10px cards used on the homepage
   (assets/css/homepage.css), keeping the blog reading experience plain
   and typographic instead of decorative.
   =================================================================== */

/* -----------------------------------------------------------------------
   Archive header

   Full-bleed: rendered directly inside #content, OUTSIDE .container (see
   archive.php/index.php/search.php), so the background spans the full
   viewport width while an inner .container keeps the title/description
   readable. The animated grid/gradient background itself is the shared
   .ep-hero-grid utility class (assets/css/global-styles.css, loaded on
   every page — also usable directly in Elementor, see its own comment
   for how) — markup is <header class="page-header ep-hero-grid">, so
   this file only needs the blog-specific bits: the margin below it, and
   the title/description text styling.
----------------------------------------------------------------------- */
.page-header {
	margin-bottom: 3rem;
}

.page-title {
	font-family: var(--font-head);
	font-size: clamp(2.25rem, 5vw, 3rem);
	letter-spacing: -0.01em;
	color: var(--ink-900);
}

.archive-description {
	max-width: 60ch;
	margin: 1rem auto 0;
	color: var(--slate-600);
}

/* -----------------------------------------------------------------------
   Post list (archive / search results)

   Single column, every card the same height (aspect-ratio on a
   fixed-width column gives equal height for free — no explicit height
   needed). The image fills the card and gets cropped to fit; the title
   is pinned over its bottom edge with a dark gradient for legibility.
   The excerpt (clamped to 4 lines, ellipsis) plus an "Expand" link stay
   hidden until hover, then fade/slide in below the title.
----------------------------------------------------------------------- */
.post-list {
	display: flex;
	flex-direction: column;
	gap: 2rem;
}

.post-list article {
	position: relative;
	overflow: hidden;
	aspect-ratio: 16 / 9;
	background: var(--ink-900);
}

.post-list .post-thumbnail {
	position: absolute;
	inset: 0;
	margin: 0;
}

.post-list .post-thumbnail img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s ease;
}

.post-list article:hover .post-thumbnail img {
	transform: scale(1.06);
}

.post-list article::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(to top, rgba(8, 15, 30, 0.92) 0%, rgba(8, 15, 30, 0.5) 45%, rgba(8, 15, 30, 0) 70%);
	transition: background 0.35s ease;
	pointer-events: none;
}

.post-list article:hover::before {
	background: linear-gradient(to top, rgba(8, 15, 30, 0.96) 0%, rgba(8, 15, 30, 0.8) 60%, rgba(8, 15, 30, 0.2) 100%);
}

.post-list .card-body {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 2;
	padding: 1.75rem;
}

.post-list .entry-header {
	margin: 0;
}

.post-list .entry-title {
	font-size: clamp(2.25rem, 5vw, 3rem);
	margin-bottom: 0;
}

.post-list .entry-title a {
	color: #fff;
}

.post-list .entry-title a:hover {
	color: var(--violet-lt);
}

.post-list .entry-meta {
	margin-top: 0.4rem;
	color: rgba(255, 255, 255, 0.7);
}

.post-list .entry-content,
.post-list .entry-summary {
	max-height: 0;
	margin-top: 0;
	opacity: 0;
	overflow: hidden;
	color: rgba(255, 255, 255, 0.85);
	font-size: 0.9rem;
	line-height: 1.65;
	transition: max-height 0.45s ease, opacity 0.3s ease, margin-top 0.45s ease;
}

.post-list article:hover .entry-content,
.post-list article:hover .entry-summary {
	max-height: 220px;
	margin-top: 0.9rem;
	opacity: 1;
}

/* Clamped to 4 lines so long excerpts end in "…" instead of just being
   cut off blank — the max-height above is a transition ceiling, not a
   crop; this is what actually truncates the text. */
.post-list .entry-content p,
.post-list .entry-summary p {
	margin: 0 0 0.75rem;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 4;
	overflow: hidden;
}

.post-list .card-expand {
	color: #fff;
}

.post-list .entry-footer {
	display: none;
}

/* -----------------------------------------------------------------------
   Entry title & meta (shared by archive cards and single post)
----------------------------------------------------------------------- */
.entry-title {
	font-family: var(--font-head);
	font-weight: 500;
	letter-spacing: -0.02em;
}

.entry-title a {
	color: var(--ink-900);
}

.entry-title a:hover {
	color: var(--violet);
}

.entry-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	font-size: 0.8rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--muted);
}

.entry-meta .byline::before {
	content: '\2022';
	margin-right: 0.5rem;
}

/* -----------------------------------------------------------------------
   Entry footer (categories, tags, comments link)
----------------------------------------------------------------------- */
.entry-footer {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	font-size: 0.75rem;
}

/* .tags-links a: shared "border" pill mode (global-styles.css). No
   separator between tags in the markup (see einar_partners_entry_footer()),
   so the gap here is what actually spaces the individual pills apart. */
.entry-footer .tags-links {
	display: inline-flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

/* Comments link keeps its own plain hairline-border chip, outside the
   shared pill component. */
.entry-footer .comments-link a {
	display: inline-block;
	padding: 0.25rem 0.6rem;
	border: 1px solid var(--border3);
	border-radius: var(--radius-xs);
	color: var(--slate-600);
	text-transform: uppercase;
	letter-spacing: 0.05em;
	transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}

.entry-footer .comments-link a:hover {
	border-color: var(--violet);
	color: var(--violet);
}

/* -----------------------------------------------------------------------
   Pagination (the_posts_navigation / the_post_navigation)
----------------------------------------------------------------------- */
.posts-navigation .nav-links,
.post-navigation .nav-links {
	display: flex;
	justify-content: space-between;
	gap: 1.5rem;
	margin-top: 3rem;
	padding-top: 1.5rem;
}

/* Breathing room before the site footer — otherwise post-navigation
   (the last element on a single post) butts straight up against it. */
body.single .post-navigation .nav-links {
	margin-bottom: 4rem;
}

.posts-navigation a,
.post-navigation a {
	font-family: var(--font-head);
	font-size: 0.9rem;
	color: var(--ink-900);
}

.posts-navigation a:hover,
.post-navigation a:hover {
	color: var(--violet);
}

.post-navigation .nav-subtitle {
	display: block;
	font-family: var(--font-body);
	font-size: 0.7rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--muted);
}

.post-navigation .nav-next {
	text-align: right;
}

/* -----------------------------------------------------------------------
   Sidebar widgets
----------------------------------------------------------------------- */
.widget-area .widget {
	padding: 1.5rem;
	border: 1px solid var(--border3);
	border-radius: 0;
}

.widget-title {
	padding-bottom: 0.75rem;
	margin-bottom: 1rem;
	font-family: var(--font-head);
	font-size: 1rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-900);
	border-bottom: 2px solid var(--ink-900);
}

.widget ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.widget ul li {
	padding: 0.5rem 0;
	border-bottom: 1px solid var(--border3);
}

.widget ul li:last-child {
	border-bottom: 0;
}

.widget a {
	color: var(--slate-600);
}

.widget a:hover {
	color: var(--violet);
}

.social-links-widget p {
	font-size: var(--fs-small);
}

/* "Social Links" widget (inc/class-social-links-widget.php) — its own
   heading style: same font as .widget-title (var(--font-head), inherited
   from the base h3 rule in style.css) but not uppercase and with a much
   lighter border, so it reads as a softer, more self-contained little
   CTA card instead of a plain list-style widget. */
.social-links-widget-title {
	font-size: 1.15rem;
}

/* Buttons stack instead of sitting side by side (the sidebar column is
   too narrow at 320px for "Follow on LinkedIn"/"Subscribe on YouTube" to
   sit next to each other without wrapping mid-label), and stretch to
   fill the column so both end up the same width — a flex item's own
   "inline-flex" (below) is blockified to "flex" once it's a flex item,
   so align-items: stretch here does size it to the full column width. */
.social-links-widget-actions {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 0.75rem;
}

/* Shape/color: shared "border" pill mode (global-styles.css) — the exact
   same outline pill look as .blog-cat-pill (transparent fill, ink-900
   text/border at rest, border+text darken on hover), including its
   font-family override. Centered content + the equal-width stretch above
   are this widget's own addition on top, since blog-cat-pill's usage
   elsewhere (inline filter chips) never needed either. Colors are
   re-asserted here (not just inherited from the shared component) since
   ".widget a"/".widget a:hover" above would otherwise win the cascade —
   same specificity tier as the shared component's plain ".social-share-btn"
   class there, but with an extra type selector each; the 2-class
   selectors here outrank both. */
.social-links-widget-actions .social-share-btn {
	font-family: var(--font-head);
	justify-content: space-between;
	color: var(--ink-900);
}
.social-links-widget-actions .social-share-btn:hover {
	color: var(--ink-900);
}

/* -----------------------------------------------------------------------
   Single post
----------------------------------------------------------------------- */
/* Sidebar on the left, content on the right — flips the base
   .has-sidebar .content-sidebar column order (content 1fr, sidebar
   320px) from style.css via `order`. Single posts are the only remaining
   template with both a sidebar and this left/right flip — archive.php/
   index.php dropped their sidebar entirely (see template-parts/post-grid.php). */
body.single.has-sidebar .content-sidebar {
	grid-template-columns: 320px 1fr;
}

body.single #primary {
	order: 2;
}

body.single #secondary {
	order: 1;
}

/* Stacked on mobile: content still reads before the sidebar. Also resets
   the 320px+1fr column tracks back to a single column — otherwise this
   rule's higher specificity (extra body.single.has-sidebar classes) would
   keep beating style.css's own mobile breakpoint, squeezing both columns
   into one narrow viewport instead of stacking them. */
@media (max-width: 782px) {
	body.single.has-sidebar .content-sidebar {
		grid-template-columns: 1fr;
	}

	body.single #primary {
		order: 1;
	}

	body.single #secondary {
		order: 2;
	}
}

body.single article {
	padding: 0;
	border: 0;
}

body.single article:hover {
	box-shadow: none;
	transform: none;
}

body.single .entry-title {
	font-size: clamp(2rem, 4vw, 3rem);
	margin-bottom: 0;
}

/* -----------------------------------------------------------------------
   Post header — title + reading-time pill, then the featured image with
   a dark overlay and author/date meta pinned to its bottom edge.
   Rendered full-width above .content-sidebar (template-parts/post-header.php,
   see single.php) so the sidebar grid below starts level with
   entry-header/entry-content instead of the image.
----------------------------------------------------------------------- */
.post-header {
	margin: 2rem 0 2.5rem;
}

.post-header-top {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	/* Was margin-bottom, spacing this row from the featured image that
	   used to follow it — now the image comes first (see
	   template-parts/post-header.php), so the gap moves above instead. */
	margin-top: 2.5rem;
}

/* Groups the reading-time pill and the "Share" pill together as one
   right-hand unit, so .post-header-top's space-between still puts the
   title on the left and this whole group on the right regardless of how
   many pills it holds. */
.post-header-pills {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	flex-shrink: 0;
}

/* Shape/color: shared "border" pill mode (global-styles.css). */
.reading-time-pill {
	flex-shrink: 0;
}

/* "Share" pill — copies the post URL to the clipboard on click (see the
   ".share-pill" block in assets/js/global-scripts.js). Shape: shared
   pill component (global-styles.css), "dark" color mode (solid ink-900
   fill, white text/icon — same mode as .dr-hero-pill) so it stands out
   from the plain "border" mode .reading-time-pill sits next to. It's a
   real <button> (needs the click handler + keyboard/touch support), so
   it also needs the native button chrome reset — same recipe as
   .sl-spotlight-tag in service-landing.css. */
.share-pill {
	appearance: none;
	-webkit-appearance: none;
	font-family: inherit;
	cursor: pointer;
	position: relative;
	flex-shrink: 0;
}

/* Two icon states swapped via the .is-copied class (added on click, then
   removed again after a short delay — see global-scripts.js): the
   share-network glyph at rest, a checkmark once the URL has been copied. */
.share-pill-icon {
	display: inline-flex;
}
.share-pill-icon--done {
	display: none;
}
.share-pill.is-copied .share-pill-icon--default {
	display: none;
}
.share-pill.is-copied .share-pill-icon--done {
	display: inline-flex;
}

/* Tooltip — hidden until .is-copied is toggled on click. Text stays the
   same throughout ("Copy url to clipboard"); only the icon above and this
   tooltip's visibility change, together, to confirm the copy happened.
   text-transform/letter-spacing are reset since both are inherited from
   the pill's own (uppercase, tracked) type styles otherwise. */
.share-pill-tooltip {
	position: absolute;
	bottom: calc(100% + 8px);
	left: 50%;
	transform: translateX(-50%) translateY(4px);
	white-space: nowrap;
	padding: 0.4rem 0.75rem;
	border-radius: var(--radius-xs);
	background: var(--ink-900);
	color: #fff;
	font-size: 0.7rem;
	font-weight: 500;
	text-transform: none;
	letter-spacing: normal;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.2s ease, transform 0.2s ease;
	z-index: 2;
}
.share-pill-tooltip::after {
	content: "";
	position: absolute;
	top: 100%;
	left: 50%;
	transform: translateX(-50%);
	border: 5px solid transparent;
	border-top-color: var(--ink-900);
}
.share-pill.is-copied .share-pill-tooltip {
	opacity: 1;
	transform: translateX(-50%) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
	.share-pill-tooltip {
		transition: opacity 0.01s linear;
	}
}

.post-header-media {
	position: relative;
	overflow: hidden;
}

.post-header-media img {
	width: 100%;
	aspect-ratio: 21 / 9;
	object-fit: cover;
}

.post-header-media-overlay {
	position: absolute;
	inset: 0;
	background: linear-gradient(to top, rgba(8, 15, 30, 0.85) 0%, rgba(8, 15, 30, 0.25) 45%, rgba(8, 15, 30, 0) 70%);
	pointer-events: none;
}

.post-header-media-meta {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 1;
	display: flex;
	align-items: flex-end;
	justify-content: flex-start;
	gap: 1.5rem;
	padding: 1.75rem;
}

.post-header-meta-block {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
}

.post-header-meta-label {
	font-size: 0.7rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: rgba(255, 255, 255, 0.7);
}

.post-header-meta-value {
	font-family: var(--font-head);
	font-size: 1rem;
	color: #fff;
}

body.single .entry-content {
	max-width: 75ch;
	line-height: 1.8;
	color: var(--slate-700);
}

body.single .entry-content ul,
body.single .entry-content ol {
	padding-left: 1.25em;
}

body.single .entry-content h2,
body.single .entry-content h3,
body.single .entry-content h4 {
	margin-top: 1.75em;
	font-family: var(--font-head);
	color: var(--ink-900);
}

/* Pinned explicitly instead of inheriting from style.css's global h2/h3/h4
   rule: homepage.css has its own unscoped h1-h6 font-size rules, and since
   it's concatenated later into style.css (see bin/build-css.js), it wins
   the cascade here too (same specificity, later source order) unless
   overridden directly. */
body.single .entry-content h2 { font-size: var(--fs-h2); }
body.single .entry-content h3 { font-size: var(--fs-h3); }
body.single .entry-content h4 { font-size: var(--fs-h4); }

body.single .entry-content h2.wp-block-heading {
	padding-bottom: 28px;
	border-bottom: 1px solid var(--border3);
}

body.single .entry-content blockquote {
	margin: 2rem 0;
	padding-left: 1.5rem;
	border-left: 3px solid var(--violet);
	color: var(--slate-600);
	font-style: italic;
}

body.single .entry-content a {
	color: var(--link);
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

body.single .entry-content img {
	margin: 2rem 0;
}

body.single .entry-footer {
	margin-top: 2rem;
	padding: 1.5rem 1.5rem 1.5rem 0;
	background: var(--block-bg);
	box-shadow: var(--block-shadow);
}

/* -----------------------------------------------------------------------
   Comments
----------------------------------------------------------------------- */
.comments-area {
	max-width: 75ch;
	margin-top: 3rem;
	padding-top: 2rem;
	border-top: 1px solid var(--border3);
}

.comments-title,
#reply-title {
	font-family: var(--font-head);
	font-size: 1.3rem;
	color: var(--ink-900);
}

.comment-list,
.comment-list .children {
	list-style: none;
	margin: 0;
	padding: 0;
}

.comment-list .children {
	margin-left: 2rem;
}

.comment-list > li {
	padding: 1.5rem 0;
	border-bottom: 1px solid var(--border3);
}

.comment-author .fn {
	font-family: var(--font-head);
	font-style: normal;
	color: var(--ink-900);
}

.comment-metadata {
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--muted);
}

.comment-content {
	margin-top: 0.5rem;
	color: var(--slate-600);
}

.comment-reply-link {
	font-size: 0.8rem;
	color: var(--violet);
}

.comment-form input[type="text"],
.comment-form input[type="email"],
.comment-form input[type="url"],
.comment-form textarea {
	width: 100%;
	padding: 0.75rem;
	border: 1px solid var(--border3);
	border-radius: 0;
	font-family: var(--font-body);
}

.comment-form textarea {
	resize: vertical;
}

.comment-form .form-submit .btn-underline {
	color: var(--ink-900);
}

/* -----------------------------------------------------------------------
   Blog listing pages — the posts page (index.php, the "Posts page" set in
   Settings > Reading) and every category/tag/date/author archive
   (archive.php). Both get a shared `.blog-layout` body class (added in
   inc/template-functions.php) so this one set of overrides covers both
   without touching single.php/page.php/search.php, which use the same
   .page-header/.content-sidebar markup but keep the original layout.
----------------------------------------------------------------------- */

/* Shorter hero — .ep-hero-grid's own animated terrain/vignette (see
   global-styles.css) is kept, just given less room here, since a
   category pill row now follows directly underneath instead of the
   content/sidebar section. */
.blog-layout .page-header {
	min-height: 16rem;
	padding: 3.5rem 2rem;
	margin-bottom: 0;
}

/* Category pills — sits full-bleed between the header and the
   content/sidebar section, same "full-bleed row with an inner
   .container" pattern as .page-header (see the comment at the top of
   this file). */
.blog-cat-pills {
	margin-bottom: 3rem;
}

.blog-cat-pills-list {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.75rem;
	list-style: none;
	margin: 0;
	padding: 1.5rem 0;
}

/* Shape: shared pill base (global-styles.css) + this component's own
   headline font. Color: shared "border" mode by default, "dark" mode
   once .is-active (both also in global-styles.css). */
.blog-cat-pill {
	font-family: var(--font-head);
}

/* Wraps the content/sidebar section AND the pagination below it — plain
   white page background (no separate section color), just supplying the
   vertical spacing between the category pills above and the footer
   below. Rendered OUTSIDE .container (see index.php/archive.php) with an
   inner .container, same structural pattern as .page-header, in case a
   section background is wanted here again later. */
.blog-content-section {
	padding: 2.5rem 0 4rem;
}

/* Post grid — reuses inc/grid-links-section.php's card markup/CSS
   (.dr-research-*, shared with templates/digital-resilience.php) called
   with wrap=>false + columns=>3 (see template-parts/post-grid.php), so
   it's just the bare `.dr-research-grid` at the component's own default
   3-up layout, full .container width (no sidebar — see archive.php/
   index.php) — no column-count modifier/override needed here, unlike the
   old 2-up layout this replaced. */
.blog-layout .dr-research-grid {
	margin-bottom: 2.5rem;
}

/* Post excerpts run longer than the original 1-line research card copy —
   clamp instead of letting them stretch card heights unevenly. */
.blog-layout .dr-research-card-text {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* -----------------------------------------------------------------------
   Featured event banner — main blog listing's first page only, between
   the first 3 posts and the rest (see the "Featured event" block in
   template-parts/post-grid.php, einar_partners_get_featured_event() in
   inc/post-type-event.php). Sits directly inside the page's own
   #primary/.container (index.php) alongside the surrounding
   .dr-research-grid post cards — same "our grid style" plain-background
   flow as those, not a separate boxed/tinted section, so it carries no
   background or padding of its own, just top/bottom spacing; the card
   below it is full .container width, matching the grid rows above/below
   it exactly. .event-pill (assets/css/event.css, also enqueued here —
   see functions.php) is reused as-is for the date/time chips, same
   "translucent-on-photo" context it was built for. Above the card,
   .blog-featured-event-header is a plain "title left / link right" row
   (.blog-featured-event-heading reuses the sitewide h2/.dr-section-title
   look as-is, no extra type rules needed here beyond a margin reset for
   the row's own vertical centering) — its "All Events" link and the
   card's own "Save your seat" link both use the shared .btn-base
   (global-styles.css) rather than a page-specific button style.
----------------------------------------------------------------------- */
.blog-featured-event {
	margin: 2.5rem 0;
}

.blog-featured-event-header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	margin-bottom: 1.5rem;
}

.blog-featured-event-heading {
	margin: 0;
}

/* Same shape/border/shadow as .dr-research-card (digital-resilience.css)
   — standalone here, not part of a collapsed-border grid, so no
   border-radius:0 caveat/negative-margin collapsing needed. --white/--bg
   re-pinned to fixed values (same technique as #sl-spotlight,
   service-landing.css) so the "Save your seat" .btn-base always reads as
   a white link against this always-dark photo card, regardless of
   whether the page's own header theme (light by default on the blog
   listing — see einar_partners_get_header_theme()) would otherwise swap
   --white to dark navy. .blog-featured-event-header above sits OUTSIDE
   this card, so it's unaffected and keeps the page's normal theme
   colors. */
.blog-featured-event-card {
	--white: #fff;
	--bg: var(--ink-900);
	position: relative;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	min-height: 22rem;
	background: var(--ink-900);
	color: #fff;
	border: 1px solid var(--block-border);
	box-shadow: var(--block-shadow);
}

.blog-featured-event-media {
	position: absolute;
	inset: 0;
}

.blog-featured-event-media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.blog-featured-event-overlay {
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgba(8, 15, 30, 0.25) 0%, rgba(8, 15, 30, 0.85) 100%);
}

/* "Upcoming" flag — .blog-featured-event-pill takes only the shared
   pill component's SHAPE (padding/radius/type, global-styles.css), not
   one of its three color modes: this one gets its own fill, the site's
   standard --gradient-accent (variables.css) as a solid background
   (same "gradient as a solid fill, not gradient-TEXT" technique
   .ee-delivery-nav-btn.is-active uses, service-landing.css) with fixed
   white text/dot — rgb(var(--white-rgb)) rather than var(--white),
   since --white itself swaps to dark navy under this page's default
   light header theme (see einar_partners_get_header_theme()) and would
   read almost invisibly against the gradient.

   Positioned top/left to match .blog-featured-event-content's own
   padding exactly (2.5rem here, 1.5rem in the mobile override below) so
   the pill's left edge lines up with the content block's left edge
   instead of sitting at its own arbitrary offset. */
.blog-featured-event-pill {
	position: absolute;
	top: 2.5rem;
	left: 2.5rem;
	z-index: 1;
	gap: .5rem;
	background: var(--gradient-accent);
	color: rgb(var(--white-rgb));
	border-color: transparent;
}

/* Pulsing dot — a static core (::before) plus a copy of itself
   (::after) that scales up and fades out on a loop, reading as a ripple
   expanding outward from the core. currentColor so it always matches
   the pill's own (fixed white) text color above with no separate color
   rule to keep in sync. */
.blog-featured-event-pill-dot {
	position: relative;
	width: 8px;
	height: 8px;
	flex-shrink: 0;
	color: currentColor;
}

.blog-featured-event-pill-dot::before,
.blog-featured-event-pill-dot::after {
	content: '';
	position: absolute;
	inset: 0;
	border-radius: 50%;
	background: currentColor;
}

.blog-featured-event-pill-dot::after {
	animation: einar-blog-featured-event-pill-ripple 1.6s cubic-bezier(0, 0, .2, 1) infinite;
}

@keyframes einar-blog-featured-event-pill-ripple {
	from {
		transform: scale(1);
		opacity: .6;
	}
	to {
		transform: scale(2.8);
		opacity: 0;
	}
}

@media (prefers-reduced-motion: reduce) {
	.blog-featured-event-pill-dot::after {
		animation: none;
	}
}

.blog-featured-event-content {
	position: relative;
	z-index: 1;
	padding: 2.5rem;
}

.blog-featured-event-title {
	margin: 0 0 0.75rem;
	color: #fff;
}

.blog-featured-event-excerpt {
	max-width: 60ch;
	margin: 0 0 1.5rem;
	color: rgba(255, 255, 255, 0.75);
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.blog-featured-event-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
}

.blog-featured-event-datetime {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
}

@media (max-width: 640px) {
	.blog-featured-event-card {
		min-height: 20rem;
	}

	.blog-featured-event-content {
		padding: 1.5rem;
	}

	/* Matches .blog-featured-event-content's own padding change above —
	   see the comment on .blog-featured-event-pill's base rule. */
	.blog-featured-event-pill {
		top: 1.5rem;
		left: 1.5rem;
	}

	.blog-featured-event-meta {
		align-items: flex-start;
	}
}

/* Fallback fill for posts with no featured image, so the card grid
   doesn't leave a blank hole in the media block. */
.dr-research-media.is-empty {
	background: linear-gradient(135deg, var(--ink-900), var(--slate-600));
}

/* -----------------------------------------------------------------------
   Numbered pagination (paginate_links, template-parts/blog-pagination.php)
   — square-ish buttons (radius-sm, not a full pill) matching this file's
   sharp-edged blog aesthetic, centered, full .container width (rendered
   inside .blog-content-section as a sibling of .content-sidebar, not
   inside it — see index.php/archive.php). The section's own padding-top/
   bottom (above) supplies the space before/after, so this only needs a
   little breathing room from the grid/sidebar directly above it.
----------------------------------------------------------------------- */
.blog-pagination {
	margin-top: 1.5rem;
}

.blog-pagination ul.page-numbers {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 0.5rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.blog-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	min-width: 2.5rem;
	height: 2.5rem;
	padding: 0 0.75rem;
	border-radius: var(--radius-sm);
	font-family: var(--font-head);
	font-size: 0.85rem;
	color: var(--slate-600);
	transition: background 0.2s ease, color 0.2s ease;
}

.blog-pagination a.page-numbers:hover {
	background: rgb(var(--accent-rgb) / 0.08);
	color: var(--violet);
}

.blog-pagination .page-numbers.current {
	background: var(--ink-900);
	color: #fff;
}

.blog-pagination .page-numbers.dots {
	color: var(--muted);
}

.blog-pagination .page-numbers.prev,
.blog-pagination .page-numbers.next {
	padding: 0 1rem;
	text-transform: uppercase;
	letter-spacing: var(--tracking-wide);
	font-size: 0.75rem;
}

.blog-pagination .page-numbers.prev svg,
.blog-pagination .page-numbers.next svg {
	width: 13px;
	height: 13px;
	flex-shrink: 0;
}

.blog-pagination .page-numbers.prev svg {
	transform: scaleX(-1);
}
/* ---------------------------------------------------------------
   Source: assets/css/digital-resilience.css
--------------------------------------------------------------- */
/* ===================================================================
   DIGITAL RESILIENCE PAGE TEMPLATE
   Styles for templates/digital-resilience.php only (enqueued in
   functions.php when is_page_template( 'templates/digital-resilience.php' )
   is true). Simple, minimalist sections — no pinned/scroll-jacked
   layouts like homepage.css. Reveal-on-scroll only, via .dr-reveal
   (see assets/js/digital-resilience.js).

   Sections below the hero use the shared "custom page" pattern — a
   light lavender page background (--page-bg-custom) with white,
   bordered, softly-shadowed "block" cards on top of it
   (--block-bg / --block-border / --block-shadow, in variables.css).
   The same pattern is used by templates/style-guide.php.
   =================================================================== */

/* .dr-reveal-right slides in from translateX(48px) off the right edge
   of its box (see below) — until it reveals, that pushes the element
   past the viewport's right edge and creates horizontal scroll on
   mobile. The sitewide `html, body { overflow-x: clip; }` guard this
   page depends on now lives in assets/css/global-styles.css (a file
   loaded on every page in both dev and built mode) — it used to sit
   here instead, which only actually covered every page once built
   mode concatenated every source file into one style.css; in dev mode
   this file only loads for a handful of templates, so plain Elementor
   pages had no guard at all. See global-styles.css for the full
   clip-vs-hidden reasoning. */

/* ═══════════════════════════════════════════════
   REVEAL-ON-SCROLL — fade + slide up once, no scroll-jacking
═══════════════════════════════════════════════ */
.dr-reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .7s ease, transform .7s ease;
}
.dr-reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .dr-reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Direction modifier — pairs with .dr-reveal (still needed for the
   shared IntersectionObserver to pick the element up and toggle
   is-visible on it); this only swaps the slide axis to "from the
   right" instead of the default "from below". Used by the IMPACT
   section's video panel + counter cards. */
.dr-reveal-right {
  transform: translateX(48px);
}
.dr-reveal-right.is-visible {
  transform: translateX(0);
}

/* ═══════════════════════════════════════════════
   SHARED SECTION RHYTHM
═══════════════════════════════════════════════ */
#dr-hero, #dr-features, #dr-cocreate, #dr-impact, #dr-research, #dr-sitemap-links {
  padding: 6rem 0;
}
/* #dr-hero:not(.dr-hero--left) — .dr-hero--left (opted into via
   einar_partners_hero_section()'s 'align_left' arg, inc/hero-section.php;
   currently only templates/eu-regulations-compliance.php) skips this
   centered title rule entirely instead of overriding it elsewhere — see
   assets/css/component-sections.css for that variant's own layout. */
#dr-hero:not(.dr-hero--left) h1, #dr-hero:not(.dr-hero--left) h2, #dr-hero:not(.dr-hero--left) h3,
#dr-features h2, #dr-cocreate h2, #dr-research h2, #dr-sitemap-links h2 {
  text-align: center;
}
#dr-features h2, #dr-cocreate h2, #dr-research h2, #dr-sitemap-links h2 {
  max-width: 640px;
  margin: 0 auto 3rem;
}
/* #dr-impact's title breaks from the shared centered pattern above — see
   the IMPACT section below for its left-aligned override. */

/* ═══════════════════════════════════════════════
   HERO — light, matching the rest of this page's sections
   (--page-bg-custom lavender, dark ink text) instead of standing out
   as a dark navy island. A soft two-tone gradient wash sits under a
   canvas-drawn particle grid (assets/js/digital-resilience.js) that
   distorts near the cursor and springs back when it leaves.
═══════════════════════════════════════════════ */
#dr-hero {
  position: relative;
  overflow: hidden;
  background:
    /* Solid white behind the fixed white #main-nav, fading down into the
       lavender wash below it — so the top of the hero (and the particle
       grid's own fade there, see verticalFade() in digital-resilience.js)
       reads as a continuation of the header instead of an abrupt seam.
       Bottom is untouched, still fading via the grid + radial washes only. */
    linear-gradient(to bottom, #fff 0, rgb(255 255 255 / 0) 260px),
    radial-gradient(ellipse 60% 55% at 15% 8%, rgb(var(--accent-rgb) / .1) 0%, transparent 70%),
    radial-gradient(ellipse 60% 55% at 88% 100%, rgb(var(--sky-rgb) / .08) 0%, transparent 70%),
    var(--page-bg-custom);
  padding-top: 9rem;
  padding-bottom: 7rem;
  min-height: 680px;
  display: flex;
  align-items: center;
  justify-content: center;
}
#dr-hero p {
  color: var(--muted);
}
.dr-hero-inner {
  position: relative;
  z-index: 2;
}
/* .dr-hero-inner--left (see inc/hero-section.php/component-sections.css)
   skips this centered reading-column sizing entirely, rather than
   fighting it with a higher-specificity override elsewhere — it falls
   back to .dr-hero-inner's OTHER class, .container (style.css), for its
   width/centering instead. */
.dr-hero-inner:not(.dr-hero-inner--left) {
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}
/* Optional dark pill/eyebrow above the title (einar_partners_hero_section()'s
   'pill_text' arg — currently only templates/eu-regulations-compliance.php).
   Shape/color: shared "dark" pill mode (global-styles.css). display:inline-flex
   there already keeps it from stretching to the hero's full text width. */
#dr-hero .dr-hero-pill {
  margin-bottom: 1.25rem;
}
#dr-hero h1 {
  font-size: clamp(2.25rem, 5vw, 3rem);
}
/* No #dr-hero-title .accent override — it uses the sitewide .accent
   gradient (global-styles.css) as-is, same as the homepage hero. */
#dr-hero .dr-hero-lead {
  font-size: 19px;
  font-weight: 400;
  color: var(--ink-900);
  margin-top: .5rem;
}
.dr-hero-text {
  max-width: 620px;
  margin: 1.5rem auto 0;
}
.dr-hero-actions {
  margin-top: 2.5rem;
  display: flex;
  justify-content: center;
}
/* .btn-base (global-styles.css) defaults to white text — right for
   the site's dark sections, invisible here, so it's pinned to dark
   ink for this one light hero. */
.dr-hero-actions .btn-base,
.dr-hero-actions .btn-base:hover {
  color: var(--ink-900);
}
.dr-hero-actions .btn-base::before,
.dr-hero-actions .btn-base::after {
  background: var(--ink-900);
}

/* ── Particle grid ──
   Canvas 2D (assets/js/digital-resilience.js) — a jittered grid of
   dots connected to their right/bottom neighbors, redrawn every
   frame. Purely decorative, so it's non-interactive itself; the
   hero section listens for the pointer instead. */
#dr-hero-canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* Fades out behind the copy so the grid frames it instead of
     competing with it. */
  mask-image: linear-gradient(90deg, black 0%, black 20%, transparent 42%, transparent 58%, black 80%, black 100%);
  -webkit-mask-image: linear-gradient(90deg, black 0%, black 20%, transparent 42%, transparent 58%, black 80%, black 100%);
}

/* ── Hover tooltip near pulsing hub dots ──
   Centered exactly on the cursor (position set every frame in JS) so it
   reads as the cursor itself morphing into a small HUD readout, rather
   than a badge trailing beside it — the native cursor is hidden over
   #dr-hero while it's visible (see digital-resilience.js). Fades/scales
   in only while the cursor sits within CONFIG.tooltipRadius of a hub
   dot. An icon chip plus a few lines of made-up telemetry (typed in
   letter-by-letter in JS), themed to that hub's icon — e.g. a "data"
   hub reads "DATA 042". Purely decorative, so it's non-interactive,
   same as the canvas it floats above. Uses the same white "block" card
   look as the rest of this page (--block-bg/--block-border/
   --block-shadow, variables.css). */
.dr-hero-tooltip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  width: max-content;
  border-radius: 10px;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  box-shadow: var(--block-shadow);
  /* Icon chip's fill="currentColor" (einar_partners_get_inline_icon())
     picks this up — the sitewide dark navy, same value as --bg, but via
     the non-swapping --bg-rgb alias since --bg itself flips to white on
     this page's light header theme (variables.css). */
  color: rgb(var(--bg-rgb));
  opacity: 0;
  transform: translate(-50%, -50%) scale(.5);
  transition: opacity .16s ease, transform .16s ease;
}
.dr-hero-tooltip-icon {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 6px;
  background: rgb(var(--bg-rgb) / .06);
}
.dr-hero-tooltip-icon svg {
  width: 15px;
  height: 15px;
  display: block;
}
.dr-hero-tooltip-text {
  display: flex;
  flex-direction: column;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 7px;
  line-height: 1.65;
  letter-spacing: .04em;
  white-space: nowrap;
  color: rgb(var(--bg-rgb) / .55);
}
/* Last line is the "● ACTIVE" status readout — picked out in green like
   a live/online indicator. */
.dr-hero-tooltip-line:last-child {
  color: rgb(34 197 94 / .9);
}
/* Blinking caret on whichever line is currently being typed (JS toggles
   .is-typing line by line as it reveals each one). */
.dr-hero-tooltip-line.is-typing::after {
  content: "";
  display: inline-block;
  width: 1px;
  height: 7px;
  margin-left: 1px;
  vertical-align: -1px;
  background: currentColor;
  animation: dr-tooltip-caret .8s steps(1) infinite;
}
@keyframes dr-tooltip-caret {
  50% { opacity: 0; }
}
.dr-hero-tooltip.is-visible {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
@media (prefers-reduced-motion: reduce) {
  .dr-hero-tooltip {
    display: none;
  }
}

/* ── Word-by-word title reveal ──
   #dr-hero-title's words are split into spans by digital-resilience.js
   and staggered in, matching the homepage hero's .hero-word pattern. */
.dr-hero-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1);
}
.dr-hero-word.is-visible {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .dr-hero-word {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ── Section title word-by-word reveal ──
   Every #dr-features/#dr-impact/#dr-research h2 carries
   .dr-section-title; digital-resilience.js splits it into .dr-title-word
   spans and staggers them in once the title scrolls into view — same
   look as the hero's .dr-hero-word, just IntersectionObserver-triggered
   instead of firing on page load. */
.dr-title-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1);
}
.dr-title-word.is-visible {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .dr-title-word {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ═══════════════════════════════════════════════
   FEATURES — icon + heading + text, 4-up grid
═══════════════════════════════════════════════ */
#dr-features {
  position: relative;
  overflow: hidden;
  background: var(--page-bg-custom);
}
/* Faint grid-line texture, same recipe as #hero (homepage.css) — just
   enough to break up the solid lavender fill, masked to fade at the
   section edges so it never reads as a hard-edged tile. */
#dr-features::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    /* Top cap — fades the grid into the solid page background over the
       section's first ~20% of height, so it only starts appearing well
       below the top edge instead of butting right up against it. Uses
       a % stop (not a px height) so it scales with the section instead
       of a fixed cap. */
    linear-gradient(to bottom, var(--page-bg-custom) 0%, transparent 20%),
    linear-gradient(rgb(var(--accent-rgb) / .05) 1px, transparent 1px),
    linear-gradient(90deg, rgb(var(--accent-rgb) / .05) 1px, transparent 1px);
  background-repeat: no-repeat, repeat, repeat;
  background-position: top, 0 0, 0 0;
  background-size: 100% 100%, 60px 60px, 60px 60px;
  mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  pointer-events: none;
}
#dr-features > .container {
  position: relative;
  z-index: 1;
}
.dr-features-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
}
/* Modifier for a 3-up row (templates/eu-regulations-compliance.php's
   #sl-it-operations) — an explicit class rather than an inline
   style="grid-template-columns:…" (same reasoning as this file's own
   .dr-research-grid--cols-2, further down): an inline style can't be
   overridden by the ≤1024px/≤640px responsive rules below without
   !important, since it always wins over an external stylesheet rule
   regardless of any media query matching. */
.dr-features-grid--cols-3 {
  grid-template-columns: repeat(3, 1fr);
}
.dr-feature-card {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* Fixed (not min-) height — .dr-feature-content below is free to grow
     as its paragraph reveals without ever resizing the card itself;
     sized generously for the longest copy among the 4 cards. */
  height: 324px;
  padding: 2rem 1.6rem;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: 0;
  /* opacity/transform here match .dr-reveal's own transition — this
     selector has the same specificity and comes later in the cascade,
     so without them it silently overwrote .dr-reveal's transition and
     the reveal state changed with no animation at all. */
  transition: border-color .2s, background .2s, opacity .7s ease, transform .7s ease;
}
.dr-feature-card + .dr-feature-card {
  margin-left: -1px;
}
.dr-feature-card:hover {
  border-color: rgb(var(--accent-rgb) / .3);
  background: #fff;
  z-index: 1;
}
/* Reveal — slides in from the right, same motion as .diff-card in the
   homepage's "Four Reasons Enterprises Choose Einar & Partners" section,
   instead of the default .dr-reveal fade-up. */
.dr-feature-card.dr-reveal {
  transform: translateX(28px);
}
.dr-feature-card.dr-reveal.is-visible {
  transform: translateX(0);
}
.dr-feature-icon {
  width: 60px;
  height: 60px;
  margin-bottom: 1.2rem;
}
/* Heading + paragraph, pinned to the card's bottom edge by default (its
   auto top-margin soaks up the flex column's leftover space) so only
   the heading shows at first. On hover/active the paragraph below it
   expands into view (.dr-feature-card p, below) — .dr-feature-content
   grows taller, its auto margin shrinks by the same amount, and since
   its own bottom stays put, both the heading and paragraph read as
   sliding up together. The card's own height (above) never changes. */
.dr-feature-content {
  margin-top: auto;
  display: flex;
  flex-direction: column;
}
/* Heading lifts first, on its own eased curve, no delay. */
.dr-feature-card h3 {
  font-size: 19px;
  margin: 0;
  transform: translateY(0);
  transition: transform .45s cubic-bezier(.22, 1, .36, 1);
}
.dr-feature-card.is-active h3 {
  transform: translateY(-6px);
}
/* Paragraph follows a beat later — its fade/slide-in (opacity, transform)
   carries a short delay so it visibly trails the heading's lift, while
   max-height/margin-top (the structural room-making, same easing) start
   immediately so the two motions still read as one continuous reveal. */
.dr-feature-card p {
  color: var(--muted);
  line-height: 1.6;
  margin: 0;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transform: translateY(10px);
  transition:
    max-height .5s cubic-bezier(.22, 1, .36, 1),
    margin-top .5s cubic-bezier(.22, 1, .36, 1),
    opacity .4s ease .12s,
    transform .45s cubic-bezier(.22, 1, .36, 1) .12s;
}
.dr-feature-card.is-active p {
  max-height: 200px;
  opacity: 1;
  margin-top: .6rem;
  transform: translateY(0);
}

/* ── Progress bar — fills over the "active" card's 5s dwell time ──
   Same gradient as the sitewide #scroll-progress-bar (homepage.css).
   Reset to 0% instantly (.no-transition, toggled in JS) when a card
   stops being active, then filled to 100% over DURATION ms (JS) while
   it's active. Frozen at whatever width it reached if the card is still
   hovered when the fill would otherwise complete (see digital-resilience.js). */
.dr-feature-progress {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: rgb(var(--accent-rgb) / 0);
}
.dr-feature-progress-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, rgb(var(--indigo-rgb) / 0) 0%, rgb(var(--accent-rgb) / .7) 40%, rgb(var(--sky-rgb) / .9) 100%);
  transition: width 5s linear;
}
.dr-feature-progress-fill.no-transition {
  transition: none;
}
@media (prefers-reduced-motion: reduce), (max-width: 1024px) {
  /* No autoplay/hover cycling (digital-resilience.js no-ops the whole
     thing under reduced motion, and under this same max-width:1024px
     condition — there's no hover on touch) — so instead of freezing on
     whichever card happened to be active, just show every card's text
     and hide the now-inert progress bars. */
  .dr-feature-card h3 {
    transform: none;
  }
  .dr-feature-card p {
    max-height: none;
    opacity: 1;
    margin-top: .6rem;
    transform: none;
  }
  .dr-feature-progress {
    display: none;
  }
}

/* ═══════════════════════════════════════════════
   IMPACT — stat cards + award highlight
═══════════════════════════════════════════════ */
#dr-impact {
  background: var(--page-bg-custom);
}
/* 4 rows: image fills the left column (wider than the other two) down
   the full height (all 4 rows — "visual" repeats in every row below),
   the 4 counter cards fill the 2x2 block on the right, the award card
   spans the bottom-right 2 columns of row 3, and the client logo slider
   is a 4th row underneath it, same 2-column span. Grid areas are
   assigned by DOM position below (nth-child) rather than named per-card
   classes, since all 4 counters share the same .dr-impact-card markup. */
.dr-impact-grid {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr;
  grid-template-rows: repeat(4, minmax(200px, 1fr));
  grid-template-areas:
    "visual s1 s2"
    "visual s3 s4"
    "visual award award"
    "visual logos logos";
  gap: 0;
  /* .dr-reveal-right's pre-reveal translateX(48px) and .dr-reveal's own
     pre-reveal translateY(24px) (see REVEAL-ON-SCROLL above — the logo
     slider row uses the plain, vertical variant) push their elements
     past this grid's right/bottom edge; that scrollable-overflow region
     bubbles all the way up to <html> and, on real mobile browsers,
     widens the whole layout viewport rather than just scrolling the
     page — on desktop it was showing up as a vertical scrollbar (and
     ~24px of extra scroll height) that appeared while the section's
     elements were still off-screen pre-reveal, then vanished the
     instant they settled into their final translateY(0)/translateX(0)
     position. Clipping both axes here, at the nearest ancestor, stops
     it before it can bubble any further — body's own overflow-x:
     hidden alone wasn't enough. */
  overflow: hidden;
}
.dr-impact-grid > .dr-impact-card:nth-child(2) { grid-area: s1; }
.dr-impact-grid > .dr-impact-card:nth-child(3) { grid-area: s2; }
.dr-impact-grid > .dr-impact-card:nth-child(4) { grid-area: s3; }
.dr-impact-grid > .dr-impact-card:nth-child(5) { grid-area: s4; }
/* Shared borders instead of doubled ones at every seam — same recipe as
   .dr-feature-card's -1px margins (digital-resilience.css, FEATURES).
   nth-child(2) (s1) needs the same -1px pull as (3)/(4)/(5): it also sits
   right of the visual column, so without it its left edge sat 1px off
   from nth-child(4) (s3, directly below it) which does get pulled in. */
.dr-impact-grid > .dr-impact-card:nth-child(2),
.dr-impact-grid > .dr-impact-card:nth-child(3),
.dr-impact-grid > .dr-impact-card:nth-child(4),
.dr-impact-grid > .dr-impact-card:nth-child(5),
.dr-impact-grid > .dr-award-card,
.dr-impact-grid > .dr-impact-logos {
  margin-left: -1px;
}
.dr-impact-grid > .dr-impact-card:nth-child(4),
.dr-impact-grid > .dr-impact-card:nth-child(5),
.dr-impact-grid > .dr-award-card,
.dr-impact-grid > .dr-impact-logos {
  margin-top: -1px;
}
/* how-we-help.php's impact section has no award card — one row fewer
   than digital-resilience.php's, so the logo slider sits directly under
   the 4 cards instead of under the award row. Scoped to this modifier
   class (set on #dr-impact only on that page) so digital-resilience.php's
   own award row is untouched. */
.dr-impact--playbook .dr-impact-grid {
  grid-template-rows: repeat(3, minmax(200px, 1fr));
  grid-template-areas:
    "visual s1 s2"
    "visual s3 s4"
    "visual logos logos";
}
.dr-impact-visual {
  grid-area: visual;
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, rgb(var(--accent-rgb) / .12), rgb(var(--sky-rgb) / .08));
  border: 1px solid var(--block-border);
}
.dr-impact-visual video,
.dr-impact-visual img {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Scrim behind the title overlay below, so white text stays legible
   over whatever the video is showing at that moment. */
.dr-impact-visual::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgb(0 0 0 / .6) 0%, transparent 45%);
  pointer-events: none;
}
/* Section title lives inside the video panel, top-left, instead of the
   shared centered block above the grid (see SHARED SECTION RHYTHM). */
.dr-impact-visual .dr-section-title {
  position: absolute;
  top: 2rem;
  left: 2rem;
  right: 2rem;
  z-index: 2;
  max-width: none;
  margin: 0;
  text-align: left;
  color: #fff;
  font-size: clamp(28px, 3.2vw, 42px);
  line-height: 1.25;
}
/* how-we-help.php wraps the title + intro paragraph in one absolutely
   positioned column instead of positioning the title alone, so the
   paragraph can sit in normal flow directly below it — whatever
   height the (variable, wraps on narrow widths) title renders at —
   instead of being independently anchored/guessed at. This has no
   effect on digital-resilience.php: its title has no such wrapper, so
   it still gets positioned by the .dr-impact-visual .dr-section-title
   rule above. */
.dr-impact-visual-copy {
  position: absolute;
  top: 2rem;
  left: 2rem;
  right: 2rem;
  z-index: 2;
}
.dr-impact-visual-copy .dr-section-title {
  position: static;
}
/* Fades in on its own delay, once the title's own word-by-word reveal
   (digital-resilience.js) has had time to finish. */
.dr-impact-visual-text {
  margin: .9rem 0 0;
  max-width: 40ch;
  color: rgb(255 255 255 / .85);
  font-size: 15px;
  line-height: 1.55;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .6s ease .9s, transform .6s cubic-bezier(.22, 1, .36, 1) .9s;
}
.dr-impact-visual.is-visible .dr-impact-visual-text {
  opacity: 1;
  transform: translateY(0);
}
/* Left-aligned, top-to-bottom layout: number, then its label directly
   underneath, both anchored to the top; the description paragraph is
   pushed all the way to the card's bottom edge (margin-top: auto in this
   flex column) — plus a hover lift and a staggered inner reveal (number
   + label settle first, the paragraph trails behind) layered on top of
   each card's own .dr-reveal stagger (see the inline --reveal-delay on
   each .dr-impact-card in the template). */
.dr-impact-card {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  min-height: 200px;
  padding: 1.2rem 1.8rem;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: 0;
  /* background is deliberately its own transition entry with no delay —
     the reveal fade/slide (opacity, transform) needs the inline
     --reveal-delay stagger (set per-card in the template) but the hover
     background swap must never inherit it. Folding all three into one
     entry here (rather than leaving opacity/transform to the generic
     .dr-reveal rule) also avoids the bug described on .dr-feature-card
     above: this selector has the same specificity and comes later, so
     without its own opacity/transform entry it would silently overwrite
     .dr-reveal's transition and the reveal state would jump with no
     animation at all. A plain inline transition-delay would work for
     the reveal but would ALSO delay this background transition (a single
     transition-delay value applies to every property in the list), so
     the delay is threaded through as a custom property instead and
     applied only to the two reveal properties. */
  transition: background .2s ease, opacity .7s ease var(--reveal-delay, 0s), transform .7s ease var(--reveal-delay, 0s);
}
.dr-impact-card:hover {
  background: #faf9ff;
  z-index: 1;
}
.dr-impact-num {
  font-family: var(--font-head);
  font-size: clamp(32px, 3.4vw, 46px);
  font-weight: 600;
  line-height: 1;
  color: var(--ink-900);
}
/* how-we-help.php's cards lead with an icon instead of a counted-up
   number — same top-of-card slot as .dr-impact-num above. */
.dr-impact-icon {
  color: rgb(var(--accent-rgb));
}
.dr-impact-icon svg {
  width: 34px;
  height: 34px;
}
.dr-impact-label {
  margin-top: .5rem;
  font-weight: 500;
  font-size: 17px;
}
/* EU card — no big number above it any more, so its label carries more
   visual weight to fill that space. */
.dr-impact-label--lg {
  font-size: 26px;
}
.dr-impact-card p {
  margin-top: auto;
  margin-bottom: 0;
  padding-top: 1.2rem;
  max-width: 90%;
  color: var(--muted);
  font-size: 12px;
}
/* "EU" card — tag/pill list instead of a plain sentence, in place of the
   paragraph the other 3 cards use. */
.dr-impact-tags {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-top: auto;
  padding-top: 1.2rem;
}
/* Shape/color: shared "border" pill mode (global-styles.css). */
/* Inner stagger: the number + label settle first, the paragraph (or the
   EU card's tag list) follows a beat later — both gated by the card's
   own .is-visible (added once, by the shared IntersectionObserver in
   digital-resilience.js). */
.dr-impact-num,
.dr-impact-icon,
.dr-impact-label,
.dr-impact-card p,
.dr-impact-tags {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .6s ease, transform .6s cubic-bezier(.22, 1, .36, 1);
}
.dr-impact-card.is-visible .dr-impact-num,
.dr-impact-card.is-visible .dr-impact-icon,
.dr-impact-card.is-visible .dr-impact-label {
  opacity: 1;
  transform: translateY(0);
  transition-delay: .15s;
}
.dr-impact-card.is-visible p,
.dr-impact-card.is-visible .dr-impact-tags {
  opacity: 1;
  transform: translateY(0);
  transition-delay: .4s;
}
/* "EU" card — the number itself is two individually-animated letters
   instead of counted-up text (see .dr-impact-num[data-count] + the
   count-up script in digital-resilience.js), so the container's own
   fade/slide above is switched off here and each .dr-impact-letter
   slides up into place on its own stagger (inline transition-delay in
   the template), same direction/easing as the count-up numbers. */
.dr-impact-num--letters {
  opacity: 1;
  transform: none;
  transition: none;
  overflow: hidden;
}
.dr-impact-letter {
  display: inline-block;
  opacity: 0;
  transform: translateY(100%);
  transition: opacity .5s ease, transform .5s cubic-bezier(.22, 1, .36, 1);
}
/* Adjacent inline-block spans don't get any natural gap the way real
   text characters would — "EU" rendered as two touching letter boxes
   otherwise reads as "EU" with zero kerning between them. */
.dr-impact-letter:first-child {
  margin-right: .06em;
}
.dr-impact-card.is-visible .dr-impact-letter {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .dr-impact-num,
  .dr-impact-icon,
  .dr-impact-label,
  .dr-impact-card p,
  .dr-impact-tags,
  .dr-impact-letter,
  .dr-impact-visual-text {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ── Award — plain, minimal panel: no gradients, no texture, no shadow.
   Just a thin border, copy right-aligned, badge led by a small star icon. */
.dr-award-card {
  grid-area: award;
  margin-top: 0;
  padding: 1.2rem 1.8rem;
  text-align: left;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: 0;
}
/* Badge (left, shape/color from the shared "border" pill mode in
   global-styles.css — same look as .dr-impact-tag) + the Swedish Chamber
   of Commerce logo (right) sharing one row above the copy. The logo is a
   fixed multi-color mark, not one of the currentColor-fill icons (see
   einar_partners_get_inline_icon()), so it's a plain <img> rather than
   inlined SVG. */
.dr-award-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem 1rem;
  margin-bottom: 1rem;
}
.dr-award-chamber-logo {
  height: 28px;
  width: auto;
  flex-shrink: 0;
}
.dr-award-card p {
  margin: 0;
  color: var(--slate-700);
}
.dr-award-lead {
  font-weight: 600;
  color: var(--ink-900) !important;
}
.dr-award-by {
  color: var(--muted) !important;
  font-size: 13px;
  margin-top: .6rem !important;
}

/* ── Logo slider — last row of .dr-impact-grid (not a standalone
   section any more): a label taking half the row's width, the marquee
   taking the other half. Marquee markup/animation is the same shape
   .dr-logos used to use (now removed) and front-page.php's #clients
   marquee. */
.dr-impact-logos {
  grid-area: logos;
  display: flex;
  align-items: stretch;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
}
.dr-impact-logos-label {
  flex: 0 0 50%;
  display: flex;
  align-items: center;
  padding: 1.4rem 1.8rem;
  font-weight: 500;
  font-size: 15px;
  color: var(--ink-900);
  border-right: 1px solid var(--block-border);
}
.dr-impact-logos-track-wrap {
  position: relative;
  flex: 1;
  overflow: hidden;
  display: flex;
  align-items: center;
}
.dr-impact-logos-track-wrap::before,
.dr-impact-logos-track-wrap::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  width: 80px;
  z-index: 2;
  pointer-events: none;
}
.dr-impact-logos-track-wrap::before { left: 0; background: linear-gradient(to right, var(--block-bg), transparent); }
.dr-impact-logos-track-wrap::after  { right: 0; background: linear-gradient(to left, var(--block-bg), transparent); }
.dr-impact-logos-track {
  display: flex;
  align-items: center;
  gap: 3rem;
  width: max-content;
  padding: 0 1.5rem;
  animation: dr-scroll-track 40s linear infinite;
}
.dr-impact-logos-track:hover { animation-play-state: paused; }
.dr-impact-logo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 140px;
  height: 84px;
  padding: .3rem;
}
.dr-impact-logo-item img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
@keyframes dr-scroll-track {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
/* how-we-help.php — label stacked above the marquee (full row width)
   instead of beside it, at every breakpoint (not just the ≤640px
   stack further below, which applies to both pages), the two sharing
   one plain box (no divider between them). Keeps the shared block's
   plain white background (and edge-fade gradient, which already fades
   to that same white) as-is — only the layout changes. Scoped to this
   page's modifier class so digital-resilience.php's side-by-side
   layout is untouched. */
.dr-impact--playbook .dr-impact-logos {
  flex-direction: column;
  align-items: stretch;
}
.dr-impact--playbook .dr-impact-logos-label {
  flex: none;
  border-right: none;
}

/* ═══════════════════════════════════════════════
   RESEARCH — simple cards, no links required
═══════════════════════════════════════════════ */
#dr-research {
  position: relative;
  overflow: hidden;
  background: var(--page-bg-custom);
}
/* Same faint grid-line texture recipe as #dr-features above. */
#dr-research::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to bottom, var(--page-bg-custom) 0%, transparent 20%),
    linear-gradient(rgb(var(--accent-rgb) / .05) 1px, transparent 1px),
    linear-gradient(90deg, rgb(var(--accent-rgb) / .05) 1px, transparent 1px);
  background-repeat: no-repeat, repeat, repeat;
  background-position: top, 0 0, 0 0;
  background-size: 100% 100%, 60px 60px, 60px 60px;
  mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  pointer-events: none;
}
#dr-research > .container {
  position: relative;
  z-index: 1;
}
.dr-research-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
}
/* Column count used to come from an inline style="grid-template-columns:…"
   (set from the $args['columns'] PHP param) so it could win over any
   responsive override below without !important — moved here as an
   explicit modifier class instead, so the ≤1024px single-column rule
   further down actually applies on the blog's 2-up grid too. */
.dr-research-grid--cols-2 {
  grid-template-columns: repeat(2, 1fr);
}
.dr-research-card {
  position: relative;
  /* Deliberately never transformed (not even translateY(0)/scale(1) via
     .dr-reveal) — this card sits directly over #dr-research's textured
     ::before background in a fractional-width (repeat(3, 1fr)) grid
     track. Giving IT a transform promotes it to its own compositing
     layer, and at that fractional edge Chromium leaves a hairline seam
     that shows the section's violet grid-texture bleeding through as a
     stray colored line. All motion (reveal + hover lift) instead lives
     on .dr-research-card-inner below, which never touches that textured
     backdrop — it's nested inside this card's own opaque white fill. */
  overflow: hidden;
  /* Padding here (rather than on .dr-research-card-inner) is what leaves
     an even white margin between the card's own edge and the grey box
     inside it. */
  padding: 28px;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: 0;
  box-shadow: var(--block-shadow);
  transition: box-shadow .2s, border-color .2s, background .2s;
  /* Each card also carries an inline transition-delay (see
     inc/grid-links-section.php) that staggers its .dr-reveal scroll-in
     fade — later cards fade in later. That inline delay is a single
     element-wide value, but it only ever reaches the properties actually
     listed in whichever `transition` declaration wins the cascade for
     this element — and this rule (box-shadow/border-color/background)
     wins that fight over .dr-reveal's own `transition: opacity, transform`
     (same specificity, this rule loads later), so the inline delay only
     ever affects THIS transition, never the reveal's opacity/transform.
     It was still wrongly delaying the hover border/background transition
     by the same growing per-card amount, both entering AND leaving hover
     (leaving hits this base, non-:hover rule). Zeroing it here is safe
     for the reveal for the same reason it was a bug for hover: opacity/
     transform were never reachable through this property in the first
     place. !important is required: an inline style always outranks a
     stylesheet rule for the same property, regardless of specificity. */
  transition-delay: 0s !important;
}
/* Shared borders collapse into 1px instead of stacking to 2px at each
   card edge — same recipe as .dr-feature-card above. */
.dr-research-card + .dr-research-card {
  margin-left: -1px;
}
/* Cancels the generic .dr-reveal rule's own translateY — this card still
   needs that rule's opacity fade (and the IntersectionObserver hook,
   via the shared .dr-reveal class) but never its transform, per the
   note above. */
.dr-research-card.dr-reveal,
.dr-research-card.dr-reveal.is-visible {
  transform: none;
}
.dr-research-card:hover {
  border-color: rgb(var(--accent-rgb) / .3);
  /* Same hover fill as .dr-impact-card's number boxes. */
  background: #faf9ff;
  z-index: 1;
}
/* The actual animated surface — fades/scales up from a slightly shrunk,
   slightly lower resting state on reveal (instead of the default
   .dr-reveal slide-up). No hover lift — hover feedback lives on the
   border color and overlay darkening instead. Safe to transform freely:
   it only ever sits against this card's own white background. */
.dr-research-card-inner {
  display: flex;
  flex-direction: column;
  height: 100%;
  transform: translateY(24px) scale(.96);
  transition: transform .6s cubic-bezier(.22, 1, .36, 1);
}
.dr-research-card.is-visible .dr-research-card-inner {
  transform: translateY(0) scale(1);
}
/* Media block — plain image, no scrim. Rendered as an <a> on the blog
   archive (see 'linked_media_title' in inc/grid-links-section.php), so it
   needs an explicit block display — anchors default to inline, which would
   break the aspect-ratio sizing below. */
.dr-research-media {
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  margin-bottom: 20px;
  border-radius: var(--radius-sm);
}
.dr-research-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .5s cubic-bezier(.22, 1, .36, 1);
}
.dr-research-card:hover .dr-research-media img {
  transform: scale(1.06);
}
/* The kind tag ("REPORT"/"VIDEO") — pinned over the image's top-left
   corner. Shape/color: shared "default" pill mode (global-styles.css). */
.dr-research-kind {
  position: absolute;
  top: .8rem;
  left: .8rem;
  z-index: 1;
}
/* Title + description now sit below the media block (on the card's own
   white fill) instead of inside the dark .dr-research-overlay — dark
   ink/slate text, matching the rest of the site's light-card copy. */
.dr-research-card-title {
  font-size: 19px;
  color: var(--ink-900);
}
/* On the blog archive the title text sits inside an <a> (see
   'linked_media_title' in inc/grid-links-section.php) — override the
   global `a { color: var(--link) }` reset so it stays dark, not link-blue. */
.dr-research-card-title a {
  color: inherit;
}
.dr-research-card-text {
  color: var(--slate-700);
  margin-top: .5rem;
}
/* "Explore Now" — the sitewide .btn-base component (global-styles.css),
   same as the hero's "Book a Strategic Conversation". It defaults to
   white text for the site's dark sections, so it's pinned to dark ink
   here, same recipe as .dr-hero-actions .btn-base above. */
.dr-research-explore {
  align-self: flex-start;
  margin-top: auto;
  padding-top: 1.4rem !important;
  color: var(--ink-900);
}
.dr-research-explore::before,
.dr-research-explore::after {
  background: var(--ink-900);
}
@media (prefers-reduced-motion: reduce) {
  .dr-research-card,
  .dr-research-card-inner,
  .dr-research-media img {
    transition: none;
  }
  .dr-research-card-inner {
    transform: none;
  }
}

/* ═══════════════════════════════════════════════
   CO-CREATION — 3-stage engagement panel (pill + heading, staggered
   left/middle/right reveal via .dr-reveal's transition-delay, same
   pattern as .dr-feature-card/.dr-research-card above) + a gradient
   "handoff" band underneath showing ownership shifting from
   Einar & Partners to the client over the engagement.
═══════════════════════════════════════════════ */
#dr-cocreate {
  position: relative;
  overflow: hidden;
  background: var(--page-bg-custom);
}
/* Same faint grid-line texture recipe as #dr-features/#dr-research above. */
#dr-cocreate::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to bottom, var(--page-bg-custom) 0%, transparent 20%),
    linear-gradient(rgb(var(--accent-rgb) / .05) 1px, transparent 1px),
    linear-gradient(90deg, rgb(var(--accent-rgb) / .05) 1px, transparent 1px);
  background-repeat: no-repeat, repeat, repeat;
  background-position: top, 0 0, 0 0;
  background-size: 100% 100%, 60px 60px, 60px 60px;
  mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  pointer-events: none;
}
#dr-cocreate > .container {
  position: relative;
  z-index: 1;
}
.dr-cocreate-intro {
  max-width: 560px;
  margin: -2rem auto 3rem;
  color: var(--muted);
  text-align: center;
}
.dr-cocreate-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
}
.dr-cocreate-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 200px;
  padding: 2rem 1.8rem;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: 0;
}
.dr-cocreate-card + .dr-cocreate-card {
  margin-left: -1px;
}
.dr-cocreate-card h3 {
  max-width: 220px;
  font-size: 20px;
  color: var(--ink-900);
  margin: 0;
}
/* Shape/color: shared "dark" pill mode (global-styles.css). */
.dr-cocreate-pill {
  align-self: flex-start;
  margin-bottom: 1.6rem;
}
/* Last stage only, in the reference design — the small note pinned to the
   top-right corner calling out what "embedded" ultimately delivers. */
.dr-cocreate-annotation {
  position: absolute;
  top: 2rem;
  right: 1.8rem;
  max-width: 150px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  color: rgb(var(--accent-rgb));
  text-align: right;
}
/* Gradient "handoff" band — each segment grows in on its own, in step
   with its stage box above it, but all 3 read as ONE continuous accent
   gradient (global-styles.css's .accent) rather than 3 repeats of it —
   see .dr-cocreate-band-fill below for how. */
.dr-cocreate-band {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
/* The .dr-reveal target itself — stays fully opaque/unclipped at all times
   (it has no visible content of its own besides .dr-cocreate-band-fill
   below) so the shared IntersectionObserver keeps tracking it normally.
   Clipping THIS element to nothing at rest, the more obvious approach,
   silently breaks that: Chromium never fires a second callback for a
   target that starts fully clip-path-collapsed, so it would never learn
   it scrolled into view. overflow:hidden here instead clips the fill to
   this element's own rounded corners as it grows. */
.dr-cocreate-band-seg {
  position: relative;
  overflow: hidden;
}
/* The actual visible surface — grows in from its own left edge on
   .is-visible (added to the parent .dr-cocreate-band-seg by the shared
   observer) instead of the generic .dr-reveal fade-up. Delayed via the
   parent's own transition-delay (see inc/co-creation-section.php), same
   as its stage box above it, so each label visibly "arrives" together
   with its box: stage 1 with E&P Led, stage 2 with Co-Created, stage 3
   with Customer Led. */
.dr-cocreate-band-fill {
  display: block;
  padding: .6rem 1.8rem;
  font-size: 14px;
  font-weight: 500;
  color: #fff;
  text-align: center;
  border: none;
  /* Standard sitewide accent gradient (.accent, global-styles.css) as a
     solid fill instead of that rule's text-clip usage — stretched to 3x
     this element's own width (one third of the full 3-column band) and
     shifted per segment below, so the 3 fills line up into one
     continuous gradient across the whole band instead of each repeating
     the same 0%->100% sweep on its own (which read as a hard seam at
     every segment boundary). */
  background-image: linear-gradient(135deg, #a78bfa 0%, #c9c7de 100%);
  background-size: 300% 100%;
  clip-path: inset(0 100% 0 0);
  transition: clip-path .7s cubic-bezier(.22, 1, .36, 1);
}
.dr-cocreate-band-seg.is-visible .dr-cocreate-band-fill {
  clip-path: inset(0 0 0 0);
}
.dr-cocreate-band-seg:nth-child(1) .dr-cocreate-band-fill {
  background-position: 0% 0%;
}
.dr-cocreate-band-seg:nth-child(2) .dr-cocreate-band-fill {
  background-position: 50% 0%;
}
.dr-cocreate-band-seg:nth-child(3) .dr-cocreate-band-fill {
  background-position: 100% 0%;
}
.dr-cocreate-band-seg:first-child .dr-cocreate-band-fill {
  text-align: left;
}
.dr-cocreate-band-seg:last-child .dr-cocreate-band-fill {
  text-align: right;
}
@media (prefers-reduced-motion: reduce) {
  .dr-cocreate-band-fill {
    transition: none;
  }
}

/* ═══════════════════════════════════════════════
   SITEMAP LINKS — minimal, no-gap grid of bordered link cards (icon +
   title only, no paragraph copy). Same bordered/border-collapsed card
   recipe as .dr-feature-card/.dr-research-card/.dr-cocreate-card above,
   but the whole card is itself the <a> (see inc/sitemap-links-section.php).
   Section sits on the tinted --page-bg-custom (same faint grid-line
   texture recipe as #dr-features/#dr-research/#dr-cocreate) so the
   cards' own solid white fill reads as distinct boxes instead of
   blending into the page.
═══════════════════════════════════════════════ */
#dr-sitemap-links {
  position: relative;
  overflow: hidden;
  background: var(--page-bg-custom);
}
#dr-sitemap-links::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to bottom, var(--page-bg-custom) 0%, transparent 20%),
    linear-gradient(rgb(var(--accent-rgb) / .05) 1px, transparent 1px),
    linear-gradient(90deg, rgb(var(--accent-rgb) / .05) 1px, transparent 1px);
  background-repeat: no-repeat, repeat, repeat;
  background-position: top, 0 0, 0 0;
  background-size: 100% 100%, 60px 60px, 60px 60px;
  mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
  pointer-events: none;
}
#dr-sitemap-links > .container {
  position: relative;
  z-index: 1;
}
.dr-sitemap-links-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
}
.dr-sitemap-link-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* Icon pinned to the top, title pinned to the bottom — the two ends of
     the card's own height, however tall .dr-sitemap-link-card grows. */
  justify-content: space-between;
  min-height: 240px;
  padding: 2rem 1.6rem;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: 0;
  text-decoration: none;
  /* opacity/transform here match .dr-reveal's own transition — this
     selector has the same specificity and comes later in the cascade,
     so without them it silently overwrote .dr-reveal's transition and
     the reveal state changed with no animation at all (same gotcha as
     .dr-feature-card above). */
  transition: border-color .2s, background .2s, opacity .7s ease, transform .7s ease;
}
.dr-sitemap-link-card + .dr-sitemap-link-card {
  margin-left: -1px;
}
.dr-sitemap-link-card:hover {
  /* Transparent, not just recolored — the animated gradient ring
     (::after below) takes over the border at the same 1px thickness. */
  border-color: transparent;
  background: #fff;
  z-index: 1;
}
/* Icon set inline as the mask-image (see inc/sitemap-links-section.php) —
   same masked-icon technique as ul.nav-menu .sub-menu-icon
   (site-header-footer.css) — so its "_wh" svg (no hardcoded fill) is
   recolored via background-color here instead of rendering at its raw
   default (black). Literal hex, not var(--bg) — --bg is this same navy
   today but swaps to white under body.header-theme-light (an editable
   per-page setting unrelated to this page template, see meta-boxes.php),
   which would make this icon disappear against the white card. */
.dr-sitemap-link-icon {
  display: inline-block;
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  background-color: #121F36;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.dr-sitemap-link-title {
  font-size: 16px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink-900);
}
/* Arrow badge — square, muted-lavender chip (matches --page-bg-custom, the
   section's own background) with the dark-ink icon; hidden/offset at rest,
   fades + settles into place at the card's top-right corner on hover. Same
   icon (einar_partners_get_inline_icon( 'arrow-btn_wh' )) the sitewide
   .btn-base buttons use, see global-styles.css — its fill="currentColor"
   just follows this badge's own (dark) text color instead of that
   button's white. */
.dr-sitemap-link-arrow {
  position: absolute;
  top: 1.2rem;
  right: 1.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 0;
  background: var(--page-bg-custom);
  color: var(--ink-900);
  opacity: 0;
  transform: translate(4px, -4px);
  transition: opacity .25s, transform .25s;
}
.dr-sitemap-link-card:hover .dr-sitemap-link-arrow {
  opacity: 1;
  transform: translate(0, 0);
}
.dr-sitemap-link-arrow svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
/* Animated gradient border on hover — same conic-gradient-rotating-behind-
   a-masked-ring recipe as .europe-item.top-card in homepage.css, just
   re-declared here (own --dr-sitemap-angle/@keyframes) so this page's CSS
   stays self-contained rather than depending on a stylesheet that's only
   enqueued on the front page in dev mode (see EINAR_PARTNERS_CSS_DEV_MODE
   in functions.php). Padding matches the card's own 1px border so the
   ring exactly replaces it rather than sitting inside/outside it. */
.dr-sitemap-link-card::after {
  content: '';
  position: absolute;
  inset: 0;
  padding: 1px;
  background: conic-gradient(from var(--dr-sitemap-angle), var(--violet), var(--cyan), rgb(var(--sky-rgb) / .2), var(--violet));
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity .25s;
  animation: dr-sitemap-rotate-border 4s linear infinite;
  pointer-events: none;
}
.dr-sitemap-link-card:hover::after {
  opacity: 1;
}
@property --dr-sitemap-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}
@keyframes dr-sitemap-rotate-border {
  to { --dr-sitemap-angle: 360deg; }
}
@media (prefers-reduced-motion: reduce) {
  .dr-sitemap-link-card::after {
    animation: none;
  }
}

/* ═══════════════════════════════════════════════
   RESPONSIVE
═══════════════════════════════════════════════ */
@media (max-width: 1024px) {
  /* Full device height on tablet/mobile instead of the desktop
     min-height:680px — dvh (falls back to vh where unsupported)
     accounts for mobile browser chrome so the hero doesn't overshoot
     the visible viewport. */
  #dr-hero {
    min-height: 100vh;
    min-height: 100dvh;
  }
  .dr-features-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .dr-research-grid {
    grid-template-columns: 1fr;
  }
  /* 3-up -> single column: shared borders now stack vertically instead
     of side-by-side. */
  .dr-research-card + .dr-research-card {
    margin-left: 0;
    margin-top: -1px;
  }
  /* Co-creation panel: cards stack in one column, but the band moves
     beside them instead of dropping below as 3 horizontal rows — a
     narrow vertical strip with rotated text, one segment next to its
     own card (segment 1 next to card 1, etc). .dr-cocreate-grid and
     .dr-cocreate-band stop generating their own box (display:contents)
     so their children become direct items of this one shared grid —
     the only way a card and "its" segment can share a row height (two
     separate grid containers can't size rows against each other). */
  #dr-cocreate .container {
    display: grid;
    grid-template-columns: 1fr 14%;
    grid-template-areas:
      "title title"
      "intro intro"
      "card1 seg1"
      "card2 seg2"
      "card3 seg3";
  }
  #dr-cocreate .dr-section-title {
    grid-area: title;
  }
  .dr-cocreate-intro {
    grid-area: intro;
  }
  .dr-cocreate-grid,
  .dr-cocreate-band {
    display: contents;
  }
  .dr-cocreate-card:nth-child(1) {
    grid-area: card1;
  }
  .dr-cocreate-card:nth-child(2) {
    grid-area: card2;
  }
  .dr-cocreate-card:nth-child(3) {
    grid-area: card3;
  }
  .dr-cocreate-band-seg:nth-child(1) {
    grid-area: seg1;
  }
  .dr-cocreate-band-seg:nth-child(2) {
    grid-area: seg2;
  }
  .dr-cocreate-band-seg:nth-child(3) {
    grid-area: seg3;
  }
  .dr-cocreate-card + .dr-cocreate-card {
    margin-left: 0;
    margin-top: -1px;
  }
  .dr-cocreate-annotation {
    position: static;
    max-width: none;
    text-align: left;
    margin-top: 1rem;
  }
  /* Separates the band column from the card column now that they're
     side by side instead of stacked — stands in for the border the
     cards collapse into among themselves. */
  .dr-cocreate-band-seg {
    border-left: 1px solid var(--block-border);
  }
  /* Rotated to match the strip's own vertical shape — grows downward
     (clip-path inset from the bottom) instead of the desktop band's
     left-to-right sweep, since there's no meaningful width left to grow
     across at ~14%. Same "one continuous gradient across 3 elements"
     trick as the desktop band (background-size/-position, see the
     unconditional .dr-cocreate-band-fill rule above), just rotated to
     the vertical axis — 300%-tall instead of 300%-wide, shifted per
     segment via background-position's Y value instead of X. */
  /* position:absolute (anchored to .dr-cocreate-band-seg's position:relative)
     instead of height:100% — a percentage height here has no definite
     containing-block height to resolve against yet (the grid row it's
     supposed to match is itself auto-sized from this same element's
     content), so it fell back to the viewport height and blew up every
     row to 900px+. Being taken out of flow sidesteps that entirely. */
  .dr-cocreate-band-fill {
    position: absolute;
    inset: 0;
    writing-mode: vertical-rl;
    /* Flips the reading direction to bottom-to-top instead of
       vertical-rl's own default top-to-bottom — rotates in place since
       this element is a symmetric inset:0 box already centered via
       flex, so it doesn't shift position. */
    transform: rotate(180deg);
    text-orientation: mixed;
    white-space: nowrap;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .8rem .3rem;
    background-size: 100% 300%;
    clip-path: inset(100% 0 0 0);
  }
  .dr-cocreate-band-seg.is-visible .dr-cocreate-band-fill {
    clip-path: inset(0 0 0 0);
  }
  .dr-cocreate-band-seg:nth-child(1) .dr-cocreate-band-fill {
    background-position: 0% 0%;
  }
  .dr-cocreate-band-seg:nth-child(2) .dr-cocreate-band-fill {
    background-position: 0% 50%;
  }
  .dr-cocreate-band-seg:nth-child(3) .dr-cocreate-band-fill {
    background-position: 0% 100%;
  }
  .dr-cocreate-band-seg:first-child .dr-cocreate-band-fill,
  .dr-cocreate-band-seg:last-child .dr-cocreate-band-fill {
    text-align: center;
  }
  /* 4-up -> 2x2: shared borders now run along both axes — same recipe
     as .wn-value-item (homepage.css) */
  .dr-feature-card + .dr-feature-card {
    margin-left: 0;
  }
  .dr-feature-card:nth-child(2n) {
    margin-left: -1px;
  }
  .dr-feature-card:nth-child(n+3) {
    margin-top: -1px;
  }
  /* Impact 4-row -> 2 cols: image banner on top (full width), 2x2
     counters, award full width, logo slider full width along the
     bottom. */
  .dr-impact-grid {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: auto repeat(2, minmax(200px, 1fr)) auto auto;
    grid-template-areas:
      "visual visual"
      "s1 s2"
      "s3 s4"
      "award award"
      "logos logos";
  }
  .dr-impact-visual {
    min-height: 200px;
  }
  .dr-impact-grid > .dr-impact-card:nth-child(2),
  .dr-impact-grid > .dr-impact-card:nth-child(4) {
    margin-left: 0;
  }
  .dr-impact-grid > .dr-impact-card:nth-child(2),
  .dr-impact-grid > .dr-impact-card:nth-child(3) {
    margin-top: -1px;
  }
  .dr-impact-grid > .dr-award-card,
  .dr-impact-grid > .dr-impact-logos {
    margin-left: 0;
  }
  /* how-we-help.php — one row fewer, no award row (see the desktop rule
     above for why). */
  .dr-impact--playbook .dr-impact-grid {
    grid-template-rows: auto repeat(2, minmax(200px, 1fr)) auto;
    grid-template-areas:
      "visual visual"
      "s1 s2"
      "s3 s4"
      "logos logos";
  }
  /* Taller than the shared 200px floor above — this page's video panel
     carries a title AND an intro paragraph (digital-resilience.php's
     only ever has the title), which doesn't fit 200px without the two
     overlapping. */
  .dr-impact--playbook .dr-impact-visual {
    min-height: 320px;
  }
  /* 5-up -> 3 cols: row 2 (2 cards) left-aligns under row 1, shared
     borders now run along both axes — same recipe as .dr-feature-card's
     4 -> 2x2 above, just against a 3-wide track. */
  .dr-sitemap-links-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .dr-sitemap-link-card:nth-child(3n+1) {
    margin-left: 0;
  }
  .dr-sitemap-link-card:nth-child(n+4) {
    margin-top: -1px;
  }
}
@media (max-width: 640px) {
  .dr-features-grid {
    grid-template-columns: 1fr;
  }
  /* #dr-research's 3-card grid is already single-column below 1024px
     (see the max-width:1024px rule above) — at true mobile widths also
     reverse the visual stack order (last card shows first). Grid's
     `order` moves placement, not just paint order, so the existing
     .dr-research-card + .dr-research-card border-collapse rule still
     lands between whichever two cards end up visually adjacent. */
  #dr-research .dr-research-grid > .dr-research-card:nth-child(1) {
    order: 3;
  }
  #dr-research .dr-research-grid > .dr-research-card:nth-child(2) {
    order: 2;
  }
  #dr-research .dr-research-grid > .dr-research-card:nth-child(3) {
    order: 1;
  }
  #dr-hero, #dr-features, #dr-cocreate, #dr-impact, #dr-research, #dr-sitemap-links {
    padding: 4rem 0;
  }
  .dr-cocreate-intro {
    margin-top: -1.2rem;
  }
  /* Section h2s (.dr-section-title) otherwise sit at the fixed sitewide
     --fs-h2 (36px, style.css) with no responsive shrink at all — pinned
     here to match the hero h1's own mobile size. h1's clamp(32px, 5vw,
     48px) only ever exceeds its 32px floor above 640px width, so within
     this same max-width:640px range h1 is always flat 32px — a vw-based
     clamp here (rather than matching that flat value directly) drifted
     above it at some widths (e.g. 34.46px at 431px). Doesn't touch the
     IMPACT section's own more-specific .dr-impact-visual .dr-section-title
     override. */
  .dr-section-title {
    font-size: 32px;
  }
  /* 2x2 -> single column: every card just stacks on the one above it */
  .dr-feature-card:nth-child(2n) {
    margin-left: 0;
  }
  .dr-feature-card + .dr-feature-card {
    margin-top: -1px;
  }
  /* 3-up -> single column: everything just stacks. */
  .dr-sitemap-links-grid {
    grid-template-columns: 1fr;
  }
  .dr-sitemap-link-card:nth-child(3n+1) {
    margin-left: 0;
  }
  .dr-sitemap-link-card + .dr-sitemap-link-card {
    margin-left: 0;
    margin-top: -1px;
  }
  /* The arrow badge otherwise only shows on :hover — meaningless on a
     touchscreen, where it'd never appear until the link is already
     tapped. Visible at rest here instead, no transition/transform to
     wait on. */
  .dr-sitemap-link-arrow {
    opacity: 1;
    transform: translate(0, 0);
  }
  /* Impact 2-col -> single column: everything just stacks. */
  .dr-impact-grid {
    grid-template-columns: 1fr;
    grid-template-rows: auto repeat(4, auto) auto auto;
    grid-template-areas:
      "visual"
      "s1"
      "s2"
      "s3"
      "s4"
      "award"
      "logos";
  }
  .dr-impact-grid > .dr-impact-card:nth-child(n+2),
  .dr-impact-grid > .dr-award-card,
  .dr-impact-grid > .dr-impact-logos {
    margin-left: 0;
    margin-top: -1px;
  }
  /* Label above the marquee instead of beside it — a third of a narrow
     column's width isn't enough room for that phrase. */
  .dr-impact-logos {
    flex-direction: column;
    align-items: stretch;
    /* Without this, the marquee track's max-content width (all logos
       laid out in one un-wrapped row) becomes this flex item's
       cross-axis (width) minimum once it's stacked in column
       direction, which blows out the single-column grid track — and
       with it every other row sharing that column — to ~5000px. */
    min-width: 0;
  }
  .dr-impact-logos-label {
    flex: none;
    border-right: none;
    border-bottom: 1px solid var(--block-border);
  }
  /* how-we-help.php — no award row (see the desktop rule above for why). */
  .dr-impact--playbook .dr-impact-grid {
    grid-template-rows: auto repeat(4, auto) auto;
    grid-template-areas:
      "visual"
      "s1"
      "s2"
      "s3"
      "s4"
      "logos";
  }
  /* Narrower column -> the title wraps to more lines, so needs more
     room than the ≤1024px override above to keep clear of the
     paragraph beneath it (see that rule for why the panel needs extra
     height at all on this page). */
  .dr-impact--playbook .dr-impact-visual {
    min-height: 360px;
  }
}
/* ---------------------------------------------------------------
   Source: assets/css/style-guide.css
--------------------------------------------------------------- */
/* ===================================================================
   STYLE GUIDE TEMPLATE
   Styles for templates/style-guide.php only (enqueued in functions.php
   when is_page_template( 'templates/style-guide.php' ) is true).
   Internal/editorial reference page — not part of the visitor-facing
   design language, so it's fine for this file to look a bit like a
   dev tool rather than match the marketing pages.

   Uses the shared "custom page" pattern — a light lavender page
   background (--page-bg-custom) with white, bordered, softly-shadowed
   "block" cards on top of it (--block-bg / --block-border /
   --block-shadow, in variables.css). Same pattern as
   assets/css/digital-resilience.css.
   =================================================================== */

body.page-template-templates-style-guide-php {
  background: var(--page-bg-custom);
}

.sg-container {
  padding: var(--space-12) 0 var(--space-10);
}

.sg-page-header {
  margin-bottom: var(--space-10);
  padding-bottom: var(--space-8);
  border-bottom: 1px solid var(--border3);
}

.sg-eyebrow {
  display: inline-block;
  font-size: 11px;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--violet);
  margin-bottom: var(--space-3) !important;
}

.sg-lead {
  max-width: 640px;
  color: var(--muted);
  font-size: 1.05rem;
}

.sg-section {
  margin-bottom: var(--space-10);
  padding-bottom: var(--space-9);
  border-bottom: 1px solid var(--border3);
}

.sg-section:last-child {
  border-bottom: 0;
}

.sg-section-note {
  max-width: 720px;
  color: var(--muted);
  font-size: 0.9rem;
}

.sg-subhead {
  margin-top: var(--space-6);
  font-size: 0.85rem;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--slate-600);
}

.sg-mt {
  margin-top: var(--space-9);
}

code {
  font-family: 'SFMono-Regular', Consolas, monospace;
  font-size: 0.85em;
  background: rgb(var(--bg-rgb) / .05);
  padding: 0.15em 0.4em;
  border-radius: var(--radius-sm);
}

/* ---- Color swatches ---- */
.sg-swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.sg-swatch {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: var(--radius-md);
  box-shadow: var(--block-shadow);
}

.sg-swatch-color {
  display: block;
  width: 100%;
  height: 56px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border3);
}

.sg-swatch-desc {
  font-size: 0.8rem;
  color: var(--muted);
}

/* ---- Type scale ---- */
.sg-type-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border3);
}

.sg-type-sample {
  margin: 0 !important;
}

.sg-type-row code {
  flex-shrink: 0;
  color: var(--muted);
}

/* ---- Spacing scale ---- */
.sg-space-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-2) 0;
}

.sg-space-row code {
  width: 90px;
  flex-shrink: 0;
}

.sg-space-bar {
  display: block;
  height: 14px;
  background: var(--violet-lt);
  border-radius: var(--radius-sm);
}

/* ---- Radius scale ---- */
.sg-radius-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--space-5);
  margin-top: var(--space-4);
}

.sg-radius-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.sg-radius-sample {
  display: block;
  width: 72px;
  height: 72px;
  background: var(--violet-lt);
}

.sg-radius-desc {
  font-size: 0.8rem;
  color: var(--muted);
}

/* ---- Buttons ---- */
.sg-btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-6);
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: var(--radius-md);
  box-shadow: var(--block-shadow);
}

.sg-btn-row--dark {
  background: var(--bg);
  border-color: transparent;
  margin-bottom: var(--space-4);
}

/* ---- Eyebrows / badges ---- */
.sg-tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin-top: var(--space-4);
}

.sg-tag-sample {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}

/* ---- Cards ---- */
.sg-card-row {
  display: flex;
  gap: var(--space-5);
  margin-top: var(--space-4);
  max-width: 320px;
}

/* ---- Snapshots -----------------------------------------------------
   These mirror real component classes defined elsewhere, purely for
   display on this page (this file isn't loaded alongside homepage.css
   or digital-resilience.css, to avoid pulling in their page-specific
   global resets/overrides). Edit the source file to change the real
   component — these will drift out of sync if you don't update them
   too when the source changes.
   ------------------------------------------------------------------ */
.sg-demo-eyebrow-flanked {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--violet-lt);
  background: var(--bg);
  padding: var(--space-3) var(--space-4);
}
.sg-demo-eyebrow-flanked::before,
.sg-demo-eyebrow-flanked::after {
  content: '';
  display: block;
  width: 24px;
  height: 1px;
  background: var(--violet-lt);
}

.sg-demo-eyebrow-plain {
  display: inline-block;
  font-size: 11px;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--violet-dim);
}

.sg-demo-badge-pill {
  display: inline-flex;
  align-items: center;
  padding: .35rem .8rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border3);
  background: transparent;
  color: var(--ink-900);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: var(--tracking-eyebrow);
}

/* Same "border" pill, but the linked/clickable instances (.blog-cat-pill,
   .entry-footer .tags-links a, .social-share-btn): hover darkens the
   border and text to ink-900 — .dr-impact-tag/.dr-award-badge above are
   plain, non-interactive spans, so they never show this state. */
.sg-demo-badge-pill-link {
  display: inline-flex;
  align-items: center;
  padding: .35rem .8rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border3);
  background: transparent;
  color: var(--ink-900);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: var(--tracking-eyebrow);
  text-decoration: none;
  cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}
.sg-demo-badge-pill-link:hover {
  border-color: var(--ink-900);
  color: var(--ink-900);
}

.sg-demo-block-sample {
  display: block;
  width: 120px;
  height: 56px;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--block-shadow);
}

.sg-demo-card {
  padding: 2rem 1.6rem;
  background: var(--block-bg);
  border: 1px solid var(--block-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--block-shadow);
  transition: border-color .3s, transform .3s;
}
.sg-demo-card:hover {
  border-color: rgb(var(--accent-rgb) / .3);
  transform: translateY(-4px);
}
.sg-demo-card h3 {
  font-size: 19px;
  margin-bottom: .6rem;
}
.sg-demo-card p {
  color: var(--muted);
  line-height: 1.6;
  margin: 0;
}
/* ---------------------------------------------------------------
   Source: assets/css/special-service.css
--------------------------------------------------------------- */
/* ===================================================================
   Einar Special Service Page  v1.0.0
   Loaded only on templates/special-service.php (see functions.php). Two
   parts: the hero (reuses .page-header/.ep-hero-grid, see archive.php +
   global-styles.css, just given a bit more height below), and the
   sticky-nav + no-gap content grid handled in this file — the sticky/
   active-link behavior itself is assets/js/special-service.js.
   =================================================================== */

/* -----------------------------------------------------------------------
   Hero — same shared .page-header/.ep-hero-grid used by the blog archive
   (see assets/css/blog.css's .page-header and .blog-layout .page-header,
   which shortens it to min-height:16rem for that context), just a little
   taller than that shortened archive version since no category-pill row
   follows it here.
----------------------------------------------------------------------- */
.sp-hero {
	min-height: 28rem;
	padding: 5rem 2rem;
	margin-bottom: 0;
}

/* Word-by-word title reveal (assets/js/special-service.js splits
   .sp-hero .page-title into these spans and staggers them in on load)
   — same look/timing as #dr-hero-title's .dr-hero-word in
   digital-resilience.css, kept local here since this hero's markup
   (.page-title/.archive-description) isn't the shared dr-hero. */
.sp-hero-word {
	display: inline-block;
	opacity: 0;
	transform: translateY(22px);
	transition: opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1);
}

.sp-hero-word.is-visible {
	opacity: 1;
	transform: translateY(0);
}

/* Subtitle fade-in, timed (in the JS) to start once the title's words
   have finished staggering in. */
.sp-hero-fade {
	opacity: 0;
	transform: translateY(16px);
	transition: opacity .6s ease, transform .6s ease;
}

.sp-hero-fade.is-visible {
	opacity: 1;
	transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
	.sp-hero-word,
	.sp-hero-fade {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* -----------------------------------------------------------------------
   Layout — sticky nav (left) + content grid (right). Rendered full-bleed
   inside #content with an inner .container, same structural pattern as
   .page-header (see the comment at the top of blog.css). Sits on the
   shared "custom page" background (--page-bg-custom + faint grid-line
   texture) — same recipe as #dr-features/#dr-sitemap-links in
   digital-resilience.css — so the white nav/grid cards read as distinct
   blocks instead of blending into a plain white page.
----------------------------------------------------------------------- */
/* Both the solid-color fade below and the pattern's own top-cap (in
   ::before) hold fully white/opaque for an initial stretch and only then
   taper off, over the same fixed lengths, so they finish together
   regardless of how tall this section grows with content. Fixed lengths,
   not %, on purpose — #dr-features/#dr-sitemap-links use a % top-cap
   (transparent 20%) because they're short, roughly one-viewport
   sections, so 20% of their own height reads as "near the top"; this
   section's height depends on how many groups/rows a given page has and
   commonly runs to several thousand px, where the same 20% would push
   the fade-in hundreds of px further down than intended. The hold (not
   fading from 0 immediately) matters even more than the fixed length:
   the faint grid-line texture below is only ~5% opacity, but a *repeating
   pattern* reads as a visible seam even at a couple of percent, well
   before the underlying solid-color blend is itself perceptible — so the
   pattern needs to stay fully hidden for a real stretch, not just
   "mostly" faded from the very first pixel. */
#sp-service {
	position: relative;
	background:
		linear-gradient(to bottom, var(--body-bg) 0, var(--body-bg) 6rem, transparent 20rem),
		var(--page-bg-custom);
	padding: 3rem 0 5rem;
	/* No overflow:hidden here (unlike #dr-features' otherwise-identical
	   background recipe) — this section has a position:sticky descendant
	   (.sp-service-nav), and overflow other than visible on an ancestor
	   breaks sticky positioning even when that ancestor never actually
	   scrolls. Not needed anyway: ::before below is inset:0 within this
	   already position:relative box, so it can't overflow it. */
}

#sp-service::before {
	content: '';
	position: absolute;
	inset: 0;
	background-image:
		linear-gradient(to bottom, var(--bg) 0, var(--page-bg-custom) 6rem, transparent 20rem),
		linear-gradient(rgb(var(--accent-rgb) / .05) 1px, transparent 1px),
		linear-gradient(90deg, rgb(var(--accent-rgb) / .05) 1px, transparent 1px);
	background-repeat: no-repeat, repeat, repeat;
	background-position: top, 0 0, 0 0;
	background-size: 100% 100%, 60px 60px, 60px 60px;
	/* Ellipse sized/positioned in fixed px, anchored well below the top
	   (at 26rem — past the hold+fade zone above), instead of "80% 80% at
	   50% 50%" (percentages of this element's own height) — the same
	   reasoning as the top-cap above: this section can run thousands of
	   px tall, and an ellipse sized/centered relative to that whole
	   height barely tapers at all near the actual top edge, leaving the
	   grid texture visibly showing through the "white zone" instead of
	   staying hidden until it. */
	mask-image: radial-gradient(900px 650px at 50% 26rem, black 20%, transparent 90%);
	-webkit-mask-image: radial-gradient(900px 650px at 50% 26rem, black 20%, transparent 90%);
	pointer-events: none;
}

#sp-service > .container {
	position: relative;
	z-index: 1;
}

.sp-service-layout {
	display: grid;
	grid-template-columns: 240px 1fr;
	gap: 3rem;
	/* Default align-items: stretch — .sp-service-nav-col (below) needs to
	   fill the row's full height (matching .sp-service-grid) so its sticky
	   child has room to travel the whole section. Do not override this
	   to "start": that shrinks .sp-service-nav-col to its own content
	   height, leaving the sticky nav only a few hundred px of scroll
	   before it runs out of containing block and scrolls away early. */
}

/* -----------------------------------------------------------------------
   Sticky left nav — sticks just under the fixed #main-nav (70px, see
   site-header-footer.css) once .sp-service-layout scrolls past it, and
   stays sticky for as long as .sp-service-nav-col (stretched to match
   .sp-service-grid's height) is still in view. White "block" card (same
   --block-bg/--block-border recipe as the content grid) so it reads as
   its own component against the page's tinted background above.
----------------------------------------------------------------------- */
.sp-service-nav {
	position: sticky;
	top: 94px;
	background: var(--block-bg);
	border: 1px solid var(--block-border);
	border-radius: var(--radius-md);
	padding: 0.5rem;
}

.sp-service-nav-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
}

.sp-service-nav-link {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	padding: 0.65rem 0.75rem;
	border-radius: var(--radius-md);
	font-family: var(--font-head);
	font-size: 0.9rem;
	font-weight: 300;
	text-decoration: none;
	color: var(--slate-600);
	transition: color 0.2s, background 0.2s;
}

/* Separator between items — a straight, full-width rule on the <li>
   itself (skipping the first) rather than on the link, so it isn't
   interrupted by the link's own border-radius/hover background. Each
   link is wrapped in its own <li> (see templates/special-service.php). */
.sp-service-nav-list li + li {
	border-top: 1px solid var(--block-border);
}

.sp-service-nav-link:hover {
	color: var(--ink-900);
	background: var(--page-bg-custom);
}

/* Active item — bolder label + trailing arrow icon, nothing else changes. */
.sp-service-nav-link.is-active {
	font-weight: 500;
}

.sp-service-nav-link-icon {
	display: none;
	flex-shrink: 0;
}

.sp-service-nav-link.is-active .sp-service-nav-link-icon {
	display: inline-flex;
}

.sp-service-nav-link-icon svg {
	width: 12px;
	height: 12px;
	flex-shrink: 0;
}

/* Mobile/tablet collapsed nav — a single sticky bar under the fixed
   header, showing only the current section's label (kept in sync with
   the desktop nav's .is-active link by special-service.js). Hidden on
   desktop; swapped in at the layout breakpoint below. */
.sp-service-nav-mobile {
	display: none;
	position: sticky;
	top: 70px;
	z-index: 10;
	padding: 0.85rem 1.5rem;
	background: rgb(var(--white-rgb) / 0.95);
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
	border-bottom: 1px solid var(--block-border);
}

.sp-service-nav-mobile-label {
	font-family: var(--font-head);
	font-size: 0.8rem;
	font-weight: 500;
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--ink-900);
}

/* -----------------------------------------------------------------------
   Content grid — one bordered, no-gap grid: each group is a full-width
   title cell followed by any number of rows, each row holding 1-4 equal
   columns (--sp-cols, set inline per row in special-service.php). Hairline
   borders collapse via negative margins (same recipe as
   .dr-sitemap-link-card in digital-resilience.css) so the whole grid —
   across every row AND every group — reads as one continuous bordered
   block with no visible gap anywhere inside it.
----------------------------------------------------------------------- */
.sp-group-title-cell,
.sp-group-col {
	border: 1px solid var(--block-border);
	background: var(--block-bg);
}

.sp-group-title-cell {
	padding: 1.5rem 1.75rem;
}

/* On the sticky nav's own scroll-margin-top: the click handler in
   special-service.js scrolls the .sp-group element itself (that's what
   carries the id="sp-<slug>" anchor each nav link points to), so the
   offset has to live here rather than on the nested .sp-group-title-cell
   — scroll-margin-top only affects scrollIntoView() for the exact element
   it's set on, not its ancestors. 94px aligns the title cell's top with
   the sticky nav's own top (also 94px) once landed. */
.sp-group {
	scroll-margin-top: 94px;
}

.sp-group + .sp-group {
	margin-top: -1px;
}

.sp-group-row {
	display: grid;
	grid-template-columns: repeat(var(--sp-cols, 1), 1fr);
	margin-top: -1px;
}

.sp-group-col {
	padding: 1.75rem;
}

.sp-group-col + .sp-group-col {
	margin-left: -1px;
}

/* No type styles here on purpose — .sp-group-title (h2), .sp-col-title
   (h3) and .sp-col-text (p) are left to inherit the sitewide base
   typography (style.css: font-family/size/color for h1-h6/p) instead of
   a page-specific look, so this grid's copy matches the rest of the
   site. Margin is the one exception: style.css's shared h1-h6 rule sets
   margin: 0 0 0.5em, which is tighter than this grid wants, so revert it
   back to each heading level's own UA-stylesheet default here. */
.sp-service-grid h2,
.sp-service-grid h3,
.sp-service-grid h4 {
	margin: revert;
}

/* -----------------------------------------------------------------------
   Responsive — below 1024px the desktop sticky sidebar gives way to the
   collapsed top bar (swapped via display above), and the grid stacks to
   a single column.
----------------------------------------------------------------------- */
@media (max-width: 1024px) {
	.sp-hero {
		min-height: 20rem;
		padding: 4rem 1.5rem;
	}

	.sp-service-layout {
		display: block;
	}

	.sp-service-nav-col {
		display: none;
	}

	.sp-service-nav-mobile {
		display: block;
		margin: 0 -1.5rem 2rem;
	}

	/* Offset for the fixed 70px header + the collapsed nav bar sitting
	   sticky right underneath it, so a clicked/observed section doesn't
	   land hidden behind either of them. */
	.sp-group {
		scroll-margin-top: 118px;
	}
}

@media (max-width: 640px) {
	.sp-group-row {
		grid-template-columns: 1fr;
	}

	.sp-group-col + .sp-group-col {
		margin-left: 0;
		margin-top: -1px;
	}

	.sp-group-title-cell,
	.sp-group-col {
		padding: 1.35rem;
	}
}
/* ---------------------------------------------------------------
   Source: assets/css/service-landing.css
--------------------------------------------------------------- */
/* ===================================================================
   Einar Service Landing Page  v1.0.0
   Loaded only on templates/service-landing.php (see functions.php).
   Reuses the exact same hero as templates/special-service.php (.sp-hero,
   see special-service.css) and its word-by-word title reveal
   (assets/js/special-service.js) — that script's scroll-spy half targets
   #sp-service specifically, so it no-ops harmlessly here since this
   template has no sticky side nav to keep in sync.

   The content grid below is a single-column, full-container-width
   version of special-service.css's grid, on a plain white page
   background (no tinted --page-bg-custom/grid-line texture). The
   visual difference from that grid: only the boundary BETWEEN groups
   (sections) is a full page width border (edge-to-edge of the viewport,
   breaking out of .container via the left/right:50% + negative 50vw
   margin trick below), so those lines read as section breaks — dividers
   between rows INSIDE a group stay container width, and the grid itself
   carries plain left/right border lines framing the (still
   container-width) content — see .sl-service-grid.
   ===================================================================
*/

/* No top padding — with one, the grid's bordered frame/first section
   divider (.sl-service-grid/.sl-group::before below) only started well
   below the hero, leaving a plain gap that read as an empty bordered
   block floating under the hero instead of the grid beginning flush
   against it. */
#sl-service {
	background: var(--body-bg);
	padding: 0 0 5rem;
}

/* Vertical frame around the content column — "container has border on
   left and right" — running the whole height of the grid. Set on
   .sl-service-grid itself rather than #sl-service > .container: .container
   has its own 1.5rem horizontal padding (style.css), so a border on
   .container would sit out at the container's outer edge with that
   padding as a blank gap before the actual grid content starts.
   .sl-service-grid fills the container's padded content box exactly, so
   its border sits flush against the title cells/rows/columns instead. */
.sl-service-grid {
	position: relative;
	border-left: 1px solid var(--block-border);
	border-right: 1px solid var(--block-border);
}

/* Each group's own top AND bottom edge are full page width — the
   "section border", differentiating one section from the next. Two
   adjacent groups' borders (this group's ::after and the next group's
   ::before) land on the exact same pixel with no gap between them, so
   they read as a single line, not a double-thick one. The left/right:50%
   + margin:-50vw breakout resolves to the true viewport edges as long as
   every ancestor box between this element and the viewport stays
   symmetrically centered (.container's margin:0 auto + equal left/right
   padding, unbroken by this file). body already carries
   overflow-x:hidden (digital-resilience.css, enqueued alongside this
   file for .dr-reveal) as the standard guard against this math ever
   introducing horizontal scroll. */
.sl-group {
	position: relative;
}

/* Opt-in modifier (added in template markup per group, e.g.
   templates/eu-regulations-compliance.php) — NOT applied to every
   .sl-group by default, since some (e.g. #sl-customer-logos, a bare logo
   marquee with no title cell) should just size to their content instead
   of being forced up to a minimum height.
   display:flex + min-height here is what lets a short group (e.g. a
   title-only intro with no row content, or a row shorter than 50vh)
   still reach a comfortable minimum height: .sl-group-title-cell below
   is the only flexible child (flex:1 0 auto), so it's the one that
   grows/centers to absorb the leftover space. .sl-group-row keeps its
   own natural/content height (default flex:0 1 auto) — a group whose
   row content already exceeds 50vh is untouched. */
.sl-group--min-height {
	display: flex;
	flex-direction: column;
	min-height: 50vh;
}

.sl-group::before,
.sl-group::after {
	content: '';
	position: absolute;
	left: 50%;
	right: 50%;
	margin-left: -50vw;
	margin-right: -50vw;
	border-top: 1px solid var(--block-border);
}

.sl-group::before {
	top: 0;
}

.sl-group::after {
	bottom: 0;
}

.sl-group-title-cell {
	padding: 1.5rem 1.75rem;
}

/* flex-direction:column + justify-content:center keeps the h2 (and its
   optional supporting p, see .sl-group-title-cell p below) stacked and
   vertically centered as this cell grows to fill .sl-group--min-height's
   50vh minimum (above) — reads as extra top/bottom padding around the
   title rather than the text sticking to the top of a tall, mostly-empty
   cell. Scoped to that modifier so a plain .sl-group-title-cell (no
   min-height on its parent) keeps its normal top-aligned block layout. */
.sl-group--min-height .sl-group-title-cell {
	display: flex;
	flex-direction: column;
	justify-content: center;
	flex: 1 0 auto;
}

/* A group's title cell can also carry a small decorative widget (e.g. the
   spinning circular badge, einar_partners_get_rotating_badge()) pinned to
   its right — title stays left, widget right, both vertically centered.
   nowrap + min-width: 0 on the title (below) keeps the badge pinned to
   the far right at every width — it never gets pushed onto its own
   wrapped line, which read as "centered under the title" rather than
   "aligned right". */
.sl-group-title-cell--with-badge {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	flex-wrap: nowrap;
}

.sl-group-title-cell--with-badge .sl-group-title {
	min-width: 0;
}

.sl-group-title-cell--with-badge .epc-rotating-badge {
	margin-left: auto;
}

/* .sl-group--min-height .sl-group-title-cell (above) and this modifier
   are both 2-class selectors, so without this override, source order
   alone would decide the flex-direction/justify-content conflict between
   them. Restated here, after both, so a badge cell inside a min-height
   group reliably stays a left title/right badge row instead of being
   flipped to the centered column the min-height rule uses for a plain
   title cell. */
.sl-group--min-height .sl-group-title-cell--with-badge {
	flex-direction: row;
	justify-content: space-between;
}

/* Dividers BETWEEN rows inside a group (including the one right under the
   title cell) stay container width on purpose — only the boundary
   between groups above is full page width. */
.sl-group-row {
	display: grid;
	grid-template-columns: repeat(var(--sl-cols, 1), 1fr);
	border-top: 1px solid var(--block-border);
}

.sl-group-col {
	padding: 1.75rem;
}

/* Column separators stay container-width on purpose — only the
   horizontal section dividers above are full page width. */
.sl-group-col + .sl-group-col {
	border-left: 1px solid var(--block-border);
}

/* A column embedding a widget that already manages its own internal
   spacing (e.g. the logo marquee's own per-logo padding, or
   inc/grid-links-section.php's own card grid) — edge-to-edge, no padding
   of its own on top of that. min-width: 0 overrides the browser default
   of min-width: auto on grid items, which otherwise refuses to shrink
   this column below the intrinsic (max-content) width of a wide child
   like the logo marquee's track — without it, that child was pushing
   this column, and the whole row, wider than the container and spilling
   out its right edge. overflow: hidden is the second half of the fix:
   it clips anything that still overflows once the column itself is
   correctly sized. */
.sl-group-col--flush {
	padding: 0;
	min-width: 0;
	overflow: hidden;
}

/* A column that's a cover image instead of text — edge-to-edge, no
   padding, stretched to the row's full height (the grid's default
   align-items: stretch) so it always matches its sibling text column's
   height regardless of how much copy that column holds. Falls back to a
   soft gradient/grid placeholder panel when no image is supplied (see
   inc/... callers) — the <img>, when present, simply covers it. */
.sl-group-col--media {
	position: relative;
	min-height: 260px;
	padding: 0;
	overflow: hidden;
	background:
		linear-gradient(rgb(var(--accent-rgb) / .06) 1px, transparent 1px),
		linear-gradient(90deg, rgb(var(--accent-rgb) / .06) 1px, transparent 1px),
		linear-gradient(135deg, rgb(var(--accent-rgb) / .16), rgb(var(--sky-rgb) / .12));
	background-size: 32px 32px, 32px 32px, 100% 100%;
}

.sl-group-col--media img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* A column showing a plain number/stat instead of a title+text pair (e.g.
   the "Our footprint in EU regulations" group). With no $stat (an
   evidence-only card, no count), .sl-stat-label--lg carries more visual
   weight to fill the space a number would otherwise take. */
.sl-stat-num {
	font-family: var(--font-head);
	font-size: clamp(28px, 3.2vw, 40px);
	font-weight: 600;
	line-height: 1;
	color: var(--ink-900);
}

.sl-stat-label {
	margin-top: .6rem;
	font-size: 14px;
	color: var(--slate-700);
	line-height: 1.5;
}

.sl-stat-label--lg {
	margin-top: 0;
	font-family: var(--font-head);
	font-size: 19px;
	font-weight: 500;
	color: var(--ink-900);
}

/* No type styles here on purpose — same reasoning as special-service.css:
   .sl-group-title (h2)/.sl-col-title (h3)/.sl-col-text (p) inherit the
   sitewide base typography; only the margin below is touched, reverted
   back from style.css's tighter shared heading margin to h2's own
   UA-stylesheet default. Targets the .sl-group-title class specifically,
   not a bare "h2" tag selector — this page also embeds other components'
   own headings (.dr-feature-card h3, .dr-research-card-title) inside
   .sl-service-grid, each with its own already-correct margin that a
   blanket h2/h3/h4 selector would otherwise clobber at equal specificity. */
.sl-service-grid .sl-group-title {
	margin: revert;
}

/* A title cell can also carry a supporting paragraph directly under the
   heading (e.g. "Not just theory…") instead of a separate text row. */
.sl-group-title-cell p {
	max-width: 640px;
	margin-top: .5rem;
}

/* Two of this page's groups embed einar_partners_features_section()'s
   .dr-features-grid/.dr-feature-card (icon, heading, paragraph hidden
   until hovered/active — see digital-resilience.css/js) instead of a
   plain sl-group-col, for that component's auto-advancing spotlight
   behavior. Icons there are inline <svg> (einar_partners_get_inline_icon())
   wrapped in a .dr-feature-icon span rather than the component's own
   default <img>, so they need their own sizing — the shared CSS only
   sizes the wrapper, not an svg child. */
.sl-group .dr-feature-icon svg {
	width: 100%;
	height: 100%;
	display: block;
}

/* "Customer Logos" — the marquee runs full page width, breaking out of
   .container/.sl-service-grid's own left/right frame instead of sitting
   inside it like every other group's content. Same left/right:50% +
   margin:-50vw breakout trick as .sl-group::before/::after above (this
   resolves to the true viewport edges only because every ancestor up to
   the viewport — .sl-service-grid, .container, #sl-service — stays
   symmetrically centered/unpadded at this level); width:100vw is needed
   explicitly here (unlike those absolutely-positioned pseudo-elements)
   since this is a real, normal-flow grid item. Scoped to this group's own
   id rather than the generic .sl-group-col--flush class, which several
   other groups below also use for their own (container-width) embedded
   widgets. min-width:0/overflow:hidden (already set on --flush above)
   keep the 100vw box from forcing its 1-column grid track to grow to
   match — without them the row/section would gain real horizontal
   overflow instead of just visually breaking out. */
#sl-customer-logos .sl-group-col--flush {
	position: relative;
	left: 50%;
	right: 50%;
	width: 100vw;
	margin-left: -50vw;
	margin-right: -50vw;
	/* Above .sl-service-grid's own left/right vertical frame border, painted
	   further out at the container's edges — that ancestor border sits
	   underneath this breakout row and was showing straight through the
	   marquee's own transparent gaps between logos. The opaque background
	   (matching the page background it sits on) hides it the same way any
	   other opaque section content already does; z-index just makes that
	   stacking explicit rather than relying on paint order alone. */
	z-index: 1;
	background: var(--body-bg);
}

/* "Customer Logos" — taller than the shared 200px default so this
   full-bleed marquee reads as its own section rather than a thin strip;
   scoped to this group's own id per the convention above rather than the
   shared .epc-logo-box default (reused elsewhere, see logo-slider-section.php). */
#sl-customer-logos .epc-logo-box {
	height: 280px;
}

/* The shared component sizes each logo off the box's own full padded
   height (.epc-logo-box img's max-height:100%) — fine at the shared
   200px default, but at this taller 280px box the logos rendered
   oversized next to each other. Capped independently here so the box
   can stay tall while the logos themselves stay a normal marquee size. */
#sl-customer-logos .epc-logo-box img {
	max-height: 100px;
}

@media (max-width: 900px) {
	#sl-customer-logos .epc-logo-box {
		height: 170px;
	}

	#sl-customer-logos .epc-logo-box img {
		max-height: 56px;
	}
}

@media (max-width: 480px) {
	#sl-customer-logos .epc-logo-box {
		height: 130px;
	}

	#sl-customer-logos .epc-logo-box img {
		max-height: 44px;
	}
}

/* ── "Where regulation meets IT Operations" — interactive 2-col split ──
   1 row / 2 col: a vertical list of topic buttons on the left (first
   active by default, click to switch — see digital-resilience.js), a
   full-bleed background panel + bottom-anchored paragraph on the right
   that swaps with whichever button is active. Custom to this one group
   rather than a shared epc-* / dr-features-grid widget — neither fits a
   persistent click-to-select split view. Background uses the same
   gradient/grid placeholder recipe as .sl-group-col--media above (this
   page has no per-topic photography yet) rather than a plain flat panel,
   so it still reads as imagery-in-waiting instead of empty color. */
.sl-itops {
	display: grid;
	grid-template-columns: minmax(260px, 400px) 1fr;
	min-height: 65vh;
}

.sl-itops-buttons {
	display: flex;
	flex-direction: column;
	border-right: 1px solid var(--block-border);
}

.sl-itops-btn {
	display: flex;
	align-items: center;
	gap: 1rem;
	width: 100%;
	padding: 1.75rem 2rem;
	border: none;
	border-bottom: 1px solid var(--block-border);
	background: transparent;
	font-family: inherit;
	text-align: left;
	cursor: pointer;
	transition: background .2s ease;
}

.sl-itops-btn:last-child {
	border-bottom: none;
}

.sl-itops-btn-icon {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	color: rgb(var(--accent-rgb));
	transition: color .2s ease;
}

.sl-itops-btn-icon svg {
	width: 100%;
	height: 100%;
	display: block;
}

.sl-itops-btn-title {
	font-family: var(--font-head);
	font-size: 16px;
	font-weight: 600;
	color: var(--ink-900);
	transition: color .2s ease;
}

.sl-itops-btn:hover {
	background: rgb(var(--accent-rgb) / .06);
}

/* Active state: shared "dark" treatment (solid ink-900) used elsewhere on
   this page for a selected/current item — see the shared pill component's
   own dark mode (global-styles.css). */
.sl-itops-btn.is-active {
	background: var(--ink-900);
}

.sl-itops-btn.is-active .sl-itops-btn-icon,
.sl-itops-btn.is-active .sl-itops-btn-title {
	color: #fff;
}

/* Real photography instead of the placeholder dot-grid texture, once art
   direction had an actual image for this section — same gradient tint
   recipe (accent -> sky) as a wash over it, dark enough to keep the
   white icon/panel-text overlays (below) legible against any part of
   the photo. */
.sl-itops-display {
	position: relative;
	overflow: hidden;
	background:
		linear-gradient(135deg, rgb(var(--accent-rgb) / .75), rgb(var(--sky-rgb) / .65)),
		url('assets/images/dora-nist2-ai-act.webp') center / cover no-repeat;
}

/* Each topic's panel stacks in the same spot; only the active one is
   visible/interactive — crossfades on click instead of layout-shifting. */
.sl-itops-panel {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	padding: 2.5rem;
	opacity: 0;
	visibility: hidden;
	transition: opacity .4s ease;
}

.sl-itops-panel.is-active {
	opacity: 1;
	visibility: visible;
}

.sl-itops-panel-icon {
	flex-shrink: 0;
	width: 72px;
	height: 72px;
	color: rgb(var(--accent-rgb) / .6);
}

.sl-itops-panel-icon svg {
	width: 100%;
	height: 100%;
	display: block;
}

/* The paragraph itself is pinned to the bottom of the panel (justify-
   content:space-between above, this is the 2nd/last flex child) in its
   own "block card" so it stays legible over the textured background
   behind it. */
.sl-itops-panel-text {
	max-width: 520px;
	margin: 0;
	padding: 1.5rem 1.75rem;
	background: var(--block-bg);
	border: 1px solid var(--block-border);
	border-radius: var(--radius-md);
	color: var(--ink-900);
	font-size: 15px;
	line-height: 1.6;
}

@media (max-width: 900px) {
	.sl-itops {
		grid-template-columns: 1fr;
		min-height: 0;
	}

	.sl-itops-buttons {
		border-right: none;
		border-bottom: 1px solid var(--block-border);
	}

	/* Stacked layout: no more crossfading over one shared spot — the
	   inactive panels are removed from flow entirely instead of just
	   hidden, so the display area's height always matches whichever one
	   is showing. */
	.sl-itops-display {
		min-height: 320px;
	}

	.sl-itops-panel {
		position: relative;
		inset: auto;
		visibility: visible;
		display: none;
	}

	.sl-itops-panel.is-active {
		display: flex;
	}
}

/* ── Section-specific sizing tweaks ─────────────────────────────────
   Scoped by each group's own id rather than the generic .sl-group-col/
   .sl-group-row selectors above, so they don't affect every other
   section's columns. */

/* "From regulatory requirements…" — the text column gets extra breathing
   room on desktop; the media column (align-items: stretch, the grid's
   default) matches whatever height that gives the row. */
@media (min-width: 1025px) {
	#sl-regulatory-reality .sl-group-row {
		min-height: 60vh;
	}

	#sl-regulatory-reality .sl-group-row > .sl-group-col:first-child {
		padding: 6rem 6rem 6rem 1.75rem;
	}
}

/* "Your AI knows your systems…" (templates/context-layer.php) — same
   text/media split and desktop spacing as "From regulatory requirements…"
   above. */
@media (min-width: 1025px) {
	#cl-context-gap .sl-group-row {
		min-height: 60vh;
	}

	#cl-context-gap .sl-group-row > .sl-group-col:first-child {
		padding: 6rem 6rem 6rem 1.75rem;
	}
}

/* "We bring the explanatory power…" (templates/enterprise-enablement.php)
   — same text/media split and desktop spacing as "From regulatory
   requirements…"/"Your AI knows your systems…" above. */
@media (min-width: 1025px) {
	#ee-explanatory-power .sl-group-row {
		min-height: 60vh;
	}

	#ee-explanatory-power .sl-group-row > .sl-group-col:first-child {
		padding: 6rem 6rem 6rem 1.75rem;
	}
}

/* "Our delivery model for successful enablement" — same desktop-only
   floor idea as the groups above (dropped below 1025px for the same
   reason: a tall empty row once everything's stacked to one narrow
   column, see .sl-group--min-height's own ≤640px override), just a
   shorter 45vh rather than their 60vh. No first-child padding override
   here (unlike those two-column groups above) — .ee-delivery-nav's own
   flex:1 1 0 buttons and .sl-group-col--media both already fill
   whatever height this gives the row on their own. */
@media (min-width: 1025px) {
	#ee-delivery .sl-group-row {
		min-height: 60vh;
	}
}

/* "Our footprint in EU regulations" — plain padding made the 4 stat
   cards read as cramped one-liners; more padding + a min-height + a
   flex column gives the number/label room to breathe. 46vh is clamped
   rather than used bare — on a very tall monitor 46vh alone would make a
   one-line stat card absurdly tall, and on a very short one (small
   landscape tablets) it could shrink below the number/label's own
   content height. justify-content:flex-end (rather than the centered
   default) anchors the number+label group to the bottom of the now-taller
   box instead of floating in the middle of it — the same bottom anchor
   applies to the one label-only card (no $stat), which has just a single
   child here. */
#sl-footprint .sl-group-row > .sl-group-col {
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	min-height: clamp(280px, 46vh, 480px);
	padding: 2.5rem 1.75rem;
}

@media (max-width: 1024px) {
	.sp-hero {
		min-height: 20rem;
		padding: 4rem 1.5rem;
	}
}

/* "Our footprint in EU regulations" — 4 stat cards across a tablet-width
   row leaves each one only ~150-200px wide, cramping the longer labels
   (e.g. "Critical Infrastructure across public sector, aviation and
   regulated industries") into several narrow-wrapped lines. Mutually
   exclusive with the ≤640px block below (which drops to a single
   column instead) rather than just a max-width, so the two never fight
   over the same viewport range. */
@media (min-width: 641px) and (max-width: 1024px) {
	#sl-footprint .sl-group-row {
		grid-template-columns: repeat(2, 1fr);
	}

	/* Same reasoning as the ≤640px override further below — the desktop
	   min-height clamp is tuned for a wide 4-across row; in this 2x2 tablet
	   layout it reads as two rows of near-full-screen-height boxes. */
	#sl-footprint .sl-group-row > .sl-group-col {
		min-height: 260px;
	}
}

@media (max-width: 640px) {
	.sl-group-row {
		grid-template-columns: 1fr;
	}

	.sl-group-col + .sl-group-col {
		border-left: none;
		border-top: 1px solid var(--block-border);
	}

	/* The 50vh minimum (.sl-group--min-height, above) reads as mostly
	   empty vertical space once phone width forces everything into a
	   single narrow column — same reasoning as the #sl-footprint
	   min-height drop below. Dropped here so short groups just size to
	   their content. */
	.sl-group--min-height {
		min-height: 0;
	}

	.sl-group-title-cell,
	.sl-group-col {
		padding: 1.35rem 0;
	}

	/* The grid's own left/right vertical frame (.sl-service-grid, above)
	   reads as a page-wide margin/rail at desktop widths; at phone widths,
	   with .sl-group-col's own padding already reduced above, that same
	   frame just eats into already-tight side space for no real benefit —
	   dropped here so content runs edge-to-edge within the container
	   instead. The full-page-width section dividers (.sl-group::before/
	   ::after) are untouched; only this vertical left/right line goes. */
	.sl-service-grid {
		border-left: none;
		border-right: none;
	}

	/* "Our footprint in EU regulations" — the desktop min-height above
	   (clamped up to 46vh/480px so the bottom-anchored label has real
	   breathing room next to its 3 siblings in a wide row) reads as mostly
	   empty space once the row drops to a single stacked column here:
	   4 cards at up to 480px each is ~1600px of mostly-blank scroll on a
	   phone. Dropped back down to a fixed, modest height — still enough
	   for justify-content:flex-end (above) to visibly anchor the text
	   toward the bottom rather than just centering it. */
	#sl-footprint .sl-group-row > .sl-group-col {
		min-height: 200px;
	}
}

/* "Beyond regulatory advisory" / "Research & Publications" — the shared
   .dr-feature-card/.dr-research-card hover states (digital-resilience.css)
   also shift border-color on hover; on this page that read as an
   unwanted flicker, so it's disabled here, scoped to these two groups'
   own ids rather than the shared classes (still wanted elsewhere, e.g.
   the homepage/blog usages of the same cards). The rest of each hover
   state (background fill, image zoom, z-index) is untouched. */
#sl-why-us .dr-feature-card:hover {
	border-color: var(--block-border);
}

#sl-research .dr-research-card:hover {
	border-color: var(--block-border);
}

/* "Our Delivery" — inverted from the shared .epc-chain-step-mark look
   (light box, accent-purple icon via currentColor): solid dark fill,
   white icon, scoped to this group's own id so the other step-chain
   instance on this page ("Not just theory") keeps the shared light
   box/accent-color icon. border-color matches the fill so there's no
   mismatched light ring around the now-dark box. */
#sl-delivery .epc-chain-step-mark {
	background: var(--ink-900);
	border-color: var(--ink-900);
	color: #fff;
}

/* "Your AI knows your systems…" (templates/context-layer.php) — the
   closing paragraph is emphasized per that page's copy, and this group
   carries its own CTA straight after the text instead of waiting for the
   page-end CTA banner. */
#cl-context-gap .sl-col-text--italic {
	font-style: italic;
}

#cl-context-gap .sl-col-actions {
	margin-top: 1.5rem;
}

/* .btn-base (global-styles.css) defaults to white text for the site's
   dark sections — pinned to dark ink here, same recipe as
   .dr-research-explore in digital-resilience.css. */
#cl-context-gap .btn-base {
	color: var(--ink-900);
}
#cl-context-gap .btn-base::before,
#cl-context-gap .btn-base::after {
	background: var(--ink-900);
}

/* "How we make AI work in IT Operations" / "Context & Graph Engineering"
   (templates/context-layer.php) — the small category line under an h2
   (like an eyebrow, but under the title instead of above it) — promoted
   out of the generic .sl-group-title-cell p style (plain body copy)
   since this one reads as a short label/kicker, not a supporting
   sentence. Muted rather than accent-colored so it reads as a secondary
   descriptor, not a second competing accent next to the title. Kept as a
   shared (non id-scoped) class — any .sl-group-title-cell on this page
   can carry one. */
.sl-group-subtitle {
	font-family: var(--font-head);
	font-size: 14px;
	font-weight: 500;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--muted);
}

/* 2 columns × 2 rows: the image spans both rows on one side, the main
   copy (paragraph + checklist) sits in the top row of the other side,
   "Our Philosophy" sits underneath it in the bottom row — a single
   .sl-group-row instead of two stacked ones, so the image can run the
   full height of both text blocks combined instead of matching just one
   of them. .cl-split--media-left/-right pick which side the image lands
   on ("swap columns" from one .sl-group to the next); now used by 3
   sections on this page, so it's a shared modifier pair instead of a
   one-off per section's own id. Explicit grid-column/-row placement
   (grid's auto-placement alone would put the 3rd child back in column 1,
   not row 2 of column 2) undone again below 640px, where .sl-group-row's
   own generic mobile override (grid-template-columns: 1fr, further down
   in this file) drops to a single column and these explicit placements
   would otherwise force a phantom 2nd column back in. */
.cl-split-grid {
	grid-template-rows: repeat(2, auto);
}
.cl-split-grid.cl-split--media-left .cl-split-media {
	grid-column: 1;
	grid-row: 1 / span 2;
}
.cl-split-grid.cl-split--media-left .cl-split-content {
	grid-column: 2;
	grid-row: 1;
}
.cl-split-grid.cl-split--media-left .cl-split-philosophy {
	grid-column: 2;
	grid-row: 2;
}
.cl-split-grid.cl-split--media-right .cl-split-content {
	grid-column: 1;
	grid-row: 1;
}
.cl-split-grid.cl-split--media-right .cl-split-philosophy {
	grid-column: 1;
	grid-row: 2;
}
.cl-split-grid.cl-split--media-right .cl-split-media {
	grid-column: 2;
	grid-row: 1 / span 2;
}
@media (max-width: 640px) {
	/* Selectors mirror the .cl-split--media-left/-right ones above
	   class-for-class (not just ".cl-split-grid .cl-split-media" etc.) so
	   this reset matches their specificity — otherwise, being inside a
	   @media block doesn't lower specificity, so the desktop 3-class
	   rules above would still win at every width and this reset would
	   silently never apply. */
	.cl-split-grid.cl-split--media-left .cl-split-media,
	.cl-split-grid.cl-split--media-left .cl-split-content,
	.cl-split-grid.cl-split--media-left .cl-split-philosophy,
	.cl-split-grid.cl-split--media-right .cl-split-media,
	.cl-split-grid.cl-split--media-right .cl-split-content,
	.cl-split-grid.cl-split--media-right .cl-split-philosophy {
		grid-column: auto;
		grid-row: auto;
	}

	/* Once the reset above drops each section back to plain DOM-order
	   stacking, put the image first on phone widths — reads better than
	   the source order (text/checklist, then philosophy quote, then
	   image) once everything is a single column. Desktop keeps its own
	   left/right split via .cl-split--media-left/-right above. */
	#cl-context-gap .sl-group-col--media {
		order: -1;
	}

	#cl-graph-engineering .cl-split-media {
		order: 1;
	}
	#cl-graph-engineering .cl-split-content {
		order: 2;
	}
	#cl-graph-engineering .cl-split-philosophy {
		order: 3;
	}
}

/* Plain accent-dot bullet list for body copy inside a .sl-group-col
   (e.g. "Giving AI agents the right context…" above) — same small-dot
   bullet language as the homepage's .pillar-items (homepage.css),
   recolored to this page's accent-purple instead of homepage's violet,
   and kept as a shared (non id-scoped) class since any .sl-group-col's
   body copy can reuse it, not just this one group. */
.sl-check-list {
	list-style: none;
	margin: 1rem 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: .65rem;
}

.sl-check-list li {
	position: relative;
	padding-left: 1.25rem;
	color: var(--slate-700);
	line-height: 1.6;
}

.sl-check-list li::before {
	content: '';
	position: absolute;
	left: 0;
	top: .65em;
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var(--gradient-accent);
}

/* "Our Philosophy" / "Our philosophy" — the text-side column of the
   image/content split above. That column itself is the "box" — filled
   with a barely-tinted off-white (the same #faf9ff used for
   .dr-impact-card/.dr-research-card hover fills, digital-resilience.css)
   rather than a hard-bordered card, so it reads as a soft, deliberate
   slab of color next to the image instead of a floating component on
   the page's own white. Kept as a shared (non id-scoped) class — any
   .sl-group in this page's grid can reuse this exact "philosophy" box,
   not just the one it first shipped on. */
.sl-philosophy-col {
	display: flex;
	align-items: center;
	background: #faf9ff;
	padding: 1.75rem 1.35rem;
}

@media (min-width: 1025px) {
	.sl-philosophy-col {
		padding: 3rem 3.5rem 3rem 1.75rem;
	}
}

/* An editorial pull-quote instead of an icon/card treatment inside that
   box: a large decorative quote mark beside a lead-sized statement that
   itself carries a "not this, but this" contrast (the rejected approach
   muted, the actual approach in full dark ink weight). Font scale
   borrowed straight from the type system (--fs-h3/--fw-heading,
   style.css) instead of a one-off size, so it still reads as part of
   this page's own hierarchy, not a foreign component. */
.sl-philosophy {
	display: flex;
	align-items: flex-start;
	gap: 1rem;
}

.sl-philosophy-mark {
	flex-shrink: 0;
	font-family: var(--font-head);
	font-size: 4rem;
	line-height: 1;
	color: rgb(var(--accent-rgb) / .22);
}

.sl-philosophy-label {
	margin: 0 0 .6rem;
	font-family: var(--font-head);
	font-size: 13px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	background: var(--gradient-accent);
	-webkit-background-clip: text;
	-webkit-text-fill-color: transparent;
	background-clip: text;
}

.sl-philosophy-lead {
	margin: 0;
	font-family: var(--font-head);
	font-size: var(--fs-h3);
	font-weight: var(--fw-heading);
	line-height: 1.5;
}

.sl-philosophy-muted {
	color: var(--muted);
}

.sl-philosophy-strong {
	color: var(--ink-900);
	font-weight: 600;
}

@media (max-width: 640px) {
	.sl-philosophy-mark {
		display: none;
	}
}

/* Word-by-word reveal for .sl-philosophy-lead — same look/timing as
   .dr-title-word (digital-resilience.css), split and staggered in by
   the dedicated block in digital-resilience.js (search
   "sl-philosophy-lead"), kept as its own class since the reveal there
   runs per child span (.sl-philosophy-muted/.sl-philosophy-strong) so
   each actual word animates in turn, not each colored phrase as one
   chunk. */
.sl-philosophy-word {
	display: inline-block;
	opacity: 0;
	transform: translateY(22px);
	transition: opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1);
}
.sl-philosophy-word.is-visible {
	opacity: 1;
	transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
	.sl-philosophy-word {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* "The business case for better context" — a 3-across grid of square
   tiles (6 items: 3 cols × 2 rows; "3×3" in the brief meant 3 per row,
   not a fixed 9-item grid — add/remove entries in
   $einar_cl_value_items in the template and rows grow/shrink to fit).
   Grid lines come from the shared-background/1px-gap trick (container
   painted --block-border, each cell painted --block-bg over it) rather
   than individual bordered cards, so cells butt up edge-to-edge like
   one sheet instead of floating tiles with gaps — no outer frame either,
   since this sits directly on a .sl-group-col--flush row and an outer
   border here would double up against .sl-group::after right below it
   (same reasoning as .sl-group-col--collapse-border elsewhere in this
   file). */
.cl-value-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 1px;
	background: var(--block-border);
}

/* Fixed min-height instead of aspect-ratio:1/1 — a true square at this
   3-across width ran much taller than the copy needed. Icon is pinned to
   the top-left corner independent of flow (position:absolute) while the
   heading/text stack is pushed to the bottom edge by the column's own
   justify-content — so every tile reads "icon corner, copy on the
   floor" the same way no matter how long its own title/text run. */
.cl-value-card {
	position: relative;
	min-height: 250px;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	padding: 1.75rem;
	background: var(--block-bg);
}

/* No background chip — plain dark-navy glyph sitting directly on the
   tile. rgb(var(--bg-rgb)) rather than var(--bg): --bg itself swaps to
   white on body.header-theme-light (the default on every page but the
   homepage), which made these icons vanish against the tile — --bg-rgb
   is the fixed, non-swapping form of the same navy (see variables.css). */
.cl-value-icon {
	position: absolute;
	top: 1.75rem;
	left: 1.75rem;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	color: rgb(var(--bg-rgb));
}

.cl-value-icon svg {
	width: 36px;
	height: 36px;
}

.cl-value-title {
	margin: 0 0 .5rem;
	font-family: var(--font-head);
	font-size: 17px;
	font-weight: 600;
	color: var(--ink-900);
}

.cl-value-text {
	margin: 0;
	font-size: 15px;
	line-height: 1.55;
	color: var(--slate-700);
}

@media (max-width: 900px) {
	.cl-value-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 480px) {
	.cl-value-grid {
		grid-template-columns: 1fr;
	}
}

/* "Conceptual understanding is not a 'soft problem'"
   (templates/enterprise-enablement.php) — intro paragraph + the
   "The consequences are real:" lead-in share this wrapper. The column
   below is .sl-group-col--flush (no padding of its own, see the class's
   docblock) so the grid further down can bleed edge to edge; this and
   .ee-consequences-stat below re-add the column's usual 1.75rem inset by
   hand instead. */
.ee-consequences-intro {
	padding: 1.75rem 1.75rem 1rem;
}

.ee-consequences-intro .sl-col-text {
	margin: 0 0 .75rem;
}

/* Lead-in line above the grid — same color as the paragraph above it
   (var(--text), the sitewide body copy color), just without a heading's
   weight, so it reads as a quiet continuation rather than a competing
   title. */
.ee-consequences-label {
	margin: 0;
	font-size: 15px;
	color: var(--text);
}

/* This grid has no per-card paragraph (just an icon + one-line term), so
   .cl-value-card's 250px min-height (tuned for icon+title+text) leaves
   it mostly empty air — sized down to fit its actual content instead,
   with the icon back in normal flow above the title rather than pinned
   to the corner of a tall empty tile. Padding:1px + the grid's own
   .cl-value-grid background (the same shared-background/1px-gap trick
   that draws the lines between cells) now also draws a matching 1px
   line around the whole outside edge, so the grid reads as a bordered
   block flush against the edges of its (flush) parent column instead of
   floating with no frame. */
#ee-consequences .cl-value-grid {
	padding: 1px;
}

#ee-consequences .cl-value-card {
	position: static;
	min-height: 0;
	padding: 1.5rem;
}

#ee-consequences .cl-value-icon {
	position: static;
	margin-bottom: .85rem;
}

#ee-consequences .cl-value-title {
	margin: 0;
}

/* Survey stat below the grid — a plain muted line instead of a bordered
   callout box, consistent with dropping the boxed treatment above. */
.ee-consequences-stat {
	margin: 0;
	padding: 1.25rem 1.75rem 1.75rem;
	font-size: 15px;
	color: var(--muted);
}

/* Both re-add the flush column's usual 1.75rem side inset by hand
   (above) — that's correct at desktop widths, but the sitewide
   ≤640px rule that drops .sl-group-col's own left/right padding to 0
   (this file, near the top) doesn't reach these two, since they're not
   .sl-group-col itself. Matched by hand here so this group runs
   edge-to-edge on phones same as every other group's flush content
   does. */
@media (max-width: 640px) {
	.ee-consequences-intro,
	.ee-consequences-stat {
		padding-left: 0;
		padding-right: 0;
	}
}

/* "Our delivery model for successful enablement"
   (templates/enterprise-enablement.php) — a plain .sl-group like every
   other group on this page (light theme, no bespoke background), using
   --sl-cols:3 for the shared bordered-column grid (.sl-group-col +
   .sl-group-col's border-left, this file above) instead of a one-off
   grid/border recipe: nav buttons / active term's copy / active term's
   image. Same data-index active-state swap as .sl-itops above (buttons/
   panels/images all keep matching data-ee-index, toggled together by
   assets/js/enterprise-enablement.js), just with a third synced list
   for the image and hover added for fine-pointer input — see that file
   for the click-vs-hover split. */
/* The column this sits in is .sl-group-col--flush (no padding of its
   own) so this list can run edge-to-edge with no inset around it.
   height:100% only actually does anything once #ee-delivery .sl-group-row
   has a real, definite height to stretch this column against — the
   desktop-only 45vh floor above (≥1025px). Below that, with no such
   floor, the row/column height is just "auto" (own content), and a
   percentage height against an auto-height ancestor computes as auto
   too (CSS2.1 §10.5) — harmless here since .ee-delivery-nav-btn below
   now has a real min-height of its own either way, so this rule simply
   has nothing to do at that point rather than actively breaking
   anything. */
.ee-delivery-nav {
	display: flex;
	flex-direction: column;
	height: 100%;
}

/* "Same height/width elements, no gap" — a real 48px min-height by
   default (buttons stay genuinely visible/tappable at any width,
   mobile included, regardless of whatever height the column around
   them resolves to) — flex:1 1 0/min-height:0 (this file, media query
   below) only takes over at ≥1025px, where #ee-delivery .sl-group-row's
   own 45vh floor actually gives this column real extra height to
   divide up evenly; below that, min-height:48px is left to size the 5
   buttons to their own natural (roughly-equal, single-line-label)
   content height instead. Width already matches since they fill the
   column's full flush width either way. No left/right border of their
   own (the column's own outer/inter-column framing already provides
   that edge) and border-bottom instead of a full border, :last-child
   removing it — same "single shared line, never doubled" technique as
   .sl-itops-btn above, just stacked with zero gap instead of that
   component's own roomy padding. position:relative + overflow:hidden is
   the anchor/clip for the ::after "flash" sweep below. */
.ee-delivery-nav-btn {
	position: relative;
	overflow: hidden;
	flex: 0 0 auto;
	min-height: 48px;
	display: flex;
	align-items: center;
	padding: 0 1.5rem;
	border: none;
	border-bottom: 1px solid var(--block-border);
	border-radius: 0;
	background: var(--block-bg);
	font-family: var(--font-head);
	font-size: 12px;
	font-weight: 600;
	letter-spacing: .04em;
	text-transform: uppercase;
	text-align: left;
	color: var(--slate-700);
	cursor: pointer;
	transition: background .3s ease, color .25s ease;
}

@media (min-width: 1025px) {
	.ee-delivery-nav-btn {
		flex: 1 1 0;
		min-height: 0;
	}
}

.ee-delivery-nav-btn:last-child {
	border-bottom: none;
}

/* Hover AND the persistent .is-active state share the same look — the
   site's one standard accent gradient (--gradient-accent, same token
   .accent/.sl-philosophy-label use for text elsewhere) as a solid fill
   instead of the flat tinted background other .ee-delivery states use,
   since a plain fill would fight the "flash" sweep below for contrast.
   rgb(var(--white-rgb)) rather than var(--white): --white itself swaps
   to dark navy under body.header-theme-light (this page's theme, see
   variables.css) — the fixed, non-swapping form is what actually keeps
   this text white against the gradient regardless of page theme. */
.ee-delivery-nav-btn:hover,
.ee-delivery-nav-btn.is-active {
	background: var(--gradient-accent);
	color: rgb(var(--white-rgb));
}

/* "Flash" on hover — a straight (no skew) light band that sweeps left
   to right and fades out as it goes, via a keyframe animation rather
   than a plain left transition: a transition only interpolates
   position, so the band either stays full-strength until overflow:
   hidden clips it or needs a second transitioned property fighting for
   the same timing — a single animation with its own opacity keyframe
   fades it out exactly as it reaches the end of the sweep. Runs once
   per hover (not an auto-looping animation — no unprompted motion,
   CLAUDE.md's animation principles), sitting above the gradient fill
   and below the label text (that stays in normal flow, unaffected by
   this absolutely-positioned layer). */
.ee-delivery-nav-btn::after {
	content: '';
	position: absolute;
	top: 0;
	left: -30%;
	width: 30%;
	height: 100%;
	background: linear-gradient(90deg, transparent 0%, rgb(var(--white-rgb) / .35) 50%, transparent 100%);
	opacity: 0;
	pointer-events: none;
}

.ee-delivery-nav-btn:hover::after {
	animation: ee-delivery-flash .3s ease-out;
}

@keyframes ee-delivery-flash {
	0% { left: -30%; opacity: 1; }
	100% { left: 100%; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
	.ee-delivery-nav-btn:hover::after {
		animation: none;
	}
}

/* Panels are stacked in the DOM (all 5), but only the .is-active one is
   ever displayed — display:none rather than the opacity-stack crossfade
   .ee-delivery-image uses below, since panels vary in text length and
   an absolutely-stacked crossfade would need a fixed shared height to
   avoid overlapping mid-transition. */
.ee-delivery-panel {
	display: none;
}

.ee-delivery-panel.is-active {
	display: block;
	animation: ee-delivery-fade .4s ease;
}

.ee-delivery-step-title {
	margin: 0 0 1rem;
	font-family: var(--font-head);
	font-size: 22px;
	font-weight: 600;
	line-height: 1.3;
	color: var(--ink-900);
}

.ee-delivery-step-text {
	margin: 0;
	font-size: 15px;
	line-height: 1.7;
	color: var(--slate-700);
}

@keyframes ee-delivery-fade {
	from { opacity: 0; }
	to { opacity: 1; }
}

/* Image column — position:absolute + inset:0 (not width/height:100%)
   fills the .sl-group-col--media it sits in exactly, however that
   column's own height ended up being resolved (min-height, desktop
   stretch, or content) — a percentage height/width can silently fall
   short of the actual box in an auto-height context (see .ee-delivery-nav
   above), which was leaving a sliver of .sl-group-col--media's own
   decorative gradient/grid placeholder background visible behind this.
   inset:0 has no such dependency: it resolves against the containing
   block's padding box directly. .sl-group-col--media already gives that
   column position:relative + overflow:hidden (see its own docblock
   above), so no extra positioning setup is needed here. All 5 images
   stack inside this same box (position:absolute + inset:0 again) and
   crossfade by opacity instead of display:none/block like the text
   panels, since a fixed-size box has no variable height to fight over. */
.ee-delivery-media {
	position: absolute;
	inset: 0;
}

.ee-delivery-image {
	position: absolute;
	inset: 0;
	opacity: 0;
	transition: opacity .4s ease;
}

.ee-delivery-image.is-active {
	opacity: 1;
}

.ee-delivery-image img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	border-radius: 0;
}

@media (prefers-reduced-motion: reduce) {
	.ee-delivery-panel.is-active {
		animation: none;
	}

	.ee-delivery-image {
		transition: none;
	}
}

/* No extra breakpoint of its own — .sl-group-row's existing ≤640px
   collapse (this file, above) already drops --sl-cols:3 to a single
   column same as every other group on this page; .ee-delivery-nav's
   buttons just stay a plain vertical list at every width above and
   below that, rather than a bespoke tablet-only row layout. */

/* "Why is context graphs for AI important? Simply explained." — the "+"
   toggle sits in the title cell, right-aligned and vertically centered,
   WITHOUT touching how the title itself lays out (still plain,
   left-aligned, exactly like every other .sl-group-title-cell). A flex
   row (display:flex/justify-content:space-between) on .cl-faq-heading
   looked like the obvious way to do that, but this title cell also
   matches .sl-group--min-height .sl-group-title-cell (this file), which
   sets flex-direction:column + justify-content:center at HIGHER
   specificity (2 classes vs. 1) — that rule silently won, so the title
   and button ended up stacked instead of side by side. Positioning the
   button with position:absolute instead sidesteps the fight entirely:
   it's taken out of flow, so whatever flex/column behavior the parent
   ends up with never matters. .cl-faq-heading itself only adds
   position:relative (the anchor for that) and right padding so a long
   title never runs underneath the button.
   Opens a box that appears BELOW the whole title cell — not inside it —
   hence .cl-faq-panel being a sibling of .sl-group-title-cell/
   .sl-group-row, not nested in either. Click handler lives in
   digital-resilience.js (search "cl-faq-toggle"), same IIFE/
   no-op-if-absent wiring style as .sl-itops there. */
.cl-faq-heading {
	position: relative;
	padding-right: 4.5rem;
}

/* Icon-only button — no border/background/padding of its own beyond
   enough tap target, unlike .sl-itops-btn's full bordered row (this
   file), since it sits inline next to the title instead of being a
   standalone row. */
.cl-faq-toggle {
	position: absolute;
	top: 50%;
	right: 1.75rem;
	transform: translateY(-50%);
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 1px solid var(--block-border);
	border-radius: var(--radius-round);
	background: var(--block-bg);
	cursor: pointer;
	transition: background .2s ease;
}

.cl-faq-toggle:hover {
	background: rgb(var(--accent-rgb) / .06);
}

/* Plus → minus indicator, same two-bars-via-pseudo-elements technique as
   the mobile menu's hamburger-to-X icon (site-header-footer.css) — the
   vertical bar scales to nothing on open, leaving just the horizontal
   one. */
.cl-faq-icon {
	position: relative;
	width: 16px;
	height: 16px;
}
.cl-faq-icon::before,
.cl-faq-icon::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	background: rgb(var(--accent-rgb));
	transition: transform .3s ease;
}
.cl-faq-icon::before {
	width: 16px;
	height: 2px;
	transform: translate(-50%, -50%);
}
.cl-faq-icon::after {
	width: 2px;
	height: 16px;
	transform: translate(-50%, -50%);
}
.cl-faq-toggle[aria-expanded="true"] .cl-faq-icon::after {
	transform: translate(-50%, -50%) scaleY(0);
}

/* Height animates via the grid-template-rows 0fr→1fr trick instead of
   max-height/JS-measured scrollHeight — smooth to any content length
   with no JS beyond the class toggle. .cl-faq-panel-inner's min-height:0
   is required for that trick to actually collapse a grid row to 0 (grid
   items default to min-height:auto, which would otherwise floor the row
   at the content's own height regardless of the 0fr track). */
.cl-faq-panel {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows .35s ease;
}
.cl-faq-panel-inner {
	min-height: 0;
	overflow: hidden;
}
.cl-faq-panel.is-open {
	grid-template-rows: 1fr;
}

/* No wrapper card around the revealed content — it's plain column copy,
   same as any other .sl-group-col on this page (which already carries
   its own padding), just with a max-width for a readable line length. */
.cl-faq-panel .sl-group-col {
	max-width: 820px;
}

.cl-faq-intro {
	margin: 0 0 1.25rem;
	font-size: 15px;
	line-height: 1.6;
	color: var(--slate-700);
}

.cl-faq-question {
	margin: 0 0 .75rem;
	font-family: var(--font-head);
	font-size: var(--fs-h4);
	font-weight: var(--fw-heading);
	color: var(--ink-900);
}

.cl-faq-answer {
	margin: 0;
	font-size: 15px;
	line-height: 1.6;
	color: var(--slate-700);
}

@media (prefers-reduced-motion: reduce) {
	.cl-faq-panel,
	.cl-faq-icon::before,
	.cl-faq-icon::after {
		transition: none;
	}
}

/* ===================================================================
   "Service delivery spotlight" — #sl-spotlight/.sl-spotlight-*. A
   reusable, data-driven section (einar_partners_service_spotlight(),
   inc/service-spotlight-section.php; behavior in
   assets/js/service-spotlight.js) — every page passing its own $args
   gets an identical interaction with entirely different content, so
   nothing here should ever reference a specific tab/group/outcome name.

   Concept: a stepped "Owned outcome" picker instead of a static
   statement. It opens as one tall row (.sl-spotlight-band--outcome,
   below) showing nothing but clickable outcome tags — optionally split
   across top-level tabs (.sl-spotlight-tabs, e.g. "All Industries" /
   "Industry-specific") and, inside a tab with more than one group, a
   second tag row for the group choice (.sl-spotlight-tags--groups, e.g.
   "MSP" / "Manufacturing") that reveals that group's own outcome tags.
   Picking a leaf outcome tag shrinks the row to its normal content
   height (.is-compact) and reveals two more rows underneath
   (.sl-spotlight-result) — Capability and Delivered by — each showing
   only the content for that outcome. Same "label column + content
   column" band shape as this file's .sl-group-title-cell/.sl-group-row
   above, framed as one card instead of full-page-width grid dividers,
   since this panel is a standalone unit, not a run of grid rows.

   Deliberately full-bleed dark, unlike every plain-white .sl-group
   above — the one section on the page meant to interrupt the white grid
   rhythm and read as a highlighted moment. --bg/--white/--border are
   re-pinned to fixed literal values on #sl-spotlight itself, exactly
   like #cta does in global-styles.css: those custom properties would
   otherwise swap to their light-header-theme values on every page but
   the homepage (body.header-theme-light, variables.css) and silently
   turn this "always dark" section light. var(--slate)/
   var(--violet-shimmer) below need no such re-pin — homepage.css
   already relies on both as fixed, non-swapping values for muted
   text/accents on a dark navy background. */
/* min-height:100vh + flex-centering the container is the "full height"
   ask — the section always fills the viewport on first paint, whether
   the outcome row is in its tall pre-selection state or has already
   collapsed to .is-compact, rather than shrinking to fit its content
   like every other section on the page. */
#sl-spotlight {
	--bg: #121F36;
	--white: #ffffff;
	--border: rgba(255, 255, 255, .1);
	position: relative;
	overflow: hidden;
	display: flex;
	align-items: center;
	min-height: 100vh;
	background: radial-gradient(ellipse 70% 55% at 50% 0%, rgb(var(--accent-rgb) / .14) 0%, rgb(var(--accent-rgb) / 0) 70%), var(--bg);
	padding: 8rem 0;
}

#sl-spotlight > .container {
	width: 100%;
}

.sl-spotlight-panel {
	border: 1px solid var(--border);
	background: rgba(255, 255, 255, 0.02);
    backdrop-filter: blur(20px);
}


/* Title left, the tab bar (or, once an outcome is picked, the single
   "Select again" control — same .sl-spotlight-tab class, swapped via
   [hidden] by assets/js/service-spotlight.js) right, vertically centered
   in the middle of the bar. Both live directly in the header now (not
   inside the outcome box below), since only one control is ever visible
   at a time and space-between naturally pins whichever one is showing
   to the right edge. */
.sl-spotlight-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	flex-wrap: wrap;
	padding: 1.1rem 1.75rem;
	border-bottom: 1px solid var(--border);
}

/* Reuses .sl-group-title (this file, above) — the exact same heading
   class/scale the plain white grid sections use for their own titles —
   just re-colored white and with its default h2 margin zeroed out, since
   here it sits alone in a padded bar instead of a .sl-group-title-cell.
   Also carries .dr-section-title (set in the PHP) for its INITIAL
   word-by-word reveal — the exact same shared mechanic/observer as every
   other section title on the site (digital-resilience.js's "SECTION
   TITLES" block), so no new reveal code was needed for that first
   appearance. The text SWAP between "picking" and "selected" states
   (assets/js/service-spotlight.js) is a separate, simpler opacity
   crossfade — see .sl-spotlight-header .sl-group-title.is-swapping
   below — since re-running the word-split reveal on every pick would be
   a lot of motion for a state that can change repeatedly. */
.sl-spotlight-header .sl-group-title {
	margin: revert;
	color: var(--white);
	transition: opacity .2s ease;
}

.sl-spotlight-header .sl-group-title.is-swapping {
	opacity: 0;
}

.sl-spotlight-band {
	display: flex;
	flex-direction: row;
}

.sl-spotlight-band + .sl-spotlight-band {
	border-top: 1px solid var(--border);
}

.sl-spotlight-band-label {
	flex-shrink: 0;
	width: 190px;
	box-sizing: border-box;
	border-right: 1px solid var(--border);
	padding: 2.25rem 1.5rem;
	display: flex;
	align-items: center;
}

/* Smaller than the header title on purpose — a quiet row label, not a
   second competing heading. */
.sl-spotlight-band-label span {
	font-family: var(--font-head);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--slate);
}

.sl-spotlight-band-content {
	flex: 1;
	min-width: 0;
	box-sizing: border-box;
	padding: 2.25rem 1.75rem;
	display: flex;
	flex-direction: column;
	justify-content: center;
}

/* The "pick one" row — tall (most of the section's height) until a leaf
   outcome tag is chosen (assets/js/service-spotlight.js adds
   .is-compact to this exact element), then it collapses back to
   whatever height its own content needs, same as every other band.
   min-height (not height) so a page with an unusually long tag list
   still gets pushed taller instead of overflowing/clipping. */
.sl-spotlight-band--outcome {
	min-height: 56vh;
	transition: min-height .6s cubic-bezier(.22, 1, .36, 1);
}

.sl-spotlight-band--outcome.is-compact {
	min-height: 0;
}

/* The "OWNED OUTCOME" label column is hidden while picking — the tall
   row shows nothing but the tag content, full width, no label competing
   for attention — and reveals once .is-compact lands (assets/js/
   service-spotlight.js), the same moment the Capability/Delivered by
   rows below start their own reveal. Scoped to THIS band only
   (.sl-spotlight-band--outcome .sl-spotlight-band-label, not the bare
   .sl-spotlight-band-label rule above) — Capability's and Delivered
   by's own labels stay exactly as they were, always present, with their
   own separate fade-in (.sl-spotlight-result .sl-spotlight-band-label
   span, below).

   The column's own width/padding/border-color transition here is
   structural, not the visual "reveal" itself — it's what lets
   .sl-spotlight-band-content (flex:1) fill the freed space continuously
   instead of jumping, and it deliberately runs FASTER (.4s) than the
   text's own fade below so it's mostly settled before the text starts
   moving, rather than reading as competing motion. border-right stays
   1px solid at all times and only its COLOR transitions transparent→
   var(--border) — border-style flips (solid⇄none) don't animate, so
   toggling the color channel alone is what keeps this smooth.

   The actual "reveal" visitors read — opacity + a slight upward
   translateY — lives on the SPAN below, not this outer column: matching
   Capability/Delivered by's own label fade-in exactly (same easing/
   duration language) is what fixed this row visually reading as a
   different "growing sideways" animation next to their "fading upward"
   one. Its .2s delay lets the column's own width settle first. */
.sl-spotlight-band--outcome .sl-spotlight-band-label {
	width: 0;
	padding: 2.25rem 0;
	border-right-color: transparent;
	overflow: hidden;
	transition: width .4s cubic-bezier(.22, 1, .36, 1), padding .4s cubic-bezier(.22, 1, .36, 1), border-color .3s ease;
}

.sl-spotlight-band--outcome.is-compact .sl-spotlight-band-label {
	width: 190px;
	padding: 2.25rem 1.5rem;
	border-right-color: var(--border);
}

.sl-spotlight-band--outcome .sl-spotlight-band-label span {
	display: inline-block;
	opacity: 0;
	transform: translateY(8px);
	transition: opacity .45s ease, transform .45s ease;
}

.sl-spotlight-band--outcome.is-compact .sl-spotlight-band-label span {
	opacity: 1;
	transform: translateY(0);
	transition-delay: .2s;
}

/* Top-level tabs (e.g. "All Industries" / "Industry-specific") — now
   live in the header (see .sl-spotlight-header above), a plain flex row
   next to the title rather than a corner overlay; the width/height
   constraints that used to matter for the old absolute-positioned
   version are gone. */
.sl-spotlight-tabs {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1.5rem;
}

/* Same [hidden]-vs-display:flex specificity fix as .sl-spotlight-tags
   elsewhere in this file — assets/js/service-spotlight.js hides this
   whole bar (swapping it for #sl-spotlight-reset) once an outcome is
   picked, and without this override it would stay visibly flexed. */
.sl-spotlight-tabs[hidden] {
	display: none;
}

/* Underline mechanic/colors copied from .btn-base (global-styles.css):
   ::before is the permanent dim (0.1 opacity) baseline line; ::after is
   the full-opacity line, pinned to 0 width at rest and grown in on
   hover/active via the same scaleX transform — see that file's own
   comment for why transform (not width) and the will-change/
   backface-visibility pair. Triggered by .is-active here in addition to
   :hover, since a tab's underline needs to STAY grown while it's the
   selected one, not just while the cursor happens to be over it.
   .sl-spotlight-reset (the "Select again" control) reuses this exact
   class, so it gets the identical look for free — no separate rule set. */
.sl-spotlight-tab {
	appearance: none;
	background: none;
	border: none;
	position: relative;
	padding: 0 0 .5rem;
	font-family: var(--font-head);
	font-size: 12px;
	font-weight: 500;
	letter-spacing: var(--tracking-wide);
	color: var(--slate);
	cursor: pointer;
	transition: color .2s ease;
}

.sl-spotlight-tab::before,
.sl-spotlight-tab::after {
	content: '';
	position: absolute;
	bottom: 0;
	left: 0;
	right: 0;
	height: 1px;
	display: block;
	background: var(--white);
}

.sl-spotlight-tab::before {
	opacity: .1;
}

.sl-spotlight-tab::after {
	transform: scaleX(0);
	transform-origin: right;
	transition: transform .5s;
	will-change: transform;
	backface-visibility: hidden;
}

.sl-spotlight-tab:hover {
	color: var(--white);
}

.sl-spotlight-tab.is-active {
	color: var(--white);
}

.sl-spotlight-tab:hover::after,
.sl-spotlight-tab.is-active::after {
	transform: scaleX(1);
	transform-origin: left;
}

/* .sl-spotlight-reset always reads as "active" (full-strength underline
   at rest, not just on hover) — unlike the tabs, it's the only control
   showing at that point, so there's no "which one is selected" state to
   distinguish; it's simply always interactive-looking. */
.sl-spotlight-reset {
	color: var(--white);
}

.sl-spotlight-reset::after {
	transform: scaleX(1);
	transform-origin: left;
}

.sl-spotlight-tab-panel {
	width: 100%;
}

/* The group-tag row (e.g. "MSP" / "Manufacturing") sits above the
   outcome tags it reveals, set off by its own dashed divider — a visual
   "step 1, step 2" cue distinguishing the group choice from the outcome
   choice even though both use the same .sl-spotlight-tag styling.
   Omitted entirely by the PHP when a tab has only one group (its own
   docblock), so this only ever appears alongside outcome tags one level
   below it, never on its own.

   The divider itself stays transparent (not just absent) until a group
   is actually picked — before that, there's nothing below it to divide,
   so a visible dashed line would read as a stray design artifact over
   an otherwise-empty gap. assets/js/service-spotlight.js adds
   .has-selection to this element the moment a group tag is clicked
   (and removes it again on tab-switch/"Select again", alongside every
   other per-group reset), matching exactly when the outcome tags below
   it become visible. border-WIDTH stays 1px throughout (only the color
   channel transitions, same border-color-only technique as the "OWNED
   OUTCOME" label column above) so the margin/padding around it never
   shifts. */
.sl-spotlight-tags--groups {
	margin-bottom: 1.5rem;
	padding-bottom: 1.5rem;
	border-bottom: 1px dashed transparent;
	transition: border-color .3s ease;
}

.sl-spotlight-tags--groups.has-selection {
	border-bottom-color: var(--border);
}

.sl-spotlight-tags {
	display: flex;
	flex-wrap: wrap;
	gap: .75rem;
}

/* [hidden]'s own UA-stylesheet display:none ties in specificity with the
   plain-class display:flex above (both 0-1-0) — without restating it
   here at equal-but-later (so it wins the cascade), a hidden group of
   outcome tags (e.g. Manufacturing's, before that group is picked) would
   render visible anyway. Same reasoning/fix on .sl-spotlight-capabilities
   and .sl-spotlight-roles below. .sl-spotlight-tag itself needs no such
   override — plain buttons set no `display` of their own, so a hidden
   ONE tag (assets/js/service-spotlight.js hides every tag but the
   chosen one on pick) never fights [hidden] the way a `display:flex`
   container does. */
.sl-spotlight-tags[hidden] {
	display: none;
}

/* Optional per-outcome description (einar_partners_service_spotlight()'s
   $outcome['description']) — MULTI-outcome variant only (2+ outcomes to
   pick from, .sl-spotlight-outcome-solo-desc below is the single-package
   one): one sentence read directly under the Owned outcome tag once it's
   the picked outcome. The PHP omits the element entirely for any outcome
   that doesn't pass one, so pages with no description (e.g.
   templates/context-layer.php, templates/platform-advisory.php) are
   unaffected; shown/hidden the same generic [data-outcome-panel] way as
   the Capability/Delivered by rows below, so assets/js/service-spotlight.js
   needs no per-element wiring. */
.sl-spotlight-outcome-desc {
	margin: 1.25rem 0 0;
	max-width: 640px;
	font-size: 14px;
	line-height: 1.6;
	color: var(--slate);
}

/* Single-package layout — rendered instead of the .sl-spotlight-tags
   picker above when einar_partners_service_spotlight() resolves to
   exactly one outcome (nothing to choose, so no button/tag at all): the
   outcome's own label reads as a small pill pinned to the top-right
   corner (align-self, not absolute positioning, so it stays correct at
   any content height/width instead of needing its own breakpoint rules),
   and its description becomes the box's real headline. Sits inside
   .sl-spotlight-band-content (flex:1; flex-direction:column already, see
   above), so this is just one more full-width column item — no extra
   layout scaffolding needed. */
.sl-spotlight-outcome-solo {
	display: flex;
	flex-direction: column;
	gap: 1.5rem;
}

.sl-spotlight-outcome-solo-tag {
	align-self: flex-start;
	background: var(--violet-shimmer);
	border: 1px solid var(--violet-shimmer);
	padding: .3rem 1rem;
	font-family: var(--font-head);
	font-size: 13px;
	font-weight: 600;
	color: var(--bg);
}

.sl-spotlight-outcome-solo-desc {
	margin: 0;
	font-family: var(--font-head);
	font-size: clamp(22px, 2.6vw, 24px);
	font-weight: 500;
	line-height: 1.4;
	color: var(--white);
}

/* Plain buttons, not a custom widget — appearance:none plus the reset
   below is what turns off each one's native button chrome so the pill
   styling reads consistently across browsers. aria-pressed (toggled by
   assets/js/service-spotlight.js) carries the actual selected state;
   the color/border/background change below is purely the visual
   reflection of it.

   transition-property/-duration/-delay are split into parallel lists
   (not the usual shorthand) so a per-tag STAGGERED DELAY can be applied
   to opacity alone (via nth-child below, the initial fade-in-on-reveal)
   without also delaying the hover/press feedback on background/border-
   color/color/transform/box-shadow — those always stay instant-on-
   hover regardless of how late this tag's own entrance was staggered. */
.sl-spotlight-tag {
	appearance: none;
	background: rgb(255 255 255 / .05);
	border: 1px solid rgb(255 255 255 / .18);
	padding: 1rem 1.85rem;
	font-family: var(--font-head);
	font-size: 15.5px;
	font-weight: 600;
	color: var(--white);
	cursor: pointer;
	box-shadow: 0 1px 2px rgb(0 0 0 / .12);
	opacity: 0;
	transition-property: opacity, background, border-color, color, transform, box-shadow;
	transition-duration: .5s, .2s, .2s, .2s, .2s, .2s;
	transition-timing-function: ease;
	transition-delay: 0s;
}

/* The stagger itself — label-then-items reveal cue shared with the
   result rows below: only OPACITY gets a per-position delay (the list
   order matches transition-property above; 0s for every other
   property keeps hover/press instant no matter how late a tag faded
   in). Covers up to 6 tags/group-tags per row, comfortably above this
   component's real-world lists (5 outcomes is the largest today). */
.sl-spotlight-band--outcome.is-visible .sl-spotlight-tag {
	opacity: 1;
}
.sl-spotlight-band--outcome.is-visible .sl-spotlight-tags .sl-spotlight-tag:nth-child(1) { transition-delay: .1s, 0s, 0s, 0s, 0s, 0s; }
.sl-spotlight-band--outcome.is-visible .sl-spotlight-tags .sl-spotlight-tag:nth-child(2) { transition-delay: .17s, 0s, 0s, 0s, 0s, 0s; }
.sl-spotlight-band--outcome.is-visible .sl-spotlight-tags .sl-spotlight-tag:nth-child(3) { transition-delay: .24s, 0s, 0s, 0s, 0s, 0s; }
.sl-spotlight-band--outcome.is-visible .sl-spotlight-tags .sl-spotlight-tag:nth-child(4) { transition-delay: .31s, 0s, 0s, 0s, 0s, 0s; }
.sl-spotlight-band--outcome.is-visible .sl-spotlight-tags .sl-spotlight-tag:nth-child(5) { transition-delay: .38s, 0s, 0s, 0s, 0s, 0s; }
.sl-spotlight-band--outcome.is-visible .sl-spotlight-tags .sl-spotlight-tag:nth-child(6) { transition-delay: .45s, 0s, 0s, 0s, 0s, 0s; }

.sl-spotlight-tag:hover {
	border-color: rgb(255 255 255 / .4);
	background: rgb(255 255 255 / .1);
	transform: translateY(-2px);
	box-shadow: 0 6px 14px rgb(0 0 0 / .22);
}

.sl-spotlight-tag:focus-visible {
	outline: 2px solid var(--violet-shimmer);
	outline-offset: 2px;
}

/* Selected state — solid accent fill reads as "chosen" against the
   otherwise all-outline tag field; --bg (this section's own re-pinned
   dark navy, above) as the text color rather than a fixed near-black
   keeps it correct even if --bg is ever retuned. */
.sl-spotlight-tag[aria-pressed="true"] {
	background: var(--violet-shimmer);
	border-color: var(--violet-shimmer);
	color: var(--bg);
	font-weight: 600;
}

/* Capability/Delivered by. Two ways .is-visible lands here, per outcome-
   count (inc/service-spotlight-section.php):
   - Multi-outcome (a real tag to pick): hidden ([hidden], no transition —
     display:none can't animate) until assets/js/service-spotlight.js
     removes [hidden] and adds .is-visible a frame later, on the outcome
     pick itself.
   - Single-package (nothing to pick, pre-selected from render): never
     [hidden], and this element also carries the shared .dr-reveal class
     (assets/js/digital-resilience.js) — its own IntersectionObserver adds
     .is-visible once the visitor actually scrolls it into view, same as
     every other scroll-triggered section on the site (e.g.
     templates/platform-advisory.php's .sl-group rows), instead of the
     whole timeline below firing at page load while the section is still
     off-screen.
   Either way, once .is-visible lands the exact same choreography plays:

   The two rows are staged in sequence, not together: Owned Outcome
   settles first (.sl-spotlight-band--outcome, above — its column/text
   reveal finishes around .65s), THEN Capability fades in and finishes,
   THEN Delivered by. Every delay below is measured from the moment
   .is-visible lands (i.e. from the outcome pick, or the scroll-reveal
   above — not from whichever row started most recently), so the whole
   file reads as one fixed timeline:
     .75s  — this wrapper's own box (border/background) appears —
             delayed to start only once Owned Outcome has settled,
             so no empty bordered box flashes in early.
     .8s   — Capability's label
     .85s+ — Capability's cards, staggered (nth-child rules below)
     1.4s  — Delivered by's label — chosen so it lands only after a
             TYPICAL Capability list (2-3 cards, this component's
             real-world usage — see the docblock on .sl-spotlight-
             capabilities) has fully finished revealing; an outcome
             with more cards than that will slightly overlap the two
             rows rather than staying strictly sequential.
     1.45s+ — Delivered by's roles, staggered
   border-top (not the generic .sl-spotlight-band + .sl-spotlight-band
   rule above) is what separates this wrapper from the outcome box
   above it — that sibling-combinator rule only ever fires BETWEEN
   bands that share this same direct parent, and Capability's actual
   previous sibling is this wrapper <div>, not the outcome band itself,
   so without restating the border here the two would butt together
   with no dividing line once revealed. */
.sl-spotlight-result {
	border-top: 1px solid var(--border);
	opacity: 0;
	transform: translateY(16px);
	transition: opacity .45s ease, transform .45s ease;
	transition-delay: .75s;
}

.sl-spotlight-result.is-visible {
	opacity: 1;
	transform: translateY(0);
}

/* Capability's label — see the timeline above (.sl-spotlight-result). */
.sl-spotlight-band--capability .sl-spotlight-band-label span {
	opacity: 0;
	transition: opacity .35s ease;
}

.sl-spotlight-result.is-visible .sl-spotlight-band--capability .sl-spotlight-band-label span {
	opacity: 1;
	transition-delay: .8s;
}

/* Delivered by's label — starts only once a typical Capability list has
   finished (see the timeline above). */
.sl-spotlight-band--delivered .sl-spotlight-band-label span {
	opacity: 0;
	transition: opacity .35s ease;
}

.sl-spotlight-result.is-visible .sl-spotlight-band--delivered .sl-spotlight-band-label span {
	opacity: 1;
	transition-delay: 1.4s;
}

/* Capability — read-only info cards (name + one-line description), not
   the old bordered-grid "sheet" or the plain pill chips the (clickable)
   outcome tags above use — a sentence of description needs more room
   than a single-line pill comfortably gives. flex-wrap with a flex-
   basis (not a fixed grid column count) so 2 or 3 cards per outcome
   both wrap sensibly without an awkward empty slot. */
.sl-spotlight-capabilities {
	list-style: none;
	margin: 0;
	padding: 0;
	width: 100%;
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
}

.sl-spotlight-capabilities[hidden] {
	display: none;
}

.sl-spotlight-capabilities li {
	flex: 1 1 260px;
	background: rgb(255 255 255 / .02);
	border: 1px solid var(--border);
	padding: 1.25rem 1.5rem;
	display: flex;
	flex-direction: column;
	gap: .5rem;
	opacity: 0;
	transition: opacity .4s ease;
}

/* "Names" bold at 500 weight, not this page's usual 600 — a deliberately
   lighter emphasis than the interactive tags/tabs above, since these are
   read-only labels sitting directly above their own description. Plain
   flex item (no wrapper div needed) now that there's no leading number
   to align it against. */
.sl-spotlight-cap-name {
	font-family: var(--font-head);
	font-size: 15px;
	font-weight: 500;
	color: var(--white);
}

.sl-spotlight-cap-desc {
	margin: 0;
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--slate);
}

/* Closing CTA — sentence + .btn-base (the theme's standard button,
   global-styles.css; --white is already re-pinned to fixed white on
   #sl-spotlight, above, so it renders correctly on this always-dark
   section regardless of the page's own header theme, same as every
   other white-on-dark element here). Sits OUTSIDE .sl-spotlight-panel,
   directly in the section's own .container (inc/service-spotlight-
   section.php) — spans the full container width rather than being
   boxed inside the bordered panel, sentence pinned left and button
   right via justify-content:space-between.
   Its own copy is static (same for every outcome), but it's still
   gated on picking one, same [hidden]/.is-visible pattern as
   #sl-spotlight-result — assets/js/service-spotlight.js shows/hides
   both together on a multi-outcome page's pick. On a single-package
   page (nothing to pick, never [hidden]) it instead carries the shared
   .dr-reveal class, same as #sl-spotlight-result, so it fades in on its
   own scroll-into-view instead — see the fuller explanation on
   .sl-spotlight-result above. */
.sl-spotlight-cta {
	margin-top: 2rem;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem 2rem;
	opacity: 0;
	transform: translateY(12px);
	transition: opacity .4s ease, transform .4s ease;
}

.sl-spotlight-cta.is-visible {
	opacity: 1;
	transform: translateY(0);
}

.sl-spotlight-cta-text {
	margin: 0;
	flex: 1 1 320px;
	font-size: 15px;
	line-height: 1.6;
	color: var(--slate);
}

/* Delivered by — its own band now (not folded under Capability), so
   just a plain flex-wrap row of role badges; no extra "Delivered by"
   sub-label needed since the band's own left-column label already says
   it. */
.sl-spotlight-roles {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 1.5rem;
}

.sl-spotlight-roles[hidden] {
	display: none;
}

.sl-spotlight-roles li {
	display: flex;
	align-items: center;
	gap: .6rem;
	font-size: 14px;
	font-weight: 500;
	color: var(--white);
	opacity: 0;
	transition: opacity .4s ease;
}

.sl-spotlight-role-code {
	flex-shrink: 0;
	border: 1px solid var(--white);
	padding: .25rem .45rem;
	font-family: var(--font-head);
	font-size: 11px;
	font-weight: 600;
	color: var(--white);
}

/* Capability's cards — start right after its own label above (.8s),
   staggered .06s apart. Covers up to 6 cards, comfortably above this
   component's real-world lists (2-3 is typical — see the docblock on
   .sl-spotlight-result above for how that assumption feeds Delivered
   by's own start time below). */
.sl-spotlight-result.is-visible .sl-spotlight-capabilities li {
	opacity: 1;
}
.sl-spotlight-result.is-visible .sl-spotlight-capabilities li:nth-child(1) { transition-delay: .85s; }
.sl-spotlight-result.is-visible .sl-spotlight-capabilities li:nth-child(2) { transition-delay: .91s; }
.sl-spotlight-result.is-visible .sl-spotlight-capabilities li:nth-child(3) { transition-delay: .97s; }
.sl-spotlight-result.is-visible .sl-spotlight-capabilities li:nth-child(4) { transition-delay: 1.03s; }
.sl-spotlight-result.is-visible .sl-spotlight-capabilities li:nth-child(5) { transition-delay: 1.09s; }
.sl-spotlight-result.is-visible .sl-spotlight-capabilities li:nth-child(6) { transition-delay: 1.15s; }

/* Delivered by's roles — same stagger shape as Capability's cards
   above, just picking up at 1.45s (right after Delivered by's own
   label, itself timed to start only once Capability has typically
   finished). */
.sl-spotlight-result.is-visible .sl-spotlight-roles li {
	opacity: 1;
}
.sl-spotlight-result.is-visible .sl-spotlight-roles li:nth-child(1) { transition-delay: 1.45s; }
.sl-spotlight-result.is-visible .sl-spotlight-roles li:nth-child(2) { transition-delay: 1.51s; }
.sl-spotlight-result.is-visible .sl-spotlight-roles li:nth-child(3) { transition-delay: 1.57s; }
.sl-spotlight-result.is-visible .sl-spotlight-roles li:nth-child(4) { transition-delay: 1.63s; }
.sl-spotlight-result.is-visible .sl-spotlight-roles li:nth-child(5) { transition-delay: 1.69s; }
.sl-spotlight-result.is-visible .sl-spotlight-roles li:nth-child(6) { transition-delay: 1.75s; }

@media (max-width: 900px) {
	.sl-spotlight-band--outcome {
		min-height: 420px;
	}
}

@media (max-width: 640px) {
	#sl-spotlight {
		padding: var(--space-7) 0;
	}

	/* The section itself stays full-height (#sl-spotlight's own
	   min-height:100vh, above) even here — only the outcome box's own
	   forced tallness backs off, since a 56vh-tall picker row reads as
	   excessive once .sl-spotlight-band stacks label-above-content
	   (below) on a narrow screen. */
	.sl-spotlight-band--outcome {
		min-height: 0;
	}

	.sl-spotlight-header {
		padding: 1rem 1.25rem;
	}

	.sl-spotlight-band {
		flex-direction: column;
	}

	.sl-spotlight-band-label {
		width: 100%;
		border-right: none;
		border-bottom: 1px solid var(--border);
		padding: 1.1rem 1.25rem;
	}

	/* Same "hidden while picking" collapse as the desktop rule above, but
	   width doesn't apply once .sl-spotlight-band stacks to a column here
	   (the label is already 100% wide, full-width top row rather than a
	   side column) — max-height is the property that actually animates
	   this row away, since height can't transition to/from its "auto"
	   resting size and this label's own content height isn't a fixed,
	   known value to target directly. The cap (100px) is just generous
	   headroom above the label's real ~1-line height, not a real limit. */
	.sl-spotlight-band--outcome .sl-spotlight-band-label {
		width: 100%;
		max-height: 0;
		padding: 0 1.25rem;
		border-bottom-color: transparent;
		transition: max-height .4s cubic-bezier(.22, 1, .36, 1), padding .4s cubic-bezier(.22, 1, .36, 1), border-color .3s ease;
	}

	.sl-spotlight-band--outcome.is-compact .sl-spotlight-band-label {
		width: 100%;
		max-height: 100px;
		padding: 1.1rem 1.25rem;
		border-bottom-color: var(--border);
	}

	.sl-spotlight-band-content {
		padding: 1.5rem 1.25rem;
	}

	.sl-spotlight-tag {
		padding: .75rem 1.35rem;
		font-size: 14px;
	}

	.sl-spotlight-capabilities li {
		flex-basis: 100%;
		padding: 1rem 1.25rem;
	}
}

@media (prefers-reduced-motion: reduce) {
	.sl-spotlight-band--outcome,
	.sl-spotlight-result,
	.sl-spotlight-header .sl-group-title,
	.sl-spotlight-tag,
	.sl-spotlight-result .sl-spotlight-band-label span,
	.sl-spotlight-capabilities li,
	.sl-spotlight-roles li,
	.sl-spotlight-cta {
		transition: none;
		opacity: 1;
	}

	/* No opacity:1/border-color override here (unlike the block above) —
	   these are meant to stay genuinely hidden until .is-compact/
	   .has-selection, just without an animated transition getting there;
	   forcing them permanently visible would defeat the "hidden until
	   picked" behavior itself. */
	.sl-spotlight-band--outcome .sl-spotlight-band-label,
	.sl-spotlight-band--outcome .sl-spotlight-band-label span,
	.sl-spotlight-tags--groups {
		transition: none;
	}
}
/* ---------------------------------------------------------------
   Source: assets/css/component-sections.css
--------------------------------------------------------------- */
/* ===================================================================
   Einar Reusable Component Widgets  v2.0.0
   Small, generic widgets — the spinning circular text badge, an infinite
   logo marquee, and an icon-or-number step chain — rendered by
   einar_partners_get_rotating_badge() (inc/template-tags.php),
   inc/logo-slider-section.php and inc/step-chain-section.php. First used
   embedded inside templates/eu-regulations-compliance.php's sl-group-col
   cells ($args['wrap'] = false — see each function's own docblock), but
   each also supports a standalone $args['wrap'] = true mode (its own
   <section>/heading and bordered "card" chrome, via the --boxed modifier
   below) for reuse on a page that isn't built from service-landing.css's
   sl-group grid. Loaded wherever any of those functions is used (see
   functions.php). Depends on service-landing.css/digital-resilience.css
   for tokens/.dr-reveal — not for any of their page-specific markup.

   Class prefix: epc- ("Einar Partners Component"), distinct from the
   dr-* / sp-* / sl-* prefixes used by page-layout stylesheets, so it's clear
   at a glance which rules are shared widgets vs. one page's own layout.
   =================================================================== */

/* ─────────────────────────────────────────────
   HERO — LEFT-ALIGNED VARIANT — .dr-hero--left, opted into via
   einar_partners_hero_section()'s 'align_left' arg (inc/hero-section.php),
   used only by templates/eu-regulations-compliance.php. digital-
   resilience.php/how-we-help.php keep the shared hero's default centered
   layout (#dr-hero base rules, assets/css/digital-resilience.css)
   untouched — everything below only ever applies where .dr-hero--left
   is present.
───────────────────────────────────────────── */
/* .dr-hero-inner--left (digital-resilience.css skips its centered
   max-width:760px/margin:auto/text-align:center for this class — see
   that rule's own comment) falls back to .dr-hero-inner's OTHER class,
   .container (style.css: width:100%; max-width:var(--container-max)/
   1330px; margin:0 auto; padding:0 1.5rem) — the exact same recipe
   every other section's content column on this page uses (#sl-service >
   .container, etc.), so this box lines up on the same left edge as the
   rest of the page automatically, at the same width, rather than
   approximating it with a different value here. Only text-align needs
   setting explicitly; .container doesn't set one. */
.dr-hero-inner--left {
	text-align: left;
}

/* #dr-hero-title/.dr-hero-lead have no max-width of their own (they
   simply filled .dr-hero-inner's old 760px column) — now that column is
   var(--container-max)/1330px wide, they need their own cap so the text
   doesn't stretch full width. .dr-hero-text already sets its own
   max-width:620px (digital-resilience.css), so it needs no repeat here.
   Scoped under .dr-hero-inner--left (rather than the bare #dr-hero-title/
   .dr-hero-lead) so this can never leak onto some future page that
   reuses einar_partners_hero_section()'s default centered layout while
   also loading this file for one of its other widgets. */
.dr-hero-inner--left #dr-hero-title,
.dr-hero-inner--left .dr-hero-lead {
	max-width: 620px;
}

/* .dr-hero-text's own margin:1.5rem auto 0 (digital-resilience.css)
   would otherwise center ITSELF within .dr-hero-inner--left's now much
   wider column — independent of, and not addressed by, that class's own
   text-align above. .dr-hero-lead has no such margin, so it needs no
   override. */
.dr-hero-inner--left .dr-hero-text {
	margin: 1.5rem 0 0;
}

/* .dr-hero-actions centers the CTA button row via justify-content
   (digital-resilience.css) — this page's hero has no CTA button today,
   but kept for parity in case a future 'align_left' page adds one. */
#dr-hero.dr-hero--left .dr-hero-actions {
	justify-content: flex-start;
}

/* Nudge the pill/title/paragraph block up slightly off dead-center —
   #dr-hero.dr-hero--left is vertically centered (align-items:center,
   inherited from #dr-hero, digital-resilience.css) inside its own
   calc(100vh - 70px) box, which on most desktop viewports reads as sitting
   a bit low/empty-feeling below. Desktop only (matches this file's other
   1024px cutoffs) — the mobile/tablet layout already pins the block near
   the top via align-items:flex-start further below, so it needs no lift. */
@media (min-width: 1025px) {
	.dr-hero-inner--left {
		margin-top: -3rem;
	}
}

/* #dr-hero-canvas's mask-image (digital-resilience.css) fades the
   particle grid out in the CENTER (transparent 42%-58%) and keeps it
   solid at both edges — tuned for the default hero's CENTERED text
   column, which sits in that same middle band. Now that the text sits
   in a left column instead, the fade needs to move there with it:
   transparent under roughly the same left-hand width the text column
   occupies, ramping up to fully solid by the right half, so the grid's
   network lines/hoverable dots read as concentrated on the open right
   side instead of fading out in empty space next to off-center text. */
#dr-hero.dr-hero--left #dr-hero-canvas {
	mask-image: linear-gradient(90deg, transparent 0%, transparent 38%, black 62%, black 100%);
	-webkit-mask-image: linear-gradient(90deg, transparent 0%, transparent 38%, black 62%, black 100%);
}

/* Below the tablet/mobile breakpoint the text column runs full-width
   (.dr-hero-inner--left no longer sits beside an open right half — see
   .container's own responsive width, style.css) instead of stacking
   beside the grid, so a left-right fade no longer makes sense: it would
   fade out a THIN vertical strip of a now full-width text block instead
   of the whole thing. Flipped to a top-bottom fade instead — solid
   removed from the top (behind the text) down to EU_MOBILE_BAND_START
   (digital-resilience.js's buildGrid()/updateFeaturedChips() — kept in
   sync with that same 55% split there), solid from there down, so the
   network lines/hoverable dots concentrate in the open band below the
   text instead of the right side that no longer exists at this width. */
@media (max-width: 1024px) {
	#dr-hero.dr-hero--left #dr-hero-canvas {
		mask-image: linear-gradient(180deg, transparent 0%, transparent 42%, black 68%, black 100%);
		-webkit-mask-image: linear-gradient(180deg, transparent 0%, transparent 42%, black 68%, black 100%);
	}
}

/* Full viewport height, bottom edge flush with the viewport's own
   bottom edge — NOT plain 100vh: #dr-hero renders inside .site-content,
   which is padding-top:70px to clear the fixed #main-nav (style.css) —
   a min-height of plain 100vh added on TOP of that 70px offset would
   push the hero's bottom edge 70px past the viewport's bottom edge
   instead of flush with it. calc(100vh - 70px) accounts for that offset
   so the hero fills exactly the space below the nav. 100dvh variant for
   mobile browsers, same reasoning as elsewhere in this file previously
   — 100vh there is measured against the LARGEST possible viewport
   (address bar hidden), so plain vh math can still end up taller than
   the actually visible area on load. Overrides #dr-hero's own
   min-height:680px (digital-resilience.css). */
#dr-hero.dr-hero--left {
	min-height: calc(100vh - 70px);
}

@supports (min-height: 100dvh) {
	#dr-hero.dr-hero--left {
		min-height: calc(100dvh - 70px);
	}
}

/* Below the tablet/mobile breakpoint, min-height above becomes a fixed
   height instead (same value, plus the matching max-height) — #dr-hero
   is already overflow:hidden (digital-resilience.css), so pinning both
   ends means the hero can never grow past exactly one device-height-
   minus-nav on a phone, regardless of how much title/paragraph copy it
   ends up holding, rather than min-height alone letting it stretch
   taller if that content needs more room than the viewport has. Also
   shifts the text up: align-items:center (#dr-hero, digital-
   resilience.css) is overridden to flex-start with a fixed padding-top,
   putting it a bit below the top edge instead of dead-centered — #dr-
   hero's own padding-top:9rem/padding-bottom:7rem (sized for the
   default hero's shorter min-height:680px) are reduced to leave more
   room for the actual copy within this shorter mobile box. */
@media (max-width: 1024px) {
	#dr-hero.dr-hero--left {
		height: calc(100vh - 70px);
		max-height: calc(100vh - 70px);
		align-items: flex-start;
		padding-top: 4.5rem;
		padding-bottom: 3rem;
	}

	@supports (height: 100dvh) {
		#dr-hero.dr-hero--left {
			height: calc(100dvh - 70px);
			max-height: calc(100dvh - 70px);
		}
	}
}

/* ─────────────────────────────────────────────
   HERO FEATURED HUD CHIPS — .dr-hero-hud-chip, the 3 persistent floating
   readouts created/positioned by assets/js/digital-resilience.js's
   updateFeaturedChips() (isEuHero branch — see that file's own
   comments). Same "block card" visual recipe as the roaming
   #dr-hero-tooltip (digital-resilience.css) — reuses that same markup's
   .dr-hero-tooltip-text/.dr-hero-tooltip-line classes as-is for the text
   itself (monospace lines, green status-line highlight, typing caret —
   all already generic, not scoped to #dr-hero-tooltip specifically) —
   but a distinct outer class, since #dr-hero-tooltip's own
   opacity:0/scale(.5)/translate(-50%,-50%) defaults are built for ITS
   job (fading in centered exactly on the cursor); these chips are
   visible from the start, anchored just above their own fixed point
   instead of centered on it.
───────────────────────────────────────────── */
.dr-hero-hud-chip {
	position: absolute;
	top: 0;
	left: 0;
	z-index: 3;
	pointer-events: none;
	padding: 8px 10px;
	width: max-content;
	border-radius: 10px;
	background: var(--block-bg);
	border: 1px solid var(--block-border);
	box-shadow: var(--block-shadow);
	color: rgb(var(--bg-rgb));
	transform: translate(-50%, calc(-100% - 14px));
}

/* ─────────────────────────────────────────────
   ROTATING BADGE — einar_partners_get_rotating_badge() (inc/template-tags.php)
   Plain HTML/CSS circular text (each character its own absolutely
   positioned span, swept around the circle via rotate()) instead of an
   SVG <textPath> — that was clipping descenders/ascenders against the
   SVG's own viewBox at this badge's small size. .epc-rotating-badge-spin
   is the part that actually rotates, kept separate from the outer
   .epc-rotating-badge box so the box itself can still be sized/positioned
   normally (flex-shrink, gap, etc.) without fighting the animation's own
   transform.
───────────────────────────────────────────── */
.epc-rotating-badge {
	position: relative;
	width: var(--epc-badge-size, 44px);
	height: var(--epc-badge-size, 44px);
	flex-shrink: 0;
}

.epc-rotating-badge-spin {
	position: absolute;
	inset: 0;
	animation: epc-badge-spin var(--epc-badge-duration, 16s) linear infinite;
}

@keyframes epc-badge-spin {
	to {
		transform: rotate(360deg);
	}
}

/* Each character sits in a fixed-width box reaching from the circle's top
   edge down to its center (height: 50%), centered on the vertical
   midline (width + negative margin-left, rather than a transform, so it
   doesn't interfere with the rotate() below) and pivoted from its own
   bottom-center point — rotating it by its assigned angle sweeps it to
   that position on the circle, tilted to follow the curve. */
.epc-rotating-badge-char {
	position: absolute;
	top: 0;
	left: 50%;
	width: 12px;
	margin-left: -6px;
	height: 50%;
	transform-origin: 50% 100%;
	text-align: center;
	font-family: var(--font-head);
	font-size: 6.5px;
	font-weight: 600;
	letter-spacing: .01em;
	text-transform: uppercase;
	color: rgb(var(--accent-rgb));
}

@media (prefers-reduced-motion: reduce) {
	.epc-rotating-badge-spin {
		animation: none;
	}
}

/* ─────────────────────────────────────────────
   LOGO SLIDER — inc/logo-slider-section.php
───────────────────────────────────────────── */
#epc-logos,
.epc-logos-section {
	padding: 5rem 0;
}

.epc-logos-title {
	text-align: center;
	max-width: 640px;
	margin: 0 auto 2.5rem;
	font-size: clamp(22px, 2.6vw, 30px);
}

.epc-logos-wrap {
	position: relative;
	overflow: hidden;
}

/* Standalone-section chrome ($args['wrap'] = true) only — embedded usage
   ($args['wrap'] = false, the primary case) relies on the caller's own
   border/background (e.g. the sl-group-col it sits inside). */
.epc-logos-wrap--boxed {
	border: 1px solid var(--block-border);
	background: var(--block-bg);
}

.epc-logos-track {
	display: flex;
	width: max-content;
	animation: epc-logos-scroll 32s linear infinite;
}

.epc-logos-track:hover {
	animation-play-state: paused;
}

/* Width is a plain fraction of the container's own max-width (not a
   percentage — this box sits in a width: max-content track, where a %
   width is meaningless) rather than a fixed px value, so close to 4
   logos actually fit in one container width at a time, as asked, instead
   of a number that only happened to work out at one particular viewport
   size. */
.epc-logo-box {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: calc(var(--container-max) / 4);
	height: 200px;
	padding: 2rem;
	border-right: 1px solid var(--block-border);
}

@media (max-width: 900px) {
	.epc-logo-box {
		width: 220px;
		height: 130px;
		padding: 1.5rem;
	}
}

/* Phone: 220px was never re-checked below the 900px tablet cutoff — on an
   actual ~390-430px-wide phone it ran over half the screen for ONE logo,
   so only 1-2 boxes (chopped mid-box) ever showed and the infinite-scroll
   track read as broken/barely moving relative to the viewport instead of
   a marquee. */
@media (max-width: 480px) {
	.epc-logo-box {
		width: 150px;
		height: 100px;
		padding: 1rem;
	}
}

.epc-logo-box:last-child {
	border-right: none;
}

.epc-logo-box img {
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
	display: block;
}

/* Optional edge fade, opt-in via $args['fade'] — off by default so the
   slider reads as one continuous strip with no gradient at either end. */
.epc-logos-wrap--fade::before,
.epc-logos-wrap--fade::after {
	content: '';
	position: absolute;
	top: 0;
	bottom: 0;
	width: 80px;
	z-index: 2;
	pointer-events: none;
}

.epc-logos-wrap--fade::before {
	left: 0;
	background: linear-gradient(to right, var(--block-bg), transparent);
}

.epc-logos-wrap--fade::after {
	right: 0;
	background: linear-gradient(to left, var(--block-bg), transparent);
}

@keyframes epc-logos-scroll {
	from {
		transform: translateX(0);
	}
	to {
		transform: translateX(-50%);
	}
}

@media (prefers-reduced-motion: reduce) {
	.epc-logos-track {
		animation: none;
	}
}

/* ─────────────────────────────────────────────
   STEP CHAIN — inc/step-chain-section.php (icon chain or numbered stepper)
───────────────────────────────────────────── */
#epc-chain,
.epc-chain-section {
	padding: 6rem 0;
}

.epc-chain-title {
	text-align: center;
	max-width: 640px;
	margin: 0 auto .75rem;
	font-size: clamp(26px, 3vw, 36px);
}

.epc-chain-intro {
	text-align: center;
	max-width: 640px;
	margin: 0 auto 1.5rem;
	color: var(--slate-700);
}

/* min-height clamped rather than a bare 40vh — see the equivalent note on
   #sl-footprint's stat cards in service-landing.css. This outer element
   just centers the .epc-chain-track row (below) vertically within that
   min-height — the actual step layout/alignment lives on the track, kept
   as a separate element so the two concerns (center the row as a whole
   vs. align items within the row) don't fight over the same flex box. */
.epc-chain {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: clamp(220px, 40vh, 420px);
}

/* align-items:flex-start (not center) so every step's icon/number box
   lines up on the same top edge — steps are equal width/height already
   (.epc-chain-step/-step-mark below), but their labels wrap to different
   line counts, and centering the whole (icon + label) column made the
   boxes drift out of line with each other whenever a neighboring label
   wrapped taller. */
.epc-chain-track {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: space-between;
	gap: 1.5rem 0;
	width: 100%;
}

/* Standalone-section chrome ($args['wrap'] = true) only — embedded usage
   ($args['wrap'] = false, the primary case) relies on the caller's own
   border/background/padding (e.g. the sl-group-col it sits inside). */
.epc-chain--boxed {
	border: 1px solid var(--block-border);
	background: var(--block-bg);
	padding: 2.5rem 1.5rem;
}

.epc-chain-step {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	width: 130px;
	gap: .75rem;
}

/* Boxed mark — a bordered "md" card around the icon/number instead of it
   floating bare, so each step reads as a distinct tile rather than just an
   icon glyph sitting on the page background. position:relative is the
   anchor for the small corner number badge below (icon+number steps
   only). */
.epc-chain-step-mark {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 88px;
	height: 88px;
	border: 1px solid var(--block-border);
	border-radius: var(--radius-md);
	background: var(--block-bg);
	color: rgb(var(--accent-rgb));
	font-family: var(--font-head);
	font-size: 28px;
	font-weight: 600;
}

.epc-chain-step-mark svg {
	width: 56px;
	height: 56px;
}

/* Small number badge pinned over the box's top-right corner — used when a
   step carries BOTH an icon and a number (e.g. "Our Delivery"'s 01-05
   steps, templates/eu-regulations-compliance.php), so the sequence order
   still reads at a glance without replacing the icon itself. */
.epc-chain-step-num {
	position: absolute;
	top: -9px;
	right: -9px;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	border-radius: var(--radius-round);
	background: rgb(var(--accent-rgb));
	color: #fff;
	font-family: var(--font-head);
	font-size: 11px;
	font-weight: 600;
	line-height: 1;
}

.epc-chain-step-label {
	font-size: 13px;
	font-weight: 500;
	color: var(--ink-900);
	line-height: 1.35;
}

/* align-self:flex-start + a fixed margin-top (rather than relying on the
   track's own cross-axis alignment) centers the arrow against the fixed
   88px icon mark specifically — (88px mark - 16px arrow) / 2 — instead
   of against the full flex line height, which varies with how many
   lines a neighboring step's label wraps to. */
.epc-chain-arrow {
	display: flex;
	align-items: center;
	align-self: flex-start;
	flex-shrink: 0;
	margin-top: 36px;
	color: var(--block-border);
}

.epc-chain-arrow svg {
	width: 16px;
	height: 16px;
}

.epc-chain-note {
	margin: 2rem auto 0;
	max-width: 720px;
	padding-top: 1.5rem;
	border-top: 1px solid var(--block-border);
	text-align: center;
	font-size: 14px;
	font-style: italic;
	color: var(--muted);
}

@media (max-width: 900px) {
	.epc-chain {
		min-height: 0;
	}

	.epc-chain-track {
		flex-direction: column;
		align-items: flex-start;
		justify-content: center;
	}

	.epc-chain-step {
		flex-direction: row;
		justify-content: stretch;
		width: 100%;
		gap: 1.5rem;
	}

	.epc-chain-step-mark svg {
		width: 40px;
		height: 40px;
	}

	.epc-chain-arrow {
		align-self: self-start;
		margin-top: 0;
		margin-left: 22px;
		rotate: 90deg;
		color: var(--muted);
	}
}

/* Phone: the 88px icon mark (60px svg inside) was never re-checked below
   900px either — stacked in a single column on an actual phone it read as
   oversized relative to the label text next to it. */
@media (max-width: 480px) {
	.epc-chain-step-mark {
		width: 60px;
		height: 60px;
	}

	.epc-chain-step-num {
		top: -7px;
		right: -7px;
		width: 20px;
		height: 20px;
		font-size: 10px;
	}
}

/* ─────────────────────────────────────────────
   DARK MODE — .sl-group--dark, a reusable modifier for any .sl-group
   (service-landing.css). Add the class to a group's own div to swap it
   from the sitewide light theme to a dark navy fill with light text —
   same "dark section on an otherwise light page" recipe already used by
   the shared CTA (#cta, global-styles.css): pin --bg/--white/--muted
   locally so body.header-theme-light's swap (variables.css) can't reach
   custom properties that inherit down from this element instead of
   :root. First use: #ee-modern-enablement
   (templates/enterprise-enablement.php, a .dr-features-grid/.dr-feature-card
   group), but deliberately not scoped to that id — any other .sl-group
   reusing this same pattern on another page just adds the class too, no
   new CSS needed here.
───────────────────────────────────────────── */
.sl-group--dark {
	--bg: #121F36;
	--white: #ffffff;
	--muted: #64748b;
	background: var(--bg);
}

/* The row's own top divider (between the title-cell and the row below,
   service-landing.css) is var(--block-border) — a near-invisible hairline
   on the sitewide light background, but plainly visible (and looking
   like a stray light line) against this dark fill. The group's OWN top/
   bottom full-page-width dividers (.sl-group::before/::after) are left
   alone — those mark the seam against this page's (light) neighboring
   groups, not this group's own background, so var(--block-border) is
   still the right color for them regardless of what's inside. */
.sl-group--dark .sl-group-row {
	border-top-color: rgb(var(--white-rgb) / .12);
}

/* #ee-modern-enablement specifically (not every .sl-group--dark use) hides
   that row divider outright — with only a title-cell above and no second
   row below it, the line had nothing to actually divide. */
#ee-modern-enablement .sl-group-row {
	border-top: none;
}

.sl-group--dark .sl-group-title,
.sl-group--dark .sl-group-title-cell p {
	color: rgb(var(--white-rgb));
}

/* .dr-feature-card's own base/hover colors (digital-resilience.css) are
   tuned for a light card on a light page (var(--block-bg) fill, #fff on
   hover) — overridden here for dark mode. At rest the card matches
   var(--bg) exactly (the same background the title-cell sits on, since
   neither has a background of its own beyond the group's), so cards
   read as flush with the section instead of standing out as distinct
   tiles; hovering then reveals them with the translucent-white fill
   that used to be the resting color. */
.sl-group--dark .dr-feature-card {
	background: var(--bg);
	border-color: rgb(var(--white-rgb) / .12);
	color: rgb(var(--white-rgb));
}

.sl-group--dark .dr-feature-card:hover {
	background: rgb(var(--white-rgb) / .04);
	border-color: rgb(var(--accent-rgb) / .5);
}

.sl-group--dark .dr-feature-card p {
	color: var(--slate);
}

/* Mobile full-bleed: .sl-group--dark's plain `background` above is boxed
   to .sl-service-grid's width (the padded content box of #sl-service's
   .container, service-grid-section.php) — fine on tablet/desktop, but on
   phones the 1.5rem container gutter reads as unwanted white space either
   side of what should look like a full-width dark panel. Can't reuse the
   .sl-group::before/::after pair for this (service-landing.css) — those
   already carry the group's own top/bottom section-divider hairline via
   border-top, and a pseudo-element only exists once each. Instead
   .sl-group-dark-bleed (a real, empty, aria-hidden span, first child of
   .sl-group--dark in the template) becomes the actual background layer
   here, breaking out to the viewport edges with the same left/right:50% +
   margin:-50vw trick as those dividers; the group's own `background` is
   cleared so it doesn't show through/double up behind it. z-index:-1
   needs its OWN stacking context to stay scoped to this group — plain
   position:relative with z-index:auto (.sl-group, service-landing.css)
   doesn't create one, so a bare z-index:-1 child would escape it and
   stack against the nearest real one up the tree (the page root),
   painting behind the entire page instead of just this section — visibly
   the section's own light-on-dark text sitting directly on the plain
   page background, background nowhere to be seen. z-index:0 here is what
   turns .sl-group--dark itself into that stacking context, so the bleed
   layer paints behind only this group's own content/dividers instead. */
.sl-group-dark-bleed {
	display: none;
}
@media (max-width: 640px) {
	.sl-group--dark {
		position: relative;
		z-index: 0;
		background: none;
	}
	.sl-group-dark-bleed {
		display: block;
		position: absolute;
		top: 0;
		bottom: 0;
		left: 50%;
		right: 50%;
		margin-left: -50vw;
		margin-right: -50vw;
		background: var(--bg);
		z-index: -1;
	}
}
/* ---------------------------------------------------------------
   Source: assets/css/governance-operating-models.css
--------------------------------------------------------------- */
/* ===================================================================
   "Iceberg" section — templates/governance-operating-models.php only.

   Two-row × two-column grid, plain document flow (no scroll-pin, no
   per-frame JS):
   - Row 1: .gom-iceberg-copy--tip (left) + .gom-iceberg-layer--top
     (right, top-iceberg.svg).
   - Row 2: .gom-iceberg-copy--base (left) + .gom-iceberg-layer--bottom
     (right, bottom-iceberg.svg).
   Each copy block has align-self: center, so it centers against its
   OWN row's height (= its own image's height) rather than the combined
   stack. The two images sit directly on top of each other (grid
   row-gap: 0 between them) as one continuous shape.

   assets/js/governance-operating-models.js drives three independent,
   symmetric (fade in AND out) IntersectionObserver triggers, each using
   the standard "center-line" rootMargin trick (a band around the
   viewport's vertical center, not a single instant): one on each copy
   block (adds/removes .is-visible as it passes through that band), and
   one that also toggles .gom-iceberg.is-phase-base — which swaps which
   image reads as highlighted vs muted, switches the section's own
   background dark, and toggles #main-nav.on-dark-section (see
   site-header-footer.css) — since the image swap, the background and
   the header are all meant to change together at that one moment, same
   idea as front-page.php's light→dark cut between #who-scroll-wrapper
   and #morph-wrapper (see homepage.js/css). That cut is a hard,
   un-faded swap made invisible by both sections being position: sticky
   (so the handoff is an instant full-viewport replace, never a visible
   seam scrolling past); this section is plain document flow, so its
   swap is a genuine color transition instead — a different technique
   for the same visual idea.

   Background: just the faint grid-line pattern (no wash gradient — the
   colored gradient was still visibly cutting off against the plain
   white sections above/below no matter how the mask was tuned, so the
   fade is simpler and more reliable without it: fewer things need to
   agree with each other for it to actually disappear into white).
   Lives on ::before, a separate layer behind the actual content
   (z-index: -1) — masking .gom-iceberg itself would mask its content
   too (the title, copy, images all faded out with it, not just the
   background), which is what happened the first time. A plain vertical
   fade (transparent → solid → transparent) reveals the page's own
   white background at the top/bottom edges, which is what actually
   removes the hard seam — not the mask shape being clever. */
.gom-iceberg {
	position: relative;
	padding: var(--space-11) 0;
	background: var(--body-bg);
	transition: background-color .8s ease;
}
.gom-iceberg.is-phase-base {
	/* rgb(var(--bg-rgb)) rather than var(--bg): --bg itself swaps to
	   white on body.header-theme-light (this page's default theme) —
	   see the note on --bg-rgb in variables.css. */
	background: rgb(var(--bg-rgb));
}
.gom-iceberg::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: -1;
	background:
		linear-gradient(rgb(var(--accent-rgb) / .04) 1px, transparent 1px),
		linear-gradient(90deg, rgb(var(--accent-rgb) / .04) 1px, transparent 1px);
	background-size: 32px 32px;
	-webkit-mask-image: linear-gradient(to bottom, transparent, #000 20%, #000 80%, transparent);
	mask-image: linear-gradient(to bottom, transparent, #000 20%, #000 80%, transparent);
	opacity: 1;
	transition: opacity .8s ease;
}
.gom-iceberg.is-phase-base::before {
	opacity: 0;
}

.gom-iceberg-title {
	margin: 0 0 var(--space-9);
	font-family: var(--font-head);
	font-size: var(--fs-h2);
	font-weight: var(--fw-heading);
	line-height: var(--lh-heading);
	color: var(--ink-900);
	text-align: center;
	transition: color .8s ease;
}
.gom-iceberg.is-phase-base .gom-iceberg-title {
	/* rgb(var(--white-rgb)) rather than var(--white): --white itself
	   swaps to dark navy on body.header-theme-light (this page's default
	   theme) — same reasoning as rgb(var(--bg-rgb)) above. */
	color: rgb(var(--white-rgb));
}

.gom-iceberg-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	column-gap: var(--space-9);
	row-gap: 0;
	align-items: stretch;
}

.gom-iceberg-copy--tip { grid-column: 1; grid-row: 1; }
.gom-iceberg-layer--top { grid-column: 2; grid-row: 1; }
.gom-iceberg-copy--base { grid-column: 1; grid-row: 2; }
.gom-iceberg-layer--bottom { grid-column: 2; grid-row: 2; }

/* Centers each copy block against its own row — i.e. against its own
   image's height, not the combined top+bottom stack — and against its
   own column too (justify-self), so it sits nearer the gutter between
   the two columns instead of pinned flush against the far-left edge.
   width: 100% (not just max-width) matters here: justify-self other
   than stretch makes a grid item shrink-to-fit its own content by
   default, so without an explicit width, the tip block (shorter list
   items) and base block ("Service & Data Models" etc.) would each
   render at a different actual width — centering two different widths
   puts their left edges in different places, which is exactly the
   misalignment this was causing. */
.gom-iceberg-copy {
	align-self: center;
	justify-self: center;
	width: 100%;
	max-width: 320px;
	opacity: 0;
	transform: translateY(16px);
	transition: opacity .6s cubic-bezier(.22,1,.36,1), transform .6s cubic-bezier(.22,1,.36,1);
}
.gom-iceberg-copy.is-visible {
	opacity: 1;
	transform: translateY(0);
}

.gom-iceberg-heading {
	margin: 0 0 var(--space-4);
	font-family: var(--font-head);
	font-size: clamp(1.4rem, 2vw, 1.75rem);
	font-weight: var(--fw-heading);
	line-height: 1.2;
	color: var(--ink-900);
	transition: color .8s ease;
}
/* Only .gom-iceberg-copy--base ever coexists with the dark background
   (--tip's own row is above it, always in the light zone) — scoped so
   .sl-check-list keeps its normal muted-slate look everywhere else on
   the page. */
.gom-iceberg.is-phase-base .gom-iceberg-copy--base .gom-iceberg-heading,
.gom-iceberg.is-phase-base .gom-iceberg-copy--base .sl-check-list li {
	color: rgb(var(--white-rgb));
}

.gom-iceberg-layer {
	display: block;
	width: 100%;
	height: auto;
	align-self: start;
	opacity: 1;
	transition: opacity .9s cubic-bezier(.22,1,.36,1);
}
.gom-iceberg-layer--bottom {
	opacity: .35;
}

.gom-iceberg.is-phase-base .gom-iceberg-layer--top {
	opacity: .35;
}
.gom-iceberg.is-phase-base .gom-iceberg-layer--bottom {
	opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
	.gom-iceberg-copy,
	.gom-iceberg-layer {
		transition: none;
	}
}

/* ---- Mobile: stack, reordered ---------------------------------------
   tip text, top image, bottom image (no gap — one continuous shape,
   same as desktop), base text. row-gap: 0 so the two images sit flush;
   spacing around each text block comes from its own margin instead. */
@media (max-width: 900px) {
	.gom-iceberg-row {
		grid-template-columns: 1fr;
		row-gap: 0;
	}
	.gom-iceberg-copy--tip,
	.gom-iceberg-layer--top,
	.gom-iceberg-layer--bottom,
	.gom-iceberg-copy--base {
		grid-column: 1;
	}
	.gom-iceberg-copy--tip   { grid-row: 1; margin-bottom: var(--space-6); }
	.gom-iceberg-layer--top  { grid-row: 2; }
	.gom-iceberg-layer--bottom { grid-row: 3; }
	.gom-iceberg-copy--base  { grid-row: 4; margin-top: var(--space-6); }

	.gom-iceberg-copy,
	.gom-iceberg-layer {
		opacity: 1;
		transform: none;
	}

	/* Mobile has no "base copy centered" moment to key off (base text
	   now sits after both images, not between them — see the reorder
	   above), so the image crossfade here uses its own trigger instead:
	   assets/js/governance-operating-models.js watches the top image's
	   own position and toggles .is-mobile-phase-base once it crosses
	   the viewport's vertical center. Independent of .is-phase-base —
	   this only ever affects image opacity, not the background/nav dark
	   switch. Placed after the opacity:1 reset above so it wins. */
	.gom-iceberg-layer--bottom {
		opacity: .35;
	}
	.gom-iceberg.is-mobile-phase-base .gom-iceberg-layer--top {
		opacity: .35;
	}
	.gom-iceberg.is-mobile-phase-base .gom-iceberg-layer--bottom {
		opacity: 1;
	}
}

/* ===================================================================
   SCOPE SECTION — #gom-scope-service, the 4 cards below the Iceberg.
   Reuses .dr-features-grid/.dr-feature-card as-is (see the template's
   own comment) — .gom-scope-subtitle is the only new piece: the
   "Service Governance · CMDB Governance · ..." line above each card's
   paragraph, gradient-text (--gradient-accent, same technique as
   .accent/.sl-philosophy-label) instead of a flat accent color, per
   feedback that a solid accent-violet line read as too loud here.
   =================================================================== */

.gom-scope-subtitle {
	display: block;
	margin-bottom: var(--space-2);
	font-size: 13px;
	font-weight: 600;
	letter-spacing: .02em;
	background: var(--gradient-accent);
	-webkit-background-clip: text;
	-webkit-text-fill-color: transparent;
	background-clip: text;
}

/* .dr-feature-card:hover normally shifts border-color to a violet tint
   (digital-resilience.css) — scoped off here only, the shared rule
   elsewhere (how-we-help.php etc.) is untouched. background/z-index
   hover still apply, just not the border color. */
#gom-scope-service .dr-feature-card:hover {
	border-color: var(--block-border);
}

/* .sl-group-title-cell p's shared 640px cap (service-landing.css) reads
   too narrow for this group's longer intro paragraph. */
#gom-scope .sl-group-title-cell p {
	max-width: none;
}

/* Breathing room before the CTA's own dark section starts — a plain
   spacer, not a component; see the .gom-scope-spacer div in the
   template right after this section. */
.gom-scope-spacer {
	height: var(--space-12);
}
/* ---------------------------------------------------------------
   Source: assets/css/contact.css
--------------------------------------------------------------- */
/* ===================================================================
   CONTACT TEMPLATE (+ shared WPForms styling)
   The page-header/.ec-contact-section rules below are for
   templates/contact.php only (enqueued in functions.php when
   is_page_template( 'templates/contact.php' ) is true).

   The hero reuses the shared blog-archive .page-header/.ep-hero-grid
   markup, so assets/css/blog.css (which holds .page-title/
   .archive-description) is enqueued alongside this file — see
   functions.php. Below the hero, the WPFORMS section further down
   overrides WPForms' own default styling for any embedded form so it
   reads as part of this site instead of a generic plugin form — scoped
   to .ec-contact-wrap, which single-event.php's registration section
   (assets/css/event.css) also wraps its own WPForms embed in, reusing
   this same styling rather than duplicating it. contact.css is enqueued
   on that template too for that reason (see functions.php).
   =================================================================== */

/* Shorter hero, matching the blog archive/index's own shorter
   .page-header (see ".blog-layout .page-header" in blog.css) instead of
   the taller 24rem/4rem default every other .ep-hero-grid header falls
   back to (global-styles.css). This page doesn't get the `.blog-layout`
   body class blog.css keys off (that's added only for index.php/
   archive.php, see inc/template-functions.php), so it's scoped here via
   `.page-template-contact` instead — the per-template-segment body class
   WordPress always adds for templates/contact.php (see get_body_class()
   in wp-includes/post-template.php), confirmed against the rendered
   markup rather than guessed. margin-bottom is reset to 0 since
   .ec-contact-section below already provides its own top spacing. */
.page-template-contact .page-header {
	min-height: 16rem;
	padding: 3.5rem 2rem;
	margin-bottom: 0;
}

.ec-contact-section {
	padding: var(--space-9) 0 var(--space-12);
}

.ec-contact-wrap {
	max-width: 720px;
}

.ec-contact-placeholder {
	color: var(--slate-600);
}

/* ===================================================================
   WPFORMS — brings the embedded form in line with the rest of the
   site's forms instead of WPForms' default look:
   - Inputs/textarea/select match assets/css/blog.css's own
     .comment-form treatment (sharp corners, --border3 hairline
     border, var(--font-body)) — the same look WordPress' native
     comment form already uses.
   - The submit button reuses the "dark" mode of the shared pill/tag
     component (global-styles.css — see .blog-cat-pill.is-active in
     the blog archive's category filter for the live reference): solid
     var(--ink-900) fill, white text, var(--radius-sm) corners (that
     component is NOT the full var(--radius-pill) despite the name —
     see global-styles.css's own comment on it).

   WPForms' own stylesheet sets background/border/padding/height
   directly on selectors like
   "div.wpforms-container-full .wpforms-form input[type=text]" and
   "...button[type=submit]" — two classes plus an attribute selector,
   higher specificity than a plain class here, and it's enqueued after
   this file regardless (see functions.php). The !important flags
   below are the same documented trade-off already used for
   .einar-base-btn/.btn-base against Elementor's kit CSS (see
   global-styles.css) — a plugin's own higher-specificity rule, not
   something a plain override could beat without it.
   =================================================================== */

.ec-contact-wrap .wpforms-field {
	padding: 0;
	margin: 0 0 var(--space-6) 0;
	clear: none;
}

.ec-contact-wrap .wpforms-field-label {
	display: block;
	margin: 0 0 var(--space-2) 0;
	font-family: var(--font-body) !important;
	font-size: var(--fs-body) !important;
	font-weight: 400 !important;
	color: var(--ink-900);
}

.ec-contact-wrap .wpforms-required-label {
	color: var(--violet);
	font-weight: 500;
}

.ec-contact-wrap input[type="text"],
.ec-contact-wrap input[type="email"],
.ec-contact-wrap input[type="tel"],
.ec-contact-wrap input[type="url"],
.ec-contact-wrap textarea,
.ec-contact-wrap select {
	background-color: var(--body-bg) !important;
	border: 1px solid var(--border3) !important;
	border-radius: 0 !important;
	color: var(--text) !important;
	font-family: var(--font-body) !important;
	font-size: var(--fs-body) !important;
	line-height: var(--lh-body) !important;
	padding: 0.75rem !important;
	height: auto !important;
	transition: border-color 0.3s;
}

.ec-contact-wrap input:focus,
.ec-contact-wrap textarea:focus,
.ec-contact-wrap select:focus {
	border-color: var(--violet) !important;
	outline: none;
	box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.12);
}

.ec-contact-wrap textarea.wpforms-field-medium {
	min-height: 9rem !important;
}

/* WPForms' <select> has no custom arrow by default; replace the
   native one with a chevron matching --slate-600 since browsers won't
   let the built-in arrow be restyled directly. */
.ec-contact-wrap select {
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23475569' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
	background-repeat: no-repeat !important;
	background-position: right 0.75rem center !important;
	padding-right: 2rem !important;
}

.ec-contact-wrap .wpforms-field-checkbox ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.ec-contact-wrap .wpforms-field-checkbox li {
	display: flex;
	align-items: flex-start;
	gap: var(--space-2);
}

.ec-contact-wrap .wpforms-field-checkbox input[type="checkbox"] {
	accent-color: var(--violet);
	width: 16px;
	height: 16px;
	margin: 0.15rem 0 0 0 !important;
	flex-shrink: 0;
}

.ec-contact-wrap .wpforms-field-label-inline {
	font-size: 0.85rem;
	color: var(--slate-600);
	line-height: var(--lh-body);
}

.ec-contact-wrap .wpforms-submit-container {
	padding: var(--space-4) 0 0 0 !important;
}

.ec-contact-wrap .wpforms-submit {
	display: inline-flex !important;
	align-items: center;
	width: auto !important;
	background: var(--ink-900) !important;
	border: none !important;
	border-radius: 0 !important;
	padding: 0.6rem 2.75rem !important;
	color: #fff !important;
	font-family: var(--font-head) !important;
	font-size: 0.85rem !important;
	font-weight: 500 !important;
	text-transform: uppercase !important;
	letter-spacing: var(--tracking-eyebrow) !important;
	cursor: pointer;
	transition: opacity 0.2s ease;
}

.ec-contact-wrap .wpforms-submit:hover,
.ec-contact-wrap .wpforms-submit:focus {
	opacity: 0.85;
}
/* ---------------------------------------------------------------
   Source: assets/css/event.css
--------------------------------------------------------------- */
/* ===================================================================
   EVENT — mainly single-event.php (enqueued in functions.php when
   is_singular( 'event' ) is true); .event-pill specifically is also
   enqueued on the main blog listing (is_home()) for its featured-event
   banner (template-parts/post-grid.php, assets/css/blog.css). Otherwise
   self-contained: only uses tokens from variables.css and the shared
   pill component in global-styles.css (both loaded on every page
   already), so this file has no dependency on blog.css/
   digital-resilience.css.
   =================================================================== */

/* -----------------------------------------------------------------------
   Hero — a boxed banner inside .container (not full-bleed): the
   featured image fills the box as its background, with the subheading/
   title/date-time pills overlaid on top. Same "block card" bordered
   treatment (--block-border/--block-shadow) as the grid boxes below it
   (.event-grid-box) — reads as one consistent box style down the page,
   just taller and dark-filled for this one. Unlike
   template-parts/post-header.php's .post-header-media, where the title
   sits below the image and only the author/date meta overlays it.
----------------------------------------------------------------------- */
.event-hero {
	padding-top: 2.5rem;
}

.event-hero-box {
	position: relative;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	min-height: 26rem;
	background: var(--ink-900);
	color: #fff;
	border: 1px solid var(--block-border);
	box-shadow: var(--block-shadow);
}

/* No featured image: box still needs real height, just shorter — no
   image behind it to fill, so no need for the full hero height. */
.event-hero-box--no-media {
	min-height: 16rem;
}

.event-hero-media {
	position: absolute;
	inset: 0;
}

.event-hero-media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.event-hero-overlay {
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgba(8, 15, 30, 0.35) 0%, rgba(8, 15, 30, 0.8) 100%);
}

.event-hero-content {
	position: relative;
	z-index: 1;
	padding: 2.5rem;
}

.event-hero-subheading {
	margin: 0 0 0.75rem;
	font-size: 0.8rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: var(--tracking-eyebrow);
	color: rgba(255, 255, 255, 0.75);
}

.event-hero-title {
	margin: 0 0 1.25rem;
	font-size: clamp(2rem, 4vw, 3rem);
	color: #fff;
}

.event-hero-pills {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
}

/* Own pill treatment, not the shared "border"/"dark" pill modes
   (global-styles.css) — both assume a light page background, but this
   sits directly over a photo, so it needs a light-on-dark treatment
   instead. Also reused by the main blog listing's featured-event banner
   (template-parts/post-grid.php, assets/css/blog.css) — same context
   (a chip over a photo), so event.css is enqueued there too for just
   this component (see functions.php). */
.event-pill {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.4rem 0.9rem;
	border-radius: var(--radius-sm);
	border: 1px solid rgba(255, 255, 255, 0.4);
	color: #fff;
	font-size: 0.8rem;
	font-weight: 500;
}

.event-pill svg {
	width: 13px;
	height: 13px;
	flex-shrink: 0;
}

/* -----------------------------------------------------------------------
   Body — one continuous bordered grid, same "shared hairline border"
   recipe as .dr-research-grid/.dr-features-grid (digital-resilience.css:
   1px borders, adjacent cells pulled together by -1px margins so shared
   edges read as one line instead of two), reused here directly rather
   than depending on that whole stylesheet for it. Every .event-grid-box
   is a full-width row (1 row, 1 col); .event-speaker-row is the one
   exception, laying its cards out in N columns (one per speaker) within
   its own single row.

   Nested, not flat: .event-grid is the outer single-column grid (its
   direct children are .event-overview and each <section> below), and
   each <section> is ALSO its own single-column grid for the boxes inside
   it. Two levels, not `display: contents` on the sections — the "pull
   the next box's border up by 1px" rule below relies on the CSS sibling
   combinator (+), which only matches actual DOM siblings; had the
   sections been flattened with display:contents to look like one level,
   a box in one section and a box in the next would no longer be DOM
   siblings of each other, and the collapse would silently stop working
   right at every section boundary. Nesting real grids sidesteps that
   entirely, while still keeping the <section> landmarks for semantics/
   accessibility. */
.event-body {
	padding-bottom: 5rem;
}

.event-grid,
.event-organizers,
.event-agenda,
.event-registration {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0;
}

.event-grid > * + *,
.event-organizers > * + *,
.event-agenda > * + *,
.event-registration > * + * {
	margin-top: -1px;
}

.event-grid-box {
	padding: 28px;
	background: var(--block-bg);
	border: 1px solid var(--block-border);
	box-shadow: var(--block-shadow);
}

.event-grid-title {
	padding-top: 1.25rem;
	padding-bottom: 1.25rem;
}

.event-section-title {
	margin: revert;
	letter-spacing: -0.02rem;
}

.event-overview {
	line-height: 1.8;
	color: var(--slate-700);
}

.event-overview p {
	margin: 0 0 1.5em;
}

.event-overview > *:last-child {
	margin-bottom: 0;
}

.event-organizers-description {
	color: var(--slate-600);
	margin: 0;
}

/* -----------------------------------------------------------------------
   Speaker row — N equal columns (--event-speaker-cols, set inline per
   event in single-event.php), each speaker its own bordered cell
   collapsing against its neighbors the same way .dr-research-card +
   .dr-research-card does.
----------------------------------------------------------------------- */
.event-speaker-row {
	display: grid;
	grid-template-columns: repeat(var(--event-speaker-cols, 3), 1fr);
	gap: 0;
}

.event-speaker-card {
	padding: 28px;
	text-align: center;
	background: var(--block-bg);
	border: 1px solid var(--block-border);
	box-shadow: var(--block-shadow);
}

.event-speaker-card + .event-speaker-card {
	margin-left: -1px;
}

.event-speaker-photo {
	display: block;
	width: 88px;
	height: 88px;
	margin: 0 auto 1rem;
	border-radius: var(--radius-round);
	object-fit: cover;
}

.event-speaker-name {
	margin: 0;
	font-family: var(--font-head);
	font-weight: 600;
	color: var(--ink-900);
}

.event-speaker-role {
	margin: 0.25rem 0 0;
	font-size: 0.85rem;
	color: var(--slate-600);
}

/* -----------------------------------------------------------------------
   Agenda Highlights — simple list, each item's ::before marker uses the
   site accent color already shared by other list/marker treatments.
----------------------------------------------------------------------- */
.event-agenda-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.event-agenda-item {
	position: relative;
	padding: 0.9rem 0 0.9rem 1.5rem;
	border-bottom: 1px solid var(--border3);
	color: var(--slate-700);
}

.event-agenda-item::before {
	content: '';
	position: absolute;
	left: 0;
	top: 1.3rem;
	width: 8px;
	height: 8px;
	border-radius: var(--radius-round);
	background: rgb(var(--accent-rgb));
}

.event-agenda-item:last-child {
	border-bottom: 0;
}

/* -----------------------------------------------------------------------
   Registration — .ec-contact-wrap (contact.css, enqueued alongside this
   file) handles the embedded WPForms form's own styling, but also caps
   its width at 720px there for templates/contact.php's own narrower
   layout. .event-registration-form overrides just that max-width, only
   on this page (event.css loads after contact.css — see the dependency
   in functions.php — so this wins the tie without touching contact.css
   itself, which keeps templates/contact.php's own 720px cap intact). */
.event-registration-form {
	max-width: none;
	width: 100%;
}

/* -----------------------------------------------------------------------
   Responsive
----------------------------------------------------------------------- */
@media (max-width: 782px) {
	.event-speaker-row {
		grid-template-columns: repeat(2, 1fr);
	}

	/* N-up -> 2 cols: shared borders now run along both axes — same
	   recipe as .dr-feature-card's own responsive collapse
	   (digital-resilience.css). Reset first: the base ".event-speaker-card
	   + .event-speaker-card" rule would otherwise still pull every card
	   left by 1px, including the left column's own cards wrapping onto a
	   new row, where that's wrong. */
	.event-speaker-card + .event-speaker-card {
		margin-left: 0;
	}
	.event-speaker-card:nth-child(2n) {
		margin-left: -1px;
	}
	.event-speaker-card:nth-child(n+3) {
		margin-top: -1px;
	}
}

@media (max-width: 640px) {
	.event-hero-box {
		min-height: 20rem;
	}

	.event-hero-box--no-media {
		min-height: 14rem;
	}

	.event-hero-content {
		padding: 1.5rem;
	}

	.event-grid-box,
	.event-speaker-card {
		padding: 20px;
	}

	.event-speaker-row {
		grid-template-columns: 1fr;
	}

	/* Single column now: every card is its own row, so none of them
	   need the horizontal collapse any more — only vertical. */
	.event-speaker-card + .event-speaker-card {
		margin-left: 0;
	}

	.event-speaker-card:nth-child(n+2) {
		margin-top: -1px;
	}
}

/* ===== END COMPILED ===== */
