/* AECA custom styles
----------------------------------------------------------------------------------------------------

Kept out of style.css on purpose: style.css is regenerated by Blocs on export, which would wipe
anything added there. This file is enqueued after it, so these rules win and survive a re-export.

Brand palette, taken from the swatch variables already in style.css:
  blue        #024DA1   var(--swatch-var-3706)
  light blue  #16ADEE   var(--swatch-var-3416)
  red         #EC1C24   var(--swatch-var-4378)
  white       #FFFFFF   var(--swatch-var-2251)

---------------------------------------------------------------------------------------------------- */

/* ====================================================================================================
   THE TWO FAMILIES, SELF-HOSTED (2026-09-03)
   ----------------------------------------------------------------------------------------------------
   WHY THIS EXISTS. Visitors kept reporting that the site's type "looked different", and it was being
   put down to Windows against iOS. It was not the operating system. Outfit and Inter were loaded from
   Google with this in parts/head.php:

       <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter...&family=Outfit..."
             media="print" onload="this.media='all'">

   `media="print"` is the trick for loading a stylesheet without blocking render: the browser fetches
   it at low priority and, crucially, DOES NOT APPLY IT -- so its @font-face rules do not exist yet --
   until the onload handler flips the media to `all`. Until that moment "Outfit" and "Inter" are
   unknown families, and the stack in --aeca-font-head / --aeca-font-body falls through to the next
   name it can resolve:

       "Outfit", "Outfit-Medium", "Outfit-Regular", system-ui, -apple-system, "Segoe UI", sans-serif

   So every uncached page load painted first in `Outfit-Medium` -- the static single-weight TTF that
   style.css declares with `font-weight: normal` -- and then swapped to the real variable Outfit. Two
   things made that swap obvious rather than subtle: the static face has no weight axis, so every
   heading at 600, 700 or 800 was SYNTHETICALLY BOLDED first and then snapped to a real bold; and 113
   rules in this theme ask for weight 600, which the static face cannot produce at all.

   Where the operating system did come into it: if the Google request failed outright -- a corporate
   proxy, an ad or tracker blocker, a bad network -- and the local statics were also unavailable, the
   stack reached `system-ui`, which is Segoe UI on Windows and San Francisco on iOS and macOS. That is
   a real per-OS difference, and it is the grain of truth the explanation was built on. It was the
   third fallback, not the cause.

   THE FIX. The exact variable WOFF2 files Google serves are now in fonts/variable/ and declared here,
   so the families are resolved from this origin on first paint with no third-party request in the
   path at all. Same fonts, same axis, same unicode-range splits, byte-for-byte the files gstatic was
   serving -- both are SIL Open Font License, which permits hosting them. 180KB for all four.

   WHAT IS DECLARED. `wght@300..900` on both, matching exactly what the old Google URL requested, so
   nothing on the site renders at a weight it was not already getting. Weight 100 appears 70 times in
   style.css, left over from Lexend Deca; it clamped to 300 before and clamps to 300 now.

   Only the `latin` and `latin-ext` subsets are here. Google splits Inter into seven -- cyrillic,
   cyrillic-ext, greek, greek-ext, vietnamese and these two. The site is English and Malay, both of
   which are covered by latin, and latin-ext carries the accented characters and the currency and
   punctuation marks. Adding a subset means downloading that .woff2 and pasting its @font-face block
   from the Google CSS; nothing else here changes.

   The static `Outfit-Medium` / `Inter-Regular` families that style.css declares are still last in the
   variable stacks below as a final fallback, and are still never to be named directly -- they have no
   weight axis. See the note in the typography section.
   ==================================================================================================== */

@font-face {
	font-family: "Outfit";
	font-style: normal;
	font-weight: 300 900;
	font-display: swap;
	src: url("../fonts/variable/outfit-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Outfit";
	font-style: normal;
	font-weight: 300 900;
	font-display: swap;
	src: url("../fonts/variable/outfit-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Inter";
	font-style: normal;
	font-weight: 300 900;
	font-display: swap;
	src: url("../fonts/variable/inter-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Inter";
	font-style: normal;
	font-weight: 300 900;
	font-display: swap;
	src: url("../fonts/variable/inter-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
	--aeca-blue: #024DA1;
	--aeca-blue-light: #16ADEE;
	--aeca-red: #EC1C24;
	--aeca-white: #FFFFFF;
	--aeca-ink: #111111;
	--aeca-muted: #5a6470;
	--aeca-surface: #eef3f9;
	--aeca-border: #cddcee;

	/* Two families, nothing else. The local Outfit and Inter-Regular @font-face names declared in
	   style.css stay in the stack as a fallback for when the Google Fonts request is blocked. */
	--aeca-font-head: "Outfit", "Outfit-Medium", "Outfit-Regular", system-ui, -apple-system, "Segoe UI", sans-serif;
	--aeca-font-body: "Inter", "Inter-Regular", system-ui, -apple-system, "Segoe UI", sans-serif;

	/* Ink, by the ground it sits on. See the "Ink follows its ground" section below. */
	--aeca-on-light: var(--aeca-blue);
	--aeca-on-dark: var(--aeca-white);
}

/* Typography
----------------------------------------------------------------------------------------------------
style.css ships nine families: Outfit-Medium (321 rules), Inter (167), Lexend Deca (127), Teko (17),
Inter-Regular (12), SourceCodePro-ExtraLight (8), Outfit-Light (7), SpaceGrotesk-Light (5),
NotoSerifCondensed-Medium (3). All of it collapses to two -- Inter for running text, Outfit for
everything else -- without touching a single one of those rules, because this file loads last.

Specificity matters here: the generated classes are single-class selectors, several with !important
(.link-sidebar-style and 32 siblings, .h3-overview-style). So these rules need !important too, and
the Inter rule needs to out-specify the Outfit one on the same element -- hence `p[class]` at
(0,1,1) against `.p-624-style` at (0,1,0).
---------------------------------------------------------------------------------------------------- */

/* Outfit everywhere. [class*="-style"] is the whole Blocs-generated namespace in one selector. */
body,
h1,
h2,
h3,
h4,
h5,
h6,
[class*="-style"],
[class^="const-"],
[class*=" const-"],
.btn,
.nav-link,
.dropdown-item,
.card-header,
.card-footer,
.navbar-nav li a,
.navbar,
button,
input,
select,
textarea,
label,
th,
.alert,
.page-numbers {
	font-family: var(--aeca-font-head) !important;
}

/* Running text back to Inter. */
p,
p[class],
li,
li[class],
dd,
dt,
blockquote,
figcaption,
td,
small,
.aeca-sol-summary,
.aeca-vmo-body,
.about-hero-lead,
.aeca-entry-content,
.aeca-industry-caption {
	font-family: var(--aeca-font-body) !important;
}

/* Links, added 2026-08-17. style.css's base rule is
   `h1,h2,h3,h4,h5,h6,p,label,.btn,a{font-family:"Helvetica"}`, and `a` is the one selector in it
   that neither block above replaced -- so every plain link on the site was rendering in the system
   Helvetica beside text set in Outfit or Inter. The skip link, the home service-card titles, the
   "Read More" links and every link inside an editorial post were all affected.

   `inherit` rather than a named family, because a link should take the voice of what it sits in:
   inside a paragraph or a list item it is running text and wants Inter, inside a heading, a card or
   a nav it wants Outfit. The components that set their own family -- .btn, .nav-link,
   .dropdown-item, the Blocs `-style` classes -- all out-specify a bare `a`, so they keep theirs. */
a {
	font-family: inherit !important;
}

/* Icon font FILES, repointed.
----------------------------------------------------------------------------------------------------
Separate bug, found while checking the above in a browser: every icon on the site was rendering as
an empty box. css/all.css declares its @font-face src as url(./fonts/fa-solid-900.woff2), and it is
itself served from /css/, so that resolves to /css/fonts/ -- a directory that does not exist. The
files are in the theme-root /fonts/. document.fonts reported "Font Awesome 5 Free|900|error".

This file is also in /css/, so ../fonts/ reaches them. Re-declaring the same family/weight/style
later in the cascade replaces the broken source. Affects the hamburger menu icon, the four homepage
shortcut-card icons, the PDF icon in the offcanvas menu and the horizontal-scroll arrows.
---------------------------------------------------------------------------------------------------- */

@font-face {
	font-family: "Font Awesome 5 Free";
	font-style: normal;
	font-weight: 900;
	font-display: block;
	src: url("../fonts/fa-solid-900.woff2") format("woff2"),
	     url("../fonts/fa-solid-900.woff") format("woff"),
	     url("../fonts/fa-solid-900.ttf") format("truetype");
}

@font-face {
	font-family: "Font Awesome 5 Free";
	font-style: normal;
	font-weight: 400;
	font-display: block;
	src: url("../fonts/fa-regular-400.woff2") format("woff2"),
	     url("../fonts/fa-regular-400.woff") format("woff"),
	     url("../fonts/fa-regular-400.ttf") format("truetype");
}

@font-face {
	font-family: "Font Awesome 5 Brands";
	font-style: normal;
	font-weight: 400;
	font-display: block;
	src: url("../fonts/fa-brands-400.woff2") format("woff2"),
	     url("../fonts/fa-brands-400.woff") format("woff"),
	     url("../fonts/fa-brands-400.ttf") format("truetype");
}

@font-face {
	font-family: "Ionicons";
	font-style: normal;
	font-weight: normal;
	font-display: block;
	src: url("../fonts/ionicons.woff") format("woff"),
	     url("../fonts/ionicons.ttf") format("truetype");
}

/* Icon font FAMILIES, re-asserted.
   The Outfit/Inter rules above are broad enough to swallow them:
   <span class="fa fa-file-pdf const-brochure-icon"> in header.php matches [class*=" const-"], and
   Font Awesome sets its family on the element itself, so the glyph would have rendered as tofu. */
.fa,
.fas,
.far,
.fal,
.fa-solid,
.fa-regular {
	font-family: "Font Awesome 5 Free" !important;
}

.fab,
.fa-brands {
	font-family: "Font Awesome 5 Brands" !important;
}

/* Ionicons markup is `class="icon-md ion ion-navicon-round const-menu-icon"`, so the hook is the
   bare `.ion` class plus a word-boundary match on the glyph class. A plain [class*="ion-"] is not
   safe here: it also matches const-sect|ion-|subtitle, const-navigat|ion-|bar and a dozen others. */
.ion,
[class^="ion-"],
[class*=" ion-"],
.ion::before,
[class^="ion-"]::before,
[class*=" ion-"]::before {
	font-family: "Ionicons" !important;
}

/* Weight map.
   These classes got their weight from a *family name* (Outfit-Bold / Outfit-Light) rather than a
   font-weight declaration. Against a single variable Outfit they would all render regular, so the
   weight has to be restated. */
.h3-best-awards-style,
.h3-success-rate-style,
.h3-stats-style,
.h3-brochures-style,
.h3-happy-clients-style,
.h3-overview-style {
	font-weight: 700;
}

.h4-stats-image-style,
.h4-6-style,
.h4-8-style,
.h3-industries-style,
.h3-77-style,
.h3-84-style,
.h3-91-style {
	font-weight: 300;
}

/* Teko is condensed; Outfit is not. Headings that used to be Teko-Medium now set the same amount of
   text noticeably wider, so they lean on the fluid scale in the "Large screens" section below. */

/* Form honeypot
----------------------------------------------------------------------------------------------------
Must not be reachable by a real visitor, but must still be submitted so the server can check it.
`display:none` is what naive bots look for, so this is pushed off-screen instead.
---------------------------------------------------------------------------------------------------- */

.aeca-hp {
	position: absolute !important;
	left: -9999px !important;
	top: auto !important;
	width: 1px !important;
	height: 1px !important;
	overflow: hidden !important;
}

/* Skip link
---------------------------------------------------------------------------------------------------- */

.aeca-skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100000;
	padding: 0.75rem 1.25rem;
	background: var(--aeca-blue);
	color: var(--aeca-white);
	font-weight: 700;
	text-decoration: none;
}

.aeca-skip-link:focus {
	left: 0;
	color: var(--aeca-white);
}

/* Visible focus
----------------------------------------------------------------------------------------------------
The Blocs stylesheet removes outlines on several interactive elements. This restores a focus ring
for keyboard users only, so it does not show up on mouse clicks.
---------------------------------------------------------------------------------------------------- */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.nav-link:focus-visible,
.dropdown-item:focus-visible,
.bloc-link:focus-visible {
	outline: 3px solid var(--aeca-blue-light) !important;
	outline-offset: 2px !important;
}

/* About page
----------------------------------------------------------------------------------------------------
Built to the mockup supplied 2026-08-08. Three pieces: the hero overlay panel, the leaf-shaped
section images, and the VISION / MISSION / OBJECTIVE cards.
---------------------------------------------------------------------------------------------------- */

/* Hero
----------------------------------------------------------------------------------------------------
The bloc itself first. Three things were wrong with it at every width:

  - `.row-hero{margin-top:-100px; margin-bottom:300px}` in style.css has no responsive override at
    all, so the 300px was still there on a 390px phone. It was not decoration -- it was the only
    thing setting the hero's height, which is why the bloc measured 565px tall with 235px of content
    and 300px of empty blue underneath it.
  - `.bloc-video` is sized `width:auto; height:auto; min-width:100%; min-height:100%`, which does not
    reliably cover; the video letterboxed inside the blue bloc on narrow viewports.
  - `.bloc-xxl-lg` adds 200px of padding top and bottom from 992px up, on top of all that.

Replaced with one model: the bloc is a flex box of a viewport-proportional height, the video is a
true cover backdrop, and the panel is centred in it. Same arrangement the home hero already uses.

Height. The first version was `clamp(480px, 68vh, 860px)`, which is a laptop problem: 68vh on a
730px viewport is 496px, and the footage is 3000x2002 -- 3:2. `cover` fills the width, so a 1520px
box wants 1014px of height to show the whole frame; at 496px it was throwing away half of it, and
what survived the crop was a band of grass with the tip of one wing in it. The drone, the launch and
the operator are all outside the visible strip.

So the height is driven off the width as well as the viewport, because the severity of the crop is a
function of the box's aspect ratio, not its height alone. `max(46vw, 64vh)` keeps roughly 70% of the
frame at any desktop width and hands over to 64vh on portrait tablets and phones, where 46vw would
collapse. Floor and ceiling stay absolute so neither a small laptop nor a 4K monitor runs away.
---------------------------------------------------------------------------------------------------- */

#about-hero {
	display: flex;
	align-items: center;
	min-height: clamp(520px, max(46vw, 64vh), 900px);
	padding-top: clamp(4rem, 8vh, 7rem);
	padding-bottom: clamp(6rem, 12vh, 9rem); /* clears the 100px shape divider at the foot */
	overflow: hidden;
}

#about-hero .bloc-video {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

#about-hero .hero-about {
	width: 100%;
	padding-top: 0;
	padding-bottom: 0;
}

/* Kill the -100px / 300px pair. The horizontal -12px is Bootstrap's row gutter -- keep it. */
#about-hero .row-hero {
	margin-top: 0;
	margin-bottom: 0;
}

/* The panel.
   A diagonal gradient rather than a flat tint, so the video still reads through the lower corner; a
   light-blue hairline and an asymmetric radius picking up the leaf curve used on the section images
   further down the page.

   NO backdrop-filter. The first version of this used blur(8px) saturate(120%) and it tinted the
   whole document: a backdrop-filter anywhere makes the nearest backdrop root -- here the document,
   and `isolation:isolate` on #about-hero did not confine it -- composite through a different
   pipeline, and every white surface on the page, the nav bar included, came out #f0f7fd instead of
   #fff. Confirmed by toggling the one declaration off in the browser and re-shooting. The gradient
   carries the contrast on its own. */
.about-hero-panel {
	max-width: 44rem;
	margin: 0 auto; /* centred in the hero, to match the home hero card */
	padding: clamp(1.5rem, 3vw, 3rem);
	background: linear-gradient(150deg, rgba(2, 77, 161, 0.94) 0%, rgba(2, 77, 161, 0.82) 45%, rgba(2, 77, 161, 0.62) 100%);
	border: 1px solid rgba(22, 173, 238, 0.55);
	border-radius: 4px 28px 4px 28px;
	box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28);
	text-align: center;
}

/* .h1-about-hero-style is 60px flat and .const-page-title 70px; both are sized for a full-width
   headline, and neither fits a panel. */
.about-hero-panel .const-page-title {
	font-size: clamp(1.85rem, 1.1rem + 2.4vw, 3.4rem);
	line-height: 1.08;
	text-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}

/* The rule under the headline, as a pseudo-element so the markup stays as Blocs wrote it.
   auto margins rather than text-align, because it is a block, not inline content. */
.about-hero-panel .const-page-title::after {
	content: "";
	display: block;
	width: 4.5rem;
	height: 3px;
	margin: 1.1rem auto 0;
	background: var(--aeca-blue-light);
}

.about-hero-lead {
	max-width: 34rem;
	margin-left: auto;
	margin-right: auto;
	font-size: clamp(1rem, 0.9rem + 0.3vw, 1.15rem);
	line-height: 1.6;
}

/* On a phone the panel is nearly the full width of the screen anyway, so let it fill. */
@media (max-width: 767.98px) {
	.about-hero-panel {
		max-width: none;
	}
}

/* Section images: the leaf shape from the design, mirrored between the two sections.
   Fixed radii, not percentages -- a percentage radius is resolved separately against the width and
   the height, so on a 676x451 landscape photo it renders as a lopsided egg rather than a curve. */
.about-begin,
.about-second {
	border-radius: 0;
	overflow: hidden;

	/* `img-fluid` only supplies `max-width: 100%`, which caps an image but never grows one. These two
	   carry width="676" for the aspect ratio, so from 768px up -- where the stacked column is wider
	   than 676px -- the photo stopped at its intrinsic width and `mx-auto` centred it, leaving it
	   visibly narrower than the copy directly above or below it. This used to be set inside the
	   min-992 block, which is exactly the range where the column is never wider than the image
	   anyway, so it was the one place it made no difference. */
	width: 100%;
	height: auto;
}

/* The curve was the only thing being done to these two, so they sat flat on the white with nothing
   to separate photo from page -- which is most of why they read as placeholders.

   Two additions, both drawn with box-shadow and nothing else. An inset ring rather than a real
   border, because a border is part of the box: 1px on each side would land straight in the width
   calculation below, where the columns already sum to within 2px of the whole row. An inset shadow
   costs no layout at all and still follows the border-radius round the curve.

   The ring is the same cyan at roughly the same alpha as --aeca-glass-border, so the edge belongs
   to the same family as the hero card. It carries the curve where the photo behind it is dark, and
   disappears where the photo is pale sky -- which is fine, because the shadow is what actually has
   to do the work.

   The drop shadow is two layers, and the first has no y-offset on purpose. An offset shadow only
   appears below the shape, so on an image whose signature is a 150px curve down one side, all the
   lift lands where nobody is looking and the curve itself gets nothing. The unoffset layer sits
   evenly round the whole outline, curve included; the offset layer underneath it is what reads as
   weight. Tinted with the deep blue rather than black, so it never greys off against the white. */
.about-begin,
.about-second {
	box-shadow:
		inset 0 0 0 1px rgba(22, 173, 238, 0.5),
		0 0 20px rgba(2, 20, 40, 0.1),
		0 20px 48px rgba(2, 20, 40, 0.18);
}

@media (min-width: 992px) {
	/* The theme already draws this curve (150px, in style.css -- theme root, not css/). Restated here
	   only so the value is visible next to the bleed it belongs with, and so the mobile reset below
	   has something to override. */
	.about-begin { border-radius: 0 150px 150px 0; }  /* image sits left,  curve on its right edge */
	.about-second { border-radius: 150px 0 0 150px; }  /* image sits right, curve on its left edge  */

	/* Both images run off the edge of the screen in the design, rather than stopping at the
	   container. style.css has its own attempt at this (margin-left:-150px; width:700px) but those
	   are fixed pixels tuned to one viewport, and a later rule cancels them outright.

	   Percentages in margin and width resolve against the containing block -- here the .row, which
	   is the container width -- so (100vw - 100%) / 2 is exactly the empty gutter outside the
	   container, at any viewport. Shift the column out by that much and grow it by the same amount.

	   flex-basis, not width, is what sizes a flex item: the theme's `flex: 0 0 50%` silently beat
	   the width below until this reset it to auto. */
	.column-about-begin,
.column-about-second {
		flex: 0 0 auto;
		width: calc(50% + (100vw - 100%) / 2);
		max-width: none;
	}

	/* The negative margin cancels the growth above, so each column's margin box comes back to
	   exactly 50% and the pair adds up to precisely 100% of the row. That is a knife edge: half a
	   pixel of rounding -- which any fractional display scale produces, a MacBook's in particular --
	   puts the pair over 100% and flex drops the second column onto its own line. The copy is then
	   left beside a blank half and the image sits below it, no longer reaching the screen edge.

	   Pulling 2px further out than the gutter makes each margin box 50% minus 2px, so the pair can
	   never reach 100%. The 2px goes off-screen, where .page-container clips it, so the bleed still
	   covers the edge. Nothing in the row can shrink, so there is no other way for it to wrap. */
	.column-about-begin {
		margin-left: calc((100% - 100vw) / 2 - 2px);
		padding-left: 0;
		padding-right: 2.5rem; /* the gap between image and copy */
	}

	.column-about-second {
		margin-right: calc((100% - 100vw) / 2 - 2px);
		padding-right: 0;
		padding-left: 2.5rem;
	}

	/* .page-container has overflow:hidden, so the overflow above cannot cause sideways scrolling. */
}

@media (max-width: 991.98px) {
	/* Stacked, there is no edge to bleed into and a 150px curve on a full-width image looks broken. */
	.about-begin,
.about-second {
		border-radius: 0;
	}

	/* The theme sizes these four columns in fixed pixels with no media query at all -- 700px on the
	   two image columns, 500px on the two copy columns. It does have a mobile rule, but that one only
	   zeroes the side margins and adds `max-width: 100%`; it never resets the width. `max-width`
	   clamps rather than replaces, so below 992px each column is min(its fixed px, the row) and the
	   pair happens to agree only while the row is narrower than 500px. At 390px both land on the row
	   width and look correct, which is what hides the bug. At 820px the row is 789px, so the image
	   column stays 700px and the copy column stays 500px: two stacked blocks, 200px apart in width,
	   with different left and right edges.

	   Resetting the width has to beat `.col-md-6` as well, which would otherwise take over at 768px
	   and set them side by side. Stacked is the right call the whole way to 992px: there is no bleed
	   to sit beside below that (see the radius reset above), and 50/50 at 768px would leave the copy
	   in a ~370px column. Equal specificity to both `.col-md-6` and the theme rule, so this wins on
	   source order -- aeca-custom.css is enqueued last. */
	.column-about-begin,
.column-about-begin-paragraph,
.column-about-second,
.column-about-second-paragraph {
		width: 100%;
	}

	/* Once stacked, the gap between photo and copy is vertical, but the only rule that spaces them is
	   the horizontal `padding-right: 2.5rem` inside the min-992 block above. So nothing separated the
	   two at all: "OUR BEGINNING" sat flush against the bottom edge of its photo, touching it, and in
	   the second section the photo cleared the last paragraph by 18px.

	   One margin each way rather than a single `gap`, because the image leads in bloc-3 and follows
	   in bloc-4, and the row is a flex container whose `gap` would also reopen on the desktop layout
	   where the 2.5rem padding already does this job. */
	.column-about-begin {
		margin-bottom: 2rem;
	}

	.column-about-second {
		margin-top: 2rem;
	}
}

/* "Our beginning" sat hard against the blue shape divider at the foot of the hero, and "What we do"
   ran straight on from it. Both sections are only as tall as their content, so they need explicit
   breathing room. */
#bloc-3 {
	padding-top: 4.5rem;
	padding-bottom: 4.5rem;
}

#bloc-4 {
	padding-top: 3rem;
	padding-bottom: 4.5rem;
}

/* These two sit on top of the container's own padding, so they were double-counted on phones: 40px
   here plus the 100px `.bloc-lg` was putting 140px above the first line of each. The container half
   is now scaled down for small screens (see MOBILE SECTION SPACING at the foot of this file) and
   these come down to match, rather than one fix cancelling out the other. */
@media (max-width: 767px) {
	#bloc-3 {
		padding-top: 1.5rem;
		padding-bottom: 1.5rem;
	}

	#bloc-4 {
		padding-top: 1.25rem;
		padding-bottom: 1.5rem;
	}
}

/* The copy in these sections runs as several paragraphs, but only some carry the theme's margin
   classes, so consecutive paragraphs collided. */
.column-about-begin-paragraph p + p,
.column-about-second-paragraph p + p,
.column-about-begin-paragraph ul,
.column-about-second-paragraph ul {
	margin-top: 1rem;
}

/* Body copy in these two sections is blue in the design, and the theme justifies it -- justified
   text in a narrow column opens rivers of white space, which is what makes it look ragged. */
.column-about-begin-paragraph,
.column-about-second-paragraph {
	color: var(--aeca-blue);
	text-align: left;
}

.column-about-begin-paragraph p,
.column-about-begin-paragraph li,
.column-about-second-paragraph p,
.column-about-second-paragraph li {
	color: var(--aeca-blue);
	text-align: left;
}

/* VISION / MISSION / OBJECTIVE
   ----------------------------------------------------------------------------------------------------
   Was a flat blue cap sitting on a separate 1px-outlined box that was square at the top and rounded
   at the bottom. Two problems with that as a piece of design: the cap and the body read as two
   objects that happen to be touching rather than as one card, and with a hairline outline as the only
   surface treatment the three of them had no presence at all next to the heavy blue bars in "Why
   choose AECA" further down the same page.

   Now one object. The card is a single rounded, clipped shape, so the cap's colour runs into its
   corners; the cap carries the same navy-to-blue gradient as the `.aeca-page-hero` band and the same
   cyan rule the section headings use, so it belongs to the family; and the body is white on a soft
   shadow instead of an outline, which is what lifts it off the page.

   The shape is built with `overflow: hidden` on the card and no radius on the children, rather than
   matching radii on the cap and the body. Matching radii is what the old rule did and it is why the
   two halves drifted apart visually -- there is no way to keep a square-bottomed cap and a
   round-bottomed body reading as one surface.
   ---------------------------------------------------------------------------------------------------- */

.aeca-vmo {
	align-items: stretch;
}

.aeca-vmo > [class*="col-"] {
	display: flex;
	margin-bottom: 1.5rem;
}

.aeca-vmo-card {
	display: flex;
	flex-direction: column;
	width: 100%;
	overflow: hidden;
	border: 1px solid var(--aeca-border, #d7e3ee);
	border-radius: 1.25rem;
	background: var(--aeca-white);
	box-shadow: 0 2px 6px rgba(1, 45, 97, 0.05), 0 12px 28px rgba(1, 45, 97, 0.07);
	transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}

.aeca-vmo-card:hover {
	transform: translateY(-4px);
	border-color: var(--aeca-blue);
	box-shadow: 0 4px 10px rgba(1, 45, 97, 0.08), 0 20px 42px rgba(1, 45, 97, 0.14);
}

@media (prefers-reduced-motion: reduce) {
	.aeca-vmo-card,
.aeca-vmo-card:hover {
		transform: none;
		transition: none;
	}
}

.aeca-vmo-head {
	position: relative;
	margin: 0;
	padding: 1.35rem 1rem 1.5rem;
	background: linear-gradient(120deg, #012d61 0%, #024da1 58%, #0b78ba 100%);
	color: var(--aeca-white);
	font-size: 1.15rem;
	line-height: 1.2;
	font-weight: 800;
	letter-spacing: 0.08em;
	text-align: center;
	text-transform: uppercase;
}

/* The same cyan rule that sits under the hero headlines and the footer headings. Centred here
   because the card is centred; it is what marks the cap as a heading rather than a coloured strip. */
.aeca-vmo-head::after {
	content: "";
	position: absolute;
	left: 50%;
	bottom: 0.85rem;
	width: 2.75rem;
	height: 3px;
	transform: translateX(-50%);
	background: var(--aeca-blue-light, #16adee);
}

.aeca-vmo-body {
	display: flex;
	flex: 1;
	align-items: center;
	padding: 1.75rem 1.5rem;
	color: var(--aeca-blue);
	text-align: center;
}

@media (max-width: 767.98px) {
	.aeca-vmo-head {
		padding: 1.15rem 1rem 1.3rem;
	}

	.aeca-vmo-body {
		padding: 1.4rem 1.25rem;
	}
}

/* Services page
----------------------------------------------------------------------------------------------------
Built to the mockup supplied 2026-08-07: category pills, then a zig-zag list of solutions where each
row is a circular line-art icon beside a bar that opens a detail panel.
---------------------------------------------------------------------------------------------------- */

/* Category pills */

.aeca-cat-pills {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	justify-content: center;
}

.aeca-cat-pill {
	display: inline-flex;
	align-items: center;
	min-height: 44px; /* keeps the tap target comfortable */
	padding: 0.6rem 1.5rem;
	border: 2px solid var(--aeca-blue);
	border-radius: 999px;
	background: var(--aeca-white);
	color: var(--aeca-blue);
	font-weight: 800;
	letter-spacing: 0.02em;
	text-align: center;
	text-transform: uppercase;
}

a.aeca-cat-pill:hover,
a.aeca-cat-pill:focus {
	background: var(--aeca-blue);
	color: var(--aeca-white);
}

/* Categories with no section built yet -- visibly inert rather than a link to nowhere. */
.aeca-cat-pill--todo {
	border-color: var(--aeca-border);
	color: var(--aeca-muted);
	cursor: not-allowed;
}

/* These two used to name their own font stack, from before the whole theme was moved onto Outfit.
   They now inherit --aeca-font-head like everything else and only need the weight. */
.aeca-cat-heading,
.aeca-vmo-head {
	font-weight: 800;
}

.aeca-cat-heading {
	color: var(--aeca-blue);
	font-size: 1.75rem;
	line-height: 1.2;
	font-weight: 800;
	letter-spacing: 0.02em;
	text-transform: uppercase;
}

/* Five category sections run back to back, so alternate ones get a tinted ground -- otherwise the
   page reads as one very long undifferentiated list. */
.aeca-cat-section {
	background: var(--aeca-white);
}

.aeca-cat-section--alt {
	background: var(--aeca-surface);
}

/* The sticky header would otherwise cover the heading of whichever section a pill jumps to. */
.aeca-cat-section {
	scroll-margin-top: 90px;
}


.aeca-icon {
	width: 68px;
	height: 68px;
}


.aeca-sol-title {
	display: block;
	color: var(--aeca-blue);
	font-weight: 800;
	font-size: 1.05rem;
	letter-spacing: 0.01em;
	text-transform: uppercase;
}

.aeca-sol-summary {
	display: block;
	margin-top: 0.35rem;
	color: var(--aeca-blue);
	font-size: 0.85rem;
	line-height: 1.45;
}


@media (max-width: 767.98px) {
	
	

	

	

	.aeca-icon {
		width: 30px;
		height: 30px;
	}

	.aeca-sol-title {
		font-size: 0.95rem;
	}
}

/* Industries strip (Services page)
----------------------------------------------------------------------------------------------------
These four were a bare photo with the caption set underneath in body text: no industry name, and no
link, so the one section on the page that names AECA's four markets was the only one you could not
click through. They now adopt the Home page's "Industries We Serve" card -- photo tile, permanent
bottom scrim, name badge top-left, copy stacked over the bottom of the photo, whole tile clickable.

Written against the <img> markup rather than reusing the Home rules directly. Home paints its photos
as background-images on `[class*="const-industries-div-"]`; those are decorative to the accessibility
tree and cannot carry alt text, and they are not a resource the browser can prioritise. A <figure>
with a real <img> and a <figcaption> is what this content actually is, so only the look is shared,
not the mechanism.
---------------------------------------------------------------------------------------------------- */

.aeca-industries {
	row-gap: 1.5rem;
	align-items: stretch;
}

/* The captions are different lengths, so the cards ran to different heights and the row bottom came
   out ragged -- 270 against 314 at 390px, 249 against 272 at 1440. Stretching the column and letting
   the card fill it lines the boxes up; the captions stay whatever length they are. */
.aeca-industries > [class*="col-"] {
	display: flex;
}

.aeca-industry-card {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	width: 100%;
	min-height: clamp(260px, 22vw, 340px);
	margin: 0;
	padding: var(--aeca-ind-pad, 1.15rem);
	overflow: hidden;
	border-radius: 16px;
	isolation: isolate;
	transition: transform 0.35s var(--aeca-ind-ease, ease), box-shadow 0.35s var(--aeca-ind-ease, ease);
}

/* The photo is the tile. Absolute rather than in flow so the copy can sit over it. */
.aeca-industry-card .aeca-industry-photo {
	position: absolute;
	inset: 0;
	z-index: -2;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.55s var(--aeca-ind-ease, ease);
}

/* Permanent scrim, weighted to the bottom where the type sits. Near-black blue rather than grey so
   it reads as shadow on the brand rather than as a wash. */
.aeca-industry-card::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: linear-gradient(
		to top,
		rgba(2, 20, 40, 0.92) 0%,
		rgba(2, 20, 40, 0.72) 28%,
		rgba(2, 20, 40, 0.24) 58%,
		rgba(2, 20, 40, 0.1) 100%
	);
}

.aeca-industry-card:hover,
.aeca-industry-card:focus-within {
	transform: translateY(-4px);
	box-shadow: 0 22px 44px rgba(0, 0, 0, 0.34);
}

.aeca-industry-card:hover .aeca-industry-photo,
.aeca-industry-card:focus-within .aeca-industry-photo {
	transform: scale(1.06);
}

.aeca-industry-badge {
	position: absolute;
	top: var(--aeca-ind-pad, 1.15rem);
	left: var(--aeca-ind-pad, 1.15rem);
	max-width: calc(100% - 2 * var(--aeca-ind-pad, 1.15rem));
	padding: 0.35em 0.85em;
	border-radius: 999px;
	background: var(--aeca-blue);
	color: var(--aeca-white);
	font-size: clamp(0.66rem, 0.9vw, 0.78rem);
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	line-height: 1.2;
}

.aeca-industry-caption {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin-top: 0;
	color: var(--aeca-white);
	font-size: 0.95rem;
	line-height: 1.45;
}

.aeca-industry-stat {
	font-weight: 600;
	text-wrap: pretty;
}

/* The link is the affordance; the stretched-link covers the tile, so this is only its label. */
.aeca-industry-link {
	color: var(--aeca-white) !important;
	font-size: 0.85rem;
	font-weight: 700;
	letter-spacing: 0.02em;
	text-decoration: none;
}

.aeca-industry-link-text::after {
	content: " \203A";
	display: inline-block;
	transition: transform 0.2s ease;
}

.aeca-industry-card:hover .aeca-industry-link-text::after,
.aeca-industry-card:focus-within .aeca-industry-link-text::after {
	transform: translateX(4px);
}

/* overflow:hidden on the card would clip the stretched link's focus ring, so it is drawn on the card
   instead -- the card is what the ring should outline anyway. */
.aeca-industry-card:focus-within {
	outline: 3px solid var(--aeca-blue-light);
	outline-offset: 2px;
}

.aeca-industry-link:focus-visible {
	outline: none;
}

/* Full width below 576px (see the column classes in page-services.php), so the tile turns landscape.
   The 260px floor was sized for a 156px-wide card that had to stack six lines of caption; at 335px
   wide the same caption is two or three lines, and holding 260px would leave a band of empty photo
   above the copy. */
@media (max-width: 575.98px) {
	.aeca-industry-card {
		min-height: 200px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.aeca-industry-card,
.aeca-industry-card .aeca-industry-photo,
.aeca-industry-link-text::after {
		transition: none;
	}

	.aeca-industry-card:hover,
.aeca-industry-card:focus-within {
		transform: none;
	}

	.aeca-industry-card:hover .aeca-industry-photo,
.aeca-industry-card:focus-within .aeca-industry-photo {
		transform: none;
	}
}

/* Partner logo strip
----------------------------------------------------------------------------------------------------
The five logos now render at genuinely equal size, but that was fixed in the ARTWORK, not here.

The originals are 280x280, 300x300, 400x400, 500x500 and 528x528, and the fraction of each canvas
the artwork actually occupies ranges from 19% (a 425x111 wordmark in a 500x500 square) to 100% (an
opaque green badge). object-fit fits the CANVAS, so no CSS box can equalise them -- an earlier pass
tried and left a TODO saying so.

tools/normalise-partner-logos.py trims each one to its own artwork, scales them all to the same
optical AREA -- equal area, not equal height: matched on height the Urbanice wordmark would carry
four times the visual mass of the PEJUTA square -- and re-centres them on a common 400x240 canvas.
Output is a .png and a .webp per logo in img/partners/; the originals are untouched.

400x240 is exactly 2x the 200x120 box below, so object-fit has nothing left to do and every logo
lands on the same baseline at the same size. A fixed box rather than the theme's width:25% keeps
them that size at every breakpoint -- the strip is a horizontal scroller, so narrow screens scroll.
---------------------------------------------------------------------------------------------------- */

/* The second half of the bug was in the CSS after all, and it is worth spelling out because it
   defeats anything written at one-class specificity. style.css carries a Blocs scroll-container
   block in which every declaration is !important:

       .scroll-container img { height: clamp(120px,15vw,180px)!important; width:auto!important;
                               max-width:none!important; object-fit:contain!important;
                               transform:scale(1.4)!important; mix-blend-mode:multiply!important }

   `width:auto` next to a fixed height means each logo is sized by its own CANVAS aspect ratio --
   the single most direct cause of five different sizes -- and the 1.4 scale then pushed each one
   40% outside the 200px card it sits in. `.aeca-partners .scroll-container img` is (0,2,1) against
   that rule's (0,1,1), so it wins, but only with !important repeated on each declaration. */

.aeca-partners .blocs-horizontal-scroll-area {
	align-items: center;
}

.aeca-partners .blocs-card-width {
	width: 200px;
}

.aeca-partners .scroll-container img,
.aeca-partners .aeca-partner-logo {
	/* Was `.aeca-partners picture { display: block }` while each logo was wrapped in a <picture> for
	   the WebP/PNG pair. The wrapper went when the PNGs did; left inline the image picks up a line
	   box and the descender space under it pushes the mark off the centre of the card. */
	display: block !important;
	width: 100% !important;
	height: 120px !important;
	max-width: 100% !important;
	max-height: none !important;
	object-fit: contain !important;
	object-position: center !important;
	transform: none !important;      /* the scale(1.4) above */
	mix-blend-mode: normal !important; /* the assets have real alpha; multiply only muddies them */
}

@media (max-width: 575px) {
	.aeca-partners .blocs-card-width {
		width: 150px;
	}

	.aeca-partners .scroll-container img,
.aeca-partners .aeca-partner-logo {
		height: 90px !important;
	}
}

/* The canvas is 400x240, so there is real resolution to spend on a big monitor. */
@media (min-width: 1600px) {
	.aeca-partners .blocs-card-width {
		width: 240px;
	}

	.aeca-partners .scroll-container img,
.aeca-partners .aeca-partner-logo {
		height: 144px !important;
	}
}

/* Continuous flow
----------------------------------------------------------------------------------------------------
The strip runs on its own rather than waiting to be dragged or arrowed through.

The markup emits the logos TWICE inside .aeca-marquee-track, the second set aria-hidden, and the
track slides by exactly -50%. (Eight of them since 2026-09-02, when GreenValley and Skyline were
added; the count is free to change, but the two halves must stay identical or the seam shows.) At that point copy B sits precisely where copy A began, so the loop
restarts with nothing to see -- no jump, no gap.

For the seam to land correctly the two halves must be identical widths, which is why the spacing is
margin-right on every card rather than `gap` on the track: `gap` puts no space after the last card,
so half the track would be one gap short and the loop would stutter once per revolution.

The theme's own scroll machinery is switched off here -- .scroll-container is
`overflow-x: scroll !important` and js/all.js binds prev/next controls to it. Those controls are
hidden because a marquee has no start or end to arrow towards.
---------------------------------------------------------------------------------------------------- */

.aeca-partners .blocs-horizontal-scroll-area {
	overflow: hidden !important;
	display: block !important;
}

.aeca-marquee-track {
	display: flex;
	align-items: center;
	animation: aeca-marquee 48s linear infinite;
	will-change: transform;
}

/* flex-wrap stated explicitly, and with !important, even though nowrap is the initial value.

   Reported from a live install: the strip rendered as two rows, and both rows showed the same three
   logos in the same order at the same offsets. That is the signature of this track wrapping rather
   than of anything scrolling -- the track holds the six logos twice, so a wrap breaks it at the
   halfway seam into two identical rows, and the visible area shows the first few of each.

   It does not reproduce here at any width from 360 to 1920, on any of the three pages that carry the
   strip, so the wrap is being introduced by something outside this theme -- most likely a CSS
   optimiser or a plugin stylesheet setting flex-wrap on flex containers. Rather than guess which,
   this makes the wrap impossible: nowrap is what the marquee requires to work at all, so there is no
   case where losing this rule is correct. */
.aeca-partners .aeca-marquee-track {
	flex-wrap: nowrap !important;
}

/* Two theme rules have to be undone on the track itself:
     .blocs-horizontal-scroll-area > * { flex-shrink: 0; max-width: 100% }  -- capped the track at
       the container width, so translateX(-50%) moved it half a *viewport* instead of half a track
       and the loop jumped every revolution;
     .scroll-container > div { margin-right: clamp(50px,8vw,100px) !important }  -- used to space
       the cards, but the track is now the only child so it spaced the track instead. */
.aeca-partners .aeca-marquee-track {
	width: max-content !important;
	max-width: none !important;
	margin-right: 0 !important;
}

.aeca-partners .blocs-card-width {
	margin-right: clamp(40px, 5vw, 96px);
	flex: 0 0 auto;
}

@keyframes aeca-marquee {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); }
}

/* Let people stop it to read a logo, by pointer or by keyboard. */
.aeca-partners:hover .aeca-marquee-track,
.aeca-partners:focus-within .aeca-marquee-track {
	animation-play-state: paused;
}

/* The arrows drive scrollLeft, which no longer moves anything. */
.aeca-partners .blocs-horizontal-scroll-control {
	display: none !important;
}

/* Motion that cannot be stopped is a vestibular trigger; fall back to the manual scroller. */
@media (prefers-reduced-motion: reduce) {
	.aeca-marquee-track {
		animation: none;
		width: auto;
	}

	.aeca-partners .blocs-horizontal-scroll-area {
		overflow-x: auto !important;
	}
}

/* Footer certification / award badges
----------------------------------------------------------------------------------------------------
All three badges now ship as 1200x800 files -- two landscape certificate scans and one portrait award
badge, each scaled to the same content height on a shared white canvas -- so the row is uniform from
the image side and the CSS only has to set one height. Before that the sources were 1417x822 and
500x625, and the portrait one rendered many times taller than the other two.
---------------------------------------------------------------------------------------------------- */

/* The rules moved to the FOOTER section at the end of this file, where the rest of the badge
   styling (the white chip, the flex row, the narrow-screen height) now lives. Two sets of
   .footer-certs rules 2000 lines apart is how one of them ends up dead unnoticed. */

/* Pagination
----------------------------------------------------------------------------------------------------
Used by parts/pagination.php on the blog, archive, search and taxonomy templates.
---------------------------------------------------------------------------------------------------- */

.aeca-pagination {
	margin-top: 3rem;
}

.aeca-pagination-list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	justify-content: center;
	list-style: none;
	margin: 0;
	padding: 0;
}

.aeca-pagination-list .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.75rem;
	height: 2.75rem; /* 44px: the minimum comfortable touch target */
	padding: 0 0.75rem;
	border: 1px solid var(--aeca-border);
	color: var(--aeca-blue);
	font-weight: 600;
	line-height: 1;
	text-decoration: none;
}

.aeca-pagination-list a.page-numbers:hover {
	background: var(--aeca-surface);
}

.aeca-pagination-list .page-numbers.current {
	background: var(--aeca-blue);
	border-color: var(--aeca-blue);
	color: var(--aeca-white);
}

.aeca-pagination-list .page-numbers.dots {
	border-color: transparent;
	color: var(--aeca-muted);
}

/* Search form
---------------------------------------------------------------------------------------------------- */

.aeca-search-form {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
}

.aeca-search-form .form-control {
	flex: 1 1 16rem;
}

/* Article body
----------------------------------------------------------------------------------------------------
The Blocs stylesheet only styles its own generated blocks, so content typed into the WordPress
editor -- lists, quotes, tables, headings -- arrived unstyled on single.php and page.php.
---------------------------------------------------------------------------------------------------- */

.aeca-entry-content > * + * {
	margin-top: 1.25rem;
}

.aeca-entry-content h2,
.aeca-entry-content h3,
.aeca-entry-content h4 {
	margin-top: 2.5rem;
	color: var(--aeca-blue);
}

.aeca-entry-content img {
	max-width: 100%;
	height: auto;
}

.aeca-entry-content ul,
.aeca-entry-content ol {
	padding-left: 1.5rem;
}

.aeca-entry-content li + li {
	margin-top: 0.5rem;
}

.aeca-entry-content blockquote {
	margin-left: 0;
	padding: 0.25rem 0 0.25rem 1.25rem;
	border-left: 4px solid var(--aeca-blue-light);
	color: var(--aeca-muted);
	font-style: italic;
}

.aeca-entry-content table {
	width: 100%;
	border-collapse: collapse;
}

.aeca-entry-content th,
.aeca-entry-content td {
	padding: 0.6rem 0.75rem;
	border: 1px solid var(--aeca-border);
	text-align: left;
}

.aeca-entry-content pre {
	overflow-x: auto;
	padding: 1rem;
	background: var(--aeca-surface);
}

/* Post meta, previous/next, link lists
---------------------------------------------------------------------------------------------------- */

.aeca-entry-meta {
	margin-top: 2.5rem;
	padding-top: 1.25rem;
	border-top: 1px solid var(--aeca-border);
	color: var(--aeca-muted);
}

.aeca-post-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
	justify-content: space-between;
	margin-top: 2.5rem;
	padding-top: 1.5rem;
	border-top: 1px solid var(--aeca-border);
}

.aeca-post-nav-next {
	margin-left: auto;
	text-align: right;
}

.aeca-post-nav a {
	color: var(--aeca-blue);
	font-weight: 600;
}

.aeca-page-links {
	margin-top: 2rem;
}

.aeca-link-list {
	margin: 0 0 2rem;
	padding-left: 1.25rem;
}

.aeca-link-list li + li {
	margin-top: 0.5rem;
}

.aeca-link-list a {
	color: var(--aeca-blue);
	font-weight: 600;
}

.aeca-comments {
	margin-top: 3rem;
	padding-top: 1.5rem;
	border-top: 1px solid var(--aeca-border);
}

/* ====================================================================================================
   LARGE SCREENS
   ----------------------------------------------------------------------------------------------------
   The theme stops growing at 1200px. style.css caps `.bloc > .container` at 1200px !important, and
   ~120 rules size their boxes in hard pixels (350px cards, 530px panels, a 500px service box), so a
   2560px monitor showed a small 1200px column stranded in white space.

   EVERYTHING in this section is inside @media (min-width: ...). That is not stylistic: this file
   loads after all of style.css including its appended @media patch blocks, so a base-level rule here
   would out-rank -- and silently cancel -- the tablet and phone rules those blocks provide.
   ==================================================================================================== */

/* Container ladder and side gutters.
   style.css re-asserts 1200px at >=1200px to undo its own `.container{max-width:100%!important}`.
   This continues that ladder. Each step has to restate the two full-bleed exemptions, because
   #sidebar (the nav bar) and #hero-carousel are `full-width-bloc`s that must reach both edges.

   SIDE PADDING ADDED 2026-09-03, and the ladder pulled in with it, on the note that "everything looks
   spread out ... include some space in between the left and right of the screen".

   Two separate things were making the Blocs-markup pages run to the edges:

   1. `.container` has NO horizontal padding at all. style.css sets `padding-left: 0; padding-right: 0`
      on it twice, so the max-width was the entire margin there was -- at 1440 that is 60px a side, or
      4.2% of the screen, and content squared off against the section edge with nothing between.
      Bootstrap's own container carries `--bs-gutter-x / 2` here for exactly this reason.

   2. The ladder grew faster than the content did. It was written to fix the opposite complaint -- a
      1200px column stranded in white space on a 2560px monitor -- and overshot: at 1920 a Blocs
      section ran 1680px while the `.aeca-*` sections beside it on the same page ran 1200px and the
      sub-page families ran 1180px. Three different measures on one site, with the widest one holding
      the smallest type.

   Both are fixed here rather than in style.css, which a Blocs re-export overwrites. Padding is inside
   the max-width (border-box is global), so the two compose: the numbers below are the CONTENT width,
   which is what actually matters.

       viewport   max-width   padding   content   space each side
       1280        1200        40        1120        80
       1440        1280        56        1168       136
       1600        1380        56        1268       166
       1920        1480        72        1336       292
       2560        1560        72        1416       572

   Content now moves 1120 -> 1416 across that range instead of 1200 -> 1680, which keeps the Blocs
   sections within about 200px of the `.aeca-*` ones at every width instead of 480px apart.

   DESKTOP ONLY, from 992px. Below that the container is nearly the whole screen and style.css's own
   phone and tablet patch blocks own the spacing -- see the note at the top of this section for why a
   base-level rule here would silently cancel them.

   THE TWO EXEMPTIONS TAKE THE PADDING OFF AS WELL AS THE CAP. #hero-carousel holds the home video and
   #sidebar holds the nav bar; both are drawn edge to edge on purpose, and 72px of inset would show as
   a white margin down each side of the video. */

/* THE `.aeca-*` SECTIONS ARE ON THE LADDER TOO, from 2026-09-03. They are not `.bloc > .container`,
   so they were never on it: they took style.css's flat 1200px cap while the Blocs section directly
   above them on the same page ran 1680px. On the home page that put "How clients rate us." and "Tell
   us what you need to see." on a different left edge from "Industries We Serve" and the footer, which
   is a large part of what read as spread out and uneven. One list, one edge.

   .aeca-sub IS EXCLUDED AND MUST STAY EXCLUDED. The three detail-page families are designed to a
   1180px measure -- see `.aeca-sub .container` in css/aeca-subpages.css and the note at the head of
   that file -- and their heroes, split figures and product stages are all positioned against it. The
   reset at the end of each step restores it, because `.aeca-action-panel` is on every detail page as
   well as on the hubs and would otherwise be dragged wide by the rule above. It still takes the
   smaller 24px gutter, purely as an edge guard at 1200px viewports. */

@media (min-width: 992px) {
	.bloc > .container,
.aeca-showcase-section > .container,
.aeca-action-panel > .container,
.aeca-faq > .container { padding-left: 40px; padding-right: 40px; }
	#hero-carousel > .container,
#sidebar > .container { padding-left: 0; padding-right: 0; }
	.aeca-sub .container { max-width: 1180px !important; padding-left: 24px; padding-right: 24px; }
}

@media (min-width: 1400px) {
	.bloc > .container,
.aeca-showcase-section > .container,
.aeca-action-panel > .container,
.aeca-faq > .container { max-width: 1280px !important; padding-left: 56px; padding-right: 56px; }
	#hero-carousel > .container,
#sidebar > .container { max-width: none !important; padding-left: 0; padding-right: 0; }
	.aeca-sub .container { max-width: 1180px !important; padding-left: 24px; padding-right: 24px; }
}

@media (min-width: 1600px) {
	.bloc > .container,
.aeca-showcase-section > .container,
.aeca-action-panel > .container,
.aeca-faq > .container { max-width: 1380px !important; }
	#hero-carousel > .container,
#sidebar > .container { max-width: none !important; }
	.aeca-sub .container { max-width: 1180px !important; }
}

@media (min-width: 1920px) {
	.bloc > .container,
.aeca-showcase-section > .container,
.aeca-action-panel > .container,
.aeca-faq > .container { max-width: 1480px !important; padding-left: 72px; padding-right: 72px; }
	#hero-carousel > .container,
#sidebar > .container { max-width: none !important; padding-left: 0; padding-right: 0; }
	.aeca-sub .container { max-width: 1180px !important; padding-left: 24px; padding-right: 24px; }
}

@media (min-width: 2400px) {
	.bloc > .container,
.aeca-showcase-section > .container,
.aeca-action-panel > .container,
.aeca-faq > .container { max-width: 1560px !important; }
	#hero-carousel > .container,
#sidebar > .container { max-width: none !important; }
	.aeca-sub .container { max-width: 1180px !important; }
}

/* Type that keeps growing.
   Below 1200px nothing changes -- style.css's own h1/h4 step-downs at 991/767/575 still govern.
   The clamp minimums are the theme's current 1200px values, so this can only ever scale up. */

/* All of these use the calc(A + X*vw) form documented in the "TYPE THAT KEEPS GROWING" section at
   the foot of this file: exactly the original size at 1200px, and the original x f at 2560px. A
   bare `vw` preferred value would top out around 1830px, which is what froze the type between 1920
   and 2560 the first time round. */
@media (min-width: 1200px) {
	h1 { font-size: clamp(80px, calc(60.24px + 1.647vw), 102px); line-height: 1.1; }   /* was 5em flat  */
	h2 { font-size: clamp(35px, calc(27.59px + 0.618vw), 43px); line-height: 1.15; }   /* was 35px      */
	h3 { font-size: clamp(30px, calc(23.65px + 0.529vw), 37px); line-height: 1.2; }    /* was 30px/30px */
	h4 { font-size: clamp(23px, calc(18.13px + 0.406vw), 28px); line-height: 1.35; }   /* was 23px/31px */

	/* 174 of the 200 generated .p-*-style classes set no font-size at all, so a bare `p` reaches
	   almost all body copy. The two dozen that do set one out-specify this and are scaled
	   proportionally by the generated block instead. */
	p,
li { font-size: clamp(16px, calc(13.18px + 0.235vw), 19.2px); line-height: 1.65; }

	.const-page-title { font-size: clamp(67px, calc(50.56px + 1.383vw), 86px); }
	.const-stat-number { font-size: clamp(60px, calc(45.18px + 1.235vw), 77px); line-height: 1.1; }

	/* .const-section-subtitle sat here on the same 60px -> 77px clamp as the stat number. It is in the
	   TYPE SCALE -- HIERARCHY section below instead, on a rule that has to reach every width rather
	   than only 1200px up. */
}

/* ====================================================================================================
   TYPE SCALE -- HIERARCHY
   ----------------------------------------------------------------------------------------------------
   Feedback came back that the home page type was too large and should drop a couple of sizes.
   Measuring first said otherwise. At 1400px the home h1 is 41px, which is the SMALLEST h1 on the
   site: About is 62px, the Services hub 70px, the twenty-two sub-page heroes 70-74px, and
   Industries / Products / Insights / Case Studies 98px. What actually reads as oversized on home is
   the label above each section ("Our Solutions", "Our Partners", "Industries We Serve", "Our
   Stories") at 60px -- half again the size of the page's own h1 sitting above it. The fault is an
   inverted hierarchy, not a page that is too big, and shrinking everything would have deepened it.

   So: the section label drops below the h1 where it belongs, home's body copy on a phone comes up to
   the size the rest of the site already uses, and the four pages running a 98px h1 come back into
   the same band as everything else.

   None of these need `!important`. Every rule they overrule is a single-class rule in style.css, and
   this file is enqueued after it, so source order carries them.
   ==================================================================================================== */

/* 1. The section label. It was 60px flat, stepping to 40px under 991px, and reaching 77px above
      1200px. Now 26px on a phone to 38px on a wide screen, so it reads as a label under the heading
      rather than a second, larger heading over it. One fluid rule rather than the old two fixed
      steps, which dropped 20px in a single pixel of viewport at 991px.

      The leading has to travel with it: style.css pins `line-height: 60px`, which was 1.0 at 60px
      type and would be 2.3 at 26px. */
.const-section-subtitle {
	font-size: clamp(26px, calc(21.2px + 1.23vw), 38px);
	line-height: 1.15;
}

/* 2. Home body copy on a phone. The hero paragraph was 15px under 991px and 10px under 575px, and
      the four shortcut cards 14px under 767px, against 16-17px everywhere else -- home was the one
      page whose copy got smaller as the screen did. The 1200px-and-up sizes are untouched; these
      breakpoints do not meet. */
@media (max-width: 991.98px) {
	.p-hero-carousel-style {
		font-size: 16px;
	}
}

@media (max-width: 767.98px) {
	.p-592-style,
.p-594-style,
.p-596-style,
.p-shortcuts-style {
		font-size: 16px;
	}
}

/* 3. The Industries / Products / Insights / Case Studies h1 lives at `.aeca-page-hero h1`, further
      down the file next to the rest of that hero's rules. */

/* 4. The home h1 itself, which is what started all of this: 41px at 1400px and 22.6px on a phone,
      the smallest heading on the site sitting on the page that has to introduce it.

      It cannot simply be given the same clamp as everything else. The headline is three authored
      lines ("Precision Hardware." / "Expert Services." / "Absolute Geospatial Clarity."), it sits in
      the glass card, and the longest of those lines measured 516px inside a 536px box -- twenty
      pixels of slack. Anything past about 43px wraps the third line. So the card comes up with the
      type: 620px -> 780px above 1200px, and the 1600/2000 steps further up this file move with it.
      At 1400px that is a 716px box holding a 698px line, and at 1600px a 796px box holding 773px.

      `!important` because the generated block above sets this class at
      `clamp(40px, ..., 49.6px) !important`, and !important beats specificity.

      Two ranges rather than one clamp. Above 1200px the card widens and the heading runs 50px to
      62px, which puts home within a few pixels of About and the sub-page heroes. Below 1200px the
      card is left exactly as it is -- it is centred over the video and its widths there were tuned
      per breakpoint -- and the heading only rises far enough to clear the section labels, which are
      26-36px across that range. The third line already wrapped below 575px before any of this, so
      the extra line the larger sizes cost on a phone is not a new behaviour. */
@media (min-width: 1200px) {
	#hero-carousel .card-hero-carousel-style {
		width: min(780px, calc(100vw - 48px));
	}

	#hero-carousel .h3-card-heading-style {
		font-size: clamp(48px, calc(18px + 2.5vw), 54px) !important;
		line-height: 0.92 !important;
	}
}

@media (max-width: 1199.98px) {
	#hero-carousel .h3-card-heading-style {
		font-size: clamp(32px, calc(24.1px + 2.03vw), 42px) !important;
		line-height: 1.02 !important;
	}
}

/* The oversized watermark word that used to sit behind section headings (.const-bg-text) is gone --
   all twenty-four instances were deleted from the templates. It was pure white on the #F9FBF7 section
   ground with `margin-bottom:-100px` pulling the real heading up over it, so it never read as a
   watermark: it read as a faint duplicate of the heading below it ("PROJECTS" over "Our Past
   Projects", "SERVICES" over "Our Services"), which looked like a rendering fault. It also put a
   stray word ahead of every section heading for a screen reader.

   The rule that used to continue its clamp above 1400px went with it. Nothing replaces it; the
   headings stand on their own. */

/* Cards stop being 350px wide in a 600px column.
   style.css Â§8b already caps every const-*-div at the viewport with a :where() rule; this releases
   the hard width so they fill the grid cell the container has just widened. Kept at >=1400px so
   the deliberate 530px centring at 992-1399px is untouched. */

@media (min-width: 1400px) {
	/* NOT :where(). The first version of this rule used :where(), which contributes zero
	   specificity, so `.const-industries-div-5 { width: 350px }` at (0,1,0) beat it and every one of
	   these cards stayed 350px wide, hard against the left edge of a column that had grown to 430px
	   at 1440 and 577px at 2560. Measured on 9 templates before the fix. Two attribute selectors
	   give (0,2,0), which wins, and the pair still only matches the card divs -- both "const-" and
	   "-div" have to be present. */
	[class*="const-"][class*="-div"] {
		width: 100%;
		max-width: none;
	}

	.const-industries-div-1,
.const-industries-div-2,
.const-industries-div-3,
.const-industries-div-4,
.const-project-div {
		width: 100%;
		max-width: none;
	}

	.const-service-box-1,
.const-service-box-2,
.const-service-box-3,
.const-service-box-4 {
		max-width: none;
	}

	/* Nothing to centre once they fill; stated so the rule below cannot leave a stale auto margin. */
	[class*="const-"][class*="-div"] {
		margin-left: 0;
		margin-right: 0;
	}

	/* Fixed heights clip once the type above has grown. */
	.const-industries-div-1,
.const-industries-div-2,
.const-industries-div-3,
.const-industries-div-4,
.const-project-div,
.const-past-projects-div-1,
.const-past-projects-div-2,
.const-past-projects-div-3 {
		height: auto;
		min-height: 400px;
	}
}

/* Below 1400px these cards keep their designed 350px/530px width, but the column around them can be
   far wider -- a full-width col-12 at 768px is 713px, so the card sat hard left with 363px of dead
   space beside it. They are decorative panels, not text columns, so centring is the right answer;
   above 1400px they fill instead and the auto margins are reset there. No const-*-div rule in
   style.css sets a margin of its own, so nothing is being overridden here. */
@media (max-width: 1399.98px) {
	[class*="const-"][class*="-div"],
[class*="const-service-box"] {   /* capped at max-width:500px below 992px, same dead space */
		margin-left: auto;
		margin-right: auto;
	}
}


/* ====================================================================================================
   INK FOLLOWS ITS GROUND
   ----------------------------------------------------------------------------------------------------
   White ink on a dark ground, brand blue #024DA1 on a white one.

   What was there instead: 153 elements at #111111, 108 at #282828, 19 at 28%-opacity grey, and 38
   still carrying .tc-3438 -- the Construction template's yellow #FFD60A, which has a 1.7:1 contrast
   ratio against white and was never part of the AECA palette. Blocs' own defaults are no better:
   .l-bloc sets rgba(0,0,0,.5) and .d-bloc rgba(255,255,255,.7), so nothing was ever full strength.

   Keyed on the bloc, not on the element, so it holds for markup added later.

   NOT touched:
     - the --swatch-var-* definitions. --swatch-var-7112 also backs 4 backgrounds and 20 borders, so
       redefining it to blue would repaint those too.
     - .tc-3416 (#16ADEE, 55 uses), kept as an accent.
     - .btn and anything inside one -- buttons carry their own colour pairs.

   WHICH GROUND IS WHICH
   d-bloc / l-bloc cannot be trusted on its own. index.php #stats-image is tagged `l-bloc` but is a
   photo under rgba(0,0,0,.5); page-case-studies #bloc-180 and the four #project-hero blocs carry no
   marker at all and are also darkened photos. The texture-darken hook catches all six. Everything
   else with a photo ground is already tagged d-bloc.
   ==================================================================================================== */

/* The elements that carry running ink. Deliberately excludes form controls, .btn, and the SVG icons
   (which paint with `fill`, not `color`). */

/* ---- Light grounds ---------------------------------------------------------------------------- */

.bloc:not(.d-bloc):not([class*="texture-darken"]) {
	color: var(--aeca-on-light);
}

.bloc:not(.d-bloc):not([class*="texture-darken"]) :is(h1,
h2,
h3,
h4,
h5,
h6,
p,
li,
dt,
dd,
blockquote,
figcaption,
td,
th,
label,
small,
strong,
em,
b,
i,
span,
.card,
a:not(.btn)):not(.tc-3416):not(.btn *) {
	color: var(--aeca-on-light);
}

/* The ink utilities, remapped. Two selectors: one for elements inside the bloc, one for when the
   bloc element itself carries the class (#services-desc is `class="bloc tc-4978"`, and every child
   without its own tc-* inherits from it). Both need !important -- every .tc-* rule has it. */
.bloc:not(.d-bloc):not([class*="texture-darken"]) :is(.tc-7112,
.tc-4978,
.tc-1589,
.tc-7343,
.tc-3438,
.tc-2807),
.bloc:not(.d-bloc):not([class*="texture-darken"]):is(.tc-7112,
.tc-4978,
.tc-1589,
.tc-7343,
.tc-3438,
.tc-2807) {
	color: var(--aeca-on-light) !important;
}

/* ---- Dark grounds ----------------------------------------------------------------------------- */

.bloc.d-bloc,
.bloc[class*="texture-darken"] {
	color: var(--aeca-on-dark);
}

:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) :is(h1,
h2,
h3,
h4,
h5,
h6,
p,
li,
dt,
dd,
blockquote,
figcaption,
td,
th,
label,
small,
strong,
em,
b,
i,
span,
.card,
a:not(.btn)):not(.tc-3416):not(.btn *) {
	color: var(--aeca-on-dark);
}

:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) :is(.tc-7112,
.tc-4978,
.tc-1589,
.tc-7343,
.tc-3438,
.tc-2807),
:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]):is(.tc-7112,
.tc-4978,
.tc-1589,
.tc-7343,
.tc-3438,
.tc-2807) {
	color: var(--aeca-on-dark) !important;
}

/* ---- Light panels sitting inside a dark bloc flip back ---------------------------------------- */
/* No such panel exists in the theme today -- the only two cards inside dark blocs are the home hero
   card (blue ground, white ink) and .const-project-card (transparent). This is a guard for content
   added later, and is why the selector is the explicit light-background classes rather than .card. */

:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) :is(.bgc-2251,
.bgc-2006,
.bgc-5877),
:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) :is(.bgc-2251,
.bgc-2006,
.bgc-5877) :is(h1,
h2,
h3,
h4,
h5,
h6,
p,
li,
td,
span,
a:not(.btn)) {
	color: var(--aeca-on-light) !important;
}

/* ---- Components that set their own pairing, re-asserted after the above ------------------------ */

/* Solid blue cap on the VISION / MISSION / OBJECTIVE cards -- white ink on blue, inside a light bloc. */
.aeca-vmo-head,
.aeca-vmo-head strong {
	color: var(--aeca-white) !important;
}

/* Pills invert on hover. */
a.aeca-cat-pill:hover,
a.aeca-cat-pill:focus,
a.aeca-cat-pill:hover span,
a.aeca-cat-pill:focus span {
	color: var(--aeca-white) !important;
}

.aeca-skip-link,
.aeca-skip-link:focus {
	color: var(--aeca-white) !important;
}

/* The home hero card has a blue ground of its own inside an already-dark bloc. */
#hero-carousel .card-hero-carousel-style,
#hero-carousel .card-hero-carousel-style :is(h1,
h2,
h3,
h4,
h5,
h6,
p,
span,
li) {
	color: var(--aeca-white) !important;
}

/* The scroll-to-top button was the last surviving piece of the Construction template's palette:
   `.scrollToTop{background-color:var(--swatch-var-primary)}` resolves to #FFD60A, with a black
   chevron on top of it. It is fixed-position, so that yellow square sat over every page at every
   width -- the only off-brand colour left on the site after the ink sweep. Blue ground, white
   chevron, light-blue on hover, to match the buttons everywhere else. */
button.scrollToTop {
	background-color: var(--aeca-blue) !important;
	border: none !important;
}

button.scrollToTop:hover,
button.scrollToTop:focus {
	background-color: var(--aeca-blue-light) !important;
}

button.scrollToTop .scroll-to-top-btn-icon {
	stroke: var(--aeca-white) !important;
}

/* ====================================================================================================
   HOME / ABOUT / SERVICES -- the last fixed sizes above 1200px
   Same rule as the section above: min-width only, so nothing here can reach the phone breakpoints.
   ==================================================================================================== */

/* Home hero.
   The video is capped at 900px and the panel at 780px, so from about 1600px up the composition stops
   growing while everything around it keeps going and the card starts to look stranded.

   These two widths were 720px and 800px, sized against the 620px the panel used to be below 1600px.
   The TYPE SCALE -- HIERARCHY section took the base up to 780px so the headline could grow, which
   left 720px here narrower than the step below it. Both moved up by the same 160px, so the panel
   still gains at each step and the 62px headline keeps its third line intact: 860px gives a 796px
   box for a 773px line. */
@media (min-width: 1600px) {
	#hero-carousel .hero-video { height: clamp(700px, 62vh, 1040px); }
	#hero-carousel .card-hero-carousel-style { width: min(860px, calc(100vw - 48px)); }

	/* A `clamp(54px, calc(18px + 2.5vw), 60px)` headline rule sat here and had no effect: the TYPE
	   SCALE section near the end of this file carries the same selector in the same `min-width:
	   1600px` media at the same specificity, and being later in source it won. Measured 49.3px at
	   1600px, which is that rule's value, not this one's. Removed 2026-08-17 rather than left to be
	   read as the live size. The headline sizes are all in that one section now. */
}

@media (min-width: 2000px) {
	#hero-carousel .hero-video { height: clamp(820px, 58vh, 1180px); }
	#hero-carousel .card-hero-carousel-style { width: min(940px, calc(100vw - 48px)); }
}

/* Bare .aeca-icon, scaled up on the widest screens.
   These two bumps were drawn for the industries hub's accordion circle, which is gone as of
   2026-09-01. They are kept because .aeca-icon is the fallback size for any icon that is not inside
   a component setting its own, and 68px is small at 2560. Every component that does set its own --
   .aeca-solcard__icon, .aeca-home-service__icon, .aeca-value-card__icon, .aeca-service-card__icon --
   uses a two-class selector, so it wins here whatever the viewport. */
@media (min-width: 1400px) {

	.aeca-icon { width: 78px; height: 78px; }
}

@media (min-width: 1920px) {

	.aeca-icon { width: 86px; height: 86px; }
}

/* The sticky bar is 80px from 1200px up and 70px/58px below it (style.css sets those explicitly), so
   the anchor offset the category pills jump to has to follow it or the heading lands underneath. */
.aeca-cat-section { scroll-margin-top: 78px; }

@media (max-width: 1199.98px) { .aeca-cat-section { scroll-margin-top: 80px; } }
@media (max-width: 575.98px)  { .aeca-cat-section { scroll-margin-top: 68px; } }

/* ====================================================================================================
   TYPE THAT KEEPS GROWING PAST 1200px
   ----------------------------------------------------------------------------------------------------
   The container ladder widened the page but the wording stayed put, for three reasons:

     1. The first clamps used a bare `vw` preferred value, so they reached their ceiling around
        1830px. 1920 and 2560 measured identical.
     2. 133 base-level Blocs rules set font-size in hard pixels on a single generated class
        (.h1-30-style, .h2-13-style, .p-11-style ...). At (0,1,0) they beat a bare `h1` at (0,0,1),
        so most headings never scaled at all -- the home h1 sat at 40px from 1200 to 2560.
     3. 34 of those 133 carry !important (.link-style, .icon-md, the whole nav). Equal specificity
        is not enough against those, which is why the nav bar stayed at 14px after the first pass.
        Hence !important here. It is safe: this block is gated at min-width:1200px and style.css has
        no font-size rule of its own above 1200px.

   Rather than flatten those values to one clamp per tag -- which would have collapsed the
   hierarchy, since `h2` is 24px on the home page and 60px on the product pages -- every distinct
   size is scaled *proportionally* from its own starting value. Each class keeps its place in the
   scale; the whole scale just gets bigger.

   The preferred value is calc(A + X*vw), solved so the size is exactly the original at 1200px and
   the original x factor at 2560px:

       A + 12X = N          X = N(f-1)/13.6
       A + 25.6X = fN       A = N - 12X

   f is 1.20 up to 20px, 1.24 to 40px, 1.28 above -- running text grows least, display type most.

   LINE HEIGHT has to come with it. 27 of these rules also pin line-height in px, so growing the
   font alone made the lines collide -- the home hero heading is font-size:40px/line-height:35px, and
   at 2560 that became 50px of type in a 35px line box. Each is restated as the unitless ratio it
   already was (35/40 = 0.875), which is identical at 1200px and scales from then on.

   Generated by walking style.css; regenerate if Blocs re-exports with new sizes.
   ==================================================================================================== */

@media (min-width: 1200px) {

	/* 14px -> 16.8px  (8 rules) */
	.const-footer-form,
.link-about-us-style,
.link-contact-us-style,
.link-industries-style,
.link-products-style,
.link-style,
.p-624-style,
.p-628-style {
		font-size: clamp(14px, calc(11.53px + 0.206vw), 16.8px) !important;
	}

	/* 15px -> 18px  (3 rules) */
	.p-3229-style,
.p-474-style,
.p-481-style {
		font-size: clamp(15px, calc(12.35px + 0.221vw), 18px) !important;
	}

	/* 16px -> 19.2px  (2 rules) */
	.const-wire-button,
.p-321-style {
		font-size: clamp(16px, calc(13.18px + 0.235vw), 19.2px) !important;
	}

	/* 18px -> 21.6px  (36 rules) */
	.const-button,
.const-button-carousel-2,
.const-button-carousel-3,
.const-button-carousel-4,
.const-button-carousel-5,
.const-button-industries,
.const-button-solutions,
.const-form-button,
.link-0-contact-us-style,
.link-0-products-style,
.link-0-style,
.link-3dt-scanfly-style,
.link-about-style,
.link-agriculture-style,
.link-defect-style,
.link-defense-style,
.link-digital-infra-style,
.link-dji-style,
.link-environment-style,
.link-environmental-style,
.link-faqs-style,
.link-geospatial-style,
.link-gis-style,
.link-infrastructure-style,
.link-insights-style,
.link-testimonials-style,
.link-micasense-style,
.link-mobile-mapping-style,
.link-ororatech-style,
.link-our-stories-style,
.link-precision-style,
.link-sentera-style,
.link-services-style,
.link-sidebar-style,
.link-smartdelta-style,
.link-sony-style,
.link-xenomatix-style,
.p-hero-carousel-style {
		font-size: clamp(18px, calc(14.82px + 0.265vw), 21.6px) !important;
	}

	/* 20px -> 24px  (8 rules) */
	.const-project-detail,
.h3-16-style,
.h3-19-bloc-5-style,
.h3-22-bloc-5-style,
.h3-card-footer-style,
.p-21-bloc-5-style,
.p-bloc-5-style,
.p-style {
		font-size: clamp(20px, calc(16.47px + 0.294vw), 24px) !important;
	}

	/* 21px -> 26px  (1 rule) */
	.const-summary-text {
		font-size: clamp(21px, calc(16.55px + 0.371vw), 26px) !important;
	}

	/* 23px -> 28.5px  (1 rule) */
	.const-read-more-link {
		font-size: clamp(23px, calc(18.13px + 0.406vw), 28.5px) !important;
	}

	/* 24px -> 29.8px  (4 rules) */
	.h2-1-style,
.h2-2-style,
.h2-shortcuts-style,
.h2-style {
		font-size: clamp(24px, calc(18.92px + 0.424vw), 29.8px) !important;
	}

	/* 25px -> 31px  (6 rules) */
	.button-forest,
.button-infra,
.button-management,
.button-plant,
.button-smart,
.const-accordion-link {
		font-size: clamp(25px, calc(19.71px + 0.441vw), 31px) !important;
	}

	/* 26px -> 32.2px  (4 rules) */
	.h3-165-style,
.h3-166-style,
.h3-167-style,
.h3-168-style {
		font-size: clamp(26px, calc(20.49px + 0.459vw), 32.2px) !important;
	}

	/* 28px -> 34.7px  (10 rules) */
	.const-navigation,
.h2-13-style,
.h2-14-style,
.h2-15-style,
.h2-environment-style,
.h4-27-style,
.p-22-style,
.p-23-style,
.p-25-style,
.p-bloc-6-style {
		font-size: clamp(28px, calc(22.07px + 0.494vw), 34.7px) !important;
	}

	/* 30px -> 37.2px  (20 rules) */
	.const-logo,
.h2-17973-style,
.h3-36879-style,
.h3-39-style,
.h3-41-style,
.h3-43-style,
.h3-45-style,
.h3-47-style,
.h3-49-style,
.h3-8-style,
.h3-bloc-6-style,
.h3-navigate-style,
.h3-services-grid-style,
.h3-style,
.h4-64-style,
.h4-65-style,
.h4-hero-carousel-style,
.icon-md,
.p-597-style,
.text-style-1 {
		font-size: clamp(30px, calc(23.65px + 0.529vw), 37.2px) !important;
	}

	/* 40px -> 49.6px  (6 rules) */
	.const-members-icon,
.h3-11-style,
.h3-14-style,
.h3-17-style,
.h3-20-bloc-5-style,
.h3-card-heading-style {
		font-size: clamp(40px, calc(31.53px + 0.706vw), 49.6px) !important;
	}

	/* 50px -> 64px  (1 rule) */
	.const-shortcut-icon {
		font-size: clamp(50px, calc(37.65px + 1.029vw), 64px) !important;
	}

	/* 60px -> 76.8px  (12 rules) */
	.h1-about-hero-style,
.h2-136-style,
.h2-139-style,
.h2-143-style,
.h2-19-style,
.h2-197-style,
.h2-7-style,
.h2-bloc-109-style,
.h2-bloc-72-style,
.h3-185-style,
.h3-projects-grid-style,
.icon-lg {
		font-size: clamp(60px, calc(45.18px + 1.235vw), 76.8px) !important;
	}

	/* 70px -> 89.6px  (1 rule) */
	.h2-212-style {
		font-size: clamp(70px, calc(52.71px + 1.441vw), 89.6px) !important;
	}

	/* 80px -> 102.4px  (4 rules) */
	.p-14-style,
.p-16-style,
.p-20-style,
.p-about-stats-style {
		font-size: clamp(80px, calc(60.24px + 1.647vw), 102.4px) !important;
	}

	/* 100px -> 128px  (3 rules) */
	.const-partners-title,
.const-quality-big,
.stats-icon {
		font-size: clamp(100px, calc(75.29px + 2.059vw), 128px) !important;
	}

	/* Leading, as the ratio each rule already expressed in pixels. */

	/* 0.8 */
	.const-quality-big {
		line-height: 0.8 !important;
	}

	/* 0.875 */
	.h3-card-heading-style {
		line-height: 0.875 !important;
	}

	/* 1 */
	.h2-136-style,
.h2-139-style,
.h2-143-style,
.h2-17973-style,
.h2-19-style,
.h2-197-style,
.h2-7-style,
.h2-bloc-109-style,
.h3-16-style,
.h3-165-style,
.h3-166-style,
.h3-167-style,
.h3-168-style,
.h3-185-style,
.h3-19-bloc-5-style,
.h3-22-bloc-5-style,
.h3-36879-style,
.h3-card-footer-style,
.h3-projects-grid-style {
		line-height: 1 !important;
	}

	/* 1.214 */
	.h2-environment-style {
		line-height: 1.214 !important;
	}

	/* 1.381 */
	.const-summary-text {
		line-height: 1.381 !important;
	}

	/* 1.5 */
	.h4-64-style,
.h4-65-style,
.h4-hero-carousel-style,
.p-597-style {
		line-height: 1.5 !important;
	}
}

/* The components written in this file rather than by Blocs.
   Same calc(A + X*vw) form, so they travel with the generated scale above instead of sitting at a
   fixed rem while everything around them grows. */
@media (min-width: 1200px) {
	/* 18.4px -> 22.1px */
	.aeca-vmo-head { font-size: clamp(18.4px, calc(15.16px + 0.271vw), 22.1px); }
	/* 28px -> 34.7px */
	.aeca-cat-heading { font-size: clamp(28px, calc(22.07px + 0.494vw), 34.7px); }
	/* 16.8px -> 20.2px */
	.aeca-sol-title { font-size: clamp(16.8px, calc(13.84px + 0.247vw), 20.2px); }
	/* 13.6px -> 16.3px */
	.aeca-sol-summary { font-size: clamp(13.6px, calc(11.2px + 0.2vw), 16.3px); }
	/* 15.2px -> 18.2px */
	.aeca-industry-caption { font-size: clamp(15.2px, calc(12.52px + 0.224vw), 18.2px); }

	/* The About hero headline and its panel. The panel is capped at 44rem, so the headline was
	   topping out at 54px while the page around it kept growing; both now widen together. */
	.about-hero-panel { max-width: clamp(44rem, 30rem + 12vw, 58rem); }
	.about-hero-panel .const-page-title { font-size: clamp(48px, calc(36.14px + 0.988vw), 61px); }
	/* 18px -> 21.6px. Its own clamp maxed out at 18.4px, i.e. by about 1230px. */
	.about-hero-lead {
		max-width: clamp(34rem, 24rem + 8vw, 44rem);
		font-size: clamp(18px, calc(14.82px + 0.265vw), 21.6px);
	}
}

/* The Services solution rows restate their own sizes at >=1400px for the larger circle; those
   values now come from the fluid scale above, so only the geometry is left there. */

/* ====================================================================================================
   NAV BAR
   ----------------------------------------------------------------------------------------------------
   The bar was the one part of the page that did not move at all above 1200px: 80px tall with an 80px
   logo and a 30px menu icon at 1200, and exactly the same at 2560, while the content column beside it
   grew from 1200 to 1760 and the type with it. It ended up looking like a strip borrowed from a
   narrower site.

   The link SIZE is handled by the generated scale above (.link-style and friends are 14px !important,
   which is what defeated the first attempt). What is left here is geometry.

   Two things deliberately not touched:
     - Anything below 1200px. style.css switches .const-navigation-bar to a 3-column grid at
       <=1199.98px with explicit 70px/58px track sizes, and those still govern.
     - `position`. js/all.js swaps the bar to position:fixed via .sticky-nav.sticky on scroll and
       pads .page-container by the bar's measured height, so it follows a fluid height on its own.
   ==================================================================================================== */

@media (min-width: 1200px) {
	/* 80px -> 96px, on the same curve as the display type (f = 1.20 for the bar's own scale). */
	#sidebar .const-navigation-bar {
		height: clamp(80px, calc(65.88px + 1.176vw), 96px);
	}

	#sidebar .const-navigation-bar > :first-child {
		flex: 0 0 clamp(80px, calc(65.88px + 1.176vw), 96px);
		width: clamp(80px, calc(65.88px + 1.176vw), 96px);
	}

	#sidebar .const-logo {
		width: clamp(80px, calc(65.88px + 1.176vw), 96px);
		height: clamp(80px, calc(65.88px + 1.176vw), 96px);
	}

	#sidebar .const-sidebar-icon {
		width: clamp(80px, calc(65.88px + 1.176vw), 96px);
		height: clamp(80px, calc(65.88px + 1.176vw), 96px);
	}

	/* The gap between menu items. 28px either side at 1200; a wider bar can afford more, but this is
	   capped well short of the width gained so the group stays a group rather than drifting apart. */
	#sidebar .navbar-nav li a {
		/* Seven top-level routes must fit at the 1200px hand-off too. */
		margin-left: clamp(12px, calc(5.65px + 0.529vw), 19px);
		margin-right: clamp(12px, calc(5.65px + 0.529vw), 19px);
		white-space: nowrap;
	}

	/* The dropdown panels hang off those links, so they follow the same type scale. */
	#sidebar .dropdown-menu .dropdown-item {
		padding-top: clamp(6px, 0.4vw, 9px);
		padding-bottom: clamp(6px, 0.4vw, 9px);
	}
}

/* ====================================================================================================
   THE GLASS PANEL, SHARED
   ----------------------------------------------------------------------------------------------------
   The treatment built for the About hero, factored into tokens and applied to the home and services
   heroes so the three read as one component.

   Applied through the markup that is already there -- the hero content column on the Services page,
   the existing card on the home page -- rather than by adding a wrapper div to twenty templates.

   THREE PAGES ONLY: home, About, Services. `#services-hero` is not the Services page -- it is a Blocs
   id copy-pasted into nineteen templates (every services-*, every products-*), so hanging the panel
   off the id alone put a card on all nineteen. The Services page is the one that wraps its hero copy
   in `.column-hero-solutions`; the other eighteen use a bare `.col`. Selecting on that class is what
   keeps the treatment to the three pages, so do not loosen these selectors back to `[class*="col"]`.
   The product and sub-service heroes are meant to read as a plain band over the photograph.

   Still no backdrop-filter, for the reason recorded against .about-hero-panel: a backdrop-filter
   anywhere makes the whole document composite through a different pipeline and every white surface
   on the page picks up a blue cast. The gradient carries the contrast on its own.
   ==================================================================================================== */

:root {
	/* Alpha history: 0.62 originally, then floored at 0.86 because the home page's autoplaying video
	   moved visibly through the lower half of the panel and the blue looked like it was failing to
	   render. 0.86 made the panel opaque, which is the opposite fault -- the photograph and the video
	   the heroes are built on stopped reaching the viewer at all.

	   Back down to 0.60 -> 0.38, so the ground is genuinely visible through the card. The moving-video
	   objection is answered by the text-shadow in the HERO PANEL TYPE section at the end of this file,
	   not by the panel's own alpha: the copy carries its own contrast now, so the blue only has to
	   tint the image rather than hide it. Border and shadow come down with it, because a hard edge
	   around a see-through panel reads as a cut-out.

	   Still no backdrop-filter -- see the note above. */
	--aeca-glass-bg: linear-gradient(150deg, rgba(2, 77, 161, 0.60) 0%, rgba(2, 77, 161, 0.50) 45%, rgba(2, 77, 161, 0.38) 100%);
	--aeca-glass-border: 1px solid rgba(22, 173, 238, 0.42);
	--aeca-glass-radius: 4px 28px 4px 28px;
	--aeca-glass-shadow: 0 18px 48px rgba(0, 0, 0, 0.22);
	--aeca-glass-pad: clamp(1.5rem, 3vw, 3rem);
}

.about-hero-panel,
#hero-carousel .card-hero-carousel-style,
#services-hero .column-hero-solutions {
	background: var(--aeca-glass-bg) !important;
	border: var(--aeca-glass-border) !important;
	border-radius: var(--aeca-glass-radius) !important;
	box-shadow: var(--aeca-glass-shadow) !important;
}

/* Home. The card was a flat rgba(0,112,227,.70) with a 20px radius; only the surface changes, the
   sizing and centring it already had are left alone. */
#hero-carousel .card-hero-carousel-style {
	overflow: hidden;
}

/* Home: separate the shortcut cards from the hero.
   style.css pulls `.const-shortcuts` up with `margin-top: -100px` above 991px, and the row's own
   container carries `bloc-no-padding-lg`, which zeroes its vertical padding from 1200px up. The two
   together meant the four cards did not merely touch the hero, they climbed 100px into it and sat
   over the video with the section boundary somewhere behind them. Below 992px the negative margin
   is already 0, but the container's 28px was the only thing between the hero panel and the first
   card, which is not enough to read as a break between two sections.

   The pull is released and the gap comes from the container instead, so it is one number at every
   width: 48px on a phone up to 88px on a wide screen.

   `#shortcuts` is only in index.php -- checked, unlike most Blocs ids in this theme. */
#shortcuts .const-shortcuts {
	margin-top: 0;
}

#shortcuts > .container {
	padding-top: clamp(48px, 5vw, 88px);
}

/* Breathing room under the Services hero, below 992px only.
   The hero container runs 200px of vertical padding on a desktop and 28px on a phone, so the panel
   there had 28px of photograph above it and 28px below before the section ended -- it read as a
   block wedged into the image rather than as a panel sitting on it. The section after it opened
   40px later, which is not enough to separate two full-width bands.

   Desktop is untouched: 200px above the panel and 100px before the pills is already generous, and
   growing it would only push the first real content further below the fold.

   `#bloc-10` is safe to select on: unlike `#services-hero`, which Blocs copy-pasted into nineteen
   templates, this id appears in page-services.php and nowhere else. Checked. */
@media (max-width: 991.98px) {
	#services-hero .container {
		padding-top: 52px;
		padding-bottom: 52px;
	}

	#bloc-10 .container {
		padding-top: 64px;
	}
}

/* Services. The hero column is `col-lg-12`, a full-width flex item, so it needs capping and centring
   before it reads as a panel rather than a band. */
#services-hero .column-hero-solutions {
	box-sizing: border-box;
	max-width: min(62rem, 100%);
	margin-left: auto;
	margin-right: auto;
	padding: var(--aeca-glass-pad);
	text-align: center;
}

/* The h1 carries `text-start` from Blocs, which beats the inherited centring on the panel.

   `!important` is not decoration here. `.text-start` is a Bootstrap utility and Bootstrap writes
   every one of them as `text-align: left !important`, so no amount of specificity on a normal
   declaration reaches it. Without this the Services h1 sat left while the h4 under it -- which
   carries no `text-start` -- sat centred, on every screen below 992px. Above 992px the h1's own
   `text-lg-center` (also !important) took over and hid the fault, which is why it only ever showed
   on a phone. */
#services-hero .column-hero-solutions > * {
	text-align: center !important;
}

/* The light-blue rule under the headline, generalised from the About panel to all three. */
.about-hero-panel .const-page-title::after,
#services-hero .column-hero-solutions .const-page-title::after,
#hero-carousel .h3-card-heading-style::after {
	content: "";
	display: block;
	width: 4.5rem;
	height: 3px;
	margin: 1.1rem auto 0;
	background: var(--aeca-blue-light);
}

/* The home card's header/body/footer sit inside the panel, so their own padding is what separates
   the copy from the new border. */
#hero-carousel .card-hero-carousel-style .card-header {
	padding-top: clamp(1.25rem, 2.5vw, 2rem);
}

#hero-carousel .card-hero-carousel-style .card-footer {
	padding-bottom: clamp(1.25rem, 2.5vw, 2rem);
	border-top: 0;
	background: transparent;
}

#hero-carousel .card-hero-carousel-style .card-header,
#hero-carousel .card-hero-carousel-style .card-body {
	background: transparent;
	border: 0;
}


/* ====================================================================================================
   HOME -- "INDUSTRIES WE SERVE" CARDS  (#projects-shortcuts)
   ----------------------------------------------------------------------------------------------------
   What was there: a 10px `double` white border at a 50px radius wrapped around a photo, a blue title
   chip floating at a different x-position in every card (the Environment one overflowed its own
   rounded corner and got clipped), and every word of the description held at `opacity:0` until hover
   -- which on a phone means it is never readable at all. On hover the card painted a *second* rounded
   box inside the first (`.const-project-card:hover{background:rgba(0,0,0,.5);border-radius:50px}`), so
   the state read as three nested rectangles.

   What it is now: one photo tile per industry with a permanent bottom-up scrim, a fixed top-left
   badge, and a bottom-left stack of kicker / title / rule. The description slides open on hover and
   on keyboard focus, and is simply always open where there is no hover (`@media (hover: none)`).

   SCOPE. Everything is keyed on #projects-shortcuts. `.const-project-card`, `.const-project-button`
   and `.const-project-main-title` are shared with the Past Projects grid on page-services.php and the
   Case Studies tiles; none of those should change, so no rule here is written on the bare class.

   SPECIFICITY. style.css states these as single classes -- `.const-industries-div-1:hover` is (0,2,0)
   and `.const-project-main-title` is (0,1,0) -- so the ID prefix carries every rule below without a
   single `!important`, except where it has to beat Bootstrap's own `.text-center{...!important}` and
   the `.const-project-title{color:#FEFFFF!important}` in the Blocs output.
   ==================================================================================================== */

#projects-shortcuts {
	--aeca-ind-radius: 20px;
	--aeca-ind-pad: clamp(1.15rem, 2.2vw, 1.9rem);
	--aeca-ind-scrim: 2, 20, 40;                    /* near-black blue, so the scrim never reads grey */
	--aeca-ind-ease: cubic-bezier(.2, .7, .3, 1);
}

/* The row is `g-0`, which zeroes both gutters, so the four tiles butted together and the only thing
   separating them was the old white border. Bootstrap's `.g-0` is (0,1,0); this is (1,2,0). */
#projects-shortcuts .row.g-0 {
	--bs-gutter-x: clamp(.9rem, 1.6vw, 1.6rem);
	--bs-gutter-y: clamp(.9rem, 1.6vw, 1.6rem);
}

/* style.css sizes these columns in hard pixels inside its phone/tablet media queries
   (`.const-project-home-bg-3{width:300px;margin-left:10px}` and friends), which fought the col-*
   classes on the same element. Re-assert the two widths the markup actually asks for -- all four
   columns are `col-12 col-md-6 col-lg-6`. */
#projects-shortcuts .row.g-0 > [class*="col-"] {
	width: 100%;
	margin-left: 0;
	margin-right: 0;
}

@media (min-width: 768px) {
	#projects-shortcuts .row.g-0 > [class*="col-"] {
		width: 50%;
	}
}

/* ---- The tile ---------------------------------------------------------------------------------- */
/* The photo lives on this element as a background-image (one per card, set in style.css). It is left
   there and picked up by ::before with `background-image:inherit`, so the zoom below works without
   restating four file paths -- and keeps working if the images are ever swapped in style.css. */

#projects-shortcuts [class*="const-industries-div-"] {
	position: relative;
	isolation: isolate;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	width: 100%;
	max-width: none;
	height: 100%;                                   /* equal heights: the col is a stretched flex item */
	min-height: clamp(320px, 27vw, 470px);
	margin: 0;
	border: 0;
	border-radius: var(--aeca-ind-radius);
	background-color: #04203d;                      /* ground while the photo is still loading */
	box-shadow: 0 10px 26px rgba(var(--aeca-ind-scrim), .18);
	transition:
		transform .45s var(--aeca-ind-ease),
		box-shadow .45s var(--aeca-ind-ease);
}

#projects-shortcuts [class*="const-industries-div-"]::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background-image: inherit;
	background-size: cover;
	background-position: center center;
	background-repeat: no-repeat;
	transition: transform .7s var(--aeca-ind-ease);
}

/* Permanent scrim. Weighted to the bottom third where the type sits, and kept faint at the top so the
   photograph is still the subject. Without it the white badge and title sat on open sky on the
   Environment and Infrastructure cards. */
#projects-shortcuts [class*="const-industries-div-"]::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(
		to top,
		rgba(var(--aeca-ind-scrim), .90) 0%,
		rgba(var(--aeca-ind-scrim), .70) 28%,
		rgba(var(--aeca-ind-scrim), .22) 58%,
		rgba(var(--aeca-ind-scrim), .06) 100%);
	pointer-events: none;
}

/* :focus-within is paired with :hover throughout so that tabbing to the card opens the same state a
   mouse does -- the description is otherwise unreachable by keyboard. */
#projects-shortcuts [class*="const-industries-div-"]:hover,
#projects-shortcuts [class*="const-industries-div-"]:focus-within {
	transform: translateY(-6px);
	box-shadow: 0 22px 44px rgba(var(--aeca-ind-scrim), .34);
	/* style.css flips these to center/center on hover, which shunted the whole stack sideways. */
	align-items: stretch;
	justify-content: flex-start;
}

#projects-shortcuts [class*="const-industries-div-"]:hover::before,
#projects-shortcuts [class*="const-industries-div-"]:focus-within::before {
	transform: scale(1.07);
}

/* The container runs full-viewport-width (style.css has `.container{max-width:100%!important}`), but
   the tile stops growing around 850px because of the section padding, so a `vw` height keeps pace
   with the width up to about 1900 and then has to be pinned. Left at 470px the tiles were 2.03:1 at
   2560 -- a letterbox strip rather than a card. Placed before the `hover: none` block below so a
   large touch screen still gets that block's height instead of this one. */
@media (min-width: 1900px) {
	#projects-shortcuts [class*="const-industries-div-"] {
		min-height: 540px;
	}
}

/* ---- The card inside it ------------------------------------------------------------------------ */

#projects-shortcuts .const-project-card {
	position: relative;
	z-index: 2;
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	height: auto;
	padding: var(--aeca-ind-pad);
	border: 0;
	background-color: transparent;
	transition: background-color .45s var(--aeca-ind-ease);
}

/* Replaces `.const-project-card:hover{background:rgba(0,0,0,.5);border-radius:50px}` -- a rounded box
   drawn inside an already-rounded box. This is a flat deepening of the scrim, clipped to the tile. */
#projects-shortcuts [class*="const-industries-div-"]:hover .const-project-card,
#projects-shortcuts [class*="const-industries-div-"]:focus-within .const-project-card {
	background-color: rgba(var(--aeca-ind-scrim), .42);
	border-radius: 0;
}

/* ---- Top-left badge ---------------------------------------------------------------------------- */
/* `.const-project-title-center` is display:flex/align-items:flex-end/justify-content:center, which is
   what put the badge in a different place on each card depending on how long the word was. */

#projects-shortcuts .const-project-card > .card-body.const-project-title-center {
	display: block;
	flex: 0 0 auto;
	padding: 0;
	border: 0;
	background: transparent;
}

#projects-shortcuts .const-project-main-title {
	display: inline-block;
	margin: 0;
	padding: .45em 1em .5em;
	border: 0;
	border-radius: 999px;
	background-color: rgba(2, 77, 161, .92);
	box-shadow: 0 2px 12px rgba(0, 0, 0, .28);
	color: var(--aeca-white);
	font-family: var(--aeca-font-head);
	font-size: clamp(.78rem, .62rem + .34vw, .95rem);
	font-weight: 600;
	letter-spacing: .08em;
	line-height: 1.15;
	text-transform: uppercase;
}

/* style.css blanks the badge's background on hover (`background-color:transparent`), which left the
   word floating unattached over the photo. It stays a badge. */
#projects-shortcuts .const-project-main-title:hover,
#projects-shortcuts [class*="const-industries-div-"]:hover .const-project-main-title {
	background-color: var(--aeca-blue);
	border: 0;
}

/* ---- Bottom-left stack ------------------------------------------------------------------------- */

#projects-shortcuts .const-project-button {
	margin-top: auto;                               /* pins the stack to the foot of the tile */
	padding: 0;
	border: 0;
	background: transparent;
	opacity: 1;                                     /* was 0 until hover -- invisible on touch */
}

/* Beats Bootstrap's `.text-center{text-align:center!important}` on the three children. Left-aligned
   so the stack lines up with the badge above it rather than drifting to the middle. */
#projects-shortcuts .const-project-button > * {
	text-align: left !important;
}

/* The markup carries a stray <br> after the link in three of the four cards, which opened a blank
   line between the title and its rule. */
#projects-shortcuts .const-project-button br {
	display: none;
}

#projects-shortcuts .const-project-button > .const-capital-subtitle {
	margin: 0 0 .4rem;
	color: var(--aeca-blue-light);
	font-family: var(--aeca-font-head);
	font-size: .72rem;
	font-weight: 600;
	letter-spacing: .2em;
	line-height: 1.2;
	text-transform: uppercase;
}

#projects-shortcuts .const-project-button > h3 {
	margin: 0;
	font-family: var(--aeca-font-head);
	font-size: clamp(1.1rem, .84rem + .72vw, 1.55rem);
	font-weight: 600;
	line-height: 1.25;
	text-shadow: 0 2px 12px rgba(0, 0, 0, .45);
}

/* `.const-project-title` is `font-weight:100` on "Lexend Deca", a family the theme no longer loads,
   so the link was rendering as a synthesised hairline. */
#projects-shortcuts .const-project-title {
	color: var(--aeca-white) !important;
	font-family: inherit;
	font-weight: inherit;
	text-decoration: none;
}

/* Chevron affordance, top-right, balancing the badge.

   NOT on `.const-project-title::after`, which is where it started. That link is a Bootstrap
   `.stretched-link`, and the thing that makes the whole tile clickable *is* its ::after -- an
   `inset:0` transparent overlay. Restyling it into a 7px chevron left the box `position:absolute`
   from Bootstrap's own rule, so the arrow rendered in the card's top-left corner and the tile stopped
   being clickable everywhere except on the words themselves.

   So it hangs off the card instead: ::before is the ring, ::after the chevron inside it. Drawn from
   borders rather than a glyph, so it depends on none of the icon fonts. */

#projects-shortcuts .const-project-card::before,
#projects-shortcuts .const-project-card::after {
	content: "";
	position: absolute;
	pointer-events: none;
	transition:
		background-color .4s var(--aeca-ind-ease),
		border-color .4s var(--aeca-ind-ease),
		transform .4s var(--aeca-ind-ease);
}

#projects-shortcuts .const-project-card::before {
	top: var(--aeca-ind-pad);
	right: var(--aeca-ind-pad);
	width: 2.5rem;
	height: 2.5rem;
	border: 1.5px solid rgba(255, 255, 255, .55);
	border-radius: 50%;
	background-color: rgba(255, 255, 255, .12);
	-webkit-backdrop-filter: blur(2px);   /* Safari/iOS still need the prefix for this one */
	backdrop-filter: blur(2px);
}

#projects-shortcuts .const-project-card::after {
	top: calc(var(--aeca-ind-pad) + 1.25rem);
	right: calc(var(--aeca-ind-pad) + 1.25rem);
	width: .5rem;
	height: .5rem;
	margin: -.25rem -.32rem 0 0;                    /* half its own box, then nudged for the rotation */
	border-top: 2px solid var(--aeca-white);
	border-right: 2px solid var(--aeca-white);
	transform: rotate(45deg);
}

#projects-shortcuts [class*="const-industries-div-"]:hover .const-project-card::before,
#projects-shortcuts [class*="const-industries-div-"]:focus-within .const-project-card::before {
	border-color: transparent;
	background-color: var(--aeca-blue-light);
	transform: scale(1.08);
}

#projects-shortcuts [class*="const-industries-div-"]:hover .const-project-card::after,
#projects-shortcuts [class*="const-industries-div-"]:focus-within .const-project-card::after {
	transform: rotate(45deg) translate(2px, -2px);
}

/* Accent rule under the title, grown on hover. */
#projects-shortcuts .const-project-button > h3::after {
	content: "";
	display: block;
	width: 2.25rem;
	height: 3px;
	margin-top: .8rem;
	background: var(--aeca-blue-light);
	transition: width .45s var(--aeca-ind-ease);
}

#projects-shortcuts [class*="const-industries-div-"]:hover .const-project-button > h3::after,
#projects-shortcuts [class*="const-industries-div-"]:focus-within .const-project-button > h3::after {
	width: 4.75rem;
}

/* The description. `p-624-style` set it in "SourceCodePro-ExtraLight" at font-weight:0 while the
   Defense card carried no class at all, so the four cards were not even the same typeface. */
#projects-shortcuts .const-project-button > p:last-of-type:not(.const-capital-subtitle) {
	max-height: 0;
	margin: 0;
	overflow: hidden;
	opacity: 0;
	transform: translateY(6px);
	color: rgba(255, 255, 255, .92);
	font-family: var(--aeca-font-body);
	font-size: clamp(.88rem, .82rem + .18vw, .98rem);
	font-weight: 400;
	line-height: 1.55;
	transition:
		max-height .5s var(--aeca-ind-ease),
		margin .5s var(--aeca-ind-ease),
		opacity .35s ease,
		transform .5s var(--aeca-ind-ease);
}

#projects-shortcuts [class*="const-industries-div-"]:hover .const-project-button > p:last-of-type:not(.const-capital-subtitle),
#projects-shortcuts [class*="const-industries-div-"]:focus-within .const-project-button > p:last-of-type:not(.const-capital-subtitle) {
	max-height: 12rem;
	margin-top: .85rem;
	opacity: 1;
	transform: none;
}

/* The stretched link covers the whole tile, so its focus ring would be clipped by `overflow:hidden`
   without pulling the outline inside the edge. */
#projects-shortcuts .const-project-title:focus-visible {
	outline-offset: -6px;
	border-radius: var(--aeca-ind-radius);
}

/* ---- No-hover devices -------------------------------------------------------------------------- */
/* Placed after the base rules on purpose: same specificity, later source position, so it wins. On a
   phone there is no hover state to reveal anything, so the description is simply always open. */

@media (hover: none) {
	#projects-shortcuts .const-project-button > p:last-of-type:not(.const-capital-subtitle) {
		max-height: none;
		margin-top: .85rem;
		opacity: 1;
		transform: none;
	}

	/* Taller than the pointer default, because the description is now taking up room the photograph
	   used to have: at 280px the image was down to a 90px strip above the type. */
	#projects-shortcuts [class*="const-industries-div-"] {
		min-height: clamp(330px, 82vw, 430px);
	}
}

@media (prefers-reduced-motion: reduce) {
	#projects-shortcuts [class*="const-industries-div-"],
#projects-shortcuts [class*="const-industries-div-"]::before,
#projects-shortcuts .const-project-card,
#projects-shortcuts .const-project-card::before,
#projects-shortcuts .const-project-card::after,
#projects-shortcuts .const-project-button > h3::after,
#projects-shortcuts .const-project-button > p:last-of-type:not(.const-capital-subtitle) {
		transition: none;
	}

	#projects-shortcuts [class*="const-industries-div-"]:hover,
#projects-shortcuts [class*="const-industries-div-"]:focus-within {
		transform: none;
	}

	#projects-shortcuts [class*="const-industries-div-"]:hover::before,
#projects-shortcuts [class*="const-industries-div-"]:focus-within::before {
		transform: none;
	}
}


/* ====================================================================================================
   THE LAST PARALLAX LAYER, MADE SAFE
   ----------------------------------------------------------------------------------------------------
   Five of the theme's six `.parallax` layers carried nothing but a flat colour, and they are gone --
   the colour now sits on the bloc itself (see the note in index.php). #stats-image is the only one
   left that carries an actual photograph, so it keeps the effect. But it inherits the same fragility,
   and it is worth naming precisely because it is what makes the backgrounds fail intermittently:

   universalParallax sizes and positions its layers only from inside

       document.addEventListener("readystatechange", e => { if (e.target.readyState === "complete") ... })

   registered when js/all.js runs. If the document has already reached "complete" by then -- a
   combined/deferred bundle, a caching or optimisation plugin, an earlier error in the same file -- the
   handler never fires. `calculateHeight` never runs, the layer keeps `height: 0`, and the section has
   no background at all. It is not reproducible on a warm local load, which is exactly why it reads as
   "sometimes".

   `.parallax__container` is `position:absolute; inset:0; z-index:-1`, so it is always the full size of
   the bloc whatever the layer inside it is doing. Colouring *it* rather than the bloc gives a floor
   that shows through when the photo is missing, without covering the photo when it is not: a bloc
   background would paint on top of a `z-index:-1` child and kill the effect outright.

   The `:has()` rule covers the other half -- the library never ran at all, so no wrapper was ever
   inserted and `.parallax` is still a direct child.
   ==================================================================================================== */

#stats-image .parallax__container {
	background-color: #04203d;                      /* the deep end of the brand blue */
	background-image: url("../img/Homepage%20Stat%202500%20x%201873%20px.webp");
	background-position: center center;
	background-repeat: no-repeat;
	background-size: cover;
}

/* Deliberately a rule of its own rather than a second selector on the block above. An unknown
   selector invalidates the whole list it appears in, so a browser without :has() would have dropped
   the .parallax__container half as well -- taking out the fallback that does not need :has() at all. */
#stats-image:has(> .parallax) {
	background-color: #04203d;
	background-image: url("../img/Homepage%20Stat%202500%20x%201873%20px.webp");
	background-position: center center;
	background-repeat: no-repeat;
	background-size: cover;
}


/* ====================================================================================================
   HOME HERO -- THE VIDEO
   ----------------------------------------------------------------------------------------------------
   #hero-carousel is `bgc-3706`, so the bloc really is brand blue, but a full-bleed autoplaying video
   covers every pixel of it and the blue is never seen.

   A two-gradient brand-blue wash used to sit on `#hero-carousel .hero-video::after`, tinting the whole
   frame so the section read blue and the glass panel was not fighting raw footage. Removed 2026-08-26
   at AECA's request: the wash was hiding detail in the footage the client wants visible, so the video
   now plays untinted and the hero's blue comes from the panel alone.

   What holds the panel legible without it: the glass token's gradient has a floor under it (see the
   --aeca-glass-* block above), so the card is unambiguously one colour over whatever frame is playing.
   Still no backdrop-filter, for the reason recorded above the token block. If a future frame ever
   swallows the panel, deepen the glass token rather than putting a tint back over the footage.
   ==================================================================================================== */

/* The video sits inside `.ratio` as an absolutely positioned child; it has to stay below
   `.div-home-hero`, which is `position:absolute` later in the DOM. */
#hero-carousel .hero-video > video {
	z-index: 0;
}

#hero-carousel .div-home-hero {
	z-index: 2;
}


/* ====================================================================================================
   THE CALL-TO-ACTION BUTTONS
   ----------------------------------------------------------------------------------------------------
   Same story as the yellow --swatch-var-primary: these are Construction-template leftovers that were
   never repainted, and they are translucent, which is why they disappear once a section has a strong
   ground under them.

     .const-button             (30 uses, incl. the hero CTA)  background rgba(0,139,227,0.55)
     .const-button-industries  ("View our Industries")        background rgba(169,169,169,0.28), ink #111
     .const-button-solutions   ("View all Services")          background rgba(169,169,169,0.28), ink #111

   28% grey with near-black ink on top of #024DA1 is the "View our Industries" button reading as a
   barely-there navy smudge; 55% blue on a blue panel is the hero CTA reading as a slightly lighter
   rectangle. Both hover states are worse still -- rgba(97,164,232,0.12), a 12% wash.

   Repainted against the ground the button is standing on, using the same d-bloc / texture-darken test
   as the ink section above. Primary is a filled button, secondary an outline that fills on hover.
   `!important` throughout because every .const-button-* rule sets `color` with it.
   ==================================================================================================== */

.const-button,
.const-button-industries,
.const-button-solutions {
	/* The generated mobile rules reduce the primary CTA to 8px. A control that can be tapped needs
	   readable type and a dependable target at every breakpoint, not just on the desktop layout. */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	border: 2px solid transparent !important;
	border-radius: 2px;
	background-image: none !important;              /* kills the .btn-glossy sheen over the flat colour */
	box-shadow: none;
	font-size: clamp(0.875rem, 0.84rem + 0.15vw, 1rem) !important;
	font-weight: 600;
	letter-spacing: .06em;
	line-height: 1.2;
	padding: .7rem 1.6rem !important;
	text-shadow: none;
	transition: background-color .25s ease, color .25s ease, border-color .25s ease, transform .25s ease;
}

.const-button:hover,
.const-button-industries:hover,
.const-button-solutions:hover {
	transform: translateY(-2px);
}

/* ---- Primary, on a dark or blue ground: white button, blue ink. The only thing that reads as a
        button against #024DA1 without inventing a fourth brand colour. ---------------------------- */

:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) .const-button {
	background-color: var(--aeca-white) !important;
	border-color: var(--aeca-white) !important;
	color: var(--aeca-blue) !important;
}

:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) .const-button:hover,
:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) .const-button:focus-visible {
	background-color: var(--aeca-blue-light) !important;
	border-color: var(--aeca-blue-light) !important;
	color: var(--aeca-white) !important;
}

/* ---- Primary, on a light ground: the reverse. ------------------------------------------------- */

.bloc:not(.d-bloc):not([class*="texture-darken"]) .const-button {
	background-color: var(--aeca-blue) !important;
	border-color: var(--aeca-blue) !important;
	color: var(--aeca-white) !important;
}

.bloc:not(.d-bloc):not([class*="texture-darken"]) .const-button:hover,
.bloc:not(.d-bloc):not([class*="texture-darken"]) .const-button:focus-visible {
	background-color: var(--aeca-blue-light) !important;
	border-color: var(--aeca-blue-light) !important;
	color: var(--aeca-white) !important;
}

/* ---- Secondary ("View our Industries", "View all Services"): outline, fills on hover. ---------- */

:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) :is(.const-button-industries,
.const-button-solutions) {
	background-color: transparent !important;
	border-color: rgba(255, 255, 255, .7) !important;
	color: var(--aeca-white) !important;
}

:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) :is(.const-button-industries,
.const-button-solutions):hover,
:is(.bloc.d-bloc,
.bloc[class*="texture-darken"]) :is(.const-button-industries,
.const-button-solutions):focus-visible {
	background-color: var(--aeca-white) !important;
	border-color: var(--aeca-white) !important;
	color: var(--aeca-blue) !important;
}

.bloc:not(.d-bloc):not([class*="texture-darken"]) :is(.const-button-industries,
.const-button-solutions) {
	background-color: transparent !important;
	border-color: var(--aeca-blue) !important;
	color: var(--aeca-blue) !important;
}

.bloc:not(.d-bloc):not([class*="texture-darken"]) :is(.const-button-industries,
.const-button-solutions):hover,
.bloc:not(.d-bloc):not([class*="texture-darken"]) :is(.const-button-industries,
.const-button-solutions):focus-visible {
	background-color: var(--aeca-blue) !important;
	border-color: var(--aeca-blue) !important;
	color: var(--aeca-white) !important;
}

@media (prefers-reduced-motion: reduce) {
	.const-button,
.const-button-industries,
.const-button-solutions {
		transition: none;
	}

	.const-button:hover,
.const-button-industries:hover,
.const-button-solutions:hover {
		transform: none;
	}
}

/* ====================================================================================================
   ONE NAVIGATION PER WIDTH
   ----------------------------------------------------------------------------------------------------
   Both navigations used to render at once above 1200px: the horizontal link list in #sidebar and the
   hamburger that opens the #const-sidebar offcanvas. The offcanvas is the portable-device menu, so it
   and its trigger now stop at the same 1200px line where style.css stops suppressing the link list
   (`@media (max-width:1199.98px){ #sidebar .d-lg-block{display:none!important} }`, style.css:118).

   1200, not the 992 the markup's `d-lg-block` implies. That class never governed -- the rule above
   out-specifies it up to 1199.98px -- so cutting the hamburger at 992 would have left 992-1199px with
   no navigation of any kind.

   What the offcanvas held that the bar did not: Our Stories, Insights and the brochure. The first two
   are top-level items in header.php now; the brochure is in the footer's Navigate column. Nothing is
   left that only the offcanvas can reach.

   The .d-flex selector is how style.css itself addresses the icon column (its Bootstrap classes are a
   Blocs export and not otherwise distinctive). Carrying the #sidebar id makes this (1,2,0) against the
   (0,2,0) `display:flex !important` in the <=1199.98px block -- different media queries, no overlap,
   but the specificity is what keeps it that way if either range is ever widened.
   ==================================================================================================== */

@media (min-width: 1200px) {
	#sidebar .const-navigation-bar > .d-flex {
		display: none !important;
	}

	/* The panel as well as the trigger. Left in the DOM it is only ever an off-screen block of
	   duplicate links, and Bootstrap can still be told to open it from a stray hash or a script. */
	#const-sidebar {
		display: none !important;
	}
}

/* ----------------------------------------------------------------------------------------------------
   DROPDOWNS WITHOUT THE JS TOGGLE

   The three parent items carry real hrefs now (see the note in header.php), which meant dropping
   `data-bs-toggle="dropdown"` -- Bootstrap's toggle calls preventDefault, so the link could never both
   open the panel and go anywhere. Opening moves to CSS.

   :focus-within is not decoration here, it is the whole keyboard story. Bootstrap's own dropdown JS is
   what used to answer the Enter key; with it gone, :hover alone would have made the seven sub-service
   pages, four industries and nine products reachable by mouse only. Tabbing to the parent link opens
   the panel, and the following Tab lands on the first item inside it.

   Scoped to #sidebar and to this media query on purpose. The offcanvas has no dropdowns, and below
   1200px there is no hover to open anything -- the rule would be dead weight at best there.
   ---------------------------------------------------------------------------------------------------- */

@media (min-width: 1200px) {
	#sidebar .navbar-nav .dropdown:hover > .dropdown-menu,
#sidebar .navbar-nav .dropdown:focus-within > .dropdown-menu {
		display: block;
	}

	/* The panel hangs directly under the link with no gap to cross, so the pointer cannot fall out of
	   the hover chain on the way down. Popper is not positioning these any more -- this is Bootstrap's
	   static `.dropdown-menu{position:absolute;top:100%}`, restated so the intent is not accidental. */
	#sidebar .navbar-nav .dropdown > .dropdown-menu {
		top: 100%;
		margin-top: 0;
	}
}

/* ====================================================================================================
   NAV AND FOOTER: STATES, CASING, HIERARCHY
   ----------------------------------------------------------------------------------------------------
   Three things this pass is fixing.

   CASING. .navbar-nav li a and .const-navigation both set text-transform:uppercase, so the twenty
   dropdown entries and the seven offcanvas links rendered as GEOSPATIAL INTELLIGENCE / OUR STORIES
   while the top-level bar beside them rendered as "Our Stories" -- .link-style and friends carry
   text-transform:capitalize and out-specify the uppercase rule. One nav, two casings. The markup was
   already authored in title case in every one of those places, so this is purely a matter of letting
   the authored text through.

   NO SENSE OF PLACE. Nothing marked the current page anywhere. functions/nav-state.php now emits
   data-current on the matching link ("page" for the exact page, "section" for an ancestor) and the
   rules below draw it. Colour is never the only carrier: the current item gets a cyan rule under it,
   and aria-current rides along in the markup for anyone not looking at the screen.

   ACCENT COLOUR. The footer's existing hover accent is --swatch-var-primary, #FFD60A, a yellow that
   appears nowhere in the brand palette documented at the top of this file -- almost certainly a
   Bootstrap default left in place. It is replaced with the brand cyan, but only on rules, borders and
   underlines. Link text on hover stays white deliberately: #16ADEE on the #024DA1 footer measures
   about 3.3:1, fine for a non-text element but short of the 4.5:1 body text needs, and the links start
   at roughly 14:1 white. Recolouring text on hover would have made it harder to read the moment you
   point at it.
   ==================================================================================================== */

:root {
	/* One accent for both nav and footer. Restore #FFD60A here if the yellow was deliberate. */
	--aeca-accent: var(--aeca-blue-light);
	--aeca-nav-hover-bg: #eef3f9;
}

/* ----------------------------------------------------------------------------------------------------
   TOP BAR, 1200px UP
   ---------------------------------------------------------------------------------------------------- */

@media (min-width: 1200px) {
	/* line-height:12px on 14px type came off the Blocs export and left the links with no vertical box
	   to speak of -- nothing to hover, nothing to hang an underline under. A real box also gives the
	   pointer somewhere to travel on its way down into the dropdown. */
	#sidebar .navbar-nav > .nav-item .nav-link {
		position: relative;
		padding-top: 10px;
		padding-bottom: 10px;
		line-height: 1.25;
		transition: color 0.16s ease;
	}

	/* ::before, not ::after: Bootstrap draws the dropdown caret with ::after on the same three links,
	   and taking it would have deleted the carets. */
	#sidebar .navbar-nav > .nav-item .nav-link::before {
		content: "";
		position: absolute;
		right: 0;
		bottom: 2px;
		left: 0;
		height: 2px;
		background: var(--aeca-accent);
		transform: scaleX(0);
		transform-origin: center;
		transition: transform 0.2s ease;
	}

	#sidebar .navbar-nav > .nav-item .nav-link:hover::before,
#sidebar .navbar-nav > .nav-item .nav-link:focus-visible::before,
#sidebar .navbar-nav > .nav-item .nav-link[data-current]::before {
		transform: scaleX(1);
	}

	/* .navbar-nav li a pins colour with !important, so matching it takes an id here. */
	#sidebar .navbar-nav > .nav-item .nav-link[data-current] {
		color: var(--aeca-blue) !important;
	}

	/* --- the panel ------------------------------------------------------------------------------- */

	#sidebar .navbar-nav .dropdown-menu {
		min-width: 244px;
		padding: 8px;
		border: 1px solid var(--aeca-border);
		border-radius: 10px;
		box-shadow: 0 18px 40px rgba(2, 77, 161, 0.16);
	}

	#sidebar .navbar-nav .dropdown-menu .dropdown-item {
		/* The casing fix. */
		text-transform: none;

		/* .navbar-nav li a puts 28px of margin either side of every link in the nav, dropdown entries
		   included, which is why the panel carried a band of dead space down both edges. And
		   .link-geospatial-style sets width:75%, which cropped the longest entry's hover area to
		   three-quarters of the panel for no reason anyone recorded.

		   width goes back to Bootstrap's own 100% rather than to `auto`: auto also undoes
		   .dropdown-item{width:100%}, which leaves every row shrink-wrapped to its label and the
		   highlight ending mid-panel. */
		position: relative;
		width: 100%;
		margin-left: 0;
		margin-right: 0;
		padding-left: 16px;
		padding-right: 12px;
		border-radius: 6px;
		color: var(--aeca-ink) !important;
		transition: background-color 0.16s ease, color 0.16s ease;
	}

	/* The accent bar is a pseudo-element rather than `box-shadow: inset 3px 0 0`. An inset shadow
	   follows the 6px corner radius, so it tapers at both ends and reads on screen as a stray opening
	   parenthesis sitting in front of the label. A positioned bar keeps its ends square. */
	#sidebar .navbar-nav .dropdown-menu .dropdown-item::before {
		content: "";
		position: absolute;
		top: 50%;
		left: 6px;
		width: 3px;
		height: 60%;
		border-radius: 2px;
		background: var(--aeca-accent);
		opacity: 0;
		transform: translateY(-50%);
		transition: opacity 0.16s ease;
	}

	#sidebar .navbar-nav .dropdown-menu .dropdown-item:hover,
#sidebar .navbar-nav .dropdown-menu .dropdown-item:focus-visible {
		background-color: var(--aeca-nav-hover-bg);
		color: var(--aeca-blue) !important;
	}

	#sidebar .navbar-nav .dropdown-menu .dropdown-item:hover::before,
#sidebar .navbar-nav .dropdown-menu .dropdown-item:focus-visible::before,
#sidebar .navbar-nav .dropdown-menu .dropdown-item[data-current="page"]::before {
		opacity: 1;
	}

	#sidebar .navbar-nav .dropdown-menu .dropdown-item[data-current="page"] {
		background-color: var(--aeca-nav-hover-bg);
		color: var(--aeca-blue) !important;
		font-weight: 600;
	}

	#sidebar .navbar-nav .dropdown-menu .dropdown-item[data-current="page"]::before {
		background: var(--aeca-blue);
	}
}

/* ====================================================================================================
   THE OFFCANVAS, BELOW 1200px

   This panel is the whole navigation on a phone. It is three parts: a brand bar that does not move, the
   seven links, and Demo & Quote pinned to the bottom where a thumb reaches. The panel is the flex column
   and only .aeca-onav-body scrolls -- `overflow: scroll` on the panel itself (style.css, .offcanvas-end)
   would carry the button off the bottom.

   Authored against the panel's own classes rather than the markup's old .const-navigation and Blocs
   `-style` ones, which would have meant out-shouting an !important for every property: .const-navigation
   alone pins Teko-Light, 28px and uppercase.

   Every row clears 48px, which is the WCAG 2.5.8 target size. They were 27px apart with a tap target no
   taller than the 18px text.
   ==================================================================================================== */

/* .bloc brings `padding: 0 20px` and `display: flex` (row); .offcanvas-end brings `overflow: scroll`.
   The panel wants none of the three. Width is capped below the viewport so a strip of the page stays
   visible behind it: on a phone that strip is the affordance saying the menu is a layer over the page,
   and it is also the easiest place to tap to dismiss. */
#const-sidebar {
	display: flex;
	flex-direction: column;
	width: min(360px, 88vw);
	padding: 0;
	overflow: hidden;
	overscroll-behavior: contain;
	box-shadow: -18px 0 48px rgba(2, 20, 40, 0.16);
}

/* ---- Brand bar ------------------------------------------------------------------------------- */

#const-sidebar .aeca-onav-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	flex: 0 0 auto;
	padding: 10px 12px 10px 16px;
	border-bottom: 1px solid var(--aeca-border);
}

#const-sidebar .aeca-onav-brand {
	display: inline-flex;
	align-items: center;
	min-height: 48px;
	padding: 4px;
	border-radius: 8px;
}

#const-sidebar .aeca-onav-brand img {
	width: 44px;
	height: 44px;
	object-fit: contain;
}

#const-sidebar .aeca-onav-close {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 48px;
	height: 48px;
	border-radius: 8px;
	transition: background-color 0.16s ease;
}

#const-sidebar .aeca-onav-close:hover,
#const-sidebar .aeca-onav-close:focus-visible {
	background-color: var(--aeca-surface);
}

/* ---- The links ------------------------------------------------------------------------------- */

#const-sidebar .aeca-onav-body {
	flex: 1 1 auto;
	min-height: 0;                                  /* without this the flex item refuses to shrink and
	                                                   the button is pushed off the bottom */
	padding: 12px;
	overflow-y: auto;
	overscroll-behavior: contain;
	-webkit-overflow-scrolling: touch;
}

#const-sidebar .aeca-onav-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

#const-sidebar .aeca-onav-link {
	position: relative;
	display: flex;
	align-items: center;
	min-height: 52px;
	padding: 12px 16px;
	border-radius: 8px;
	color: var(--aeca-ink);
	font-family: var(--aeca-font-head) !important;  /* beats the blanket `a { font-family: inherit }` */
	font-size: 1.0625rem;
	font-weight: 600;
	line-height: 1.25;
	text-decoration: none;
	transition: background-color 0.16s ease, color 0.16s ease;
}

#const-sidebar .aeca-onav-link:hover,
#const-sidebar .aeca-onav-link:focus-visible {
	background-color: var(--aeca-surface);
	color: var(--aeca-blue);
}

/* Both the page itself and any of its sub-pages light the row, so the menu still says which section you
   are in from /services-geo, where no row is the page you are on. */
#const-sidebar .aeca-onav-link[data-current] {
	background-color: var(--aeca-surface);
	color: var(--aeca-blue);
}

/* Positioned bar, not an inset shadow: an inset shadow tapers into the 8px corner radius and reads as a
   parenthesis rather than a marker. */
#const-sidebar .aeca-onav-link[data-current]::before {
	content: "";
	position: absolute;
	top: 50%;
	left: 6px;
	width: 3px;
	height: 54%;
	border-radius: 2px;
	background: var(--aeca-accent);
	transform: translateY(-50%);
}

/* ---- The button ------------------------------------------------------------------------------ */

#const-sidebar .aeca-onav-foot {
	flex: 0 0 auto;
	padding: 14px 16px 16px;
	/* Separate declaration, not folded into the shorthand: an unsupported env() would take the whole
	   shorthand down with it, and this one only needs to clear the iOS home indicator. */
	padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
	border-top: 1px solid var(--aeca-border);
}

/* .const-button is the site's primary CTA and already resolves to a filled blue button on a light
   ground; this only stretches it across the panel and undoes its inline-flex width. */
#const-sidebar .aeca-onav-cta {
	display: flex !important;
	width: 100%;
	text-align: center;
}

@media (prefers-reduced-motion: reduce) {
	#const-sidebar .aeca-onav-link,
#const-sidebar .aeca-onav-close {
		transition: none;
	}
}

/* ----------------------------------------------------------------------------------------------------
   FOOTER
   ---------------------------------------------------------------------------------------------------- */

/* A short accent rule under each column heading. Gives the three columns a shared baseline to hang
   from -- they previously ran straight from a 30px heading into 14px links with nothing between. */
#footer h3 {
	position: relative;
	padding-bottom: 12px;
}

#footer h3::after {
	content: "";
	position: absolute;
	bottom: 0;
	left: 0;
	width: 34px;
	height: 3px;
	background: var(--aeca-accent);
}

#footer .const-footer-link {
	display: inline-block;
	position: relative;
	transition: transform 0.16s ease;
}

/* Text stays white on hover (see the note on contrast at the top of this section); the affordance is
   the underline and the nudge, neither of which costs any legibility. */
#footer .const-footer-link::after {
	content: "";
	position: absolute;
	right: 0;
	bottom: -2px;
	left: 0;
	height: 1px;
	background: var(--aeca-accent);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform 0.18s ease;
}

#footer .const-footer-link:hover,
#footer .const-footer-link:focus-visible {
	color: var(--aeca-white) !important;
	transform: translateX(3px);
}

#footer .const-footer-link:hover::after,
#footer .const-footer-link:focus-visible::after,
#footer .const-footer-link[data-current]::after {
	transform: scaleX(1);
}

#footer .const-footer-link[data-current] {
	font-weight: 600;
}

/* --- the Quick Message form ---------------------------------------------------------------------- */

/* The fields looked empty. .const-footer-form sets the typed colour to #FEFFFF but says nothing about
   the placeholder, so Name / Email / Message fell to the theme-wide
   `.form-control::placeholder{color:var(--swatch-var-2807)!important}` -- a grey at 0.28 alpha. Over
   the #024DA1 footer that lands around 1.3:1, which is not a faint label so much as an absent one.

   !important is not optional here: that rule has it, and a plain declaration loses to it however
   specific this selector is.

   Scoped to #footer on purpose. The same 0.28-alpha grey is the placeholder for every form in the
   theme, including the ones on white, where it is also far below the 4.5:1 placeholders need -- but
   that is a site-wide change and this pass was asked for the footer. */
#footer .const-footer-form::placeholder {
	color: rgba(255, 255, 255, 0.72) !important;
	opacity: 1; /* Firefox dims placeholders by default and would undo the line above. */
}

/* Keep empty fields legible on light form surfaces. */
input::placeholder,
textarea::placeholder,
select:invalid {
	color: #64748b !important;
	opacity: 1;
}

#footer .const-footer-form {
	border-color: rgba(255, 255, 255, 0.4) !important;
	transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

#footer .const-footer-form:hover {
	border-color: var(--aeca-accent) !important;
}

/* .const-footer-form:focus sets `box-shadow: 0px 2px 2px transparent`, i.e. it explicitly removes
   Bootstrap's focus ring and puts nothing back. Tabbing through the form gave no indication of where
   you were. */
#footer .const-footer-form:focus {
	border-color: var(--aeca-accent) !important;
	box-shadow: 0 0 0 3px rgba(22, 173, 238, 0.45) !important;
	outline: none;
}

/* --- the seam between footer and copyright ------------------------------------------------------- */

/* Both blocs are the same #024DA1, so the copyright line read as one more unlabelled footer row. */
#copyright {
	border-top: 1px solid rgba(255, 255, 255, 0.16);
}

/* ----------------------------------------------------------------------------------------------------
   MOTION
   ---------------------------------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	#sidebar .navbar-nav > .nav-item .nav-link,
#sidebar .navbar-nav > .nav-item .nav-link::before,
#sidebar .navbar-nav .dropdown-menu .dropdown-item,
#const-sidebar .const-navigation,
#footer .const-footer-link,
#footer .const-footer-link::after,
#footer .const-footer-form {
		transition: none;
	}

	#footer .const-footer-link:hover,
#footer .const-footer-link:focus-visible {
		transform: none;
	}
}

/* ====================================================================================================
   ABOUT: "WHY CHOOSE AECA SOLUTIONS?" BELOW THE DESKTOP GRID
   ----------------------------------------------------------------------------------------------------
   The four points are built as `col-lg-1` (icon) + `col` (blue pill). `col-lg-1` only takes effect
   from 992px, so below that the icon column falls back to a full-width block and the row becomes two
   stacked pieces: a loose 110x100 red tick sitting on its own line, then the pill under it. Each of
   the four rows measured 199-325px tall on a 390px screen for what is one line of text, and the tick
   no longer read as belonging to the sentence beside it -- the whole section ran to 1472px.

   The desktop design is a checkmark and a pill side by side. That relationship is what should survive
   the narrow screen, so the row is held in one line and the tick is scaled to suit instead.

   Gated at 1199.98px, the same line the navigation uses, because the band just above the stacking
   breakpoint is wrong too. From 992 to 1199 `col-lg-1` is live but is only ~80px wide, while the tick
   is 110px with a -10px left margin -- so it outgrows its column and laps about 32px over the blue
   pill's left edge. One rule covers both faults.

   The tick is sized fluidly rather than fixed so the step at 1200px is a change of degree, not of
   kind: 72px just under the breakpoint against 110px just over it, tapering to 36px on a phone.
   ==================================================================================================== */

@media (max-width: 1199.98px) {
	#bloc-6 [class*="row-why-aeca"] {
		flex-wrap: nowrap;
		align-items: stretch;
		margin-left: 0;
		margin-right: 0;
		gap: 0.75rem;
	}

	/* Shrink-to-fit rather than the 100% a bare `col-lg-1` collapses to below its breakpoint. */
	#bloc-6 [class*="row-why-aeca"] > .col-lg-1 {
		display: flex;
		flex: 0 0 auto;
		align-items: center;
		width: auto;
		max-width: none;
		padding: 0;
	}

	/* .svg-icon-fill pins 110x100 with !important, and the -10px left margin was there to pull the
	   tick back into a 1/12 column that is either too narrow for it or gone entirely down here. */
	#bloc-6 .svg-icon-fill {
		width: clamp(36px, 6vw, 72px) !important;
		height: clamp(36px, 6vw, 72px) !important;
		margin-left: 0;
		margin-bottom: 0;
	}

	#bloc-6 [class*="column-why-aeca"] {
		display: flex;
		align-items: center;
		padding: 0;
	}

	/* The pill's shape came from the paragraph's own 30px/35px margins, which is fine beside a 1/12
	   column and far too much of a 390px screen. Same shape, expressed as padding, at a size that
	   fits: 28px display type wraps a one-line point onto four lines here. */
	#bloc-6 [class*="column-why-aeca"] p {
		margin: 0;
		padding: 0.85rem 1.1rem;
		font-size: clamp(15px, 2.4vw, 24px);
		line-height: 1.4;
	}
}

/* Requested industry, product, case study and resource redesign. */
.aeca-page-hero { position: relative; overflow: hidden; padding: clamp(80px, 11vw, 150px) 0 clamp(64px, 9vw, 116px); color: #fff; background: linear-gradient(120deg, #012d61 0%, #024da1 58%, #0b78ba 100%); }
.aeca-page-hero::after { content: ""; position: absolute; inset: auto -8% -45% auto; width: min(58vw, 760px); aspect-ratio: 1; border: 1px solid rgba(255,255,255,.2); border-radius: 50%; box-shadow: 0 0 0 60px rgba(255,255,255,.04), 0 0 0 120px rgba(255,255,255,.03); }
.aeca-page-hero .container { position: relative; z-index: 1; max-width: 1080px; }
/* TYPE SCALE -- HIERARCHY, item 3. This one rule is the whole reason Industries, Products, Insights
   and Case Studies ran a 98px h1 at 1400px while About sat at 62px and the sub-page heroes at 70-74px:
   `7vw` with a 6.4rem ceiling does not top out until 1463px, so on any ordinary laptop it was still
   climbing. It is the widest heading on the site by 28px and it was not deliberate.
   `clamp(2.5rem, calc(1.82rem + 2.77vw), 4.5rem)` is 40px at 390px, 68px at 1400px and caps at 72px,
   which lands it beside the sub-page heroes so the two systems agree. Contact uses this hero too and
   comes up slightly, from 56px, into the same band. */
.aeca-page-hero h1 { max-width: 820px; margin: .2rem 0 1.1rem; color: #fff; font-family: var(--aeca-heading-font, "Teko", sans-serif); font-size: clamp(2.5rem, calc(1.82rem + 2.77vw), 4.5rem); line-height: .92; letter-spacing: -.025em; }
.aeca-page-hero__lead { max-width: 750px; margin: 0; font-size: clamp(1.05rem, 1.8vw, 1.32rem); line-height: 1.65; color: rgba(255,255,255,.9); }
.aeca-eyebrow { margin: 0 0 .55rem; color: #16adee; font-size: .73rem; font-weight: 700; letter-spacing: .16em; line-height: 1.2; text-transform: uppercase; }
.aeca-page-hero .aeca-eyebrow { color: #a8e5ff; }
.aeca-showcase-section,
.aeca-resources,
.aeca-faq,
.aeca-testimonials { padding: clamp(56px, 8vw, 110px) 0; }
.aeca-showcase-section { background: #fff; }
.aeca-resources,
.aeca-testimonials { background: #f1f6fa; }
.aeca-faq { background: #fff; }
.aeca-section-heading { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(240px, .75fr); gap: 2rem 5rem; align-items: end; margin-bottom: clamp(28px, 4vw, 52px); }
.aeca-section-heading h2 { margin: 0; color: #024da1; font-size: clamp(2rem, 4vw, 3.35rem); line-height: 1; }
.aeca-section-heading > p { margin: 0; color: #405266; line-height: 1.6; }
.aeca-showcase-card,
.aeca-story-card { height: 100%; overflow: hidden; border: 1px solid #dce7ef; background: #fff; box-shadow: 0 14px 36px rgba(2,77,161,.09); }
.aeca-showcase-card img { width: 100%; height: 235px; object-fit: cover; }
.aeca-showcase-card__body,
.aeca-story-card > div,
.aeca-product-card,
.aeca-resource-card,
.aeca-quote-card { padding: clamp(24px, 3vw, 35px); }
.aeca-showcase-card h2,
.aeca-story-card h2,
.aeca-product-card h3,
.aeca-resource-card h3 { margin: 0 0 .85rem; color: #024da1; font-size: clamp(1.55rem, 2.4vw, 2.05rem); line-height: 1.05; }
.aeca-showcase-card p:not(.aeca-eyebrow),
.aeca-product-card p:not(.aeca-eyebrow),
.aeca-resource-card p:not(.aeca-eyebrow),
.aeca-story-card p,
.aeca-quote-card { color: #405266; line-height: 1.62; }
.aeca-showcase-card__tags { padding-top: .8rem; border-top: 1px solid #dce7ef; font-size: .86rem; font-weight: 600; }
.aeca-text-link { display: inline-block; margin-top: .85rem; color: #024da1; font-weight: 700; text-decoration: none; }
.aeca-text-link:hover,
.aeca-story-card h2 a:hover { color: #16adee; }
.aeca-product-group + .aeca-product-group { margin-top: clamp(54px, 8vw, 105px); padding-top: clamp(54px, 8vw, 105px); border-top: 1px solid #dce7ef; }
.aeca-product-group .aeca-section-heading { margin-bottom: 1.8rem; }
.aeca-product-card,
.aeca-resource-card,
.aeca-quote-card { height: 100%; border: 1px solid #dce7ef; border-top: 4px solid #16adee; background: #fff; }
.aeca-story-card img { width: 100%; height: 230px; object-fit: cover; }
.aeca-story-card h2 a { color: inherit; text-decoration: none; }
.aeca-empty-state { padding: clamp(30px, 5vw, 58px); border: 1px dashed #9ab5c8; background: #f1f6fa; text-align: center; }
.aeca-empty-state h2 { color: #024da1; }
.aeca-empty-state--compact { padding: 1rem 1.5rem; }
.aeca-pagination { margin-top: 3rem; }
.aeca-quote-card { border-top-color: #024da1; }
.aeca-quote-card blockquote { margin: 0 0 1.4rem; color: #024da1; font-family: Georgia, serif; font-size: 1.18rem; line-height: 1.55; }
.aeca-action-panel { padding: clamp(52px, 8vw, 92px) 0; background: #012d61; color: #fff; }
.aeca-action-panel__inner { display: flex; gap: 2rem 4rem; align-items: center; justify-content: space-between; }
.aeca-action-panel h2 { margin: 0 0 .8rem; color: #fff; font-size: clamp(2rem, 4vw, 3.2rem); }
.aeca-action-panel p:not(.aeca-eyebrow) { max-width: 670px; margin: 0; color: rgba(255,255,255,.8); line-height: 1.6; }
.aeca-action-panel__actions { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: .75rem; }
.aeca-action-panel .btn-clean { border: 1px solid rgba(255,255,255,.72); color: #fff; }
.aeca-action-panel .btn-clean:hover { background: #fff; color: #024da1; }
/* Every action panel carries a single button. Anything else a panel needs to point at is a text
   link: inside the paragraph, or, for the editorial panel's way back to the archive, on its own
   line under the button. */
.aeca-action-panel p:not(.aeca-eyebrow) a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.aeca-action-panel p:not(.aeca-eyebrow) a:hover { color: rgba(255,255,255,.78); }
.aeca-action-panel__back { flex: 0 0 100%; margin: .35rem 0 0; font-size: .95rem; }
.aeca-action-panel__back a { color: rgba(255,255,255,.8); text-decoration: underline; text-underline-offset: 3px; }
.aeca-action-panel__back a:hover { color: #fff; }
.aeca-faq-toggle { display: block; width: 100%; padding: 1.1rem 1.25rem; border: 0; background: transparent; color: #024da1; font-weight: 700; text-align: left; }
@media (max-width: 767.98px) { .aeca-section-heading { grid-template-columns: 1fr; gap: 1rem; } .aeca-action-panel__inner { display: block; } .aeca-action-panel__actions { margin-top: 1.5rem; } .aeca-showcase-card img,
.aeca-story-card img { height: 205px; } }

/* Industries hub: the Home card system, adjusted for a dedicated destination page. */
/* Two industry names are substantially longer than the Home labels. Reserve room for the arrow and
   let the badge form a compact second line instead of colliding with the affordance. */
#projects-shortcuts.aeca-industries-grid .const-project-main-title {
	max-width: calc(100% - 4.25rem);
	white-space: normal;
}

/* On this destination page the descriptive line is information, not just a hover reward. The photo,
   scrim, badge, arrow and hover lift remain the shared Home pattern; the copy stays readable before
   interaction and gives each route enough context to earn the click. */
#projects-shortcuts.aeca-industries-grid .const-project-button > p:last-of-type:not(.const-capital-subtitle) {
	max-height: 12rem;
	margin-top: .85rem;
	opacity: 1;
	transform: none;
}

#projects-shortcuts.aeca-industries-grid [class*="const-industries-div-"] {
	min-height: clamp(350px, 30vw, 490px);
}

@media (min-width: 1900px) {
	#projects-shortcuts.aeca-industries-grid [class*="const-industries-div-"] {
		min-height: 530px;
	}
}

@media (max-width: 575.98px) {
	#projects-shortcuts.aeca-industries-grid [class*="const-industries-div-"] {
		min-height: clamp(350px, 105vw, 430px);
	}
}


/* Industries hub: section heading follows the AECA primary-blue brand treatment. */
#projects-shortcuts.aeca-industries-grid .const-section-subtitle {
	color: var(--aeca-blue, #024da1) !important;
}


/* ====================================================================================================
   SERVICES: CATEGORY SECTIONS AND THE SOLUTION ACCORDION
   ----------------------------------------------------------------------------------------------------
   The five category sections alternated flat white and flat #eef3f9 and nothing else, so a page of
   twenty-nine near-identical bars had only that one 4%-lightness step to tell you where one category
   ended and the next began -- and on the white sections the bars (also #eef3f9) were the only thing
   with any tone, which inverted the relationship: the content read as the background and the
   background as the content.

   Each section now carries a soft wash off the top edge and a hairline seam, and the bars flip to
   solid white on the tinted sections so a bar is always lighter than the ground it sits on. The
   category heading gets the same cyan rule the footer headings use, so a section opens with a
   consistent signal rather than with bare uppercase type.
   ==================================================================================================== */

.aeca-cat-section {
	position: relative;
	background:
		radial-gradient(125% 70% at 50% 0%, rgba(22, 173, 238, 0.05) 0%, rgba(22, 173, 238, 0) 62%),
		var(--aeca-white);
}

.aeca-cat-section--alt {
	background:
		radial-gradient(125% 70% at 50% 0%, rgba(2, 77, 161, 0.06) 0%, rgba(2, 77, 161, 0) 62%),
		var(--aeca-surface);
}

/* The seam. A section boundary between two grounds only 4% apart is otherwise invisible, and the
   pills jump you straight to one -- you should be able to see you have arrived somewhere. */
.aeca-cat-section + .aeca-cat-section::before {
	content: "";
	position: absolute;
	top: 0;
	right: 0;
	left: 0;
	height: 1px;
	background: linear-gradient(
		to right,
		rgba(2, 77, 161, 0) 0%,
		rgba(2, 77, 161, 0.18) 22%,
		rgba(2, 77, 161, 0.18) 78%,
		rgba(2, 77, 161, 0) 100%
	);
}

/* The sections were running at 20px of container padding, so a heading sat 20px under the seam and
   the list ran to the bottom edge. These are the five biggest blocks on the page; they get room. */
.aeca-cat-section > .container {
	padding-top: clamp(3.25rem, 5.5vw, 5.5rem);
	padding-bottom: clamp(3.25rem, 5.5vw, 5.5rem);
}

.aeca-cat-heading {
	position: relative;
	padding-bottom: 0.85rem;
}

/* The first circle overhangs its bar by about 29px, so Bootstrap's `mb-4` (1.5rem) left it five
   pixels ON TOP of the heading rather than below it. !important because mb-4 carries one. */
.aeca-cat-section .aeca-cat-heading {
	margin-bottom: clamp(2.75rem, 4vw, 4rem) !important;
}


.aeca-cat-heading::after {
	content: "";
	position: absolute;
	bottom: 0;
	left: 0;
	width: 44px;
	height: 3px;
	background: var(--aeca-accent);
}

/* ---- the sector mark ----------------------------------------------------------------------------
   Flat artwork with no disc behind it, unlike the solution circles further down the same section.
   That is the point of the difference: a mark on a pale disc at this size would read as one more
   solution row rather than as the heading of the group they sit in.

   The row aligns on flex-start, not centre. The heading wraps to two lines below 768px, where
   `.aeca-cat-heading-more` drops under the title, and centring would float the mark down level with
   the second line instead of the sector name.
   -------------------------------------------------------------------------------------------------- */

.aeca-cat-head {
	display: flex;
	align-items: flex-start;
	gap: clamp(0.7rem, 1.1vw, 1.05rem);
}

/* A flex item will not shrink below the width of its longest word unless told it may. "ENVIRONMENTAL"
   set at 800 weight clears the narrowest phone by a small margin today, so this is insurance against
   a longer sector name rather than a fix for one. */
.aeca-cat-head > .aeca-cat-heading {
	min-width: 0;
}

.aeca-cat-head__mark {
	flex: 0 0 auto;
	width: clamp(34px, 3vw, 44px);
	height: auto;
	/* A line box is taller than the capital it contains, so a mark flush with the top of the box
	   reads as floating above the word. This settles it onto the cap line. */
	margin-top: 3px;
}

/* ---- the bar ------------------------------------------------------------------------------------
   Square corners against the fully round circle beside them read as two unrelated shapes. Rounding
   the bar to the same family as the dropdown panels and the industry tiles ties the page together,
   and gives the open state somewhere to put its elevation.
   -------------------------------------------------------------------------------------------------- */


/* ---- the circle ---------------------------------------------------------------------------------- */


/* ====================================================================================================
   PAST PROJECTS / FEATURED PROJECTS CARDS  (#projects-shortcut, #featured-projects)
   ----------------------------------------------------------------------------------------------------
   Sixty-five cards across twenty-two templates, and none of them had been touched. They carried the
   same Blocs defaults the Home "Industries We Serve" tiles had before that section was rebuilt:

     .const-past-projects-div-N   10px `double` white border at a 50px radius, rgba(0,0,0,.2) ground,
                                  and a hard 350x400px box that ignored the col-* classes around it
     .const-project-card:hover    background:rgba(0,0,0,.5) + border-radius:50px, painting a second
                                  rounded box inside the first
     .const-project-button        opacity:0 -- the category, the subtitle and the whole description
                                  held invisible until hover, so on any touch device the only thing
                                  ever readable was the title chip
     .const-project-title         font-weight:100 on "Lexend Deca", a family the theme stopped loading

   The result on screen was a row of empty grey boxes with a floating blue "Project Title" chip.

   These are deliberately a separate block from the Home one rather than a widening of its selectors.
   The two are not the same card: Home has a photograph per tile and can afford to hold the
   description back as a hover reward, while every card in these two sections has no photograph at
   all. So the ground is a designed surface rather than a scrim over an image, and the copy stays
   visible -- with nothing behind it to reveal, a hover-only description here would just be the same
   bug in a nicer wrapper.

   SCOPE. #projects-shortcut (singular, 18 templates) and #featured-projects (4 industry pages).
   #projects-shortcuts (plural -- Home and the Industries hub) is a different section with its own
   block above, and nothing here touches it. The one-letter difference between the two ids is the
   only thing separating them in the markup, which is worth knowing before editing either.

   PHOTOGRAPHS. Every tile is already wired for one. Set the custom property on that tile's own class
   and the placeholder is replaced wholesale -- one line, no restructuring, no specificity fight,
   because a custom property set on the element always beats this rule's fallback:

       .const-past-projects-div-1 { --aeca-pp-photo: url("../img/kl-digital-twin.webp"); }

   TODO(AECA): the copy on these cards is placeholder on every page except page-services.php --
   "Project Title" / "Project Subtitle" / "Here goes a simple description." Replace it per page.
   ==================================================================================================== */

:is(#projects-shortcut,
#featured-projects) {
	--aeca-pp-radius: 20px;
	--aeca-pp-pad: clamp(1.15rem, 2.2vw, 1.9rem);
	--aeca-pp-ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* The row is `g-0`, which zeroes both gutters, so the tiles butt together. Same fix as Home. */
:is(#projects-shortcut,
#featured-projects) .row.g-0 {
	--bs-gutter-x: clamp(0.9rem, 1.6vw, 1.6rem);
	--bs-gutter-y: clamp(0.9rem, 1.6vw, 1.6rem);
}

/* style.css sizes these columns in hard pixels inside its own media queries
   (`.const-project-services-bg-1{width:350px}` and friends) at the same (0,1,0) as Bootstrap's
   `.col-lg-4{width:33.333%}`, and it is written later in the file, so the pixel value wins and the
   col-* classes on the element do nothing. The widths the markup actually asks for -- every column
   here is `col-12 col-md-6 col-lg-4` -- are re-asserted per breakpoint.

   Three steps, not the two the Home block needs: Home is a two-up grid of col-lg-6, these are
   three-up. Restating 100% here and widening upward is what keeps that difference explicit. */
:is(#projects-shortcut,
#featured-projects) .row.g-0 > [class*="col-"] {
	width: 100%;
	margin-left: 0;
	margin-right: 0;
}

@media (min-width: 768px) {
	:is(#projects-shortcut,
#featured-projects) .row.g-0 > [class*="col-"] {
		width: 50%;
	}
}

@media (min-width: 992px) {
	:is(#projects-shortcut,
#featured-projects) .row.g-0 > [class*="col-"] {
		width: 33.3333%;
	}
}

/* ---- the tile ------------------------------------------------------------------------------------
   Selected structurally, with :has(> .const-project-card), rather than by class. The wrapper class
   differs on almost every page -- const-past-projects-div-N, const-products-div-N,
   const-services-geo-div-N, const-services-precision-div-N, const-industries-div-N and five more --
   so a class-based selector would have meant listing nine patterns and missing the tenth the next
   time a page is added. The structure is identical everywhere: the tile is whatever element the card
   is a direct child of.

   Specificity is unchanged by the swap: :has() takes the weight of its most specific argument, so
   `:has(> .const-project-card)` is (0,1,0), exactly what `[class*="..."]` was.
   -------------------------------------------------------------------------------------------------- */

:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card) {
	position: relative;

	/* Flex column, not block. The card asks for `height:100%`, which resolves against the parent's
	   height -- and a block parent whose height comes from `min-height` is `height:auto`, so the
	   percentage had nothing to resolve against and the card collapsed to its content. The copy then
	   sat at the top of the tile with the rest of it empty. As a flex column the card can simply
	   grow to fill instead. */
	display: flex;
	flex-direction: column;
	width: 100%;
	min-height: clamp(320px, 26vw, 420px);
	height: auto;
	margin: 0;
	overflow: hidden;

	/* Kills the 10px double white border. The `border-color:...!important` in style.css survives, but
	   it has nothing to colour once the width is zero. */
	border-width: 0;
	border-radius: var(--aeca-pp-radius);

	/* The placeholder ground, and the seam a photograph drops into. Two washes plus a fine diagonal
	   hatch, so an empty tile still reads as a designed surface rather than as a missing image. */
	background-color: #06305f;
	background-image: var(
		--aeca-pp-photo,
		radial-gradient(125% 95% at 12% 0%, rgba(22, 173, 238, 0.3) 0%, rgba(22, 173, 238, 0) 58%),
		radial-gradient(110% 85% at 100% 100%, rgba(2, 77, 161, 0.6) 0%, rgba(2, 77, 161, 0) 62%),
		repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0 2px, rgba(255, 255, 255, 0) 2px 14px)
	);
	background-position: center;
	background-size: cover;
	transition: transform 0.35s var(--aeca-pp-ease), box-shadow 0.35s var(--aeca-pp-ease);
}

/* Scrim, so the copy holds whether the ground is the placeholder or a photograph dropped in later. */
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card)::before {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(
		to top,
		rgba(2, 20, 40, 0.9) 0%,
		rgba(2, 20, 40, 0.66) 30%,
		rgba(2, 20, 40, 0.18) 62%,
		rgba(2, 20, 40, 0.06) 100%
	);
}

:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):hover,
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):focus-within {
	/* style.css swaps the ground to rgba(0,0,0,.5) and drops the border colour on hover; both are
	   overridden here so the tile keeps its surface and only lifts. */
	background-color: #06305f;
	transform: translateY(-5px);
	box-shadow: 0 24px 46px rgba(0, 0, 0, 0.3);
}

/* ---- the card ------------------------------------------------------------------------------------ */

:is(#projects-shortcut,
#featured-projects) .const-project-card {
	position: relative;
	z-index: 1;
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	justify-content: flex-end;
	height: auto;
	padding: var(--aeca-pp-pad);
	background-color: transparent;
	border-radius: 0;
}

/* Replaces `.const-project-card:hover{background:rgba(0,0,0,.5);border-radius:50px}` -- a second
   rounded box drawn inside the first, which read as nested rectangles. */
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):hover .const-project-card,
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):focus-within .const-project-card,
:is(#projects-shortcut,
#featured-projects) .const-project-card:hover {
	background-color: transparent;
	border-radius: 0;
}

/* ---- top-left badge (the project name) ----------------------------------------------------------- */

/* `.const-project-title-center` is flex/align-items:flex-end/justify-content:center, which centred
   the chip and let it drift to a different x in every card. Pinned top-left instead. */
:is(#projects-shortcut,
#featured-projects) .const-project-card > .card-body.const-project-title-center {
	position: absolute;
	top: var(--aeca-pp-pad);
	left: var(--aeca-pp-pad);
	right: var(--aeca-pp-pad);
	display: block;
	padding: 0;
	background: transparent;
}

:is(#projects-shortcut,
#featured-projects) .const-project-main-title {
	display: inline-block;
	max-width: 100%;
	padding: 0.4em 0.9em;
	border-width: 0;
	border-radius: 999px;
	background-color: var(--aeca-blue);
	font-size: clamp(0.72rem, 1vw, 0.86rem);
	font-weight: 700;
	letter-spacing: 0.08em;
	line-height: 1.25;
	text-transform: uppercase;
}

/* style.css blanks the chip's background on hover, which left white text on the photograph. */
:is(#projects-shortcut,
#featured-projects) .const-project-main-title:hover,
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):hover .const-project-main-title {
	background-color: var(--aeca-blue);
	border-width: 0;
}

/* ---- bottom stack -------------------------------------------------------------------------------- */

/* The opacity:0 is the headline fix. Everything below the title was invisible until hover, which on a
   phone means it was never readable at all. */
:is(#projects-shortcut,
#featured-projects) .const-project-button {
	position: static;
	display: block;
	padding: 0;
	border-width: 0;
	background: transparent;
	opacity: 1;
}

/* Beats Bootstrap's `.text-center{text-align:center!important}` on the three children. */
:is(#projects-shortcut,
#featured-projects) .const-project-button > * {
	text-align: left !important;
}

/* The markup carries a stray <br> after the link, which opened a blank line in the stack. */
:is(#projects-shortcut,
#featured-projects) .const-project-button br {
	display: none;
}

:is(#projects-shortcut,
#featured-projects) .const-project-button > .const-capital-subtitle {
	margin-bottom: 0.3rem;
	color: var(--aeca-blue-light) !important;
	font-family: var(--aeca-font-head);
	font-size: 0.74rem;
	font-weight: 700;
	letter-spacing: 0.14em;
	line-height: 1.2;
}

:is(#projects-shortcut,
#featured-projects) .const-project-button > h3 {
	position: relative;
	margin-bottom: 0.55rem;
	padding-bottom: 0.6rem;
	font-size: clamp(1.1rem, 1.7vw, 1.4rem);
	line-height: 1.2;
	text-shadow: none;
}

/* `.const-project-title` is font-weight:100 on "Lexend Deca", a family the theme no longer loads, so
   it fell back to a thin system face at a size meant for a display weight. */
:is(#projects-shortcut,
#featured-projects) .const-project-title {
	color: var(--aeca-white) !important;
	font-family: var(--aeca-font-head);
	font-weight: 700;
	text-decoration: none;
}

/* Accent rule under the title, grown on hover. */
:is(#projects-shortcut,
#featured-projects) .const-project-button > h3::after {
	content: "";
	position: absolute;
	bottom: 0;
	left: 0;
	width: 34px;
	height: 3px;
	background: var(--aeca-blue-light);
	transition: width 0.35s var(--aeca-pp-ease);
}

:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):hover .const-project-button > h3::after,
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):focus-within .const-project-button > h3::after {
	width: 62px;
}

:is(#projects-shortcut,
#featured-projects) .const-project-button > p:last-of-type:not(.const-capital-subtitle) {
	margin-bottom: 0;
	color: rgba(255, 255, 255, 0.88) !important;
	font-family: var(--aeca-font-body);
	font-size: 0.92rem;
	line-height: 1.55;
	text-wrap: pretty;
}

/* ---- focus -------------------------------------------------------------------------------------- */

/* The stretched link covers the whole tile, so its own focus ring would be clipped by the tile's
   overflow:hidden. Draw it on the tile, which is the shape the ring should describe anyway. */
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):focus-within {
	outline: 3px solid var(--aeca-blue-light);
	outline-offset: 3px;
}

:is(#projects-shortcut,
#featured-projects) .const-project-title:focus-visible {
	outline: none;
}

@media (prefers-reduced-motion: reduce) {
	:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card),
:is(#projects-shortcut,
#featured-projects) .const-project-button > h3::after {
		transition: none;
	}

	:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):hover,
:is(#projects-shortcut,
#featured-projects) :has(> .const-project-card):focus-within {
		transform: none;
	}
}

/* ====================================================================================================
   NAV BAR LOGO
   ----------------------------------------------------------------------------------------------------
   What was there: `transition: .5s ease-in-out` on the anchor, and a :hover that swapped
   background-image to a different file (img/AECA-Logo-1) and the background-colour to a pale blue.

   Two things wrong with that. background-image is not an animatable property -- it hard-cuts -- so
   the half-second transition applied to nothing you could see and the mark simply popped. And the
   file it popped to is a 3KB copy of the same logo against the 40KB original: visibly softer, with
   the "SOLUTIONS" wordmark almost illegible. Pointing at the logo made it look like it had failed
   to load properly.

   What it is now: one mark, the good one, at all times. The motion is carried by transform on a
   pseudo-element, which composites on the GPU and actually interpolates -- a small lift and scale,
   a cyan ring that expands out from behind the mark, and a light tint behind it. Hover and
   keyboard focus get the same state, so tabbing to the logo shows what pointing at it shows.

   The mark moved off the anchor and onto ::before so it can be transformed without dragging the
   anchor's own box, its focus ring or the sticky bar's layout with it.
   ==================================================================================================== */

#sidebar .const-logo {
	position: relative;
	isolation: isolate;

	/* Moved to ::before. Left on the anchor it would paint underneath the pseudo-element as a second,
	   un-transformed copy showing through every time the top one scaled. */
	background-image: none;

	/* The pale-blue hover fill and the image swap both came off style.css; the fill is restated here
	   as a token so the resting state stays white and only the interaction tints it. */
	background-color: var(--aeca-white);
	transition: background-color 0.3s ease;
}

#sidebar .const-logo::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;

	/* Same asset and the same 50px the bar has always drawn, now with a little room to grow on the
	   wider bar: the bar itself runs 80px -> 96px above 1200px while the mark stayed pinned at 50. */
	background-image: url("../img/AECA%20Logo%201000x1000.webp");
	background-repeat: no-repeat;
	background-position: center center;
	background-size: clamp(46px, 3.1vw, 58px) auto;

	transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* The ring. Sits behind the mark, at rest it is collapsed and invisible; on hover it expands past
   the mark and fades, which reads as a pulse outward from the logo rather than a border appearing. */
#sidebar .const-logo::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	z-index: 0;
	width: clamp(46px, 3.1vw, 58px);
	aspect-ratio: 1;
	border: 2px solid var(--aeca-blue-light);
	border-radius: 50%;
	opacity: 0;
	transform: translate(-50%, -50%) scale(0.55);
	transition: transform 0.55s cubic-bezier(0.2, 0.7, 0.3, 1), opacity 0.55s ease;
}

#sidebar .const-logo:hover,
#sidebar .const-logo:focus-visible {
	background-color: #f2f8fd;
}

#sidebar .const-logo:hover::before,
#sidebar .const-logo:focus-visible::before {
	transform: scale(1.09);
}

#sidebar .const-logo:hover::after,
#sidebar .const-logo:focus-visible::after {
	opacity: 0;
	transform: translate(-50%, -50%) scale(1.55);
	/* Fades up quickly then out, so the ring reads as a single pulse and not as a ring that lingers. */
	animation: aeca-logo-pulse 0.75s cubic-bezier(0.2, 0.7, 0.3, 1);
}

@keyframes aeca-logo-pulse {
	0%   { opacity: 0;   transform: translate(-50%, -50%) scale(0.55); }
	35%  { opacity: 0.7; }
	100% { opacity: 0;   transform: translate(-50%, -50%) scale(1.55); }
}

/* The mark springs back a touch faster than it grows, which stops the release feeling sluggish. */
#sidebar .const-logo:active::before {
	transform: scale(1.02);
	transition-duration: 0.12s;
}

/* style.css sets `.const-logo:hover{ color:#FEFFFF!important }` for wordmark text that no longer
   exists in the anchor. The visually-hidden span inherits it, and while it is off-screen either way,
   leaving white text on a white ground is the kind of thing that surfaces the moment someone removes
   the hiding class. */
#sidebar .const-logo:hover {
	color: var(--aeca-ink) !important;
}

/* The bar drops to 58px here, which left the mark 6px of air either side. */
@media (max-width: 575.98px) {
	#sidebar .const-logo::before {
		background-size: 40px auto;
	}

	#sidebar .const-logo::after {
		width: 40px;
	}
}

@media (prefers-reduced-motion: reduce) {
	#sidebar .const-logo,
#sidebar .const-logo::before,
#sidebar .const-logo::after {
		transition: none;
	}

	#sidebar .const-logo:hover::before,
#sidebar .const-logo:focus-visible::before {
		transform: none;
	}

	#sidebar .const-logo:hover::after,
#sidebar .const-logo:focus-visible::after {
		animation: none;
		opacity: 0;
	}
}

/* SCROLL REVEAL
----------------------------------------------------------------------------------------------------

The resting and revealed states for the site-wide reveal in js/aeca-custom.js. The `.aeca-reveal`
class is added by that script and never sits in the markup, so a browser with JS off, or a script
that fails to parse, leaves every page fully visible rather than blank.

The script also skips anything already inside the first screen at load, which is why there is no
guard here against the hero being hidden while it is the thing being painted.

`transform: none` rather than `translateY(0)` on the revealed state on purpose: a transform of any
value makes the element a containing block for absolutely positioned descendants, and `.stretched-link`
covers most of the cards on this site. `none` releases that once the reveal has finished.
---------------------------------------------------------------------------------------------------- */

.aeca-reveal {
	opacity: 0;
	transform: translateY(24px);
	transition:
		opacity 0.6s cubic-bezier(0.22, 0.61, 0.36, 1),
		transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1);
	transition-delay: var(--aeca-reveal-delay, 0ms);
}

.aeca-reveal.is-visible {
	opacity: 1;
	transform: none;
}

/* The counting stats, so the digits do not shuffle the line width on every frame. */
.const-stat-number {
	font-variant-numeric: tabular-nums;
}

/* The script bails before marking anything when this is set at load. The rule below is for the
   preference being switched on afterwards, which leaves elements already carrying the class. */
@media (prefers-reduced-motion: reduce) {
	.aeca-reveal,
.aeca-reveal.is-visible {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* MOBILE SECTION SPACING
----------------------------------------------------------------------------------------------------

Blocs writes its section padding as one unsuffixed value that is the DESKTOP value, then adds
`-sm` / `-md` / `-lg` suffixed classes that override it from that breakpoint UP. There is no smaller
step below the first breakpoint, so `.bloc-lg` puts 100px above and below a section at 390px exactly
as it does at 1920px. On About that stacked up: 100px of container padding on top of the section's
own padding meant 288px of empty screen between the last line of "Our beginning" and the first line
of "What we do" -- most of a phone screen with nothing in it, four times down the page.

Two steps rather than one, because the jump from 100px to a phone value is too big to apply at 991px
where a tablet still has the height to carry it.

`:not(.bloc-no-padding)` and its siblings are the important part. A handful of containers pair a size
class with a no-padding class that wins at the width it applies to -- `bloc-sm-sm bloc-no-padding` is
asking for zero on phones and 20px from 576px up. This file loads after style.css, so an unguarded
`.bloc-sm-sm` here would beat `.bloc-no-padding` on source order and put padding back where the page
asked for none. A class selector matches the exact token, so `:not(.bloc-no-padding)` leaves
`bloc-no-padding-lg` alone -- which is what the About sections carry, and what has to stay reachable.

Only the vertical values are touched. The 20px sides are the page gutter and are already right.
---------------------------------------------------------------------------------------------------- */

@media (max-width: 575.98px) {
	.bloc-xxl:not(.bloc-no-padding) { padding-top: 64px; padding-bottom: 64px; }
	.bloc-xl:not(.bloc-no-padding) { padding-top: 56px; padding-bottom: 56px; }
	.bloc-lg:not(.bloc-no-padding) { padding-top: 40px; padding-bottom: 40px; }
	.bloc-md:not(.bloc-no-padding) { padding-top: 28px; padding-bottom: 28px; }
}

@media (min-width: 576px) and (max-width: 991.98px) {
	.bloc-xxl:not(.bloc-no-padding):not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-xxl-sm:not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-xxl-md:not(.bloc-no-padding-md) { padding-top: 112px; padding-bottom: 112px; }

	.bloc-xl:not(.bloc-no-padding):not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-xl-sm:not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-xl-md:not(.bloc-no-padding-md) { padding-top: 88px; padding-bottom: 88px; }

	.bloc-lg:not(.bloc-no-padding):not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-lg-sm:not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-lg-md:not(.bloc-no-padding-md) { padding-top: 64px; padding-bottom: 64px; }

	.bloc-md:not(.bloc-no-padding):not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-md-sm:not(.bloc-no-padding-sm):not(.bloc-no-padding-md),
.bloc-md-md:not(.bloc-no-padding-md) { padding-top: 40px; padding-bottom: 40px; }
}

/* The other half of the gap. Blocs gives every section heading `margin-top: 100px` at every width,
   which lands on top of whatever padding the section already has -- on About it is the whole 100px
   above "Our Partners", since that container is set to no padding and the margin is all there is.
   Reduced rather than removed: on the sections that do zero their padding it is the only separation
   between the heading and the section above it. */
@media (max-width: 767.98px) {
	.const-section-subtitle {
		margin-top: 48px;
	}
}

/* ====================================================================================================
   404 -- PAGE NOT FOUND
   ----------------------------------------------------------------------------------------------------
   Namespaced under `.aeca-404`, so none of it can reach a real page. It borrows the tokens and the
   two typefaces from the rest of the site, but not a single layout: the 404 is the one view where
   the visitor arrived by accident, so it leads with the failed address and the way out rather than
   with a banner image.

   The hero ground is the graticule a mapping company draws on: two repeating gradients for the grid,
   one radial wash for depth, and a single pin dropped off-centre where nothing was found. All of it
   is CSS, so there is no image request on a page nobody meant to load. `prefers-reduced-motion`
   stops the pin pulsing.
   ==================================================================================================== */

.aeca-404 {
	--e-navy: #012340;
	--e-navy-2: #01315a;
	--e-ink: #14212e;
	--e-muted: #4a5b6b;
	--e-line: #d7e3ee;
	--e-tint: #f2f7fb;
	--e-accent: var(--aeca-blue-light, #16adee);

	color: var(--e-ink);
	font-family: var(--aeca-font-body, "Inter", system-ui, sans-serif);
}

.aeca-404 :is(h1,
h2,
h3) {
	font-family: var(--aeca-font-head, "Outfit", system-ui, sans-serif);
	letter-spacing: -0.015em;
}

.aeca-404 .container {
	max-width: 1180px;
}

.aeca-404__eyebrow {
	margin: 0 0 0.7rem;
	color: var(--e-accent);
	font-family: var(--aeca-font-head, "Outfit", sans-serif);
	font-size: 0.74rem;
	font-weight: 700;
	letter-spacing: 0.18em;
	line-height: 1.2;
	text-transform: uppercase;
}

/* ---- Hero -------------------------------------------------------------------------------------- */

.aeca-404-hero {
	position: relative;
	overflow: hidden;
	padding: clamp(64px, 9vw, 128px) 0 clamp(56px, 7vw, 104px);
	background: var(--e-navy);
	color: #fff;
}

/* The graticule. Kept on a child rather than on the section so the grid can be faded out at the
   edges with a mask without taking the copy's contrast down with it. */
.aeca-404-hero__plot {
	position: absolute;
	inset: 0;
	background-image:
		linear-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px),
		linear-gradient(90deg, rgba(255, 255, 255, 0.07) 1px, transparent 1px),
		radial-gradient(120% 90% at 78% 15%, rgba(22, 173, 238, 0.28), transparent 62%);
	background-size: 64px 64px, 64px 64px, 100% 100%;
	-webkit-mask-image: radial-gradient(130% 100% at 50% 40%, #000 45%, transparent 100%);
	mask-image: radial-gradient(130% 100% at 50% 40%, #000 45%, transparent 100%);
	pointer-events: none;
}

/* The pin sits out in the empty quarter of the plot. It is the one piece of decoration on the page
   and it carries the point: something was looked for out here, and there is nothing at it. */
.aeca-404-hero__pin {
	position: absolute;
	top: 22%;
	left: 76%;
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: var(--aeca-red, #ec1c24);
	box-shadow: 0 0 0 6px rgba(236, 28, 36, 0.18);
}

.aeca-404-hero__pin::after {
	content: "";
	position: absolute;
	inset: -14px;
	border: 1px solid rgba(236, 28, 36, 0.5);
	border-radius: 50%;
	animation: aeca-404-ping 2.8s ease-out infinite;
}

@keyframes aeca-404-ping {
	0%   { transform: scale(0.7); opacity: 0.9; }
	70%  { transform: scale(1.9); opacity: 0; }
	100% { transform: scale(1.9); opacity: 0; }
}

.aeca-404-hero > .container {
	position: relative;
	z-index: 1;
}

.aeca-404-hero__grid {
	display: grid;
	gap: clamp(24px, 4vw, 56px);
	grid-template-columns: minmax(0, 1fr) auto;
	align-items: center;
}

.aeca-404-hero h1 {
	margin: 0 0 1rem;
	font-size: clamp(2.1rem, 4.6vw, 3.4rem);
	font-weight: 700;
	line-height: 1.08;
}

.aeca-404-hero__lead {
	max-width: 46ch;
	margin: 0;
	color: rgba(255, 255, 255, 0.82);
	font-size: clamp(1.02rem, 1.35vw, 1.16rem);
	line-height: 1.6;
}

/* The failed address, shown rather than described. A visitor who mistyped a slug can see the typo
   here without reading the URL bar, which on a phone is usually truncated or hidden entirely. */
.aeca-404-path {
	display: flex;
	flex-wrap: wrap;
	gap: 0.55rem;
	align-items: center;
	margin: 1.5rem 0 0;
	color: rgba(255, 255, 255, 0.62);
	font-size: 0.78rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.aeca-404-path code {
	max-width: 100%;
	padding: 0.35rem 0.7rem;
	border: 1px solid rgba(255, 255, 255, 0.22);
	border-radius: 2px;
	background: rgba(255, 255, 255, 0.06);
	color: #fff;
	font-size: 0.92rem;
	letter-spacing: 0;
	overflow-wrap: anywhere;
	text-transform: none;
}

.aeca-404-search {
	max-width: 34rem;
	margin-top: 1.9rem;
}

.aeca-404-search .form-control {
	min-height: 52px;
	padding: 0.6rem 1rem;
	border: 1px solid rgba(255, 255, 255, 0.28);
	border-radius: 0;
	background: rgba(255, 255, 255, 0.08);
	color: #fff;
}

.aeca-404-search .form-control::placeholder {
	color: rgba(255, 255, 255, 0.55);
}

.aeca-404-search .form-control:focus {
	border-color: var(--e-accent);
	background: rgba(255, 255, 255, 0.14);
	box-shadow: none;
	color: #fff;
}

/* The numeral. Sized off the viewport rather than the type scale because it is a graphic, not a
   heading, and it is the one thing on the page that should read from across the room. */
.aeca-404-code {
	margin: 0;
	color: transparent;
	font-family: var(--aeca-font-head, "Outfit", sans-serif);
	font-size: clamp(6rem, 13vw, 12rem);
	font-weight: 800;
	letter-spacing: -0.05em;
	line-height: 0.82;
	-webkit-text-stroke: 2px rgba(255, 255, 255, 0.34);
	user-select: none;
}

/* ---- Sections ---------------------------------------------------------------------------------- */

.aeca-404-section {
	padding: clamp(52px, 6.5vw, 92px) 0;
	background: #fff;
}

.aeca-404-section--tint {
	background: var(--e-tint);
}

.aeca-404-section__head {
	max-width: 60ch;
	margin: 0 0 clamp(28px, 3.5vw, 44px);
}

.aeca-404-section__head h2 {
	margin: 0 0 0.6rem;
	font-size: clamp(1.5rem, 2.6vw, 2.05rem);
	font-weight: 700;
	line-height: 1.15;
}

.aeca-404-section__head h2::after {
	content: "";
	display: block;
	width: 46px;
	height: 3px;
	margin-top: 0.85rem;
	background: var(--e-accent);
}

.aeca-404-section__head p {
	margin: 0;
	color: var(--e-muted);
	font-size: 1.02rem;
	line-height: 1.6;
}

/* ---- Near misses ------------------------------------------------------------------------------- */

.aeca-404-matches {
	display: grid;
	gap: 12px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.aeca-404-matches a {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem 1rem;
	align-items: baseline;
	justify-content: space-between;
	padding: 1.05rem 1.25rem;
	border: 1px solid var(--e-line);
	border-left: 3px solid var(--aeca-blue, #024da1);
	background: #fff;
	color: inherit;
	text-decoration: none;
	transition: border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}

.aeca-404-matches a:hover,
.aeca-404-matches a:focus-visible {
	border-left-color: var(--e-accent);
	box-shadow: 0 8px 22px rgba(1, 35, 64, 0.09);
	transform: translateX(3px);
}

.aeca-404-matches__title {
	font-family: var(--aeca-font-head, "Outfit", sans-serif);
	font-size: 1.08rem;
	font-weight: 600;
}

.aeca-404-matches__slug {
	color: var(--e-muted);
	font-size: 0.86rem;
	overflow-wrap: anywhere;
}

/* ---- Destinations ------------------------------------------------------------------------------ */

/* Three across, not four. auto-fit puts four on a 1180px container, which leaves the six
   destinations as a row of four and an orphaned pair; three gives two even rows. */
.aeca-404-dest {
	display: grid;
	gap: 16px;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

@media (min-width: 992px) {
	.aeca-404-dest {
		grid-template-columns: repeat(3, 1fr);
	}
}

.aeca-404-card {
	position: relative;
	display: flex;
	flex-direction: column;
	padding: 1.6rem 1.5rem 1.5rem;
	border: 1px solid var(--e-line);
	background: #fff;
	color: inherit;
	text-decoration: none;
	transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}

/* The top accent bar is the services family's card signature, reused here on purpose: the 404 is
   not part of a family, but it should still look like this site rather than a default template. */
.aeca-404-card::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 3px;
	background: var(--e-accent);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform 0.24s ease;
}

.aeca-404-card:hover,
.aeca-404-card:focus-visible {
	border-color: #bfd4e6;
	box-shadow: 0 14px 30px rgba(1, 35, 64, 0.1);
	transform: translateY(-3px);
}

.aeca-404-card:hover::before,
.aeca-404-card:focus-visible::before {
	transform: scaleX(1);
}

.aeca-404-card__icon {
	display: block;
	width: 30px;
	height: 30px;
	margin-bottom: 1rem;
	color: var(--aeca-blue, #024da1);
}

.aeca-404-card__icon svg {
	display: block;
	width: 100%;
	height: 100%;
}

.aeca-404-card__title {
	margin-bottom: 0.45rem;
	font-family: var(--aeca-font-head, "Outfit", sans-serif);
	font-size: 1.16rem;
	font-weight: 600;
}

/* The gap above the arrow lives here rather than on the arrow itself. .aeca-404-card__go is a 9px
   box rotated 45 degrees to make the chevron, so padding or a margin on it would deform the glyph
   instead of moving it. */
.aeca-404-card__body {
	margin-bottom: 1.15rem;
	color: var(--e-muted);
	font-size: 0.95rem;
	line-height: 1.55;
}

/* margin-top:auto rather than a fixed gap, so the arrows line up across a row whatever length the
   card bodies run to. */
.aeca-404-card__go {
	width: 9px;
	height: 9px;
	margin-top: auto;
	border-top: 2px solid var(--aeca-blue, #024da1);
	border-right: 2px solid var(--aeca-blue, #024da1);
	transform: rotate(45deg);
	transition: transform 0.2s ease;
}

.aeca-404-card:hover .aeca-404-card__go {
	transform: rotate(45deg) translate(3px, -3px);
}

/* ---- Tail -------------------------------------------------------------------------------------- */

.aeca-404-tail {
	padding: clamp(48px, 5.5vw, 76px) 0;
	background: var(--e-navy-2);
	color: #fff;
}

.aeca-404-tail__inner {
	display: flex;
	flex-wrap: wrap;
	gap: 28px;
	align-items: center;
	justify-content: space-between;
}

.aeca-404-tail h2 {
	margin: 0 0 0.55rem;
	font-size: clamp(1.4rem, 2.3vw, 1.85rem);
	font-weight: 700;
}

.aeca-404-tail p {
	max-width: 52ch;
	margin: 0;
	color: rgba(255, 255, 255, 0.78);
	line-height: 1.6;
}

.aeca-404-tail__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
}

.aeca-404-btn {
	display: inline-block;
	padding: 0.85rem 1.6rem;
	border: 1px solid rgba(255, 255, 255, 0.45);
	color: #fff;
	font-family: var(--aeca-font-head, "Outfit", sans-serif);
	font-size: 0.95rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	text-decoration: none;
	transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}

.aeca-404-btn:hover,
.aeca-404-btn:focus-visible {
	border-color: #fff;
	background: #fff;
	color: var(--e-navy);
}

.aeca-404-btn--solid {
	border-color: var(--e-accent);
	background: var(--e-accent);
	color: var(--e-navy);
}

.aeca-404-btn--solid:hover,
.aeca-404-btn--solid:focus-visible {
	border-color: #fff;
	background: #fff;
}

/* ---- Narrow ------------------------------------------------------------------------------------ */

/* The numeral is the first thing to go: below ~900px there is no column left for it, and stacking a
   12rem graphic above the copy pushes the search field off the first screen. */
@media (max-width: 900px) {
	.aeca-404-hero__grid {
		grid-template-columns: minmax(0, 1fr);
	}

	.aeca-404-code {
		order: -1;
		font-size: clamp(4.5rem, 20vw, 7rem);
	}

	.aeca-404-hero__pin {
		left: 82%;
	}
}

@media (max-width: 575.98px) {
	.aeca-404-search .btn,
.aeca-404-tail__actions {
		width: 100%;
	}

	.aeca-404-btn {
		flex: 1 1 auto;
		text-align: center;
	}
}

@media (prefers-reduced-motion: reduce) {
	.aeca-404-hero__pin::after {
		animation: none;
	}

	.aeca-404-card,
.aeca-404-card::before,
.aeca-404-card__go,
.aeca-404-matches a {
		transition: none;
	}

	.aeca-404-card:hover,
.aeca-404-matches a:hover {
		transform: none;
	}
}


/* ====================================================================================================
   RECOVERED RULES
   ----------------------------------------------------------------------------------------------------
   These twelve rules were added to this file after the 2026-08-11 12:07 backup was taken (the hero
   panel scope, the category heading links, and the >=1200px type scale). They were recovered from the
   browser's parsed stylesheet, which means the original comments explaining them did not survive.
   The rules themselves are byte-identical to what was live.

   TODO(AECA): fold these back into the matching sections above and re-comment them.
   ==================================================================================================== */
.aeca-cat-heading-link { color: inherit; text-decoration: none; }

.aeca-cat-heading-link:hover,
.aeca-cat-heading-link:focus-visible { color: var(--aeca-accent); }

.aeca-cat-heading-more { display: block; margin-top: 0.35rem; font-size: 0.85rem; font-weight: 600; letter-spacing: 0.04em; text-transform: none; color: var(--aeca-accent); }

@media (min-width: 768px) {
  .aeca-cat-heading-more { display: inline-block; margin-top: 0px; margin-left: 0.9rem; vertical-align: middle; }
}

.aeca-icon--art { object-fit: contain; }

@media (min-width: 1200px) {
  .const-footer-form,
.link-about-us-style,
.link-contact-us-style,
.link-industries-style,
.link-products-style,
.link-style,
.p-624-style,
.p-628-style { font-size: clamp(14px, 11.53px + 0.206vw, 16.8px) !important; }
  .p-3229-style,
.p-474-style,
.p-481-style { font-size: clamp(15px, 12.35px + 0.221vw, 18px) !important; }
  .const-wire-button,
.p-321-style { font-size: clamp(16px, 13.18px + 0.235vw, 19.2px) !important; }
  .const-button,
.const-button-carousel-2,
.const-button-carousel-3,
.const-button-carousel-4,
.const-button-carousel-5,
.const-button-industries,
.const-button-solutions,
.const-form-button,
.link-0-contact-us-style,
.link-0-products-style,
.link-0-style,
.link-3dt-scanfly-style,
.link-about-style,
.link-agriculture-style,
.link-defect-style,
.link-defense-style,
.link-digital-infra-style,
.link-disaster-style,
.link-dji-style,
.link-downloads-style,
.link-drone-style,
.link-events-style,
.link-environment-style,
.link-environmental-style,
.link-geospatial-style,
.link-gis-style,
.link-government-style,
.link-infrastructure-style,
.link-faqs-style,
.link-insights-style,
.link-lidar-style,
.link-micasense-style,
.link-mobile-mapping-style,
.link-news-style,
.link-ororatech-style,
.link-our-stories-style,
.link-precision-style,
.link-sentera-style,
.link-services-style,
.link-sidebar-style,
.link-smartdelta-style,
.link-quote-style,
.link-sony-style,
.link-testimonials-style,
.link-xenomatix-style,
.p-hero-carousel-style { font-size: clamp(18px, 14.82px + 0.265vw, 21.6px) !important; }
  .const-project-detail,
.h3-16-style,
.h3-19-bloc-5-style,
.h3-22-bloc-5-style,
.h3-card-footer-style,
.p-21-bloc-5-style,
.p-bloc-5-style,
.p-style { font-size: clamp(20px, 16.47px + 0.294vw, 24px) !important; }
  .const-summary-text { font-size: clamp(21px, 16.55px + 0.371vw, 26px) !important; }
  .const-read-more-link { font-size: clamp(23px, 18.13px + 0.406vw, 28.5px) !important; }
  .h2-1-style,
.h2-2-style,
.h2-shortcuts-style,
.h2-style { font-size: clamp(24px, 18.92px + 0.424vw, 29.8px) !important; }
  .button-forest,
.button-infra,
.button-management,
.button-plant,
.button-smart,
.const-accordion-link { font-size: clamp(25px, 19.71px + 0.441vw, 31px) !important; }
  .h3-165-style,
.h3-166-style,
.h3-167-style,
.h3-168-style { font-size: clamp(26px, 20.49px + 0.459vw, 32.2px) !important; }
  .const-navigation,
.h2-13-style,
.h2-14-style,
.h2-15-style,
.h2-environment-style,
.h4-27-style,
.p-22-style,
.p-23-style,
.p-25-style,
.p-bloc-6-style { font-size: clamp(28px, 22.07px + 0.494vw, 34.7px) !important; }
  .const-logo,
.h2-17973-style,
.h3-36879-style,
.h3-39-style,
.h3-41-style,
.h3-43-style,
.h3-45-style,
.h3-47-style,
.h3-49-style,
.h3-8-style,
.h3-bloc-6-style,
.h3-navigate-style,
.h3-services-grid-style,
.h3-style,
.h4-64-style,
.h4-65-style,
.h4-hero-carousel-style,
.icon-md,
.p-597-style,
.text-style-1 { font-size: clamp(30px, 23.65px + 0.529vw, 37.2px) !important; }
  .const-members-icon,
.h3-11-style,
.h3-14-style,
.h3-17-style,
.h3-20-bloc-5-style,
.h3-card-heading-style { font-size: clamp(40px, 31.53px + 0.706vw, 49.6px) !important; }
  .const-shortcut-icon { font-size: clamp(50px, 37.65px + 1.029vw, 64px) !important; }
  .h1-about-hero-style,
.h2-136-style,
.h2-139-style,
.h2-143-style,
.h2-19-style,
.h2-197-style,
.h2-7-style,
.h2-bloc-109-style,
.h2-bloc-72-style,
.h3-185-style,
.h3-projects-grid-style,
.icon-lg { font-size: clamp(60px, 45.18px + 1.235vw, 76.8px) !important; }
  .h2-212-style { font-size: clamp(70px, 52.71px + 1.441vw, 89.6px) !important; }
  .p-14-style,
.p-16-style,
.p-20-style,
.p-about-stats-style { font-size: clamp(80px, 60.24px + 1.647vw, 102.4px) !important; }
  .const-partners-title,
.const-quality-big,
.stats-icon { font-size: clamp(100px, 75.29px + 2.059vw, 128px) !important; }
  .const-quality-big { line-height: 0.8 !important; }
  .h3-card-heading-style { line-height: 0.875 !important; }
  .h2-136-style,
.h2-139-style,
.h2-143-style,
.h2-17973-style,
.h2-19-style,
.h2-197-style,
.h2-7-style,
.h2-bloc-109-style,
.h3-16-style,
.h3-165-style,
.h3-166-style,
.h3-167-style,
.h3-168-style,
.h3-185-style,
.h3-19-bloc-5-style,
.h3-22-bloc-5-style,
.h3-36879-style,
.h3-card-footer-style,
.h3-projects-grid-style { line-height: 1 !important; }
  .h2-environment-style { line-height: 1.214 !important; }
  .const-summary-text { line-height: 1.381 !important; }
  .h4-64-style,
.h4-65-style,
.h4-hero-carousel-style,
.p-597-style { line-height: 1.5 !important; }
}

.bg-aeca-placeholder { background-image: url("../img/placeholder-image.webp"); }

.about-hero-panel,
#hero-carousel .card-hero-carousel-style,
#services-hero .column-hero-solutions,
#industries-hero .column-hero-solutions { background: var(--aeca-glass-bg) !important; border: var(--aeca-glass-border) !important; border-radius: var(--aeca-glass-radius) !important; box-shadow: var(--aeca-glass-shadow) !important; }

#services-hero .column-hero-solutions,
#industries-hero .column-hero-solutions { box-sizing: border-box; max-width: min(62rem, 100%); margin-left: auto; margin-right: auto; padding: var(--aeca-glass-pad); text-align: center; }

#services-hero .column-hero-solutions > *,
#industries-hero .column-hero-solutions > * { text-align: center !important; }

.about-hero-panel .const-page-title::after,
#services-hero .column-hero-solutions .const-page-title::after,
#industries-hero .column-hero-solutions .const-page-title::after,
#hero-carousel .h3-card-heading-style::after { content: ""; display: block; width: 4.5rem; height: 3px; margin: 1.1rem auto 0px; background: var(--aeca-blue-light); }

@media (min-width: 1200px) {
  #sidebar .navbar-nav > .nav-item .nav-link { position: relative; padding-top: 10px; padding-bottom: 10px; line-height: 1.25; transition: color 0.16s; }
  #sidebar .navbar-nav > .nav-item .nav-link::before { content: ""; position: absolute; right: 0px; bottom: 2px; left: 0px; height: 2px; background: var(--aeca-accent); transform: scaleX(0); transform-origin: center center; transition: transform 0.2s; }
  #sidebar .navbar-nav > .nav-item .nav-link:hover::before,
#sidebar .navbar-nav > .nav-item .nav-link:focus-visible::before,
#sidebar .navbar-nav > .nav-item .nav-link[data-current]::before { transform: scaleX(1); }
  #sidebar .navbar-nav > .nav-item .nav-link[data-current] { color: var(--aeca-blue) !important; }
  #sidebar .navbar-nav .dropdown-menu { min-width: 244px; padding: 8px; border: 1px solid var(--aeca-border); border-radius: 10px; box-shadow: rgba(2, 77, 161, 0.16) 0px 18px 40px; }
  #sidebar .navbar-nav .dropdown-menu .dropdown-item { text-transform: none; position: relative; width: 100%; margin-left: 0px; margin-right: 0px; padding-left: 16px; padding-right: 12px; border-radius: 6px; transition: background-color 0.16s, color 0.16s; color: var(--aeca-ink) !important; }
  #sidebar .navbar-nav .dropdown-menu .dropdown-item::before { content: ""; position: absolute; top: 50%; left: 6px; width: 3px; height: 60%; border-radius: 2px; background: var(--aeca-accent); opacity: 0; transform: translateY(-50%); transition: opacity 0.16s; }
  #sidebar .navbar-nav .dropdown-menu .dropdown-item:hover,
#sidebar .navbar-nav .dropdown-menu .dropdown-item:focus-visible { background-color: var(--aeca-nav-hover-bg); color: var(--aeca-blue) !important; }
  #sidebar .navbar-nav .dropdown-menu .dropdown-item:hover::before,
#sidebar .navbar-nav .dropdown-menu .dropdown-item:focus-visible::before,
#sidebar .navbar-nav .dropdown-menu .dropdown-item[data-current="page"]::before { opacity: 1; }
  #sidebar .navbar-nav .dropdown-menu .dropdown-item[data-current="page"] { background-color: var(--aeca-nav-hover-bg); font-weight: 600; color: var(--aeca-blue) !important; }
  #sidebar .navbar-nav .dropdown-menu .dropdown-item[data-current="page"]::before { background: var(--aeca-blue); }
  #sidebar .navbar-nav .dropdown-menu .aeca-dropdown-header { margin: 10px 0px 2px; padding: 0px 12px 0px 16px; font-size: 12px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--aeca-muted); }
  #sidebar .navbar-nav .dropdown-menu li:first-child .aeca-dropdown-header { margin-top: 0px; }

}

/* ====================================================================================================
   FOOTER LAYOUT (rebuilt 2026-08-11, compacted 2026-09-02)
   ----------------------------------------------------------------------------------------------------
   The old grid put the logo alone in a col-lg-4 measuring 438x494, so a third of the footer was empty
   while three columns crowded the right. The brand column now carries the mark, a line of copy and the
   contact details, which is both a use for the space and the information the footer was missing.

   COMPACTION, 2026-09-02. The brief was to make the footer shorter without dropping anything from it.
   Measured at 1536px it was 802px: a 449px four-column row, a 185px proof band under it, 100px of the
   container's own padding and a 68px copyright bar. Only the first column used its 449px; the other
   three ran between 200 and 270px and left the rest as air, and the band existed to show three 72px
   chips and five link names.

   So nothing here is a type or spacing squeeze looking for pixels. The height came out of the LAYOUT:
   the band's two contents moved into the two columns that had room for them (see footer.php), which
   removes the band, its 1px rule and the 80px of separation around it in one move. What is left in
   this file is the handful of rules those two relocations need -- a two-across social list instead of
   a right-aligned row of five, a second heading inside a column, a two-column contact grid -- plus a
   smaller logo and a step down in the container padding.
   ==================================================================================================== */

.aeca-footer-main {
	row-gap: 40px;
}

/* `bloc-md` is 50px top and bottom and is already the smaller of the two steps Blocs offers here --
   the one below it is `bloc-sm` at 20px, which puts the logo almost against the section seam. 40px is
   between them. The copyright bar under this carries its own 20px, so the gap below the last line of
   the footer is 60px either way. */
#footer .container {
	padding-top: 40px;
	padding-bottom: 40px;
}

/* 132px at 2026-08-11, when this column had 449px to fill and the mark was the thing holding the
   left third together. The column is the tallest of the four either way, so the mark is what sets the
   footer's height; 104px still reads at arm's length and is the size the sub-lg rule already used. */
.aeca-footer-logo {
	width: 104px;
	height: auto;
	margin: 0 0 16px;
}

.aeca-footer-tagline {
	max-width: 38ch;
	margin: 0 0 24px;
	font-size: 14px;
	line-height: 1.65;
	color: rgba(255, 255, 255, 0.72);
}

/* A small caps label above each value, so address / phone / email stay scannable without needing an
   icon set the theme does not have.

   Two columns since 2026-09-02. Four values in single file ran 227px down the tallest column in the
   footer, and three of the four -- two phone numbers and an email -- are short enough to sit two
   across. The address is not, so its <li> carries `is-wide` and spans the pair. That turns four rows
   into three and takes about 55px off the column that sets the footer's height.

   At every width, not just lg. This column is 438px at lg, about 360px at md and the full 359px of a
   390px screen, because it drops to col-12 below md rather than getting narrower -- so half of it is
   never less than about 168px, and the longest value that has to fit one is the email address at
   roughly 155px. */
.aeca-footer-contact {
	margin: 0;
	padding: 0;
	list-style: none;
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 14px 24px;
}

.aeca-footer-contact .is-wide {
	grid-column: 1 / -1;
}

.aeca-footer-contact li {
	display: grid;
	gap: 2px;
	max-width: 34ch;
	font-size: 14px;
	line-height: 1.55;
}

.aeca-footer-contact__label {
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--aeca-accent);
}

/* Replaces a stack of <p class="mg-sm"> wrappers each ending in a <br>. Spacing is the list's job
   now, which is why the rows sit evenly. */
.aeca-footer-links {
	margin: 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: 11px;
}

.aeca-footer-links li {
	line-height: 1.4;
}

/* Two lists under one heading, 2026-09-03 -- the reference footer's "Customer Support" group. It is
   two `ul`s in a grid rather than one list with `columns`, because a CSS column break is free to fall
   anywhere and put four links on the left and six on the right; two lists divide 5 / 5 and stay
   divided. Below md they collapse to one column and read as a single ten-item list, which is what
   they were before this row existed. */
.aeca-footer-explore__lists {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 11px 24px;
}

/* --- the proof bar -------------------------------------------------------------------------------
   Certification marks and the legal links on one line, under the main row -- where the reference
   design puts its accreditation badge.

   .aeca-footer-band, the rule-separated 185px block this replaces, was removed on 2026-09-02 and is
   not coming back: everything here sits on ONE row about 54px tall, and the legal links are sharing
   it rather than costing a bar of their own. .aeca-footer-band__label went with the markup that used
   it, and the badges no longer carry a "Certified and awarded" caption -- three certification marks
   beside a privacy policy link do not need to be introduced.
   ---------------------------------------------------------------------------------------------- */

.aeca-footer-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 16px 32px;
	margin-top: 40px;
	padding-top: 26px;
	border-top: 1px solid rgba(255, 255, 255, 0.16);
}

/* The badges are dark artwork on white, so they need their own light chip rather than floating on the
   blue. A shared height keeps the portrait award badge in line with the two landscape marks. */
/* `width` is here to beat style.css, which sets `.footer-certs { width: 300px }` twice (once on its
   minified line 9, once as `width:100%; max-width:300px`). The row needs about 307px at the current
   chip height, so that 300px cap wrapped the third badge onto its own line -- and it had been
   invisible until then only because the smaller chips happened to fit inside it. `auto` rather than
   the `100%` it was until 2026-09-03: this now sits inside the proof bar's own space-between flex
   row, and a 100% item there takes the whole line and pushes the legal links onto a second one. */
.footer-certs {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
	width: auto;
	max-width: none;
	margin-left: 0;
	margin-right: 0;
}

/* Sized for artwork that is trimmed to its own edges (`img/cert-*-mark.*`). The chip is 72px tall
   with 6px of padding, so the mark itself renders 60px tall -- twice what the old 1200x800 canvas
   gave inside a 54px chip, where most of the box was the canvas's own white margin. Padding is
   even on all four sides now: the 7px/10px pair was compensating for a canvas that was wider than
   its content, and against trimmed art it just reads as a mark pushed off-centre.

   Widths are left to the aspect ratio: 47px for the portrait award, 72px and 127px for the two
   landscape certificates. That is the same normalisation the canvas used to bake in. */
.footer-certs img {
	width: auto;
	max-width: 100%;
	height: 72px;
	max-height: 72px;
	object-fit: contain;
	padding: 6px;
	border-radius: 6px;
	background: var(--aeca-white);
}

/* Social: a row of icon chips, 2026-09-03. It was five names two-across from 2026-09-02, which was
   the cheapest way to carry five links inside a text column; the reference footer gives the group a
   heading of its own and a row of marks under it, and at that point the names are the expensive part.
   Five 40px chips and their gaps come to 232px, so the row holds one line inside a 359px column on a
   390px screen.

   Each link keeps a visually-hidden name, so nothing is lost by dropping the visible word -- see the
   markup in footer.php. The glyph is Font Awesome 5 Brands, which is already loaded.

   The chip is a translucent white circle rather than each network's own colour: the theme's palette
   is two blues, and five brand colours in a 232px row would be the loudest thing in the footer. It
   fills to the accent blue on hover, which is the same affordance every other footer link has, drawn
   as a fill instead of an underline because there is no text here to underline. */
.aeca-footer-social {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin: 0 0 20px;
	padding: 0;
	list-style: none;
}

.aeca-footer-social__link {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 1px solid rgba(255, 255, 255, 0.28);
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.08);
	color: var(--aeca-white, #fff);
	font-size: 16px;
	line-height: 1;
	text-decoration: none;
	transition: background-color .18s ease, border-color .18s ease, color .18s ease;
}

.aeca-footer-social__link:hover,
.aeca-footer-social__link:focus-visible {
	border-color: var(--aeca-accent);
	background: var(--aeca-accent);
	color: var(--aeca-white, #fff);
}

/* The copyright line, and by 2026-09-03 nothing else: the legal links moved up into the proof bar
   and the small-print line that briefly sat under this -- registered address plus a specifications
   disclaimer, `.aeca-footer-smallprint` -- was removed at AECA's request along with its rule.

   The flex is kept rather than reverted to a bare block, because it is what lets a company
   registration number or a second line sit beside the copyright without revisiting this. */
.aeca-copyright {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: baseline;
	gap: 6px 24px;
}

/* --- below lg -------------------------------------------------------------------------------------
   The footer ran to 1389px on a 390px screen and 1410px at 768px. Most of that was desktop rhythm
   applied unchanged to a narrow column: a 30px column heading over 15px links, a 132px logo, and
   full-size gaps between four stacked blocks. Type and spacing step down here instead.
   ---------------------------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
	.aeca-footer-main {
		row-gap: 30px;
	}

	/* 30px is a page heading, not a column label, once the columns are stacked. */
	#footer h3 {
		font-size: 22px;
		padding-bottom: 10px;
	}

	#footer h3::after {
		width: 28px;
		height: 2px;
	}

	.aeca-footer-logo {
		width: 92px;
		margin-bottom: 14px;
	}

	.aeca-footer-tagline,
.aeca-footer-contact li {
		max-width: 46ch;
	}

	.aeca-footer-tagline {
		margin-bottom: 20px;
		font-size: 13.5px;
	}

	.aeca-footer-contact {
		gap: 12px;
	}

	.aeca-footer-bar {
		margin-top: 30px;
		padding-top: 22px;
	}

	/* At md this column sits on its own full-width row, so the lead would otherwise run the whole
	   734px in a single line. Was `.aeca-footer-form-col form` when a sign-up form stood here. */
	.aeca-footer-cta-col .aeca-footer-cta__lead {
		max-width: 520px;
	}
}

@media (max-width: 575.98px) {
	.aeca-footer-main {
		row-gap: 26px;
	}


	/* The award badge is portrait, so a shared height renders it narrower than the landscape marks.
	   62px keeps it legible without making the other two oversized. The three chips come to 236px
	   plus gaps at that height, so they still hold one line on a 320px screen. */
	.footer-certs img {
		height: 62px;
		max-height: 62px;
	}

	.footer-certs {
		gap: 10px;
	}

	/* space-between would push wrapped items to opposite ends of a narrow bar. */
	.aeca-copyright {
		justify-content: flex-start;
		gap: 4px;
	}

	.aeca-footer-bar {
		justify-content: flex-start;
	}

	/* One list below 576px. The column is about 359px on a 390px screen, so two lists get 167px each
	   and "Webinars & Events" wraps in every one of the five rows beside it. It holds two columns at
	   md and sm, where the column is 428px or more. */
	.aeca-footer-explore__lists {
		grid-template-columns: minmax(0, 1fr);
	}
}
/* ====================================================================================================
   HERO PANEL TYPE (2026-08-13)
   ----------------------------------------------------------------------------------------------------
   Pairs with the --aeca-glass-* alpha drop above. Two changes, and they depend on each other.

   1. Legibility. The panel used to be the contrast: white on a near-solid blue ground needed nothing
      else. At 0.60 -> 0.38 the ground is a tint, so the copy has to carry its own separation from
      whatever is behind it -- the city photograph on Industries, the drone video on home. A two-stop
      text-shadow does that: a wide soft one to lift the glyphs off busy detail, a tight one to keep
      the edges crisp. This is deliberately on the panels only. A shadow on body copy elsewhere on a
      flat ground would just look smudged.

      backdrop-filter is still not an option here, for the reason recorded twice already: one anywhere
      in the document re-composites every white surface on the page with a blue cast.

   2. Size. Everything in the panels comes down by about 15%. The sizes are the existing clamps
      multiplied through, not new numbers, so each one keeps the ramp it already had and the
      breakpoint steps still line up with the panel-width steps they were tuned against.

        home h1        clamp(48,54) -> clamp(41,46)     [and clamp(54,60) -> clamp(46,51) above 1600]
        home h1  <1200 clamp(32,42) -> clamp(27,36)
        about h1       clamp(60,76.8) -> clamp(51,65)
        svc/ind h1     clamp(67,86) -> clamp(57,73)
        svc/ind h4     clamp(23,28) -> clamp(20,24)
        leads/button   clamp(18,21.6) -> clamp(15,18)

      Body copy below 1200px is left alone. The hero paragraph's 16px there is a floor that was raised
      to 16px on purpose (see "Home body copy on a phone" above); taking 15% off it would undo that.
      Headings have no such floor, so they scale at every width.

   `!important` throughout because the generated type scale sets these classes at !important, and the
   1200px-and-up block near the end of this file is later in source order than any of it. Scoped to the
   four panels by id + panel class, so nothing here reaches the eighteen sub-page heroes that share
   `#services-hero` and are meant to stay a plain band over the photograph.
   ==================================================================================================== */

.about-hero-panel :is(h1,
h2,
h3,
h4,
p),
#hero-carousel .card-hero-carousel-style :is(h1,
h2,
h3,
h4,
p),
#services-hero .column-hero-solutions :is(h1,
h2,
h3,
h4),
#industries-hero .column-hero-solutions :is(h1,
h2,
h3,
h4) {
	text-shadow: 0 1px 14px rgba(0, 26, 56, 0.55), 0 1px 3px rgba(0, 26, 56, 0.45);
}

/* Home. */
#hero-carousel .h3-card-heading-style {
	font-size: clamp(27px, calc(20.49px + 1.726vw), 36px) !important;
}

/* ---- HOME HERO -- LONG HEADLINE ---------------------------------------------------------------
   The headline became "Empowering Industries with Smart Aerial & Geospatial Solutions" on
   2026-08-17: 61 characters against the 20 of the "Clear data. / Confident decisions." it replaced.
   Two things it inherited no longer suited it.

   Leading. `.h3-card-heading-style` is set `line-height: 0.875 !important` by the 1200px block near
   the end of this file. At 0.875 the lines overlap their own descenders, which nobody noticed while
   the headline was three authored short lines with a `<br>` between each. A sentence that wraps
   needs real leading, so 1.08 here -- still tight enough to read as a display face.

   Line breaks. The old copy carried its own `<br>`s. This one is one sentence, so it wraps, and
   left alone it broke 3 / 2 / 2 / 3 / 2 / 2 across 390 -> 2000px: ragged, and worst at 1440 where
   the panel is still 780px but the type had reached 46px. `text-wrap: balance` evens the two lines,
   and the caps below are set so the balanced longest line clears the panel's inner width at every
   step. Measured, not estimated -- see the per-width check in the session that built this.

   `!important` on font-size only, matching the rules it overrides. */
#hero-carousel .h3-card-heading-style {
	line-height: 1.08 !important;
	text-wrap: balance;
}

/* About, Services and Industries share `.const-page-title`, whose sub-1200px size is one clamp for
   all three. */
.about-hero-panel .const-page-title,
#services-hero .column-hero-solutions .const-page-title,
#industries-hero .column-hero-solutions .const-page-title {
	font-size: clamp(26px, 4.76vw, 60px) !important;
}

/* The Services / Industries sub-heading steps with the h4 sizes in style.css rather than clamping. */
#services-hero .column-hero-solutions h4,
#industries-hero .column-hero-solutions h4 {
	font-size: 20px !important;
}

@media (max-width: 991.98px) {
	#services-hero .column-hero-solutions h4,
#industries-hero .column-hero-solutions h4 {
		font-size: 18px !important;
	}
}

@media (max-width: 767.98px) {
	#services-hero .column-hero-solutions h4,
#industries-hero .column-hero-solutions h4 {
		font-size: 16px !important;
	}
}

@media (min-width: 1200px) {
	/* Cap 46 -> 43. The panel is fixed at 780px across this whole range but the type kept growing,
	   so from about 1350px the headline tipped to three lines and stayed there until 1600px, where
	   the panel steps to 860px and it snapped back to two. 43px is the largest that holds the
	   balanced longest line (681px) inside the 714px the panel gives it. */
	#hero-carousel .h3-card-heading-style {
		font-size: clamp(41px, calc(15.3px + 2.125vw), 43px) !important;
	}

	#hero-carousel .card-hero-carousel-style .p-hero-carousel-style,
#hero-carousel .card-hero-carousel-style .const-button {
		font-size: clamp(15px, calc(12.6px + 0.225vw), 18px) !important;
	}

	.about-hero-panel .const-page-title {
		font-size: clamp(51px, calc(38.4px + 1.05vw), 65px) !important;
	}

	.about-hero-panel .about-hero-lead {
		font-size: clamp(15px, calc(12.6px + 0.225vw), 18px) !important;
	}

	#services-hero .column-hero-solutions .const-page-title,
#industries-hero .column-hero-solutions .const-page-title {
		font-size: clamp(57px, calc(42.98px + 1.176vw), 73px) !important;
	}

	#services-hero .column-hero-solutions h4,
#industries-hero .column-hero-solutions h4 {
		font-size: clamp(20px, calc(15.41px + 0.345vw), 24px) !important;
	}
}

/* The panel is 860px from 1600px up, so the home headline's cap moves with it -- same relationship the
   pre-reduction pair had. Cap 51 -> 48: at 51 the balanced longest line came to 780px inside a 794px
   box, fourteen pixels of clearance, which is not enough to survive a copy edit or a font swap. */
@media (min-width: 1600px) {
	#hero-carousel .h3-card-heading-style {
		font-size: clamp(45px, calc(15.3px + 2.125vw), 48px) !important;
	}
}

/* The panel steps again to 940px at 2000px, so the headline is allowed one more step with it. This
   has to live here rather than in the `min-width: 2000px` block further up the file: that block is
   earlier in source than the 1600px rule above, and at equal specificity the later rule would win
   and pin the headline to 48px on the widest screens. */
@media (min-width: 2000px) {
	#hero-carousel .h3-card-heading-style {
		font-size: clamp(48px, calc(15.3px + 2.125vw), 52px) !important;
	}
}


/* Loading overlay
====================================================================================================

What Blocs shipped: a stock spinner GIF centred on flat cyan, held for a fixed two seconds by an
animation-delay and then faded. It said nothing about the company, and the two seconds were spent
whether the page needed them or not.

What is here instead: a sweep line crossing a field of survey points, each point flaring as the line
reaches it, with the mark held in the centre. It is a lidar pass, which is the work the company
actually does, and it is drawn entirely in CSS -- no GIF, no canvas, no library.

The one thing worth understanding before editing any number below is the phasing. The sweep and the
point flares are separate animations that share --aeca-preload-span as their period, and the flares
are delayed per column so that each point lights at the moment the line's bright edge reaches it.
That is what makes the sweep look like it is causing the flares rather than passing near them.

Solved for an 11-column grid over a 2.3s span, in gaps rather than pixels so the whole thing scales
off the single --aeca-preload-gap value:

	point centres      x = 0.5 + column                       (gaps)
	bar                0.9 wide, bright core 88% along it, so 0.792 from the bar's left edge
	travel             translateX -2.60 to 15.09, linear      (gaps)
	column delay       0.30s + (column * 0.13s)

Check either end and the two agree: at 0.30s the bar has moved 2.308 gaps, putting its core at 0.5,
which is column 0; at 1.60s it has moved 12.306, core at 10.498, which is column 10. Linear in
between, so every column in the middle lands too. Change the column count, the bar width, the core
position, the travel or the delay and all five have to be re-solved together.

Two consequences of those numbers worth not undoing by accident. The bar leaves the field at 1.77s
and the last flare finishes decaying at 2.15s, so the 2.3s span ends on an empty field rather than
cutting a tail off mid-decay. And the flare peaks 1% into its own timeline, which is 23ms, because
anything slower puts the peak visibly behind the line it is supposed to be answering.

The small per-row delay tilts the wavefront about 4.5 degrees off vertical; the skewX on the bar
matches that tilt, which is the only reason the bar is not upright.

The markup, the session check and the exit timing are in parts/head.php.

---------------------------------------------------------------------------------------------------- */

.page-preloader.aeca-preloader {
	/* One value drives the grid, the stage, the bar, the veil and the mark. It caps at 32px from
	   500px of viewport up and shrinks below that, which is the whole of the responsive handling. */
	--aeca-preload-gap: clamp(19px, 6.2vw, 32px);
	--aeca-preload-span: 2.3s;  /* one pass of the sweep; the flares are phased against it */

	position: fixed;
	inset: 0;
	width: 100%;
	height: 100%;
	z-index: 100000;
	display: flex;
	align-items: center;
	justify-content: center;

	/* Overrides the pageload-spinner.gif and the flat --swatch-var-3416 cyan from style.css. The
	   centre is lifted off the brand blue so the stage has something to sit in front of. */
	background: radial-gradient(120% 120% at 50% 42%,
		#0a5cb8 0%,
		var(--aeca-blue, #024DA1) 46%,
		#01356f 100%);
	background-color: var(--aeca-blue, #024DA1);

	opacity: 1;
	visibility: visible;
	transition: opacity .5s ease, visibility 0s linear .5s;

	/* The safety net, and the only reason this is an animation rather than a transition alone.
	   parts/head.php dismisses the overlay long before 3.2s in every normal case, but if that
	   script never runs -- JavaScript off, or it threw -- this is what stops an opaque screen
	   sitting over the site forever. It replaces style.css's preloader-fade, and because it is
	   in its delay phase until 3.2s with no backwards fill, it leaves the transition above
	   untouched up to that point. */
	animation: aeca-preload-bail .4s ease 3.2s forwards;
}

@keyframes aeca-preload-bail {
	to { opacity: 0; visibility: hidden; }
}

/* Exit. Driven by .is-done from the script under the markup; the node is then removed outright. */
.page-preloader.aeca-preloader.is-done {
	opacity: 0;
	visibility: hidden;
}


/* Stage
---------------------------------------------------------------------------------------------------- */

.aeca-preloader__stage {
	position: relative;
	width: calc(var(--aeca-preload-gap) * 11);
	height: calc(var(--aeca-preload-gap) * 7);
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;  /* clips the bar at the edge of the field, so it enters and leaves cleanly */
	transition: transform .5s cubic-bezier(.4, 0, .2, 1), filter .5s ease;
}

/* The stage pulls away as the overlay goes, so the exit is a departure rather than a dimmer. */
.aeca-preloader.is-done .aeca-preloader__stage {
	transform: scale(1.07);
	filter: blur(3px);
}

/* Suppresses the points immediately behind the mark. Without it the grid reads through the gaps in
   the logo's counters and the whole centre goes busy. Sized in gaps so it tracks the mark. */
.aeca-preloader__stage::after {
	content: "";
	position: absolute;
	inset: 0;
	background: radial-gradient(circle calc(var(--aeca-preload-gap) * 2.3) at 50% 50%,
		rgba(10, 92, 184, .88) 0%,
		rgba(10, 92, 184, .72) 55%,
		rgba(10, 92, 184, 0) 100%);
	pointer-events: none;
}


/* Point field
---------------------------------------------------------------------------------------------------- */

.aeca-preloader__field {
	position: absolute;
	inset: 0;
}

/* --c and --r come from the inline style on each point in parts/head.php. */
.aeca-preloader__field i {
	position: absolute;
	left: calc(var(--aeca-preload-gap) / 2 + var(--aeca-preload-gap) * var(--c));
	top: calc(var(--aeca-preload-gap) / 2 + var(--aeca-preload-gap) * var(--r));
	width: 3px;
	height: 3px;
	margin: -1.5px 0 0 -1.5px;  /* so left/top are the centre, which is what the phasing assumes */
	border-radius: 50%;
	background-color: rgba(255, 255, 255, .22);
	animation: aeca-preload-ping var(--aeca-preload-span) linear infinite;
	animation-delay: calc(.30s + var(--c) * .13s + var(--r) * .012s);
}

/* Near-instant up, unhurried down: a return the sweep has already moved past, not a pulse. The
   decay runs 552ms against a 130ms column step, so four or five columns are still glowing behind
   the line at any moment, which is the tail that makes it read as a pass over a surface. */
@keyframes aeca-preload-ping {
	0%, 100% {
		opacity: .22;
		transform: scale(1);
		background-color: rgba(255, 255, 255, .22);
		box-shadow: 0 0 0 0 rgba(22, 173, 238, 0);
	}
	1% {
		opacity: 1;
		transform: scale(2.8);
		background-color: #d6f3ff;
		box-shadow: 0 0 11px 2px rgba(22, 173, 238, .9);
	}
	6% {
		opacity: .8;
		transform: scale(1.45);
		background-color: var(--aeca-blue-light, #16adee);
		box-shadow: 0 0 7px 0 rgba(22, 173, 238, .5);
	}
	24% {
		opacity: .3;
		transform: scale(1.05);
		background-color: rgba(255, 255, 255, .3);
		box-shadow: 0 0 0 0 rgba(22, 173, 238, 0);
	}
}


/* Sweep line
---------------------------------------------------------------------------------------------------- */

.aeca-preloader__sweep {
	position: absolute;
	top: -8%;
	bottom: -8%;   /* overshoots the field so the skew never exposes a corner */
	left: 0;
	width: calc(var(--aeca-preload-gap) * .9);

	/* Mostly a trailing glow with one bright filament at the leading edge. Anything wider or
	   flatter stops being a line and becomes a translucent slab crossing the mark. */
	background: linear-gradient(90deg,
		rgba(22, 173, 238, 0) 0%,
		rgba(22, 173, 238, .22) 62%,
		rgba(150, 226, 255, .75) 84%,
		rgba(255, 255, 255, .95) 88%,   /* the bright core the flares are phased to */
		rgba(255, 255, 255, 0) 96%,
		rgba(255, 255, 255, 0) 100%);

	/* Without this the bar's clipped ends read as the two vertical sides of a rectangle. */
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 86%, transparent 100%);
	mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 86%, transparent 100%);

	animation: aeca-preload-sweep var(--aeca-preload-span) linear infinite;
	pointer-events: none;
}

@keyframes aeca-preload-sweep {
	from { transform: translateX(calc(var(--aeca-preload-gap) * -2.6)) skewX(-4.5deg); }
	to   { transform: translateX(calc(var(--aeca-preload-gap) * 15.09)) skewX(-4.5deg); }
}


/* Mark
---------------------------------------------------------------------------------------------------- */

.aeca-preloader__mark {
	position: relative;
	z-index: 2;  /* above the stage's veil */
	width: calc(var(--aeca-preload-gap) * 3.8);
	height: auto;
	animation: aeca-preload-mark-in .9s cubic-bezier(.22, .61, .36, 1) both;
}

/* It arrives once and then holds still. The sweep is carrying the motion; a mark that also breathed
   would put two unrelated rhythms in the same 224px. */
@keyframes aeca-preload-mark-in {
	from {
		opacity: 0;
		transform: scale(.86);
		filter: blur(7px) drop-shadow(0 6px 22px rgba(0, 0, 0, 0));
	}
	to {
		opacity: 1;
		transform: scale(1);
		filter: blur(0) drop-shadow(0 6px 22px rgba(0, 0, 0, .35));
	}
}


/* Second and later pages in a session
----------------------------------------------------------------------------------------------------

No overlay at all, not a shorter one.

This started as a ~260ms blue fade, on the reasoning that the ground was needed to cover the paint.
It is not. The browser already holds the previous page on screen until the next one is ready to
render, so there is nothing to cover -- and dropping an opaque blue screen over that hand-off was
adding a flash rather than hiding one. Once per session is a title card; on every click of a
thirty-page nav it is an interruption.

What carries the hand-off instead is the cross-document view transition further down this file, and
the navigation progress bar below it covers the one thing the overlay was genuinely doing on a slow
page, which is telling the visitor the click registered.

display:none rather than a JS-side guard alone, so the overlay never gets a frame: the class is on
<html> before the body is parsed. parts/head.php also removes the node outright.

---------------------------------------------------------------------------------------------------- */

html.aeca-preload-quick .page-preloader.aeca-preloader {
	display: none;
}


/* Reduced motion
----------------------------------------------------------------------------------------------------

The overlay itself stays -- it is covering an unpainted page, which is a job, not a decoration -- but
everything that moves inside it stops. The points hold lit, the mark is simply there, and the whole
thing fades. aeca-preload-bail is left alone: it only touches opacity, and it is the safety net.

---------------------------------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {

	.page-preloader.aeca-preloader {
		transition: opacity .25s linear, visibility 0s linear .25s;
	}

	.aeca-preloader__sweep {
		display: none;
	}

	.aeca-preloader__field i {
		animation: none;
		opacity: .28;
	}

	.aeca-preloader__mark {
		animation: none;
		opacity: 1;
		filter: drop-shadow(0 6px 22px rgba(0, 0, 0, .35));
	}

	.aeca-preloader.is-done .aeca-preloader__stage {
		transform: none;
		filter: none;
	}
}


/* Page-to-page transitions
====================================================================================================

The overlay above only runs once per session. Every navigation after it is carried by this instead.

`navigation: auto` opts the site into cross-document view transitions: on a same-origin navigation
the browser keeps the outgoing page on screen, waits for the incoming one to be ready to render,
then cross-fades between the two. No router, no fetch-and-swap, no JavaScript at all -- which is the
reason to do it this way rather than with a client-side navigation library. Both documents have to
opt in, and every page on this site shares this stylesheet, so they all do.

Browsers without it ignore the whole block and get an ordinary page load, exactly as before.

Two things this theme happens to make safe, both worth re-checking if either changes:

  - Nothing on the site uses Blocs' .animated / .hideMe scroll-reveal classes, so the incoming page
    is fully painted when its snapshot is taken. If reveal-on-load animations are ever added, they
    will run underneath the cross-fade and the new page will arrive mid-animation.
  - No element is given a view-transition-name. Naming the sticky nav was tempting, since it is
    identical on every page, but a plain root cross-fade between two identical navs is already
    indistinguishable from no transition at all, and a named element that changes position between
    pages slides across the screen instead. Not worth the failure mode for no visible gain.

---------------------------------------------------------------------------------------------------- */

@view-transition {
	navigation: auto;
}

/* Shorter than the 250ms default and weighted so the incoming page is most of what is on screen for
   most of the transition. A long cross-fade reads as a dissolve; this should read as a cut that
   someone softened. */
::view-transition-old(root) {
	animation: aeca-nav-fade-out 140ms cubic-bezier(.4, 0, 1, 1) both;
}

::view-transition-new(root) {
	animation: aeca-nav-fade-in 220ms cubic-bezier(0, 0, .2, 1) both;
}

@keyframes aeca-nav-fade-out {
	to { opacity: 0; }
}

@keyframes aeca-nav-fade-in {
	from { opacity: 0; }
}


/* Navigation progress bar
====================================================================================================

The consequence of the view transition is that the outgoing page stays on screen for the whole of
the next page's load. That is what makes it seamless, and it is also the one thing it makes worse:
click a nav item on a slow connection and nothing visibly happens at all. The old overlay was at
least proof the click had registered.

So: a hairline at the top of the outgoing page. It never completes, because the page it lives on is
about to be replaced -- it eases out towards 97% and creeps, which is the honest shape for a
progress indicator that cannot know its own progress.

It waits 220ms before showing. Most navigations on this site finish inside that, and a bar that
appears and vanishes inside a fifth of a second is worse than no bar.

Driven from js/aeca-custom.js.

---------------------------------------------------------------------------------------------------- */

.aeca-nav-progress {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 3px;
	z-index: 100001;  /* over the sticky nav, and over the loading overlay's 100000 */
	pointer-events: none;
	opacity: 0;
	transform: scaleX(0);
	transform-origin: 0 50%;
	background: linear-gradient(90deg,
		var(--aeca-blue, #024DA1) 0%,
		var(--aeca-blue-light, #16adee) 55%,
		#7fe0ff 100%);
}

.aeca-nav-progress.is-running {
	opacity: 1;
	/* Linear, with the shape carried entirely by the keyframe positions. An eased timing function
	   would be re-applied between every pair of stops rather than across the whole run, which makes
	   the curve much harder to reason about than it looks. Percentages against 12s: 2% is 240ms. */
	animation: aeca-nav-progress 12s linear forwards;
}

@keyframes aeca-nav-progress {
	0%   { transform: scaleX(0); }
	2%   { transform: scaleX(.28); }   /* 240ms  -- straight to a quarter, so it reads as a response */
	8%   { transform: scaleX(.55); }   /* 960ms  -- a typical navigation on this site ends near here */
	20%  { transform: scaleX(.75); }   /* 2.4s */
	50%  { transform: scaleX(.89); }   /* 6s */
	100% { transform: scaleX(.97); }   /* 12s    -- never 100%; only the next page can mean that */
}


/* Reduced motion: no cross-fade and no crawl. The navigation still happens, it just cuts, and the
   bar becomes a static presence rather than a moving one. A hard cut is the honest reading of
   "reduce motion" here -- there is no way to soften a page swap without animating something. */
@media (prefers-reduced-motion: reduce) {

	::view-transition-old(root),
::view-transition-new(root) {
		animation: none;
	}

	.aeca-nav-progress.is-running {
		animation: none;
		transform: scaleX(.4);
	}
}

/* Home page: the brief's "icons & short descriptions" on the four Core Services cards, plus the two
----------------------------------------------------------------------------------------------------
   sections it asks for that the page never had -- Case Studies & Testimonials, and a closing CTA.
   Added 2026-08-14 with the structure rebuild.

   The cards already carry per-position background art through .const-service-box-1..4, so everything
   here is additive: an icon above the title and a description under it, both sized to leave that
   artwork readable. */

/* Same pale disc as the About value cards and the solution rows, so the icon reads as one component
   across the site. The explicit width/height on .aeca-icon is required: its base rule is 68px and
   would otherwise burst the disc. */
.aeca-home-service__icon { display: flex; align-items: center; justify-content: center; width: 62px; height: 62px; margin-bottom: 1.15rem; border-radius: 50%; background: #eaf6fd; color: #024da1; }
.aeca-home-service__icon .aeca-icon { width: 32px; height: 32px; }
.aeca-home-service__body { max-width: 34ch; color: #405266; font-size: .97rem; line-height: 1.62; }

/* The "+" affordance is the card's only link, so it carries the accessible name -- a screen reader
   otherwise announces four links all called "+". The visually-hidden span supplies it; this keeps
   the glyph itself centred as before. */
.solutions-link-1 .visually-hidden,
.solutions-link-2 .visually-hidden,
.solutions-link-3 .visually-hidden,
.solutions-link-4 .visually-hidden { position: absolute; }

/* .aeca-home-stories went out with the home page's case-study row on 2026-09-02, and its five rules
   went with it -- an h3 size, two link states and a "more" link, none of which had another caller.
   .aeca-story-card itself stays: page-case-studies.php, parts/post-grid.php, single.php and
   functions/editorial.php all render one. Only the home-page variant is gone. */

/* The no-posts signpost card. It has no image where a real story card has one, so it takes the
   accent top-border of the .aeca-resource-card family instead -- that is the site's existing
   treatment for a card that is all text, and it stops the two cards reading as broken image cards
   sitting next to each other. Still rendered by the Case Studies archive; only the home page's copy
   of the pattern was removed. */
.aeca-story-card--signpost { border-top: 4px solid #16adee; box-shadow: none; }

/* Client ratings, 2026-09-02, and the quotes added 2026-09-03. The section was two quote cards from
   2026-08-28 until AECA supplied nine real ratings; it showed those ratings without their words for
   one day, and now shows all nine in full. See the note above the markup in index.php for what was
   decided about the Malay quotes, the one submitted in capitals and the two-word one. It used to sit
   between .aeca-home-stories (tinted) and the white .aeca-home-cta and took white to keep the
   tint/white alternation running. With the case-study row gone it is the first .aeca-* section on the
   page and follows #intro, which is the same off-white, so it takes the tint instead -- see the rule
   under HOME -- SECTION BANDING below.

   NO CARDS. This was a 3x3 grid of bordered, shadowed cards for about an hour on the day it was
   built, and measured 1002px -- 1.37 screens, and roughly double every other band on the page. A
   card is a container for a photograph and a paragraph; nine of them holding two short lines each
   were almost entirely container. What is left is nine plain lines in the same three columns, with
   no border, no background, no shadow and no padding, so the only height they take is the height of
   their own text.

   THE QUOTES DID NOT BRING THE CARDS BACK. AECA's second request was for the written feedback as
   well, without the section growing much. Nine quotes cannot be free, but everything that could be
   spent on the words rather than on containers has been: the quote is set at .95rem against the
   .93rem attribution, the line-height stays at 1.45, and the only new space in the block is the
   .35rem between the quote and the name under it. Nine ratings with their words now run about 700px
   against the 542px they took without them -- still well under the 1002px the card version needed
   with no quote text at all.

   The overrides below are scoped to .aeca-home-voices because .aeca-showcase-section and
   .aeca-section-heading are shared with page-case-studies, page-events, page-insights,
   page-products and parts/archive-loop.php, none of which asked to be shorter. */
.aeca-home-voices { padding: clamp(44px, 5.6vw, 74px) 0; }
.aeca-home-voices .aeca-section-heading { margin-bottom: clamp(20px, 2.4vw, 30px); }

/* One rating. The stars sit on their own line above the quote rather than inline with anything:
   inline, the longest attribution of the nine ("Senior Geospatial Scientist, SD Guthrie Research")
   wraps under the stars anyway at a third of the container, and a wrapped second line indented to
   nothing reads as a broken row. Stacked, all nine have the same shape whether they wrap or not. */
.aeca-rating-line { margin: 0; line-height: 1.45; }
.aeca-rating-line__stars { display: block; margin-bottom: .15rem; color: #16adee; font-size: .92rem; letter-spacing: .18em; }
.aeca-rating-line__stars .is-empty { color: #c3d4e6; }

/* The client's own words. Ink is the body colour rather than the muted grey the attribution takes,
   because the quote is the thing being read and the name under it is the credential.

   NOT ITALIC. Two of the nine are in Malay and one is a two-word phrase; italics on a language the
   reader may not be reading anyway buys emphasis at the cost of legibility, and the quotation marks
   already mark this as speech. They are written into the markup as characters rather than drawn with
   `::before`, so selecting the text copies them too. */
.aeca-rating-line__quote { display: block; margin-bottom: .35rem; color: #24384c; font-size: .95rem; }

.aeca-rating-line__who { display: block; color: #5c7085; font-size: .93rem; }
.aeca-rating-line__who strong { color: #024da1; font-weight: 600; }

/* A hairline above each line, not around it. It groups the nine as one set and keeps the columns
   reading down rather than across, which four sides of border did at ten times the visual weight.
   The Bootstrap gutter supplies the space under it, so the rule needs padding only above.

   `align-items: start` matters now that the quotes are different lengths: without it every column in
   a Bootstrap row stretches to the tallest in that row, and the SALCRA quote would set the height of
   the two beside it. */
.aeca-rating-list { align-items: start; }
.aeca-rating-list .col-12 > .aeca-rating-line { padding-top: .7rem; border-top: 1px solid #dbe6f0; }

/* Three actions rather than the two parts/industry-cta.php carries, so they are allowed to wrap
   before the panel does. */
.aeca-home-cta .aeca-action-panel__actions { max-width: 100%; }
.aeca-faq__more { margin: 1.5rem 0 0; }

/* Consecutive FAQ sections on /faqs would otherwise run together as one undifferentiated white
   column three groups long. Banding them makes the three topics read as three groups. */
.aeca-faq:nth-of-type(even) { background: #f1f6fa; }

@media (max-width: 767.98px) {
	.aeca-home-service__body { max-width: none; }
	.aeca-home-service__icon { width: 52px; height: 52px; }
	.aeca-home-service__icon .aeca-icon,
.aeca-value-card__icon .aeca-icon { width: 27px; height: 27px; }
	.aeca-value-card__icon { width: 52px; height: 52px; }
}

/* Footer: the brief's newsletter capture and legal row.
----------------------------------------------------------------------------------------------------
   .aeca-copyright is already a space-between flex row, so the legal list becomes its second child
   and sits opposite the copyright line with no layout change. Below lg it wraps underneath, which
   that container's existing flex-wrap already handles. */

/* Was .aeca-footer-newsletter__lead until 2026-08-20; same treatment, and no max-width here because
   the col-lg-4 already bounds it. The md rule above caps it for the full-width row. */
.aeca-footer-cta__lead { margin-bottom: 1.05rem; font-size: .92rem; line-height: 1.55; opacity: .85; }

.aeca-legal-links { display: flex; flex-wrap: wrap; gap: 6px 20px; margin: 0; padding: 0; list-style: none; }
.aeca-legal-links a { text-decoration: none; }
.aeca-legal-links a:hover { text-decoration: underline; }

/* About: Core Values, Team & Leadership, Certifications & Recognitions.
----------------------------------------------------------------------------------------------------
   Three sections the brief names that the page did not have.

   The card shell is the .aeca-resource-card treatment -- 1px border, 4px accent top edge, no shadow
   -- rather than a new one, so About introduces no fourth card style. The accent edge is what marks
   a text-led card across this site; the drop shadow belongs to cards that lead with a photograph.

   The three sections band white / tinted / white so they read as three sections rather than one
   long white column, matching the rhythm the Industries hub already uses. */

.aeca-value-card,
.aeca-person-card,
.aeca-cert-card { height: 100%; padding: clamp(22px, 2.6vw, 30px); border: 1px solid #dce7ef; border-top: 4px solid #16adee; background: #fff; }
.aeca-value-card h3,
.aeca-person-card h3,
.aeca-cert-card h3 { margin: 0 0 .6rem; color: #024da1; font-size: clamp(1.15rem, 1.6vw, 1.35rem); line-height: 1.25; }
.aeca-value-card p,
.aeca-person-card p,
.aeca-cert-card p { margin: 0; color: #405266; font-size: .95rem; line-height: 1.62; }

.aeca-about-team { background: #f1f6fa; }
.aeca-about-team .aeca-value-card { background: #fff; }

/* The icon sits in the same pale disc the solution rows use, so a value card and a solution row
   read as the same component family seen at two sizes. */
.aeca-value-card__icon { display: flex; align-items: center; justify-content: center; width: 62px; height: 62px; margin-bottom: 1.15rem; border-radius: 50%; background: #eaf6fd; color: #024da1; }
.aeca-value-card__icon .aeca-icon { width: 32px; height: 32px; }

.aeca-person-card img { width: 100%; height: 260px; margin-bottom: 1rem; object-fit: cover; }
.aeca-person-card__role { margin-bottom: .6rem !important; font-weight: 700; color: #16adee !important; }

/* The three certificates are different shapes -- two landscape scans and one tall award portrait --
   so the frame is fixed and the artwork is contained inside it rather than cropped. The tinted
   ground stops the two scans, which carry their own white background, from dissolving into the
   card. */
.aeca-cert-card__art { display: flex; align-items: center; justify-content: center; height: 168px; margin-bottom: 1.25rem; padding: 12px; border-radius: 4px; background: #f6fafd; }
.aeca-cert-card__art img { max-width: 100%; max-height: 144px; width: auto; height: auto; object-fit: contain; }

@media (max-width: 767.98px) {
	.aeca-cert-card__art { height: 140px; }
	.aeca-cert-card__art img { max-height: 116px; }
}

/* Home "What We Do": heading row carries the section's own action; seven compact tiles under it.
----------------------------------------------------------------------------------------------------
   Reworked 2026-08-17. Three things made this section 1,245px tall for what is seven short links:
   tiles with a 285px floor and 1.75rem of padding, an "Explore service" line repeated on every one
   of them, and a trailing row that existed only to hold the button.

   The button is now beside the heading -- where the request wanted it, on the right, and where it
   costs no vertical space. The repeated link became the title's stretched link. The floor is gone,
   so a tile is as tall as its content.

   Four tiles divide evenly at every breakpoint the grid uses -- four up from 992px, two by two from
   576px, stacked below -- so no row ever ends short. `justify-content: center` is kept anyway: it
   costs nothing while the count divides, and it is what stops a short row reading as a hole if the
   list ever grows past four again. */

/* The container carries `bloc-no-padding-lg`, so from 992px up the section had no vertical padding
   at all -- the heading sat on its top edge and the grid on its bottom one. That went unnoticed
   while the trailing button row and the tall tiles stood in for the padding; with both gone the
   last row of tiles ran straight into the next section's shape divider. Real padding now, rather
   than height borrowed from the content. */
#bloc-6 > .container {
	padding-top: 3rem;
	padding-bottom: 4.5rem;
}

@media (max-width: 991.98px) {
	#bloc-6 > .container { padding-top: 2.25rem; padding-bottom: 3rem; }
}

/* On top of that padding the heading was adding 100px of its own, so the hero handed over to 148px of
   empty off-white before "What We Do" appeared. The markup already asks for no margin -- the h2 is
   `mg-clear` -- but `.const-section-subtitle{margin-top:100px}` sits after `.mg-clear{margin:0}` in
   style.css and they are both (0,1,0), so source order decided it. This honours what the markup asked
   for and leaves the section's top space to the 3rem the container above declares.

   Scoped to #bloc-6. `.const-section-subtitle` also heads "Industries We Serve", "Our Partners" and
   "Our Stories", and those keep their 100px -- each of them opens against a section that needs the
   separation, where this one opens against the hero. */
#bloc-6 .const-section-subtitle { margin-top: 0; }

#bloc-6 .aeca-home-services-head {
	display: flex;
	flex-wrap: wrap;
	gap: 1.25rem 2.5rem;
	align-items: flex-end;
	justify-content: space-between;
}

#bloc-6 .aeca-home-services-head__lead {
	max-width: 56ch;
	margin: .7rem 0 0;
	color: #405266;
	font-size: 1rem;
	line-height: 1.55;
}

/* A size down from the btn-lg it was. Beside a heading this is a secondary route into the section,
   not the page's main call, and btn-lg next to the heading would read as the louder of the two. */
#bloc-6 .aeca-home-services-head__cta {
	flex: 0 0 auto;
	padding: .7rem 1.5rem;
	white-space: nowrap;
}

#bloc-6 .aeca-home-services-head__cta span {
	display: inline-block;
	margin-left: .5rem;
	transition: transform .2s ease;
}

#bloc-6 .aeca-home-services-head__cta:hover span {
	transform: translateX(3px);
}

#bloc-6 .aeca-home-services-grid {
	--bs-gutter-y: 1.5rem;
	margin-top: 2rem;
	justify-content: center;
}

/* Equal-height tiles: the column is the flex line and the card fills it. Scoped to the grid -- as
   `#bloc-6 .row > [class*="col"]` this also caught the button's own row and shrank that column to
   its content, which is why `justify-content: flex-end` on the old button wrapper moved nothing. */
#bloc-6 .aeca-home-services-grid > [class*="col"] {
	display: flex;
}

/* HOW THE LAST LINE CLOSES. Three across on a wide screen and two on a tablet, so the seven
   solutions leave one card alone on the last line at both widths. `justify-content: center` above
   centres it under the lines it follows, at the same width as every other card.

   Deliberately not the close /solutions uses. .aeca-service-grid widens its stranded card to fill
   the line and relays it horizontally, driven by `container-type: inline-size` on the card. That was
   tried here on 2026-08-20 and reverted: the card is a `width: 100%` flex item rather than a grid
   item with a definite column span, so its width is not settled before the query is asked. Chrome
   wedged during that test and the cause was never isolated -- it may have been the query or it may
   have been the test itself -- so treat `container-type` on .aeca-home-service-card as unproven
   rather than merely unused, and test it in a throwaway profile if it is tried again. */

@media (max-width: 767.98px) {
	#bloc-6 .aeca-home-services-head { align-items: flex-start; }
	#bloc-6 .aeca-home-services-head__lead { font-size: .95rem; }
}

/* About page */
.c-about { color: #18324c; }
/* The family is deliberately absent: this is a <p>, and the one Inter rule at the top of this file
   claims every <p> with !important, so naming a family here (it named the static "Outfit-Medium"
   face) only made the declaration read as something it never was. Set as longhands so the shorthand
   cannot silently reset the family again. Matches .aeca-eyebrow, which is Inter on the same terms. */
.c-about__eyebrow { margin: 0 0 .65rem; color: #024da1; font-weight: 700; font-size: .8rem; line-height: 1; letter-spacing: .12em; text-transform: uppercase; }
.c-about__heading { margin-bottom: 2.25rem; }
.c-about__heading h2,
.c-about-overview h2,
.c-cta-strip h2 { color: #024da1; }
.c-about-overview__hero { position: relative; display: grid; min-height: 430px; place-items: center; overflow: hidden; background: #024da1; color: #fff; isolation: isolate; }
.c-about-overview__hero::after { position: absolute; inset: 0; z-index: -1; background: rgba(2, 35, 75, .66); content: ''; }
.c-about-overview__video { position: absolute; z-index: -2; width: 100%; height: 100%; object-fit: cover; }
.c-about-overview__hero-content { padding: 5rem 0; }
.c-about-overview__hero h1 { margin: 0; color: #fff; }
.c-about-overview__subtitle { max-width: 54ch; margin: 1rem 0 0; color: #fff; font-size: 1.2rem; }
.c-about-overview__content,
.c-about-purpose,
.c-about-values,
.c-about-team,
.c-about-certifications { padding: 5rem 20px; }
.c-about-overview__mission { margin-top: 4rem; }
.c-about-overview__image { width: 100%; height: auto; box-shadow: 0 16px 34px rgba(2, 77, 161, .16); }
/* Vision / mission / objective. A dark band between the white overview and the light values
   section, so the three statements read as the page's centre of gravity rather than a third
   card grid. Cards are translucent white so they lift off the blue without a second colour. */
.c-about-purpose { background: linear-gradient(135deg, #024da1 0%, #01356f 100%); color: #fff; }
.c-about-purpose__heading h2,
.c-about-purpose .c-about__eyebrow { color: #fff; }
.c-about-purpose .c-about__eyebrow { opacity: .8; }
.c-about-purpose__card { height: 100%; padding: 2rem 1.75rem; background: rgba(255, 255, 255, .08); border-top: 4px solid #fff; }
.c-about-purpose__number { display: block; margin-bottom: .9rem; color: #fff; font-weight: 700; font-size: 1.75rem; line-height: 1; font-family: var(--aeca-font-head); opacity: .45; }
.c-about-purpose__card h3 { margin-bottom: .75rem; color: #fff; }
.c-about-purpose__card p { margin-bottom: 0; color: #fff; }
.c-about-values { background: #f2f7fb; }
.c-about-values__card,
.c-about-team__card,
.c-about-certifications__card { height: 100%; padding: 1.75rem; background: #fff; border-top: 4px solid #024da1; box-shadow: 0 10px 24px rgba(2, 77, 161, .1); }
/* The team card is the one card whose image runs to the edges, so its padding moves to the body. */
.c-about-team__card { display: flex; max-width: 300px; margin-inline: auto; padding: 0; flex-direction: column; overflow: hidden; }
.c-about-values__icon { display: flex; width: 58px; height: 58px; margin-bottom: 1.2rem; align-items: center; justify-content: center; border-radius: 50%; background: #eaf6fd; color: #024da1; }
.c-about-values__icon .aeca-icon { width: 30px; height: 30px; }
/* The four value marks are artwork now and carry their own disc, so they fill the plate rather
   than sitting at 30px inside it. The #eaf6fd plate stays: the baked disc is a low-alpha wash. */
.c-about-values__icon .aeca-icon--art { width: 58px; height: 58px; }
.c-about-team { background: #fff; }
.c-about-team__photo { display: block; width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; object-position: top center; background: #eaf1f7; }
.c-about-team__body { padding: 1.5rem 1.75rem 1.75rem; }
.c-about-team__body h3 { margin-bottom: .35rem; }
.c-about-team__role { margin-bottom: 0; color: #024da1; font-weight: 600; }
/* Circular monogram, for a profile added in the site settings with no photo and for the empty state. */
.c-about-team__avatar { display: grid; width: 92px; height: 92px; place-items: center; border-radius: 50%; background: #024da1; color: #fff; font-weight: 700; font-size: 1rem; font-family: var(--aeca-font-head); }
.c-about-team__card > .c-about-team__avatar { margin: 1.75rem 1.75rem 0; }
.c-about-team__empty { display: flex; gap: 1rem; align-items: center; padding: 1.75rem; border: 1px solid #d7e1ea; }
.c-about-team__empty .c-about-team__avatar { flex: 0 0 auto; margin: 0; }
.c-about-certifications { background: #f2f7fb; }
.c-about-certifications__badge { display: grid; min-height: 145px; margin-bottom: 1.25rem; place-items: center; }
.c-about-certifications__badge img { max-width: 170px; max-height: 130px; object-fit: contain; }
.c-cta-strip { padding: 3.5rem 20px; background: #024da1; color: #fff; }
.c-cta-strip__inner { display: flex; gap: 2rem; align-items: center; justify-content: space-between; }
.c-cta-strip h2,
.c-cta-strip .c-about__eyebrow { color: #fff; }
.c-cta-strip p { margin-bottom: 0; }
@media (max-width: 767px) { .c-about-overview__content,
.c-about-purpose,
.c-about-values,
.c-about-team,
.c-about-certifications { padding-top: 3.5rem; padding-bottom: 3.5rem; } .c-cta-strip__inner { display: block; } .c-cta-strip .btn { margin-top: 1.5rem; } }
/* The tile. Rebuilt 2026-08-18 to the reference card AECA sent: a white card inside a thin blue
   outline with rounded corners, everything centred on one axis, and nothing in it but the icon, an
   uppercase title and a short descriptor. Four things left with the old card because that design
   has none of them -- the navy top cap, the resting drop shadow, the ghosted numeral and the arrow
   at the foot. Only the arrow was load-bearing, and it pointed at a click target the whole card
   already is through `stretched-link` on the title.

   Rounded, in a theme of square corners (`.card-sq`, `.btn-sq`): the radius is the reference's, and
   `card-sq` is off the card in index.php rather than overridden here. If this card ever has to go
   back to the house style, `border-radius` below is the one line to change.

   No min-height: with equal-height columns the tallest tile in a row sets the rest, and a floor on
   top of that only added empty space under the short ones.

   The pale disc behind the icon stays, and it is not this file's to remove: it is baked into
   img/solutions/*.png, so three of these four icons carry it whatever the CSS says. The disc drawn
   here (base rule further up) is what gives the fourth -- drone-sensor, the only one of the four
   with no artwork, rendered as inline SVG -- the same ground. See functions/icons.php. */
#bloc-6 .aeca-home-service-card { position: relative; width: 100%; background: #fff; border: 1.5px solid #1c62b5; border-radius: 14px; box-shadow: none; transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease; }
#bloc-6 .aeca-home-service-card:hover,
#bloc-6 .aeca-home-service-card:focus-within { transform: translateY(-3px); border-color: #024da1; box-shadow: 0 10px 24px rgba(2, 77, 161, .14); }
#bloc-6 .aeca-home-service-card .card-body { display: flex; flex-direction: column; align-items: center; padding: 1.9rem 1.35rem 1.75rem; text-align: center; }
#bloc-6 .aeca-home-service__icon { width: 64px; height: 64px; margin-bottom: 1.05rem; }
#bloc-6 .aeca-home-service__icon .aeca-icon { width: 34px; height: 34px; }
/* The artwork is its own disc, so it fills the icon box instead of sitting inside the CSS one --
   at 34px it would have drawn a small disc floating in a larger one. */
#bloc-6 .aeca-home-service__icon .aeca-icon--art { width: 64px; height: 64px; }
#bloc-6 .aeca-home-service-card h3 { margin: 0 0 .4rem; font-size: 1rem; line-height: 1.3; letter-spacing: .02em; text-transform: uppercase; }
#bloc-6 .aeca-home-service-card__title { color: #024da1; }
#bloc-6 .aeca-home-service-card__title:hover { text-decoration: underline; }
/* margin-top:auto keeps the descriptors of a row on one baseline when a title wraps to two lines in
   one card and one in the next. */
#bloc-6 .aeca-home-service__body { max-width: none; margin: auto 0 0; color: #1c62b5; font-size: .92rem; line-height: 1.5; }
@media (prefers-reduced-motion: reduce) {
	#bloc-6 .aeca-home-service-card,
#bloc-6 .aeca-home-services-head__cta span { transition: none; }
	#bloc-6 .aeca-home-service-card:hover,
#bloc-6 .aeca-home-service-card:focus-within,
#bloc-6 .aeca-home-services-head__cta:hover span { transform: none; }
}

/* Services hub: the seven service cards
----------------------------------------------------------------------------------------------------

Built 2026-08-17 for page-services.php, replacing the Blocs `.const-shortcut-card` row it used to
render. Three things were wrong with that row and only the last is cosmetic:

	1. `.const-shortcuts` in style.css carries `margin-top: -100px` above 991px. That was the home
	   page's pull, deliberately climbing the shortcut row into the hero, and `#shortcuts
	   .const-shortcuts { margin-top: 0 }` further up this file released it there. The home section
	   was rebuilt on 2026-08-17 onto `.aeca-home-service-card` and stopped using those classes, so
	   the services hub inherited a pull written for a section it has nothing to do with. It was
	   dragging the cards up over the "Our Services" heading, which never appeared on the page.
	2. Seven cards in `col-lg-3` is 4 + 3, leaving a card-sized hole at the end of the grid.
	3. Card and section were both `bgc-5877`, so only the drop shadow separated them.

This is the home page's card system at hub scale: same navy cap, same ghosted numeral, same arrow
pinned to the foot, larger type and more air because this is the page those cards are the content of
rather than a summary on the way past.

THE GRID IS TWELVE COLUMNS WIDE, NOT FOUR. A card spans three of them on a wide screen and six
between 768px and 1199px, which is the ordinary 4-up and 2-up. Twelve is the base so that a stranded
card can take a quarter, a third, a half or a whole row -- four columns could only express the whole
row, and a set that strands two or three cards needs the fractions in between.

Four across on the desktop, not three, since 2026-09-07: AECA asked for the eight solutions as one
4 x 2 block, because three across left the last two cards sitting alone under a full row. Eight
divides evenly by four, so the tail rules below currently do nothing at all -- they are there for
the ninth solution, not for today.

The template hands the remainder over as two classes rather than marking one card wide, because
which card is stranded depends on the breakpoint, and whether any card is stranded depends on how
many services there are. `--tail4-N` is count % 4 and applies to the 4-up; `--tail2-N` is count % 2
and applies to the 2-up.

A widened card must not read as a normal card stretched sideways, so it relays itself: icon, then
heading, then body and arrow, across the width. That switch is a container query on the card rather
than another breakpoint, so it fires on the card's own measurement and cannot fall out of step with
whichever tail rule widened it. */

.aeca-services-hub {
	padding: clamp(56px, 7vw, 104px) 0;
	background: var(--swatch-var-5877, #f9fbf7);
}

.aeca-service-grid {
	display: grid;
	grid-template-columns: repeat(12, minmax(0, 1fr));
	gap: clamp(1rem, 1.6vw, 1.6rem);
}

.aeca-service-card {
	position: relative;
	grid-column: span 12;
	container-type: inline-size;
	padding: clamp(1.5rem, 2.2vw, 2.1rem) clamp(1.4rem, 2vw, 2rem) clamp(1.4rem, 2vw, 1.9rem);
	border-top: 4px solid #024da1;
	background: #fff;
	box-shadow: 0 8px 20px rgba(2, 77, 161, .1);
	transition: transform .2s ease, box-shadow .2s ease, border-top-color .2s ease;
}

.aeca-service-card:hover,
.aeca-service-card:focus-within {
	transform: translateY(-4px);
	border-top-color: #16adee;
	box-shadow: 0 14px 30px rgba(2, 77, 161, .18);
}

/* Stacked by default. `height: 100%` plus the `margin-top: auto` on the arrow is what lines the
   affordances of a row up on one edge when the copy above them is unequal. */
.aeca-service-card__inner {
	display: flex;
	flex-direction: column;
	height: 100%;
}

.aeca-service-card__icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 58px;
	height: 58px;
	margin-bottom: 1.15rem;
	border-radius: 50%;
	background: #eaf6fd;
	color: #024da1;
}

.aeca-service-card__icon .aeca-icon {
	width: 30px;
	height: 30px;
}

.aeca-service-card__heading {
	margin: 0 0 .6rem;
	/* Room for the numeral parked in the top corner, so a three-line title cannot run under it. */
	padding-right: 2.75rem;
	font-size: clamp(1.15rem, 1.05rem + .35vw, 1.4rem);
	line-height: 1.24;
}

.aeca-service-card__title {
	color: #024da1;
}

.aeca-service-card__title:hover {
	text-decoration: underline;
}

.aeca-service-card__foot {
	display: flex;
	flex: 1;
	flex-direction: column;
}

.aeca-service-card__body {
	margin: 0;
	color: #405266;
	font-size: .95rem;
	line-height: 1.6;
}

.aeca-service-card__go {
	margin-top: auto;
	padding-top: 1rem;
	color: #024da1;
	font-size: 1.15rem;
	line-height: 1;
	transition: transform .2s ease;
}

.aeca-service-card:hover .aeca-service-card__go {
	transform: translateX(4px);
}

/* The numeral sits outside `__inner` so the relayout below cannot make it a grid item. Same ghosted
   treatment as the home cards: it orders the set at a glance without competing with the title. */
.aeca-service-card__num {
	position: absolute;
	top: clamp(1.5rem, 2.2vw, 2.1rem);
	right: clamp(1.4rem, 2vw, 2rem);
	color: #c8d8e6;
	font-family: var(--aeca-font-head);
	font-weight: 700;
	font-size: 1.5rem;
	line-height: 1;
}

/* Two across from 768px, four from 1200px. */
@media (min-width: 768px) {
	.aeca-service-card { grid-column: span 6; }
	.aeca-service-grid--tail2-1 > .aeca-service-card:last-child { grid-column: span 12; }
}

@media (min-width: 1200px) {
	.aeca-service-card { grid-column: span 3; }
	.aeca-service-grid--tail4-1 > .aeca-service-card:last-child { grid-column: span 12; }
	.aeca-service-grid--tail4-2 > .aeca-service-card:nth-last-child(-n+2) { grid-column: span 6; }
	.aeca-service-grid--tail4-3 > .aeca-service-card:nth-last-child(-n+3) { grid-column: span 4; }
}

/* The widened card, relaid on its own width rather than the viewport's. 640px is comfortably above
   the widest a 3-up card reaches (a 1180px container gives roughly 380px) and comfortably below the
   full-width card at the smallest size it can occur, so only a card that has actually been widened
   picks this up. */
@container (min-width: 640px) {
	.aeca-service-card__inner {
		display: grid;
		/* Both text tracks are flexible, in proportion. A fixed heading track would take its full
		   width first and leave the body whatever was left, which on the narrower of the two widths
		   this layout runs at wrapped a one-line summary into four. */
		grid-template-columns: auto minmax(0, 1fr) minmax(0, 1.35fr);
		align-items: center;
		column-gap: clamp(1.5rem, 3vw, 3.25rem);
	}

	.aeca-service-card__icon { margin-bottom: 0; }
	.aeca-service-card__heading { margin: 0; padding-right: 0; }

	/* The numeral is parked in the same top corner the arrow now ends in, so the foot gives up the
	   width the narrow card's heading gives up instead. Without it the arrow sits under the 07. */
	.aeca-service-card__foot {
		flex: 0 1 auto;
		flex-direction: row;
		gap: 1.5rem;
		align-items: center;
		justify-content: space-between;
		padding-right: 2.75rem;
	}

	.aeca-service-card__go { margin-top: 0; padding-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
	.aeca-service-card,
.aeca-service-card__go { transition: none; }
	.aeca-service-card:hover,
.aeca-service-card:focus-within,
.aeca-service-card:hover .aeca-service-card__go { transform: none; }
}


/* ====================================================================================================
   HOME -- SEAMS AROUND THE STATS PHOTO
   ----------------------------------------------------------------------------------------------------
   The stats photo carried two Blocs shape dividers, both filled #F7F7F7 -- a white that matches
   nothing else on the page -- so both read as opaque grey slabs lying across the photograph rather
   than as a transition. The top one lay across the stat figures; the bottom one put a light stripe
   between the photo and the navy "Industries" section, the one place on the page where the eye had to
   cross a light band to get from one dark section to the next.

   The two seams are treated differently, by decision:

   TOP. The divider is switched off, and since 2026-08-19 the seam is carried by a haze instead.
   The hard horizontal edge it left behind was the harshest join on the page: #F9FBF7 at 98% white
   meeting a treeline that sits around 20% after the section's 50% black, with no ramp between them.
   The haze is a two-part ramp in one colour, #E8EFF4, and it is described under the rules below.
   TOP was also the reason a gradient was tried on the hero above it. That was rejected; the hero
   keeps its hard edge.

   FOOT. The divider stays, refilled navy and masked so it dissolves as it climbs, which lets the
   "Industries" colour rise out of the photograph. A fade works here where it failed at the top
   because it is dark-on-dark: it deepens the image instead of washing it out.

   No section background is touched -- every section keeps its flat colour, only the joins change --
   and nothing from the foot of "Industries" down is altered. Every rule is keyed on `body.home`, and
   the three divider fill classes touched here appear in index.php and nowhere else in the theme.

   ONE COLOUR NOTE the values below depend on. `.bloc-bg-texture::before` paints a black noise PNG
   averaging 24% alpha over #hero-carousel, #stats-image and #projects-shortcuts. It is a positioned
   child, so it covers its own section but NOT a divider belonging to the section above -- that
   divider lives in a different element and paints over its own section's texture. A divider that has
   to match a textured section therefore has to be filled with that section's colour already
   multiplied by the 24%, not with the colour itself.
   ==================================================================================================== */

/* The wedge at the foot of the stats photo. It is `.bloc-divider-bottom .bloc-divider-flip-x`, so the
   SVG's `transform: scale(-1, 1)` mirrors it horizontally but leaves the vertical axis alone: `to
   top` below is solid along the bottom edge, the edge that meets "Industries We Serve", and thins out
   as the wedge climbs into the photograph. */
body.home .bloc-divider-b-style {
	-webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, .42) 30%, rgba(0, 0, 0, 0) 72%);
	        mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, .42) 30%, rgba(0, 0, 0, 0) 72%);
	-webkit-mask-repeat: no-repeat;
	        mask-repeat: no-repeat;
	-webkit-mask-size: 100% 100%;
	        mask-size: 100% 100%;
}

/* #013A7A is #projects-shortcuts' #024DA1 under its 24% black paper texture, per the colour note
   above; filling this with #024DA1 itself left a visible horizontal step exactly on the seam. */
body.home .bloc-divider-b-style { fill: #013a7a; }

/* Top of the stats photo. The wedge stays switched off -- the seam is a straight line now, softened
   by the two ramps below rather than by a shape. */
body.home .bloc-divider-t-fill { fill: transparent; }


/* THE HAZE. Two ramps in #E8EFF4, meeting edge to edge at the seam so the join itself has no step in
   it: the lower half of "What We Do" darkens from its own off-white down to #E8EFF4 at the very last
   pixel, and the photograph starts under a full-strength #E8EFF4 that thins out as it descends. The
   eye crosses roughly 290px of continuous ramp instead of one hard line.

   Why #E8EFF4 and not #F9FBF7, the section's own colour: pure off-white sitting over a dark canopy
   reads as a blown-out patch. A light blue-grey reads as aerial haze, which is what a drone photograph
   of a treeline actually has in it, and it carries a hint of the brand blue rather than a warm cast.

   Both ramps are eased rather than linear. A linear alpha ramp against a photograph shows its two ends
   as faint lines, because the rate of change jumps at each end; these hold near their start value for
   the first few percent and tail off to nothing over the last quarter, so neither end has an edge.

   BOTH HEIGHTS ARE MEASURED, NOT CHOSEN, and they are why the 992px breakpoint below exists. Each
   pseudo-element paints above its section's in-flow content, so a ramp that is too tall hazes over
   something it should not:

     .. #bloc-6::after is exactly the clear space under the last row of service cards -- 72px at lg,
        48px below it -- so the ramp reaches zero alpha precisely at the cards' bottom edge.
     .. #stats-image::after has to finish before the first "More Than". RE-MEASURED 2026-09-02, when
        that section went to one row: the heading used to sit 493px down, behind 200px of Blocs
        padding and a 100px column staircase, and 220px of ramp cleared it easily. Both are gone, so
        the heading now sits at the section's top padding exactly -- 108px at lg, 76px below 992px,
        60px below 576px -- and the ramp is set 8px inside each of those.

        The top padding is 108px rather than the 84px the rest of the section needs, and it is the
        ramp that buys the extra 24px: below about 90px the gradient stops reading as haze and starts
        reading as a band. The bottom padding is the smaller number because the wedge divider carries
        that seam instead.

   Re-measure both if the card grid or the stat columns are re-laid out. */
body.home #bloc-6::after {
	content: "";
	position: absolute;
	right: 0;
	bottom: 0;
	left: 0;
	height: 72px;
	pointer-events: none;
	background-image: linear-gradient(to bottom,
		rgba(232, 239, 244, 0)    0%,
		rgba(232, 239, 244, .08) 18%,
		rgba(232, 239, 244, .24) 38%,
		rgba(232, 239, 244, .48) 58%,
		rgba(232, 239, 244, .74) 78%,
		rgba(232, 239, 244, .93) 92%,
		rgba(232, 239, 244, 1)  100%);
}

body.home #stats-image::after {
	content: "";
	position: absolute;
	top: 0;
	right: 0;
	left: 0;
	height: 100px;
	pointer-events: none;
	background-image: linear-gradient(to bottom,
		rgba(232, 239, 244, 1)     0%,
		rgba(232, 239, 244, .99)   3%,
		rgba(232, 239, 244, .95)   7%,
		rgba(232, 239, 244, .87)  12%,
		rgba(232, 239, 244, .75)  18%,
		rgba(232, 239, 244, .60)  26%,
		rgba(232, 239, 244, .45)  35%,
		rgba(232, 239, 244, .31)  46%,
		rgba(232, 239, 244, .19)  58%,
		rgba(232, 239, 244, .10)  72%,
		rgba(232, 239, 244, .035) 86%,
		rgba(232, 239, 244, 0)   100%);
}

@media (max-width: 991.98px) {
	body.home #bloc-6::after { height: 48px; }
	body.home #stats-image::after { height: 68px; }
}

@media (max-width: 575.98px) {
	body.home #stats-image::after { height: 52px; }
}

/* The wedge at the top of "Industries", which duplicated the one at the foot of the stats section
   and put two opposing wedges either side of the same seam -- an off-white one pointing down and a
   navy one pointing up. The stats wedge above carries that seam on its own now. */
body.home .bloc-fill { fill: transparent; }


/* ====================================================================================================
   HOME -- "VIEW OUR INDUSTRIES"
   ----------------------------------------------------------------------------------------------------
   It was a secondary outline: a 70%-white hairline with white ink, on #024DA1, sitting alone at the
   foot of a 1300px section under four large photographic tiles. Nothing above it competes with it, so
   there is no primary for it to defer to, and a translucent outline at that size simply did not read
   as the thing to click.

   Promoted to the filled treatment this file already defines for a primary on a dark ground (white
   button, blue ink, `--aeca-blue-light` on hover), plus a little more padding and a shadow so it
   stands off the navy rather than sitting flat in it.

   SCOPE. `#projects-shortcuts` only. `.const-button-industries` and `.const-button-solutions` share
   the secondary rules further up this file; the ID here lifts this above them (1,2,0 beats 0,3,0)
   without touching "View all solutions" in #bloc-6, which is a secondary next to nothing and is
   correct as an outline. Both classes are used in index.php and nowhere else in the theme.
   ==================================================================================================== */

body.home #projects-shortcuts .const-button-industries {
	background-color: var(--aeca-white) !important;
	border-color: var(--aeca-white) !important;
	color: var(--aeca-blue) !important;
	padding: .85rem 2.4rem !important;
	box-shadow: 0 10px 26px rgba(0, 20, 45, .3);
}

body.home #projects-shortcuts .const-button-industries:hover,
body.home #projects-shortcuts .const-button-industries:focus-visible {
	background-color: var(--aeca-blue-light) !important;
	border-color: var(--aeca-blue-light) !important;
	color: var(--aeca-white) !important;
	box-shadow: 0 14px 30px rgba(0, 20, 45, .38);
}

@media (prefers-reduced-motion: no-preference) {
	body.home #projects-shortcuts .const-button-industries { transition: background-color .25s ease, color .25s ease, border-color .25s ease, box-shadow .25s ease, transform .25s ease; }
}


/* Solution card -- the industries hub
----------------------------------------------------------------------------------------------------

Replaced the accordion bar on 2026-09-01. The bar carried its icon in an absolutely positioned circle
overhanging one edge, alternated that edge down the list, and opened a Bootstrap collapse panel; none
of those can be drawn in Blocs 6, so the hub could only ever go back into the project as a code
widget. This is a card in a Bootstrap column instead, which is what Blocs writes for a Card bric.

`.aeca-sol-title` and `.aeca-sol-summary` are reused verbatim from the old bar. Those two rules were
pure typography -- colour, weight, size, transform, plus the fluid clamps further down this file --
and they carry over unchanged, so the card reads in the same voice as the bar it replaces. Only the
box around them is new.

---------------------------------------------------------------------------------------------------- */

.aeca-solcard {
	display: flex;
	flex-direction: column;
	height: 100%;
	padding: 1.75rem 1.5rem;
	border: 1px solid var(--aeca-border);
	border-radius: 12px;
	background: var(--aeca-surface);
	color: inherit;
	text-decoration: none;
	transition: background-color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}

/* A card must always be lighter than its ground. On the tinted sections both were --aeca-surface. */
.aeca-cat-section--alt .aeca-solcard {
	background: var(--aeca-white);
}

/* Same raised-card treatment the open accordion bar used, moved onto hover: the card being read
   separates from the six or twelve beside it. */
.aeca-solcard:hover,
.aeca-solcard:focus-visible {
	border-color: var(--aeca-blue);
	background: var(--aeca-white);
	box-shadow: 0 18px 34px -20px rgba(2, 77, 161, 0.55);
	transform: translateY(-2px);
}

.aeca-solcard:focus-visible {
	outline: 2px solid var(--aeca-blue);
	outline-offset: 3px;
}

.aeca-solcard__icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 84px;
	height: 84px;
	margin-bottom: 1.15rem;
	border: 2px solid var(--aeca-blue);
	border-radius: 50%;
	background: var(--aeca-white);
	color: var(--aeca-blue);
	box-shadow: 0 6px 18px rgba(2, 77, 161, 0.14);
	transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

/* Scoped, because the bare `.aeca-icon` rule is 68px and is bumped to 78px and 86px in the wide
   media queries below -- those sizes existed for the circle this card replaced. Two classes beat
   one, so these win wherever the viewport lands. The artwork runs larger than the inline SVG: it is
   drawn to fill its square, where the SVG is drawn inside a margin. */
.aeca-solcard__icon .aeca-icon { width: 46px; height: 46px; }
.aeca-solcard__icon .aeca-icon--art { width: 66px; height: 66px; }

.aeca-solcard:hover .aeca-solcard__icon {
	border-color: var(--aeca-blue);
	box-shadow: 0 8px 22px rgba(2, 77, 161, 0.24);
}

/* The heading and the summary are real block elements here, not the spans they were inside the
   button, so their own margins have to be reset before the shared typography applies. */
.aeca-solcard__title { margin: 0; }
.aeca-solcard__summary { margin: 0.5rem 0 0; }

@media (prefers-reduced-motion: reduce) {
	.aeca-solcard,
.aeca-solcard__icon { transition: none; }
	.aeca-solcard:hover { transform: none; }
}

@media (max-width: 575.98px) {
	.aeca-solcard { padding: 1.35rem 1.15rem; }

	.aeca-solcard__icon {
		width: 64px;
		height: 64px;
		margin-bottom: 0.9rem;
	}

	.aeca-solcard__icon .aeca-icon { width: 34px; height: 34px; }
	.aeca-solcard__icon .aeca-icon--art { width: 50px; height: 50px; }
}

/* ====================================================================================================
   HOME -- SECTION BANDING AFTER THE CASE-STUDY ROW CAME OUT (2026-09-02)
   ----------------------------------------------------------------------------------------------------
   The page used to run #intro (off-white) -> .aeca-home-stories (tinted) -> .aeca-home-voices (white)
   -> .aeca-home-cta (navy), so every seam had a colour change to sit on. Removing the tinted row put
   an off-white section directly above a white one, which is a 6-unit step across three channels: on
   a MacBook at full screen it reads as a printing fault rather than as a section change.

   The tint moves to the section that inherited the position. Same value the stories row carried.
   ==================================================================================================== */

.aeca-home-voices { background: #f1f6fa; }


/* ====================================================================================================
   HOME STATS -- ONE ROW (2026-09-02)
   ----------------------------------------------------------------------------------------------------
   #stats-image measured 900px tall for four numbers and their labels, which is more than a laptop
   viewport, and the four stats read as four separate things rather than as one set. Where the 900px
   went, measured at 1536px:

     .. 400px  the container's own `bloc-xxl-lg` padding, 200px top and 200px bottom
     .. 300px  a STAIRCASE. style.css sets `.const-2nd-column{margin-top:100px}`, `.const-3rd{200px}`,
               `.const-4th{300px}`, so the four columns start at four different heights and the row
               is as tall as the first column plus the last column's offset
     .. 200px  the content itself -- eyebrow, number, label -- which is all the section actually is

   So a quarter of the height was the stats and three quarters was arrangement. Both of the other two
   are undone here rather than in style.css, which is regenerated from the Blocs export (see
   [[aeca-blocs-divergence]]): the staircase margins are zeroed and the padding drops to the
   `bloc-md-lg` 50px the markup now asks for, plus a little breathing room added back below.

   `col-3` in the markup replaces `col-sm-6 col-md-6 col-lg-3`, so the four hold one row at every
   width instead of stacking 2x2 below lg. That is the point of the change -- a stacked pair costs
   the visitor another screen of scrolling -- but it means the type has to be sized for a quarter of
   a 390px screen at the bottom end, which is what the breakpoints below do. The number is Teko-Medium,
   which is condensed enough that "12,500" still fits a 78px column at 26px.

   THE PARALLAX AND THE HAZE RAMP BOTH DEPEND ON THIS HEIGHT. `#stats-image::after` is a 220px haze
   gradient that has to finish before the first "More Than", and that heading used to sit 493px into
   the section; it is much higher now, so the ramp is re-measured below in the same place the other
   two heights live. The parallax layer needs the section shorter than the viewport to travel at all,
   which it now is.
   ==================================================================================================== */

/* The staircase. Written at two classes so it beats the one-class rules in style.css without
   !important, and scoped to the section because those `const-Nth-column` classes exist nowhere else
   in the theme -- index.php is their only caller. */
#stats-image .const-2nd-column,
#stats-image .const-3rd-column,
#stats-image .const-4th-column {
	margin-top: 0;
}

/* `.const-stat-column` also carries `margin: 50px 0` below 992px, which was spacing for the stacked
   layout that no longer happens. */
#stats-image .const-stat-column {
	margin-top: 0;
	margin-bottom: 0;
}

/* Asymmetric on purpose: the top carries the haze ramp (see HOME -- SEAMS below, which measures its
   height against this number), the bottom carries the wedge divider and needs less. */
#stats-image .container {
	padding-top: 108px;
	padding-bottom: 84px;
}

/* "More Than" was an h3 -- 32px, the size of a section heading -- repeated four times above four
   numbers. It is a qualifier, not a heading, so it takes the eyebrow treatment used elsewhere on the
   site: small, uppercase, letter-spaced. The number is what the eye should land on, and at 64px
   against a 32px lead-in it was not.

   The h3 stays an h3 in the markup. Four of them under a section with no h2 is already the outline
   this section has had since it was built, and changing the level is a separate question from
   changing the size. */
#stats-image .const-stat-column h3 {
	margin: 0;
	font-size: 13px;
	font-weight: 600;
	line-height: 1.3;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	opacity: 0.82;
}

/* `margin-top: 28px` comes off `.const-stat-number` in style.css and was clearing the h3's 24px
   bottom margin on top of it. One gap, set here. */
#stats-image .const-stat-number {
	margin-top: 10px;
	margin-bottom: 0;
}

#stats-image .const-stat-column h4 {
	margin: 6px 0 0;
	line-height: 1.25;
}

@media (max-width: 1199.98px) {
	#stats-image .const-stat-number { font-size: 52px; line-height: 1.05; }
	#stats-image .const-stat-column h4 { font-size: 20px; }
}

@media (max-width: 991.98px) {
	#stats-image .container { padding-top: 76px; padding-bottom: 60px; }
	#stats-image .const-stat-number { font-size: 42px; }
	#stats-image .const-stat-column h3 { font-size: 11px; letter-spacing: 0.08em; }
	#stats-image .const-stat-column h4 { font-size: 16px; }
}

/* Below 768 the container has no fixed width left, so a quarter of it is a straight fraction of the
   viewport and a stepped font-size overflows somewhere inside every step. Measured against the widest
   value the row carries, "12,500" in Teko-Medium: it renders about 3.8x its own font-size wide, and
   the column offers (100vw - 30px) / 4 minus the gutter. Both clamps below are that inequality with
   a couple of pixels of margin, which is why they are vw and not px.

     576-767px  gutter 24px  ->  28.8px of type at 576, 38.4px at 767
     under 576  gutter  8px  ->  21.1px of type at 390, 31.1px at 575 */
@media (max-width: 767.98px) {
	#stats-image .const-stat-number { font-size: clamp(28px, 5vw, 39px); }
	#stats-image .const-stat-column h4 { font-size: 14px; }
}

/* A quarter of a 390px screen is 78px once the container's own 15px is off each side, so the Bootstrap
   gutter is the difference between a label wrapping to two lines and to four. It comes down to 8px
   here, and the eyebrow drops to 10px, which is the smallest the site sets uppercase type anywhere. */
@media (max-width: 575.98px) {
	#stats-image .container { padding-top: 60px; padding-bottom: 44px; }
	#stats-image .row { --bs-gutter-x: 8px; }
	#stats-image .const-stat-column { padding-left: 4px; padding-right: 4px; }
	#stats-image .const-stat-number { font-size: clamp(19px, 5.4vw, 31px); }
	#stats-image .const-stat-column h3 { font-size: 10px; letter-spacing: 0.05em; }
	#stats-image .const-stat-column h4 { font-size: 12px; }
}


/* ====================================================================================================
   WebP backgrounds
   ====================================================================================================

   Every image the site serves is WebP. Two background rules were still reaching for the raster
   original, and both live in style.css, which a Blocs re-export overwrites -- so they are restated
   here rather than edited there, the same way the logo and the preloader are.

     .texture-paper::before                 style.css: url("img/texture-paper.png")
     .bg-Homepage-20Carousel-20KLCC-203D    style.css: an image-set that lists the JPEG first, so
                                            the JPEG is what every browser actually picked

   The texture is lossless WebP, not lossy. It is a 560px noise tile drawn at 24% black over a
   section colour, and lossy compression of noise costs more in seam artefacts than the 7KB it
   saves. See the note on the texture overlay before changing that.
   ==================================================================================================== */

.texture-paper::before { background-image: url("../img/texture-paper.webp"); }

.bg-Homepage-20Carousel-20KLCC-203D { background-image: url("../img/Homepage%20Carousel%20KLCC%203D.webp"); }


/* ====================================================================================================
   HOME: INDUSTRIES WE SERVE -- SIX INDUSTRY CARDS
   ====================================================================================================

   Built 2026-09-03, replacing the 2x2 grid of photographic tiles in #projects-shortcuts. AECA asked
   for all six industries on the home page; only four have card photography, and there is no picture
   for Government & Smart Cities or Disaster Management, so the band was rebuilt as one uniform card
   rather than four photographs beside two invented grounds. The reasoning is written out above the
   markup in index.php.

   THIS IS THE #bloc-6 CARD, INVERTED. "What We Do" and "Industries We Serve" are the two card grids
   on the home page and they are now one component seen on two grounds: same 14px radius, same 1.5px
   outline, same centred icon-title-body stack, same 3px lift on hover. The only differences are the
   ones the ground forces -- #bloc-6 sits on off-white and draws in blue, this sits on #024da1 under
   the paper texture and draws in white. Change one and change the other.

   Everything under "HOME: INDUSTRY TILES" further up this file is now dead: it is keyed on
   `#projects-shortcuts [class*="const-industries-div-"]` and `.const-project-card`, and nothing in
   the live theme renders that markup any more. It is left in place for a separate pass rather than
   cut out alongside a design change.
   ==================================================================================================== */

/* The container carries `bloc-no-padding-lg`, so from 992px up this section has no vertical padding
   of its own and the old design borrowed it from `.const-section-subtitle`'s 100px top margin and a
   `voffset-lg` row. Both are gone with the tiles. The floor is set by the shape dividers rather than
   by taste: they are `.svg-divider{height:100px}` at the top and bottom edges, and content inside
   that band sits under the wave. */
#projects-shortcuts > .container {
	padding-top: clamp(104px, 8vw, 128px);
	padding-bottom: clamp(112px, 9vw, 144px);
}

/* `.const-section-subtitle{margin-top:100px}` in style.css is what used to provide the space above.
   With real padding on the container it would now be 100px of it twice over, and inside the flex
   heading row it would push the heading away from the action beside it. */
#projects-shortcuts .const-section-subtitle {
	margin-top: 0;
}

/* Heading, lead and the section's own action on one row, as #bloc-6 does it. The action moved up
   here from a trailing row of its own, which is most of what makes the band shorter with six cards
   in it than it was with four tiles. It falls under the heading below 768px, where there is no room
   beside it. */
#projects-shortcuts .aeca-home-industries-head {
	display: flex;
	flex-wrap: wrap;
	gap: 1.25rem 2.5rem;
	align-items: flex-end;
	justify-content: space-between;
	margin-bottom: clamp(28px, 3.4vw, 48px);
}

#projects-shortcuts .aeca-home-industries-head__lead {
	max-width: 56ch;
	margin: .7rem 0 0;
	color: rgba(255, 255, 255, .82);
	font-size: 1rem;
	line-height: 1.55;
}

/* A size down from the btn-lg the trailing row used. Beside a heading this is a secondary route into
   the section, not the page's main call. */
#projects-shortcuts .aeca-home-industries-head__cta {
	flex: 0 0 auto;
	display: inline-flex;
	gap: .55rem;
	align-items: center;
}

/* ---- The card ---------------------------------------------------------------------------------- */

#projects-shortcuts .aeca-home-industries {
	--bs-gutter-x: clamp(.9rem, 1.6vw, 1.5rem);
	--bs-gutter-y: clamp(.9rem, 1.6vw, 1.5rem);
	justify-content: center;
}

#projects-shortcuts .aeca-home-industry-card {
	position: relative;
	width: 100%;
	height: 100%;
	border: 1.5px solid rgba(255, 255, 255, .30);
	border-radius: 14px;
	background: rgba(255, 255, 255, .07);
	box-shadow: none;
	transition: transform .2s ease, background-color .2s ease, border-color .2s ease;
}

#projects-shortcuts .aeca-home-industry-card:hover,
#projects-shortcuts .aeca-home-industry-card:focus-within {
	transform: translateY(-3px);
	border-color: rgba(255, 255, 255, .72);
	background: rgba(255, 255, 255, .13);
}

#projects-shortcuts .aeca-home-industry-card .card-body {
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: 1.9rem 1.35rem 1.75rem;
	text-align: center;
}

/* AN OPAQUE PALE PLATE, NOT A TRANSLUCENT ONE. The disc in img/solutions/*.webp is not the solid
   pale-blue chip it looks like on a white page: the fill is a low-alpha light wash and the line art
   is a low-alpha dark stroke, so both are drawn ON something. Everywhere else in the theme that
   something is a white or off-white section, and the disc needs nothing under it. Here the ground is
   #024da1 under the paper texture, and with a translucent chip the six marks rendered as ghosts --
   a barely-lighter circle with a barely-darker drawing inside it, unreadable at 64px.

   #eaf6fd is the same plate .aeca-home-service__icon uses in #bloc-6, so the mark is identical in
   both card grids and the two sections stay one component. It is also what makes the marks the
   brightest thing in the tile, which is the right emphasis on a dark ground.

   Check a disc icon's alpha against a dark ground before assuming it carries its own background --
   this is the second time that assumption has cost a rebuild. */
#projects-shortcuts .aeca-home-industry__icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 64px;
	height: 64px;
	margin-bottom: 1.05rem;
	border-radius: 50%;
	background: #eaf6fd;
	color: #024da1;
}

/* The drawn SVG fallback has no disc of its own and inherits the blue above through currentColor, so
   it is sized to sit inside the plate rather than to fill it. None of the six sectors uses it today
   -- all six have artwork -- but a renamed slug would fall back to one. */
#projects-shortcuts .aeca-home-industry__icon .aeca-icon {
	width: 34px;
	height: 34px;
}

#projects-shortcuts .aeca-home-industry__icon .aeca-icon--art {
	width: 64px;
	height: 64px;
}

#projects-shortcuts .aeca-home-industry-card h3 {
	margin: 0 0 .35rem;
	font-size: 1rem;
	line-height: 1.3;
	letter-spacing: .02em;
	text-transform: uppercase;
}

#projects-shortcuts .aeca-home-industry-card__title {
	color: #fff;
	text-decoration: none;
}

#projects-shortcuts .aeca-home-industry-card__title:hover,
#projects-shortcuts .aeca-home-industry-card__title:focus-visible {
	color: #fff;
	text-decoration: underline;
}

/* The capability line -- what AECA sells that sector, under the sector's own name. It was the
   linked h3 on the old tile and is a label here, because the sector name is the link now. */
#projects-shortcuts .aeca-home-industry__cap {
	margin: 0 0 .55rem;
	color: #7fd4f6;
	font-size: .85rem;
	font-weight: 600;
	line-height: 1.35;
}

/* `margin-top: auto` pins the sentence to the foot of the card, so three cards whose capability
   lines wrap to one, one and two lines still line their body copy up with each other. */
#projects-shortcuts .aeca-home-industry__body {
	max-width: 38ch;
	margin: auto 0 0;
	color: rgba(255, 255, 255, .82);
	font-size: .92rem;
	line-height: 1.55;
}

@media (max-width: 767.98px) {
	#projects-shortcuts .aeca-home-industries-head {
		align-items: flex-start;
	}

	#projects-shortcuts .aeca-home-industry-card .card-body {
		padding: 1.6rem 1.2rem 1.5rem;
	}
}

@media (prefers-reduced-motion: reduce) {
	#projects-shortcuts .aeca-home-industry-card {
		transition: none;
	}

	#projects-shortcuts .aeca-home-industry-card:hover,
	#projects-shortcuts .aeca-home-industry-card:focus-within {
		transform: none;
	}
}


/* ====================================================================================================
   THE ARROW, DRAWN RATHER THAN TYPED (2026-09-03)
   ====================================================================================================

   `&#8594;` -- the RIGHTWARDS ARROW, U+2192 -- was set as a character in three places: the two
   "view all" buttons on the home page and the go affordance on the /solutions service cards.

   NO LATIN WEBFONT SUBSET CONTAINS IT. Google's `latin` range runs U+0000-00FF plus a short list of
   extras, and that list includes U+2191 (up) and U+2193 (down) but not U+2192. `latin-ext` does not
   carry it either. So the arrow fell past both Outfit faces every time and was served by the next
   family in the stack that had the glyph -- the static `fonts/Outfit-Medium/Outfit-Medium.ttf`, a
   whole extra file downloaded on the home page for one character, in a face the rest of the page is
   deliberately not set in. This was true of the Google-hosted fonts as well; self-hosting only made
   it visible in the network log.

   Drawn in CSS instead: a 2px rule with a rotated corner for the head, which is exactly how
   `.aeca-sub-card__go` already draws the same affordance on the detail pages. No glyph, no font
   dependency, and the two arrows on the site now match. `currentColor` means it takes the ink of
   whatever it sits in, so the same span works on the white button and on the blue card.

   The span is empty and `aria-hidden`, as the character was: it decorates a link that already has a
   name. Nothing about its own transitions is set here -- the existing
   `.aeca-home-services-head__cta span` and `.aeca-service-card__go` rules still own the hover
   nudge, because they select the wrapper, not the mark inside it. */

.aeca-arrow {
	position: relative;
	display: inline-block;
	width: 16px;
	height: 2px;
	vertical-align: middle;
	background: currentColor;
}

.aeca-arrow::after {
	content: "";
	position: absolute;
	top: -3px;
	right: 0;
	width: 8px;
	height: 8px;
	border-top: 2px solid currentColor;
	border-right: 2px solid currentColor;
	transform: rotate(45deg);
}

/* The /solutions card wraps the mark in `.aeca-service-card__go`, which sets a 1.15rem font-size for
   the character it used to hold. That does nothing to a drawn arrow, and the wrapper's `margin-top:
   auto` and hover nudge are what it is really there for, so it is left alone apart from giving the
   line inside it somewhere to sit. */
.aeca-service-card__go {
	display: flex;
	align-items: center;
}

/* Blocs pager
-----------------------------------------------------------------------------------------------------
Blocs' Pagination bric wraps the_posts_pagination() in .wp-paginate. That prints WordPress' own
markup (nav.pagination > .nav-links > a.page-numbers), which none of the rules above match, so it
arrived as a row of bare blue links. Same visual treatment as .aeca-pagination, applied to WordPress'
class names instead. See functions/blocs-pagination.php for what makes the bric print at all.
----------------------------------------------------------------------------------------------------- */

.wp-paginate {
	margin-top: 3rem;
}

.wp-paginate .screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.wp-paginate .nav-links {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	justify-content: center;
}

.wp-paginate .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.75rem;
	height: 2.75rem;
	padding: 0 0.75rem;
	border: 1px solid var(--aeca-line, #d7e3ee);
	border-radius: 2px;
	background: #fff;
	color: var(--aeca-blue, #024da1);
	font-weight: 600;
	line-height: 1;
	text-decoration: none;
	transition: background-color 0.16s ease, border-color 0.16s ease;
}

.wp-paginate a.page-numbers:hover,
.wp-paginate a.page-numbers:focus-visible {
	border-color: var(--aeca-line-hover, #b9d2e6);
	background: var(--aeca-surface, #eef3f9);
}

.wp-paginate .page-numbers.current {
	border-color: var(--aeca-blue, #024da1);
	background: var(--aeca-blue, #024da1);
	color: #fff;
}

.wp-paginate .page-numbers.dots {
	border-color: transparent;
	background: none;
	color: var(--aeca-muted, #4a5b6b);
}


/* ====================================================================================
   AECA revision pass, 2026-09-07
   ------------------------------------------------------------------------------------
   Fifteen changes AECA asked for after reviewing the site against the Blocs project.
   Everything in this block is one of them; each rule says which.
   ==================================================================================== */


/* 1. ONE CARD MEASURE ACROSS THE WHOLE SITE.
   ------------------------------------------------------------------------------------
   The site had grown five unrelated card treatments -- .aeca-solcard on the industries
   hub, .aeca-service-card on the Solutions hub, .aeca-story-card on Case Studies,
   .aeca-sub-card inside the three detail families, and the .div-* boxes Blocs writes --
   each with its own border colour, its own padding and its own idea of where the body
   copy starts. Put two of them on one page and the lines do not agree.

   These four tokens are the whole fix: one hairline, one radius, one internal padding
   and one gap. Everything below reads from them, so changing a card measure site-wide
   is a one-line edit here rather than five.

   NOT SET HERE: colour, type size or icon treatment. Each family keeps its own look;
   what is unified is the box and the rhythm inside it, which is what "aligned" meant. */
:root {
	--aeca-card-line: #d7e3ee;
	--aeca-card-radius: 2px;
	--aeca-card-pad: clamp(22px, 2.4vw, 32px);
	--aeca-card-gap: clamp(18px, 2vw, 28px);
}

.aeca-solcard,
.aeca-service-card,
.aeca-story-card,
.aeca-sub-card,
.div-solutions-industries,
.div-products,
.div-home-industries,
.div-industries-disaster,
.div-industries-environment,
.div-infra-solutions-card-1 {
	box-sizing: border-box;
	padding: var(--aeca-card-pad);
	border-radius: var(--aeca-card-radius);
}

/* THE ALIGNMENT ITSELF. A card is a column; its heading sits at the top, its body copy
   follows, and `margin-top: auto` on the last element pins any footer row to the bottom
   edge. Without this a three-card row with one short summary left that card's link
   floating in the middle of its box while its neighbours' sat on the floor, which is
   the single thing that read most clearly as "not aligned". */
.aeca-solcard,
.aeca-service-card,
.aeca-sub-card,
.div-solutions-industries,
.div-products,
.div-home-industries,
.div-industries-disaster,
.div-industries-environment,
.div-infra-solutions-card-1 {
	display: flex;
	flex-direction: column;
	height: 100%;
}

/* The grid parents. Equal track heights, so every card in a row is the height of the
   tallest one and their top and bottom lines agree. */
.aeca-solcards,
.aeca-sub-cards,
.aeca-service-grid,
.aeca-showcase-grid {
	align-items: stretch;
}

.aeca-solcards > [class*="col-"] {
	display: flex;
}

.aeca-solcards > [class*="col-"] > * {
	width: 100%;
}

/* One gap value between cards, in both axes, in every grid. The Bootstrap rows used
   `g-4` (1.5rem flat) while the CSS grids used their own clamps, so the same three
   cards sat further apart on one page than on another. */
.aeca-solcards.row {
	--bs-gutter-x: var(--aeca-card-gap);
	--bs-gutter-y: var(--aeca-card-gap);
}

.aeca-sub .aeca-sub-cards {
	gap: var(--aeca-card-gap);
}

/* The hairline. Some cards drew a 1px border, some a box-shadow ring, some both, and
   the colours differed by a shade or two. Ring rather than border everywhere, so the
   line never takes part in the box model and card interiors stay the same width. */
.aeca-solcard,
.aeca-service-card,
.aeca-story-card {
	border: 0;
	box-shadow: 0 0 0 1px var(--aeca-card-line);
}

/* Body copy starts on the same line in every card of a row. The headings are given the
   same bottom margin and line-height, so a two-line heading pushes its body down by
   exactly one line rather than by whatever its own leading happened to be. */
.aeca-solcard > h3,
.aeca-service-card h3,
.aeca-sub-card > h3,
.div-solutions-industries > :is(h3, h4, h5),
.div-products > :is(h3, h4, h5),
.div-home-industries > :is(h3, h4, h5),
.div-industries-disaster > :is(h3, h4, h5),
.div-industries-environment > :is(h3, h4, h5),
.div-infra-solutions-card-1 > :is(h3, h4, h5) {
	margin: 0 0 0.75rem;
	line-height: 1.26;
}

/* And the body copy itself takes the remaining height, so anything after it -- a link
   row, an arrow -- lands on the bottom edge of every card alike. */
.aeca-solcard > p:last-of-type,
.aeca-service-card p:last-of-type,
.div-solutions-industries > p:last-of-type,
.div-products > p:last-of-type,
.div-home-industries > p:last-of-type,
.div-industries-disaster > p:last-of-type,
.div-industries-environment > p:last-of-type,
.div-infra-solutions-card-1 > p:last-of-type {
	margin-bottom: auto;
}


/* 2. THE HOME RATINGS ARE GOLD, NOT BLUE.
   ------------------------------------------------------------------------------------
   The stars were on --aeca-blue-light, which made them read as part of the brand
   furniture rather than as a rating. Gold is what a reader expects a star to be.

   The empty state stays neutral rather than a tinted gold: a faded gold star is hard to
   tell from a filled one at this size, which would misreport the score. */
.aeca-rating-line__stars {
	color: #e8a90c;
}

.aeca-rating-line__stars .is-empty {
	color: #c9d4de;
}


/* 3. THE "OUR SOLUTIONS" DESCRIPTION SITS CLOSER TO ITS HEADING.
   ------------------------------------------------------------------------------------
   .aeca-section-heading is a two-column grid with a 5rem column gap, which on the
   Solutions hub left a visible trench between the heading and the paragraph that frames
   it. Narrowed to 2.5rem and the paragraph column widened a little to take up the
   slack, so the sentence wraps to fewer lines rather than more.

   SCOPED TO THIS PAGE. The same component runs on Case Studies, Events, Insights and
   the home page, where the wider gap is doing no harm; AECA asked about the Solutions
   page only. */
.page-template-page-solutions .aeca-section-heading {
	gap: 1.5rem 2.5rem;
	grid-template-columns: minmax(0, 1.1fr) minmax(260px, 0.9fr);
}


/* 4. SIDE PADDING BELOW 992px.
   ------------------------------------------------------------------------------------
   The container ladder further up this file starts at 992px, so everything under it ran
   on whatever padding style.css happened to give it: 20px for a `.bloc`, and nothing at
   all for the `.aeca-*` sections, which meant headings and card edges touched the glass
   on a phone and a portrait tablet. That is the "too fit on any screen" complaint.

   A clamp rather than a flat number so the inset grows with the screen instead of
   stepping at the breakpoint: 20px at 360px, 32px at 991px, and the ladder's own 40px
   takes over immediately above.

   THE SAME TWO EXEMPTIONS AS THE LADDER. #sidebar is the nav bar and #hero-carousel is
   the home video; both are drawn edge to edge deliberately and an inset would show as a
   white margin down each side. */
@media (max-width: 991.98px) {

	.bloc > .container,
	.aeca-showcase-section > .container,
	.aeca-action-panel > .container,
	.aeca-faq > .container,
	.aeca-sub .container {
		padding-left: clamp(20px, 4.4vw, 32px);
		padding-right: clamp(20px, 4.4vw, 32px);
	}

	#hero-carousel > .container,
	#sidebar > .container {
		padding-left: 0;
		padding-right: 0;
	}

	/* `.bloc` already carries a flat `padding: 0 20px` from style.css. Zeroed here so it
	   does not add to the container inset above and double the gutter. */
	.bloc {
		padding-left: 0;
		padding-right: 0;
	}
}


/* 5. NO BREADCRUMBS ANYWHERE.
   ------------------------------------------------------------------------------------
   Both breadcrumb trails stop rendering at source -- see aeca_subpage_crumbs() in
   functions/subpages.php and parts/editorial-hero.php -- so nothing below should ever
   match. It is kept as a backstop for any Blocs-drawn trail that arrives in a future
   export, and because the export's own hero markup can carry one. */
.aeca-sub-hero__crumbs,
.aeca-editorial-hero__crumbs {
	display: none !important;
}


/* 6 + 7. THE INDUSTRIES HUB: ONE CARD PER SECTOR.
   ------------------------------------------------------------------------------------
   Replaces `.aeca-cat-section` and its three-card grid on /industries. Those rules are
   left in this file untouched -- parts/solution-category.php and parts/solution-card.php
   are still on disk and still work, and this is a change AECA may want to see reversed
   side by side. Nothing on the site renders them today.

   The banding and the scroll offset are copied from `.aeca-cat-section` rather than
   shared with it, so retiring either component later does not disturb the other. */
.aeca-ind-summary {
	background: var(--aeca-white);
	scroll-margin-top: 90px;
}

.aeca-ind-summary--alt {
	background: var(--aeca-surface);
}

@media (max-width: 1199.98px) { .aeca-ind-summary { scroll-margin-top: 80px; } }
@media (max-width: 575.98px)  { .aeca-ind-summary { scroll-margin-top: 68px; } }

/* WITHDRAWN 2026-09-07, fourth round: AECA asked for the section title to come off,
   because it is the card's own heading repeated forty pixels above it. Nothing renders
   either of these two rules now -- see parts/industry-summary.php. They are kept, not
   deleted, because the label is the only thing that gave a scrolling reader a fixed
   marker per sector, and restoring it is putting one row back in that file. */
.aeca-ind-summary__label {
	margin: 0 0 0.6rem;
	color: var(--aeca-blue);
	font-family: var(--aeca-font-head, "Outfit", sans-serif);
	font-size: 0.78rem;
	font-weight: 700;
	letter-spacing: 0.18em;
	line-height: 1.2;
	text-transform: uppercase;
}

.aeca-ind-summary__rule {
	width: 72px;
	height: 3px;
	margin-bottom: clamp(20px, 2.4vw, 32px);
	background: var(--aeca-blue-light);
}

/* The card. On the shared measure from heading 1 of this pass via `.div-home-industries`,
   so it lines up with every other card on the site; everything below is the centring and
   the link behaviour, which are particular to this one. */
.aeca-ind-card {
	text-align: center;
	align-items: center;
	text-decoration: none;
	transition: box-shadow 0.16s ease, transform 0.16s ease;
}

.aeca-ind-card__title {
	margin: 0 0 0.85rem;
	color: var(--aeca-blue);
	font-size: clamp(1.5rem, 2.6vw, 2.1rem);
	font-weight: 700;
	line-height: 1.18;
}

.aeca-ind-card__rule {
	width: 56px;
	height: 2px;
	margin: 0 0 1.1rem;
	background: var(--aeca-blue-light);
}

.aeca-ind-card__lead {
	max-width: 62ch;
	margin: 0;
	color: var(--aeca-muted);
	font-size: 1.02rem;
	line-height: 1.68;
}

/* The affordance. `margin-top: auto` is doing nothing here -- these cards are one to a
   row so there is no neighbour to align a bottom edge with -- but it is left in so the
   card behaves the same way if it is ever put into a grid. */
.aeca-ind-card__go {
	margin-top: 1.15rem;
	color: var(--aeca-blue-light);
	font-family: var(--aeca-font-head, "Outfit", sans-serif);
	font-size: 0.85rem;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

/* THE WHOLE CARD IS THE LINK, as it was on the solution cards this replaces, so the
   click target is the box rather than a phrase inside it. */
a.aeca-ind-card:hover,
a.aeca-ind-card:focus-visible {
	box-shadow: 0 0 0 1px var(--aeca-blue-light), 0 14px 34px rgba(2, 77, 161, 0.1);
	transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
	.aeca-ind-card { transition: none; }
	a.aeca-ind-card:hover,
	a.aeca-ind-card:focus-visible { transform: none; }
}


/* ====================================================================================
   AECA revision pass, 2026-09-07 -- second round
   ==================================================================================== */


/* 1. THE SOLUTIONS HUB CARDS -- the row in AECA's screenshot.
   ------------------------------------------------------------------------------------
   Same fix as the detail-page cards, and the same reasoning: see the long note under
   heading 1 of the second round in css/aeca-subpages.css. The row tracks live on the
   grid, every card spans all three, so the four titles -- three lines, one line, two
   lines, three lines -- occupy one shared track and all four paragraphs start together.

   NO MARKUP CHANGE WAS NEEDED HERE. `.aeca-service-card__inner` already wraps exactly
   three children: the icon, the heading and the foot. Setting it to `display: contents`
   lifts those three out to become the card's own grid items, and the wrapper's old job
   -- a full-height flex column -- is what the subgrid now does properly.

   `.aeca-service-card__num` stays outside `__inner` and is absolutely positioned, so it
   never becomes a grid item. That was already true and is why the numeral still sits in
   the top corner. */
.aeca-service-card {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 3;
	row-gap: 0;
}

.aeca-service-card__inner {
	display: contents;
}

/* The foot fills its own track, so the arrow's `margin-top: auto` still puts it on the
   bottom edge when one card's body runs longer than another's. */
.aeca-service-card__foot {
	height: 100%;
}

@supports not (grid-template-rows: subgrid) {

	.aeca-service-card__inner {
		display: flex;
		flex-direction: column;
		height: 100%;
	}
}


/* 1b. THE PRODUCTS HUB CARDS.
   ------------------------------------------------------------------------------------
   Same treatment, but the row had to become a grid first: this one is a Bootstrap
   `.row g-4` of `.col-lg-4` columns, and subgrid needs a real grid parent.

   The column classes stay on the markup and simply stop doing anything, which is
   deliberate -- it means the Blocs canvas can still hold this as a normal Row of Columns
   and only the CSS knows about the grid.

   Four tracks, not three: an eyebrow, a title, a body and a link. Every product card
   carries all four, so there is nothing to pad out. */
.aeca-product-group .row {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--aeca-card-gap);
	margin: 0;
}

.aeca-product-group .row > [class*="col-"] {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 4;
	width: auto;
	max-width: none;
	padding: 0;
}

.aeca-product-card {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 4;
	row-gap: 0;
}

.aeca-product-card > .aeca-eyebrow { align-self: start; }
.aeca-product-card > h3 { align-self: start; }
.aeca-product-card > .aeca-text-link { align-self: end; justify-self: start; }

@media (max-width: 991.98px) {
	.aeca-product-group .row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575.98px) {
	.aeca-product-group .row { grid-template-columns: minmax(0, 1fr); }
}

@supports not (grid-template-rows: subgrid) {

	.aeca-product-group .row > [class*="col-"],
	.aeca-product-card {
		display: flex;
		flex-direction: column;
	}
}


/* 2. THE INDUSTRIES HUB CARD LOSES ITS "EXPLORE THE SECTOR" LINE.
   ------------------------------------------------------------------------------------
   AECA asked for the card to be clickable and the link text removed. The whole card has
   been an <a> since it was built, so the click target was never the phrase -- but a card
   that carries a link-shaped line at the bottom invites you to aim at it, which is
   precisely the confusion that prompted the question.

   The line is gone from parts/industry-summary.php. What replaces it as the affordance is
   the pointer cursor and the lift on hover, plus a focus ring for the keyboard, which
   was previously left to the browser default. */
.aeca-ind-card {
	cursor: pointer;
}

a.aeca-ind-card:focus-visible {
	outline: 3px solid var(--aeca-blue-light);
	outline-offset: 3px;
}


/* 1c. CONTAINMENT VERSUS SUBGRID -- why the first attempt at the Solutions row did nothing.
   ------------------------------------------------------------------------------------
   `.aeca-service-card` carries `container-type: inline-size`, which applies layout
   containment. Per CSS Grid Level 2, a grid container that is forced to establish an
   independent formatting context -- which containment does -- has `subgrid` compute to
   `none`. So the cards fell back to being ordinary three-row grids, `align-content`
   stretched those three auto rows equally inside each card, and a one-line title like
   "Mobile Mapping" ended up floating in the middle of the space the three-line titles
   beside it needed. Which is worse than where it started.

   The containment is not decoration -- the `@container (min-width: 640px)` block above
   relays a widened card horizontally. But it is only needed where that query can
   actually fire, and it can fire on exactly one card: the last child of a
   `--tail2-1` grid, which spans all twelve columns and so reaches roughly 1100px. Every
   other card tops out around 380px in a 1180px container and never crosses 640px.

   So containment is scoped to that one card, and that card opts out of the subgrid --
   it is alone on its row, so it has no neighbour to align with and loses nothing. Every
   other card gets the row tracks. */
.aeca-service-card {
	container-type: normal;
}

.aeca-service-grid--tail2-1 > .aeca-service-card:last-child {
	container-type: inline-size;
	display: block;
}

/* That card keeps the flex column it always had. The container query above swaps it to a
   horizontal grid at 640px, which is immediately, so this is really just the state it
   passes through -- but leaving it on `display: contents` would drop the layout
   entirely if the query ever failed to match. */
.aeca-service-grid--tail2-1 > .aeca-service-card:last-child .aeca-service-card__inner {
	display: flex;
	flex-direction: column;
	height: 100%;
}


/* ====================================================================================
   AECA revision pass, 2026-09-07 -- third round
   ==================================================================================== */


/* 3b. THE "OUR SOLUTIONS" GAP, PROPERLY THIS TIME.
   ------------------------------------------------------------------------------------
   The first attempt scoped the fix to `.page-template-page-solutions` and therefore did
   nothing at all. That body class is only added when a page has a template assigned in
   the editor's Template dropdown; /solutions resolves through the slug branch of the
   template hierarchy instead -- page-solutions.php matches the slug -- and WordPress
   reports `page-template-default` on it. The rule never matched a single element.

   Scoped to the section id instead, which is in the markup and cannot drift.

   AND THE GAP WAS NOT REALLY THE GAP. `.aeca-section-heading` splits 1.25fr / 0.75fr, so
   the heading column is the wider of the two while "Our Solutions" is two short words.
   Most of the space in the middle of AECA's screenshot was the empty right-hand end of
   the HEADING's column, not the 5rem gutter between the columns.

   So the heading column is sized to its content now and the paragraph takes the rest.
   `minmax(0, max-content)` rather than bare `max-content`: it lets the column shrink
   below its content if the viewport ever demands it, instead of forcing an overflow. */
#services-capabilities .aeca-section-heading {
	grid-template-columns: minmax(0, max-content) minmax(320px, 1fr);
	gap: 1.5rem clamp(2rem, 3.5vw, 3.25rem);
}


/* 1d. THE REST OF THE SITE'S CARDS.
   ------------------------------------------------------------------------------------
   AECA: "make sure to do the same for all the card in the whole website". The mechanism
   is the one described at length under heading 1 of the second round in
   css/aeca-subpages.css -- the grid owns the row tracks, each card spans them all, so
   every card in a row starts its title on one line and its body copy on another.

   THE ROWS HAD TO BECOME GRIDS FIRST. These four families all sit in Bootstrap rows,
   and subgrid needs a real grid parent. `:has()` does that without touching a single
   template: a row that contains one of these cards is a grid, and any other row on the
   site is left exactly as it was. The `col-*` classes stay in the markup and simply stop
   being the thing that sets the width, which keeps the Blocs canvas able to hold these
   as ordinary Rows of Columns.

   THE SPAN COUNT IS THE NUMBER OF PARTS IN THE CARD, and each family's is fixed:

     home services    3   icon / title / body
     home industries  4   icon / title / capability / body
     resources        4   eyebrow / title / body / link
     case studies     4   image / title / body / link
     solution cards   3   icon / title / summary

   A family whose card can be missing a part needs that part placed explicitly, or the
   card shifts up a track and drops out of step with its neighbours. Case studies is the
   one that can -- a post with no featured image -- and it is handled below. */

/* -- Home: the services band (#bloc-6) and the industries band ---------------------- */

.row:has(> [class*="col-"] > .aeca-home-service-card),
.row:has(> [class*="col-"] > .aeca-home-industry-card) {
	display: grid;
	gap: var(--aeca-card-gap);
	margin: 0;
}

/* FOUR ACROSS, matching the `col-lg-3` the markup carries -- this band has run at four
   since 2026-08-18 and the card count (7 -> 4 -> 6 -> 8) is written around it. */
.row:has(> [class*="col-"] > .aeca-home-service-card) {
	grid-template-columns: repeat(4, minmax(0, 1fr));
}

.row:has(> [class*="col-"] > .aeca-home-industry-card) {
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

.row:has(> [class*="col-"] > .aeca-home-service-card) > [class*="col-"],
.row:has(> [class*="col-"] > .aeca-home-industry-card) > [class*="col-"] {
	display: grid;
	grid-template-rows: subgrid;
	width: auto;
	max-width: none;
	padding: 0;
}

.row:has(> [class*="col-"] > .aeca-home-service-card) > [class*="col-"] { grid-row: span 3; }
.row:has(> [class*="col-"] > .aeca-home-industry-card) > [class*="col-"] { grid-row: span 4; }

.aeca-home-service-card,
.aeca-home-industry-card {
	display: grid;
	grid-template-rows: subgrid;
	row-gap: 0;
}

.aeca-home-service-card { grid-row: span 3; }
.aeca-home-industry-card { grid-row: span 4; }

/* `.card-body` IS THE SUBGRID, rather than being flattened out of the way.
   ------------------------------------------------------------------------------------
   The first attempt set `display: contents` on it, to lift the card's parts up into the
   card's own subgrid. It never applied: the theme styles both of these at id specificity
   -- `#bloc-6 .aeca-home-service-card .card-body` and
   `#projects-shortcuts .aeca-home-industry-card .card-body` -- and a class-level rule
   cannot reach that. So the whole card stayed a single grid item, the icons and titles
   lined up only because they happened to be one line each, and the paragraphs went on
   following their own capability lines. Which is what AECA screenshotted.

   Nesting is the better answer anyway. `.card-body` holds the padding, the centring and
   the text alignment; making IT the subgrid keeps all three where the theme put them
   instead of relocating them onto the card. Its padding offsets its tracks from the
   card's, but by the same amount on every card in the row, so they stay in step with
   each other -- which is the whole of what alignment means here. */
#bloc-6 .aeca-home-service-card .card-body,
#projects-shortcuts .aeca-home-industry-card .card-body {
	display: grid;
	grid-template-rows: subgrid;
	row-gap: 0;
	/* `align-items: center` was the flex cross-axis rule that centred these cards'
	   contents. Grid's equivalent for that axis is `justify-items`. */
	justify-items: center;
	align-content: start;
}

#bloc-6 .aeca-home-service-card .card-body { grid-row: 1 / span 3; }
#projects-shortcuts .aeca-home-industry-card .card-body { grid-row: 1 / span 4; }


@media (max-width: 991.98px) {
	.row:has(> [class*="col-"] > .aeca-home-service-card),
	.row:has(> [class*="col-"] > .aeca-home-industry-card) {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* The industries band is `col-sm-6 col-lg-4`, so it is three then two then one. The
   services band is `col-lg-3 col-sm-6`: four, then two, then one -- it never runs at
   three, which is why the two families part company here rather than sharing a rule. */

@media (max-width: 575.98px) {
	.row:has(> [class*="col-"] > .aeca-home-service-card),
	.row:has(> [class*="col-"] > .aeca-home-industry-card) {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* -- Resources -------------------------------------------------------------------- */

.row:has(> [class*="col-"] > .aeca-resource-card) {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--aeca-card-gap);
	margin: 0;
}

.row:has(> [class*="col-"] > .aeca-resource-card) > [class*="col-"] {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 4;
	width: auto;
	max-width: none;
	padding: 0;
}

.aeca-resource-card {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 4;
	row-gap: 0;
}

.aeca-resource-card > .aeca-text-link { align-self: end; justify-self: start; }

@media (max-width: 991.98px) {
	.row:has(> [class*="col-"] > .aeca-resource-card) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575.98px) {
	.row:has(> [class*="col-"] > .aeca-resource-card) { grid-template-columns: minmax(0, 1fr); }
}

/* -- Case studies and the post grids ------------------------------------------------
   TWO TRACKS ONLY: the image, and everything else. That is a deliberate retreat from the
   four this had on the first attempt, and the case studies page is what forced it.

   `.aeca-story-card` is not one shape. The case studies grid puts a title, an excerpt and
   a link inside it; parts/post-grid.php and single.php put a date above those, so four;
   and aeca_case_study_schema_card() puts an eyebrow, a title, one paragraph per schema
   step and a link, so as many as seven. A fixed `span 4` gave that last card three tracks
   for six items and stacked four paragraphs on top of each other -- which is what AECA
   would have seen on /case-studies.

   Two tracks fit all three, because all three have exactly the same OUTER shape: an
   optional image, then one block of copy. So every image row is one height and every card
   starts its copy on the same line, which is the alignment that was actually missing. The
   inside of the copy block goes back to being a flex column, and since all the cards in
   any one grid come from the same loop, their inner parts line up with each other anyway.

   The image is placed explicitly on track 1 so a post with no featured image leaves that
   track empty rather than sliding its copy up into it. */

.row:has(> [class*="col-"] > .aeca-story-card) {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--aeca-card-gap);
	margin: 0;
}

/* single.php lays its related posts out three across (`col-md-4`); everything else is two
   (`col-md-6`). Read off the column class so the grid follows the markup rather than
   overriding it. */
.row:has(> .col-md-4 > .aeca-story-card) {
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

.row:has(> [class*="col-"] > .aeca-story-card) > [class*="col-"] {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 2;
	width: auto;
	max-width: none;
	padding: 0;
}

.aeca-story-card {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 2;
	row-gap: 0;
}

.aeca-story-card > a:first-child { grid-row: 1; }

.aeca-story-card > div {
	grid-row: 2;
	display: flex;
	flex-direction: column;
}

/* The link goes to the foot of the copy block, so the calls to action across a row land on
   one line however long the excerpts run. */
.aeca-story-card > div > .aeca-text-link {
	margin-top: auto;
}

@media (max-width: 767.98px) {
	.row:has(> [class*="col-"] > .aeca-story-card),
	.row:has(> .col-md-4 > .aeca-story-card) { grid-template-columns: minmax(0, 1fr); }
}

/* -- Solution cards ------------------------------------------------------------------
   Nothing renders these today: the industries hub moved to one summary card per sector on
   2026-09-07 and parts/solution-card.php has had no caller since. Kept in step with the
   rest anyway, because the component is still on disk and AECA may want it back. */

.aeca-solcards.row {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	margin: 0;
}

.aeca-solcards.row > [class*="col-"] {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 3;
	width: auto;
	max-width: none;
	padding: 0;
}

.aeca-solcard {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 3;
	row-gap: 0;
}

@media (max-width: 991.98px) { .aeca-solcards.row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 575.98px) { .aeca-solcards.row { grid-template-columns: minmax(0, 1fr); } }

/* THE FALLBACK, for a browser without subgrid. Every family goes back to a flex column,
   which is the behaviour these cards had before this pass: bottoms aligned by the auto
   margins, tops left to the copy. Degraded, not broken. */
@supports not (grid-template-rows: subgrid) {

	.row:has(> [class*="col-"] > .aeca-home-service-card) > [class*="col-"],
	.row:has(> [class*="col-"] > .aeca-home-industry-card) > [class*="col-"],
	.row:has(> [class*="col-"] > .aeca-resource-card) > [class*="col-"],
	.row:has(> [class*="col-"] > .aeca-story-card) > [class*="col-"],
	.aeca-solcards.row > [class*="col-"],
	.aeca-home-service-card,
	.aeca-home-industry-card,
	.aeca-resource-card,
	.aeca-story-card,
	.aeca-story-card > div,
	.aeca-solcard {
		display: flex;
		flex-direction: column;
	}

	#bloc-6 .aeca-home-service-card .card-body,
	#projects-shortcuts .aeca-home-industry-card .card-body {
		display: flex;
		flex-direction: column;
		flex: 1;
	}
}


/* 2b. THE INDUSTRIES HUB CARD HAS TO BE VISIBLE AT REST.
   ------------------------------------------------------------------------------------
   AECA: it "need to be visible even if user not hover on it". Correct, and the cause is
   worth recording because it will catch the next component built this way.

   The card was given the class `.div-home-industries` to put it on the shared card
   measure. That class carries a border, a white ground and padding -- in the BLOCS
   PROJECT. It is defined in _blocs/_src-header-styles.css, which is pasted into the Blocs
   Header code zone and exists only in an export; the live theme's header.php has never
   contained it. So on this site the class matched nothing, the card inherited the section
   ground, and the only thing that made it look like a card was the hover shadow.

   THE LESSON: a `.div-*` class is a Blocs-canvas class. It is fine as a hook so the
   canvas and the site agree, but anything the live site must actually draw belongs in a
   file the theme loads. The resting surface is therefore set here, on the card's own
   class, and the hover state now lifts something that was already there. */
.aeca-ind-card {
	background: var(--aeca-white, #fff);
	box-shadow: 0 0 0 1px var(--aeca-card-line, #d7e3ee);
	transition: box-shadow 0.16s ease, transform 0.16s ease;
}

.aeca-ind-summary--alt .aeca-ind-card {
	box-shadow: 0 0 0 1px #c6d7e6;
}


/* 1e. THE COLUMN WRAPPERS TAKE ZERO ROW GAP.
   ------------------------------------------------------------------------------------
   A subgrid inherits its parent's `gap`, and these Bootstrap columns are subgrids sitting
   inside a row whose gap is the space BETWEEN cards. Left alone, that gap is also applied
   between the tracks each column spans -- so the card gap reappeared inside every card.

   It only showed on the case studies card, because every other family has the card itself
   as a nested subgrid with `row-gap: 0` already, which overrides it for its own span. The
   story card's copy block is a plain flex div, so nothing overrode it and the empty image
   track opened a 28px hole above the eyebrow.

   Setting it on the wrappers fixes that case and makes the rest explicit rather than
   accidentally correct. The COLUMN gap is untouched: that one is the space between cards
   and is doing exactly what it should. */
.row:has(> [class*="col-"] > .aeca-home-service-card) > [class*="col-"],
.row:has(> [class*="col-"] > .aeca-home-industry-card) > [class*="col-"],
.row:has(> [class*="col-"] > .aeca-resource-card) > [class*="col-"],
.row:has(> [class*="col-"] > .aeca-story-card) > [class*="col-"],
.aeca-solcards.row > [class*="col-"],
.aeca-product-group .row > [class*="col-"] {
	row-gap: 0;
}


/* 1f. THE HOME CARDS' BODY COPY IS TOP-ALIGNED IN ITS TRACK.
   ------------------------------------------------------------------------------------
   AECA's first screenshot of the third round: the industries band, where "Hazard mapping
   & emergency response" wraps to two lines and its neighbours' capability lines do not.
   The capability lines agreed; the paragraphs under them did not, and the shorter cards'
   copy sat LOW rather than high.

   `margin: auto 0 0` is why. Both home card bodies carry it -- `#bloc-6
   .aeca-home-service__body` and `#projects-shortcuts .aeca-home-industry__body` -- and
   in the old flex column that auto top margin was exactly right: it pushed the paragraph
   to the foot of the card so the bottom edges of a row agreed.

   As a grid item in its own track it does the opposite of what is wanted. The track is
   already the height of the tallest paragraph in the row, so an auto top margin pushes a
   shorter one to the BOTTOM of that track and opens a gap above it. The track placement
   is the mechanism now, so the margin comes off.

   Kept in the no-subgrid fallback, where it is still doing its original job. */
#bloc-6 .aeca-home-service__body,
#projects-shortcuts .aeca-home-industry__body {
	margin-top: 0;
}

@supports not (grid-template-rows: subgrid) {

	#bloc-6 .aeca-home-service__body,
	#projects-shortcuts .aeca-home-industry__body {
		margin-top: auto;
	}
}


/* ====================================================================================
   AECA revision pass, 2026-09-07 -- fourth round
   ==================================================================================== */


/* THE SOLUTIONS HUB HERO PHOTOGRAPH.
   ------------------------------------------------------------------------------------
   AECA supplied it on 2026-09-07 and it retires the navy-and-paper ground this hero had
   been holding. A nadir aerial of a flooded Malaysian residential block; see the note in
   page-solutions.php for why it suits this page.

   DECLARED HERE RATHER THAN IN style.css. Blocs writes background images as
   `.bg-Some-20Name` rules into style.css, and a re-export overwrites that file -- see the
   note at the head of this one. A class of our own survives the export, and adding the
   same class to the bloc on the canvas keeps the two in step.

   TWO WIDTHS. `image-set` hands 2200px to a retina screen and 1400px to everything else,
   which is 300KB rather than 590KB on the machines that cannot show the difference. The
   jpg fallback line Blocs normally writes is not needed: every browser that supports
   `image-set` supports WebP, and the plain `background-image` above it is the fallback. */
.aeca-hero-solutions {
	background-color: #0d2b45;

	/* Two layers: a flat navy wash over the photograph. A SCRIM IS NEEDED HERE -- the bloc
	   is `d-bloc`, so every piece of type in it is white, and floodwater under midday sun
	   is a pale, busy ground for that. 42% settles the frame without dulling the roofs.

	   A gradient layer rather than `background-blend-mode`, which needs the colour and the
	   image to be separate layers anyway and behaves differently across engines when the
	   colour carries alpha. Two stops of the same value is the ordinary way to write a flat
	   overlay. */
	background-image:
		linear-gradient(rgba(9, 38, 63, 0.42), rgba(9, 38, 63, 0.42)),
		url("../img/solutions-hero-flood-aerial-1400.webp");
	background-image:
		linear-gradient(rgba(9, 38, 63, 0.42), rgba(9, 38, 63, 0.42)),
		image-set(
			url("../img/solutions-hero-flood-aerial-1400.webp") 1x,
			url("../img/solutions-hero-flood-aerial-2200.webp") 2x
		);
	background-position: 50% 50%;
	background-repeat: no-repeat;
	background-size: cover;
}


/* The Solutions hub hero's two calls to action, added 2026-09-08 from the Blocs canvas. The
   headline's own rule is drawn by `.const-page-title::after` above, so the hero carries no
   separate divider element. The row is centred with the rest of the glass panel and wraps to a
   column on narrow screens rather than letting two full-width buttons sit side by side. */

#services-hero .hero-solutions-cta { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.9rem; margin-top: 1.6rem; }

@media (max-width: 575px) {
  #services-hero .hero-solutions-cta { flex-direction: column; align-items: stretch; }
}
