/**
 * Aledink — accessibility & responsive override layer
 * ===================================================
 * Target: WCAG 2.1 Level AA
 * Built:  2026-08-01
 *
 * Every colour in this file was measured against the live rendered DOM with a
 * WCAG relative-luminance calculation, not estimated by eye. The passing ratio
 * is recorded beside each declaration so a future editor can tell immediately
 * whether a change breaks compliance.
 *
 * SPECIFICITY NOTE
 * Divi prints per-module styles into `et-divi-dynamic-*.css` and inline <style>
 * blocks whose document order relative to this file is not guaranteed. Rules
 * that must beat a Divi module default are therefore prefixed with `body` (or
 * marked `!important` where Divi writes the property inline on the element).
 * That is deliberate, not sloppiness — see the comment on each occurrence.
 *
 * QA: verify with
 *   npx @axe-core/cli https://aledink.com/ --tags wcag2a,wcag2aa,wcag21a,wcag21aa
 * at 1440x900 and 390x844. Expect zero critical and zero serious violations.
 */

/* ==========================================================================
   1. Contrast-verified colour tokens
   Each token replaces a measured failure. Original value and both ratios
   are recorded so the regression is traceable.
   ========================================================================== */

:root {
	/* Was #ef8451 — 2.60:1 on white, 2.34:1 on #f5f3ee. Both fail 4.5:1.
	   Replacement keeps the orange hue and clears AA on every site background. */
	--aledink-orange-accessible: #c2410c;   /* 5.18:1 on #fff · 4.67:1 on #f5f3ee */

	/* Was #a66d03 — 4.37:1 on white at 12px. Marginal fail.
	   Replacement carries real headroom rather than sitting on the threshold. */
	--aledink-gold-accessible: #7a5002;     /* 7.06:1 on #fff */

	/* Sand used as a BACKGROUND behind white text on the pricing headings.
	   Was #bbab87 — white on it measured 2.26:1. Darkened until white clears
	   AA, keeping the sand hue so the pricing cards still read as warm.
	   NOTE: this token is a background, never a text colour. See the warning
	   in section 5 about where #bbab87 is legitimately used as text. */
	--aledink-sand-surface: #7d6e52;        /* white on it: 4.97:1 */

	/* Was #999999 — 2.85:1 on white. Pricing "$" / "/mo" and form field text. */
	--aledink-muted-accessible: #595959;    /* 7.00:1 on #fff */

	/* Focus ring. Brand red, chosen because it clears the 3:1 non-text floor
	   against BOTH the light sections (4.57:1 on #fff) and the black CTA
	   bands (4.60:1 on #000) — the site uses both, so a single-colour ring
	   had to work on either. */
	--aledink-focus: #d93b1f;
	--aledink-focus-halo: #ffffff;

	--aledink-focus-width: 3px;
	--aledink-focus-offset: 2px;
}

/* ==========================================================================
   2. Skip link — WCAG 2.4.1 Bypass Blocks (A)
   Divi emits no bypass mechanism. Printed by aledink_skip_link() on
   wp_body_open, so it is the first focusable element in the document.
   Visible only on focus, as permitted by the technique.
   ========================================================================== */

.aledink-skip-link {
	position: absolute;
	top: 0;
	left: 0;
	z-index: 100000;              /* above Divi's fixed header (z-index 99999) */
	padding: 0.875rem 1.5rem;
	background: #ffffff;
	color: #1b2a4a;               /* 14.22:1 on #fff */
	font-family: 'Poppins', Helvetica, Arial, sans-serif;
	font-size: 1rem;
	font-weight: 600;
	text-decoration: underline;
	border: 2px solid var(--aledink-focus);
	border-radius: 0 0 4px 0;
	transform: translateY(-120%);
	transition: transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
}

.aledink-skip-link:focus {
	transform: translateY(0);
}

/* ==========================================================================
   3. Focus indicators — WCAG 2.4.7 Focus Visible (AA)

   THE SINGLE LARGEST DEFECT FOUND IN THE AUDIT.
   A keyboard probe of the first 24 tab stops on the homepage found 18 with
   neither an outline nor a box-shadow — every navigation link, the logo, and
   the primary "Schedule a Free Strategy Call" call to action. The only stops
   that showed a ring belonged to the cookie-consent plugin.

   Divi resets `outline: none` on interactive elements in several places, so
   these rules use `!important`. That is the correct tool here: an
   accessibility floor must not be defeatable by a module-level style.

   The ring is drawn twice — a solid brand-red outline plus a white halo via
   box-shadow — so it stays visible on light sections, on the black CTA bands,
   and over photographic backgrounds alike.
   ========================================================================== */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:not([tabindex^="-"]):focus-visible,
[role="button"]:focus-visible,
[role="link"]:focus-visible,
.et_pb_button:focus-visible,
#top-menu li a:focus-visible,
#et_mobile_nav_menu a:focus-visible,
.et_pb_menu__logo a:focus-visible,
.et-pb-arrow-prev:focus-visible,
.et-pb-arrow-next:focus-visible {
	outline: var(--aledink-focus-width) solid var(--aledink-focus) !important;
	outline-offset: var(--aledink-focus-offset) !important;
	box-shadow: 0 0 0 calc(var(--aledink-focus-width) + var(--aledink-focus-offset)) var(--aledink-focus-halo) !important;
	border-radius: 2px;
}

/*
 * Fallback for browsers without :focus-visible support.
 * Scoped to keyboard users via the .aledink-kbd class that a11y.js puts on
 * <html> after the first Tab keypress, so mouse clicks do not draw rings.
 */
.aledink-kbd a:focus,
.aledink-kbd button:focus,
.aledink-kbd input:focus,
.aledink-kbd select:focus,
.aledink-kbd textarea:focus {
	outline: var(--aledink-focus-width) solid var(--aledink-focus) !important;
	outline-offset: var(--aledink-focus-offset) !important;
}

/* ==========================================================================
   4. Restore text selection

   `wp-content-copy-protector` and `no-right-click-images-plugin` set
   `user-select: none` on the body and all paragraphs, and bind a contextmenu
   blocker. Visitors cannot copy the phone number or the email address.

   For a marketing agency whose conversion path is "call or email us", this is
   an own-goal, and it degrades screen-reader and translation tooling that
   depends on text selection.

   The durable fix is deactivating both plugins (see ACCESSIBILITY.md). Until
   that happens these rules restore selection at the CSS layer, and a11y.js
   unbinds the contextmenu handler.
   ========================================================================== */

html body,
html body *,
html body p,
html body span,
html body a,
html body h1, html body h2, html body h3,
html body h4, html body h5, html body h6 {
	-webkit-user-select: text !important;
	-moz-user-select: text !important;
	user-select: text !important;
}

/* ==========================================================================
   5. Measured contrast repairs — WCAG 1.4.3 Contrast Minimum (AA)

   Selectors were captured from the live DOM by matching computed colour
   values, so each one is a confirmed occurrence rather than a guess.

   WHY `!important` APPEARS THROUGHOUT THIS SECTION
   Divi's dynamic stylesheet prefixes its rules with `#page-container`, giving
   them ID-level specificity (1,2,0) that a `body`-prefixed class selector
   (0,2,1) cannot beat. Divi also writes some colours as inline `style`
   attributes on the element — the mailto and tel links on /contact/ carry
   `style="color: #ef8451;"` directly. Inline styles are unreachable by any
   selector. `!important` is the only mechanism that wins both cases, and an
   accessibility floor is exactly the situation it exists for.

   ⚠ REGRESSION GUARD — READ BEFORE WIDENING ANY SELECTOR BELOW
   The first version of this file mapped every `.et_pb_blurb_content
   .et_pb_module_header` to a darkened sand, on the assumption that those
   headers sat on the cream background. They do not — they sit on the BLACK
   feature rows, where the original #bbab87 already measured 9.29:1 and passed
   comfortably. The "fix" dropped them to 3.55:1 and created three new serious
   violations on two pages.
   Lesson: a colour is only ever failing or passing *against a specific
   background*. Verify the background before retargeting a foreground.
   ========================================================================== */

/* --- #ef8451 (2.60:1 on #fff) --> #c2410c (5.18:1) -----------------------
   The primary navigation sits on a fixed white header on every page, so its
   background is known and a static rule is safe here.

   The mailto/tel links are deliberately NOT in this rule. They appear on white
   on /contact/ and on black or dark teal elsewhere; a static colour repaired
   one and broke the others. They are handled by the measured contrast guard in
   assets/js/a11y.js — see the long comment there. */
body #top-menu li a,
body #top-menu li.current-menu-item > a,
body #top-menu-nav > ul > li > a {
	color: var(--aledink-orange-accessible) !important;
}

/* Underline link text inside prose so colour is not the sole affordance.
   WCAG 1.4.1 Use of Colour (A). Safe on any background — affects decoration
   only, never colour. */
body .et_pb_text_inner p a,
body a[href^="mailto:"],
body a[href^="tel:"] {
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

/* --- #a66d03 (4.37:1) --> #7a5002 (7.06:1) -------------------------------
   REMOVED — now handled by the measured contrast guard in a11y.js.

   This was `body .et_pb_text_3 .et_pb_text_inner, body .et_pb_text_4
   .et_pb_text_inner { color: gold }`, written to catch the small gold eyebrow
   text. Divi numbers its text modules PER PAGE, so those class names point at
   completely different content depending on which page you are on:

     /          .et_pb_text_3 = "FEATURED SERVICE" eyebrow   #a66d03  (fails)
     /          .et_pb_text_4 = body paragraph               #000000  (passes)
     /services/ .et_pb_text_4 = "THE DIFFERENCE" eyebrow     #a66d03  (fails)

   The rule turned the homepage's black body paragraph gold. axe never caught
   it, because gold on white passes contrast comfortably — it was only visible
   in a screenshot of production.

   Third instance of the same mistake in this file's history: a Divi class name
   is not a stable identifier for a piece of content. Match on measured values,
   not on module indices. */

/* --- Pricing headings: white on #bbab87 measured 2.26:1 -------------------
   The failure here is the BACKGROUND, not the text. `.et_pb_pricing_heading`
   is filled with the sand tone and holds white `.et_pb_pricing_title` text,
   so the surface is what has to move.

   SPECIFICITY
   Divi sets this from et-core-unified-deferred-457.min.css with
   `.et_pb_pricing .et_pb_pricing_table_0.et_pb_pricing_table
   .et_pb_pricing_heading { ... !important }` — specificity (0,4,0) AND
   `!important`. A `body`-prefixed rule at (0,2,1) loses even with
   `!important`, which is why the first attempt at this silently did nothing.
   The `#page-container` prefix takes it to (1,4,0) and wins.

   :not(.et_pb_featured_table) IS REQUIRED
   The middle "Growth" card is the featured tier and its heading is
   deliberately black with white text — already 21:1 and passing. Only the two
   sand-filled cards ("Starter", "Scale") fail. Darkening all three would
   erase the featured card's visual emphasis for no accessibility gain. */
#page-container .et_pb_pricing .et_pb_pricing_table:not(.et_pb_featured_table) .et_pb_pricing_heading {
	background-color: var(--aledink-sand-surface) !important;
}

#page-container .et_pb_pricing .et_pb_pricing_table:not(.et_pb_featured_table) .et_pb_pricing_heading .et_pb_pricing_title {
	color: #ffffff !important;     /* 4.97:1 on the darkened surface */
}

/* --- #999999 (2.85:1) --> #595959 (7.00:1) -------------------------------
   Pricing currency symbol and billing frequency, plus the contact form's own
   field text — the inputs themselves render their typed value at #999999. */
body .et_pb_pricing_table .et_pb_dollar_sign,
body .et_pb_pricing_table .et_pb_frequency,
body .et_pb_pricing_table .et_pb_frequency_slash,
body .et_pb_contact_form input[type="text"],
body .et_pb_contact_form input[type="email"],
body .et_pb_contact_form input[type="tel"],
body .et_pb_contact_form select,
body .et_pb_contact_form textarea,
body .et_pb_contact_form select option {
	color: var(--aledink-muted-accessible) !important;
}

body .et_pb_contact_form ::placeholder {
	color: var(--aledink-muted-accessible) !important;
	opacity: 1;                    /* Firefox dims ::placeholder by default */
}

/* ==========================================================================
   6. Form labels — WCAG 4.1.2 Name, Role, Value (A) / 3.3.2 Labels (A)

   THIS WAS THE P0 THAT LOOKED LIKE IT NEEDED A DATABASE EDIT AND DID NOT.

   Divi renders `label.et_pb_contact_form_label` for all six contact fields
   with correct `for` attributes already pointing at the right inputs — then
   hides them with `display: none`. `display: none` removes an element from the
   accessibility tree entirely, so the labels exist in markup but do not reach
   assistive technology.

   The two `<select>` fields (Role, Institution Type) therefore had no
   accessible name at all — axe reported both as CRITICAL. A screen-reader user
   could not complete the only lead-capture form on the site.

   Swapping `display: none` for the visually-hidden pattern keeps the design
   identical while restoring every label to the accessibility tree. No content
   edit required.
   ========================================================================== */

body .et_pb_contact_form .et_pb_contact_form_label,
body .et_pb_contact_field .et_pb_contact_form_label {
	display: block !important;     /* beats Divi's inline `display: none` */
	position: absolute !important;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ==========================================================================
   7. Touch targets — WCAG 2.5.8 Target Size Minimum (AA, 24px)
   moving toward 2.5.5 Enhanced (AAA, 44px) where layout permits.

   Measured at 390x844: 13-16 interactive elements per page fell below 44x44.
   Most belong to the cookie-consent plugin, which is being removed; these
   rules cover the site's own controls.
   ========================================================================== */

@media (max-width: 980px) {
	body #top-menu li a,
	body #et_mobile_nav_menu a,
	body .et_mobile_menu li a,
	body .et_pb_menu__wrap a {
		min-height: 44px;
		display: flex;
		align-items: center;
	}

	body .et-pb-arrow-prev,
	body .et-pb-arrow-next,
	body .et_pb_social_media_follow li a {
		min-width: 44px;
		min-height: 44px;
		display: inline-flex;
		align-items: center;
		justify-content: center;
	}

	/* Footer social icons sat at 20px tall. */
	body .et_pb_social_media_follow_network a.icon {
		width: 44px;
		height: 44px;
		line-height: 44px;
	}
}

/* ==========================================================================
   8. Responsive typography — WCAG 1.4.4 Resize Text / 1.4.10 Reflow (AA)

   Divi carries desktop heading sizes onto small viewports unmodified. On a
   390px screen the homepage h2 "Digital Solutions That Inspire Learning &
   Discovery" rendered across six lines at roughly 72px line-height, consuming
   an entire viewport for a single heading.

   clamp() keeps the display weight the brand depends on while bounding the
   size to the viewport. Maximums stay at or below the values Divi already uses
   at desktop, so nothing changes on large screens.
   ========================================================================== */

@media (max-width: 980px) {
	body h1,
	body .et_pb_module h1,
	body .et_pb_slide_description h1 {
		font-size: clamp(2rem, 8.5vw, 3.25rem) !important;
		line-height: 1.08 !important;
		letter-spacing: -0.02em;
		text-wrap: balance;
		overflow-wrap: break-word;
	}

	body h2,
	body .et_pb_module h2,
	body .dsm_block_reveal_text_header {
		font-size: clamp(1.625rem, 6.5vw, 2.5rem) !important;
		line-height: 1.12 !important;
		letter-spacing: -0.015em;
		text-wrap: balance;
		overflow-wrap: break-word;
	}

	body h3,
	body .et_pb_module h3 {
		font-size: clamp(1.25rem, 4.8vw, 1.75rem) !important;
		line-height: 1.2 !important;
		text-wrap: balance;
	}

	body h4,
	body .et_pb_module h4,
	body .et_pb_blurb_content .et_pb_module_header {
		font-size: clamp(1.0625rem, 4vw, 1.375rem) !important;
		line-height: 1.25 !important;
	}

	/* Body copy floor. Several modules dropped to 12px on mobile, below the
	   comfortable reading minimum and pushing contrast requirements higher. */
	body .et_pb_text_inner,
	body .et_pb_text_inner p,
	body .et_pb_blurb_description,
	body .et_pb_blurb_description p {
		font-size: max(1rem, 1em) !important;
		line-height: 1.6 !important;
	}
}

/* Cap measure on long-form prose at all sizes. Uncomfortable line lengths
   were reaching ~110 characters on the FAQ page at 1440px. */
body .et_pb_text_inner p,
body .et_pb_blurb_description p {
	max-width: 72ch;
}

/* ==========================================================================
   9. Mobile hero ordering

   On mobile the decorative hero illustration renders above the h1, occupying
   roughly 40% of the first screen and pushing the value proposition below the
   fold behind an image that carries no information.

   Reordering with flexbox is a presentation change only — DOM order, and
   therefore screen-reader and tab order, is unaffected.
   ========================================================================== */

/*
 * NOTE ON THE SELECTOR: `body.home`, not `body .home`.
 * WordPress puts the `home` class on <body> itself, so the descendant form
 * matches nothing. The first version of this block used the descendant
 * combinator and silently did nothing — the illustration stayed above the H1
 * through a full verification pass because axe has no opinion about visual
 * order. Caught by screenshot, not by the scanner.
 */
@media (max-width: 767px) {
	body.home .et_pb_section_0 .et_pb_row_0 {
		display: flex;
		flex-direction: column;
	}

	body.home .et_pb_section_0 .et_pb_row_0 > .et_pb_column_0 {
		order: 2;                  /* illustration drops below the headline */
	}

	body.home .et_pb_section_0 .et_pb_row_0 > .et_pb_column_1 {
		order: 1;                  /* headline + subhead + CTA lead */
	}

	/*
	 * Neutralise the authored negative margin on the headline module.
	 *
	 * .et_pb_text_0 carries `margin-top: -49.9px`, set in the builder to pull
	 * the H1 up over the illustration back when the illustration stacked FIRST.
	 * Reordering the columns above makes the headline the first thing in the
	 * row, so that same negative margin now pulls the H1 up underneath the
	 * fixed header — it was clipping the top ~10px of "YOUR MISSION IS
	 * EDUCATION" on a 390px viewport.
	 *
	 * The offset was only ever a compensation for the old order. With the new
	 * order it has nothing to compensate for.
	 */
	body.home .et_pb_section_0 .et_pb_row_0 > .et_pb_column_1 .et_pb_text_0 {
		margin-top: 0 !important;
	}

	/* Bound the illustration so it never dominates the fold again. */
	body.home .et_pb_section_0 .et_pb_image img {
		max-height: 32vh;
		width: auto;
		margin-inline: auto;
	}
}

/* ==========================================================================
   10. Reduced motion — WCAG 2.3.3 Animation from Interactions (AAA)
   and a correctness fix.

   The Supreme Modules block-reveal headings gate their own visibility on a
   scroll-triggered animation. When that animation does not run — a crawler, a
   print stylesheet, a screenshot service, a user with reduced-motion
   preferences — the heading renders clipped rather than simply un-animated.

   Forcing the finished state under `prefers-reduced-motion` guarantees the
   content is legible regardless of whether the animation ever fires.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}

	body .dsm_block_reveal_text_header,
	body .dsm_block_reveal_text_wrapper,
	body .dsm_block_image_reveal_content,
	body [class*="dsm_block_reveal"] {
		clip-path: none !important;
		opacity: 1 !important;
		transform: none !important;
		visibility: visible !important;
	}
}

/* Same guarantee for print. */
@media print {
	body [class*="dsm_block_reveal"] {
		clip-path: none !important;
		opacity: 1 !important;
		transform: none !important;
	}
}

/* ==========================================================================
   11. Forced-colors / Windows High Contrast support
   Ensures the focus ring survives when the user's OS replaces the palette.
   ========================================================================== */

@media (forced-colors: active) {
	a:focus-visible,
	button:focus-visible,
	input:focus-visible,
	select:focus-visible,
	textarea:focus-visible {
		outline: 3px solid Highlight !important;
		outline-offset: 2px !important;
	}

	.aledink-skip-link {
		border: 2px solid ButtonText;
	}
}


/* ==========================================================================
   TOGGLE TITLE BUTTON  —  added 2026-08-24
   ==========================================================================
   wireToggleKeyboard() moves each Divi toggle label into a real <button> so
   the accordion is keyboard operable while the <h5> stays a heading. The
   button must be visually invisible: it inherits the title's font, colour and
   line-height and carries none of the UA button chrome, so nothing on the page
   moves by a pixel. Verified against the rendered title: Poppins 16px,
   rgb(15,57,59), padding 0 50px 0 0 on the heading with the ::before arrow
   sitting in that right gutter, which this button does not cover.

   The focus ring is NOT declared here on purpose. button:focus-visible is
   already covered by the sitewide rule above, so this control inherits the
   same red outline plus white halo as every other control.
   ========================================================================== */

.et_pb_toggle_title .aledink-toggle-button {
	display: block;
	width: 100%;
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	font: inherit;
	color: inherit;
	letter-spacing: inherit;
	line-height: inherit;
	text-align: left;
	text-transform: inherit;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
}
