/*!
 * AppAgentix - homepage.
 *
 * Loaded only on the front page (see inc/scripts.php). Depends on
 * appagentix-premium.css for the brand tokens and on appagentix-motion
 * for .lp-reveal, so it defines only what is specific to this page.
 *
 * Contents
 *   1. Tokens
 *   2. Section rhythm          the 80/64/48 spacing system
 *   3. Buttons
 *   4. Hero
 *   5. Service panel           optional, renders only when data is given
 *   6. Marquee
 *   7. Category carousel       tab rail + infinite auto-advancing cards
 *   8. Split band              a claim beside the points that back it
 *   9. Tinted section heading
 *  10. Solution slider
 *  11. Stage cards
 *  12. Capability areas        disclosure list beside a click-to-play video
 *  13. Service tabs            tablist + one panel of cards per discipline
 *  14. Industry cards          photo tiles with a rising brand panel
 *  15. Engagement stages       numbered rail beside one media panel
 *  16. The stack               rail of areas beside their technology chips
 *  17. About                   copy beside four counters
 *  18. Testimonials            draggable track of quote cards
 *  19. Audit badges            certification marks beside our practices
 *  20. Contact band            the lead form beside the office map
 *  21. Case studies            one study at a time, copy beside the photo
 *  22. Reduced motion
 */

/* =====================================================================
 * 1. Tokens
 * ================================================================== */

/* The theme does not set border-box globally, so a container that is
   width:100% with horizontal padding resolves to 100% PLUS the padding
   and pushes past the viewport. Scoped to this page's own components
   rather than applied globally, which would reflow everything else. */
.ao-home :where(.ao-section,
	.ao-section__inner,
	.ao-section__head,
	.ao-hero,
	.ao-hero__inner,
	.ao-hero__copy,
	.ao-marquee,
	.ao-marquee__viewport,
	.ao-marquee__track,
	.ao-service,
	.ao-service__body,
	.ao-btn,
	.ao-work,
	.ao-work__stage,
	.ao-work__viewport,
	.ao-work__panel,
	.ao-work__slide,
	.ao-work-card,
	.ao-work__tab) {
	box-sizing: border-box;
}

.ao-home {
	/* The rhythm and the measure are inherited from layout.css, so the
	   homepage cannot drift away from the rest of the site. */
	--ao-space-section: var(--section-space);
	--ao-space-section-tight: calc(var(--section-space) * .7);
	--ao-space-section-loose: calc(var(--section-space) * 1.4);

	--ao-home-max: var(--container);
	--ao-home-wide: 1440px;
	--ao-home-gutter: var(--gutter);

	/* The hero CTA gradient from the approved design: warm cream on the
	   left running to lavender on the right. Kept as a token because it is
	   a brand surface, not a one-off button style. */
	--ao-grad-cta: linear-gradient(90deg, #fdeacd 0%, #f4dcd8 42%, #d9c6f5 100%);

	/* The button rim. The same warm-to-cool journey as the fill, but
	   saturated enough to read as an edge against it. */
	--ao-grad-btn-ring: linear-gradient(95deg, #f0a94e 0%, #ef7f93 38%, #c96fd0 68%, #8f6bf0 100%);
	--ao-btn-ring-w: 1.5px;

	--ao-purple-band: linear-gradient(270deg, #3F2171 0%, #703BC8 97.6%);

	/* Hero. The heading gradient runs warm to cool across the
	   heading's box - see section 4. */
	--ao-grad-hero: linear-gradient(90deg, #f2a03a 0%, #ec6a7e 34%, #d24fa8 62%, #8347ec 100%);
	--ao-hero-bg: #fdfbf8;
	--ao-hero-grid: rgba(20, 18, 28, .055);
	--ao-hero-cell: 72px;
	/* How far down the page the two colour washes reach. They are
	   sized in percent, so without this they would scale with the
	   whole document and diffuse into nothing. */
	--ao-home-wash: 760px;

	--ao-ink: #14121c;
	--ao-ink-soft: #0D0D0D;
}

/* =====================================================================
 * 2. Section rhythm
 * ================================================================== */

.ao-section {
	padding-block: var(--ao-space-section);
	position: relative;
}

.ao-section--space-tight {
	padding-block: var(--ao-space-section-tight);
}

.ao-section--space-loose {
	padding-block: var(--ao-space-section-loose);
}

.ao-section--space-none {
	padding-block: 0;
}

/*
 * Two transparent sections in a row share one gap.
 *
 * Every section pads itself top and bottom, which is right beside a
 * coloured band - each side of that edge needs its own room. Between two
 * sections on the page's own background there is no edge, so the two
 * paddings simply stacked: 160px of blank page on desktop, 128px on a
 * tablet and 96px on a phone, the same at every such boundary. The
 * following section keeps a quarter of its top padding, so the gap is
 * the rhythm token once and a little over, at every breakpoint.
 *
 * The contact band paints its own background, so its edge is a real
 * boundary and it keeps full padding on both sides.
 */
.ao-home :is(.ao-section--light, .ao-section--plain):not(.ao-section--contact) + :is(.ao-section--light, .ao-section--plain):not(.ao-section--contact) {
	padding-top: calc(var(--ao-space-section) * .25);
}

/*
 * Transparent, not white: the page's base colour and its grid now come
 * from .ao-home-bg underneath, and a white section would hide them.
 */
.ao-section--light {
	background: transparent;
}

.ao-section--plain {
	background: transparent;
}

/* Full-bleed brand band. The wrapper paints it; the inner element keeps
   the content on the same measure as every other section. */
.ao-section--purple {
	/* background: var(--ao-purple-band); */
	background:radial-gradient(circle at 12% 10%,rgba(91,61,245,0.58) 0,rgba(91,61,245,0) 42%),radial-gradient(ellipse 60% 55% at 55% 112%,rgba(226,63,159,0.3) 0,rgba(226,63,159,0) 60%),radial-gradient(circle at 94% 96%,rgba(255,138,0,0.3) 0,rgba(255,138,0,0) 24%),#100C24;
	color: #fff;
}

/* .ao-section__inner takes its geometry from layout.css. */

.ao-section__inner--wide {
	max-width: var(--ao-home-wide);
}

/* "full" keeps the header on the measure but lets the content beneath
   run edge to edge, which is what the marquee needs. */
.ao-section__inner--full {
	max-width: none;
	padding-inline: 0;
}

/* The head of a full-bleed section is re-contained by layout.css. */

.ao-section__head {
	margin-bottom: 36px;
}

.ao-section__head--center {
	text-align: center;
}

.ao-section__eyebrow {
	margin: 0 0 10px;
	font-size: .78rem;
	font-weight: 700;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--color-border-blue, #8347ec);
}

.ao-section--purple .ao-section__eyebrow {
	color: #e6d8ff;
}

.ao-section__title {
	margin: 0;
	font-size: clamp(1.75rem, 1.15rem + 2.1vw, 2.5rem);
	line-height: 1.9;
	font-weight: 800;
	letter-spacing: -.02em;
	
	background: var(--ao-grad-hero);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	text-wrap: pretty;
	
}

.ao-section--purple .ao-section__title {
	color: #fff;
}

.ao-section__lead {
	margin: 14px auto 0;
	max-width: 60ch;
	font-size: 24px;
	line-height: 1.6;
	color: var(--ao-ink-soft);
}

.ao-section__head--left .ao-section__lead {
	margin-inline: 0;
}

.ao-section--purple .ao-section__lead {
	color: rgba(255, 255, 255, .84);
}

/* =====================================================================
 * 3. Buttons
 * ================================================================== */

.ao-btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 14px;
	padding: 14px 18px;
	border-radius: 10px;
	font-size: .98rem;
	font-weight: 600;
	line-height: 1.2;
	text-decoration: none;
	color: var(--ao-ink);
	transition:
		transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		box-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/*
 * The gradient rim.
 *
 * A border cannot take a gradient, so the ring is a pseudo-element
 * filled with the gradient and masked down to its own padding box: the
 * fill minus the content box leaves exactly the rim. Same technique as
 * .lp-form-glow in lp-components.css, which is where it is explained at
 * length.
 *
 * It inherits the radius, so the rim follows whatever corner the button
 * has and needs no second value to keep in sync. It is inert to the
 * pointer and sits above the fill but only ever paints at the perimeter,
 * so it cannot come between the label and a click.
 */
.ao-btn::before {
	content: "";
	position: absolute;
	inset: 0;
	padding: var(--ao-btn-ring-w);
	border-radius: inherit;
	background: var(--ao-grad-btn-ring);
	opacity: .65;
	/* Keep only the rim: the fill minus the content box. */
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor;
	mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	mask-composite: exclude;
	pointer-events: none;
	transition: opacity var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-btn:hover::before,
.ao-btn:focus-visible::before {
	opacity: 1;
}

.ao-btn--gradient {
	background: var(--ao-grad-cta);
	box-shadow: 0 10px 24px -14px rgba(74, 40, 140, .55);
}

.ao-btn--ghost {
	background: transparent;
	border: 1px solid rgba(20, 18, 28, .18);
}

/* The solid counterpart to the gradient button, for the second
   action in a pair where two gradient pills would compete. */
.ao-btn--dark {
	background: var(--ao-ink, #14121c);
	color: #fff;
	box-shadow: 0 10px 24px -14px rgba(20, 18, 28, .7);
}

.ao-btn--dark .ao-btn__icon {
	color: #fff;
}

.ao-btn--dark .ao-eng__go {
	border: 1.5px solid #fff;
}

.ao-btn--dark .ao-eng__go {
	color: #fff !important;
}

.ao-btn--dark.ao-btn::before {
	background: #000;
}

.ao-btn:hover,
.ao-btn:focus-visible {
	transform: translateY(-2px);
	box-shadow: 0 16px 30px -16px rgba(74, 40, 140, .6);
}

.ao-btn:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: 3px;
}

/*
 * The ring around the arrow is drawn here rather than inside the SVG, so
 * the glyph can come from the registry and bring its animation with it.
 * 22px matches the r=11 circle the hand-drawn version used, so the
 * button's metrics are unchanged.
 */
.ao-btn__icon {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	border: 1.5px solid currentColor;
	border-radius: 50%;
	color: var(--ao-ink);
	transition:
		background-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/*
 * Only ever reaches the registry-off fallback: when the icon layer is
 * enqueued the glyph is wrapped in .ai-ico, and `.ai-ico .ai-svg` is the
 * more specific selector, so it sizes the svg to the wrapper instead.
 * Matched to .ai-ico--xs so the button looks the same either way.
 */
.ao-btn__icon svg {
	width: 16px;
	height: 16px;
}

/*
 * appagentix-icons.css paints .ai-ico with the muted --ai-color, and
 * repaints a hovered [data-ai-trigger] with the brand purple. Neither is
 * right on a button, so the glyph takes the button's own colour and only
 * the animation is taken from the trigger.
 */
.ao-btn .ai-ico,
.ao-btn:hover .ai-ico,
.ao-btn:focus-visible .ai-ico {
	color: inherit;
}

.ao-btn:hover .ao-btn__icon,
.ao-btn:focus-visible .ao-btn__icon {
	background: rgba(20, 18, 28, .06);
}

.ao-btn--dark:hover .ao-btn__icon,
.ao-btn--dark:focus-visible .ao-btn__icon {
	background: rgba(255, 255, 255, .14);
}

/* =====================================================================
 * 4. Hero - static
 *
 * One title, one line of copy, one call to action. The slider that used
 * to live here is gone: no stage, no slides, no controls, no JavaScript.
 *
 * THE BACKGROUND
 *
 * Three layers on one element, painted in this order (first = on top):
 *
 *   1. two colour washes - warm cream toward the top, a cool lilac at
 *      the lower left, both very low contrast
 *   2. the ruled grid, two repeating gradients at a 64px cell
 *   3. the warm off-white base
 *
 * The washes sit above the grid so the rules soften where the colour is
 * strongest, which is what stops the grid reading as a table. It is all
 * gradients - no image request, nothing animated, and it repaints only
 * on resize.
 *
 * THE HEADING
 *
 * `background-clip: text` with a horizontal gradient. Because the
 * gradient spans the heading's box rather than each line, every line
 * starts at the same orange and travels toward purple in proportion to
 * how far across the box it reaches - so the full-width first line ends
 * in purple while the shorter second line stops in magenta. That is the
 * behaviour the reference shows, and it comes free from the geometry.
 * ================================================================== */

/* Spacing only. The background belongs to .ao-home-bg on the wrapper. */
.ao-hero {
	position: relative;
	/* The video layer sits inside this box, so the hero owns a stacking
	   context of its own rather than competing with the page's own
	   background pseudo-element at z-index -1. */
	isolation: isolate;
	padding-block: clamp(44px, 2rem + 4.5vw, 96px) clamp(52px, 2rem + 5vw, 104px);
}

/* --- The background loop ---------------------------------------------
 *
 * The hero has no background of its own - the cream, the washes and the
 * ruled grid all belong to .ao-home-bg on the wrapper, and they still
 * do. This is a layer over that and under the copy, so everything the
 * page already paints is what shows through the footage.
 * ------------------------------------------------------------------- */

.ao-hero__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	/* Inert: the loop is decoration and must never intercept the button
	   sitting on top of it. */
	pointer-events: none;
}

.ao-hero__video {
	display: block;
	width: 100%;
	height: 100%;
	/* Cover, so a 2.33:1 clip crops rather than letterboxes when the
	   hero is tall and narrow on a phone. */
	object-fit: cover;
	object-position: center;
}

/*
 * Below desktop the hero turns tall and narrow, so `cover` keeps only a
 * thin vertical slice of the 2.33:1 clip - on a 390px phone about a
 * quarter of its width. Centred, that slice is mostly blurred factory
 * and cuts the robot arm off at the right edge. The arm's gripper, the
 * subject of the shot, sits about 74% across the frame, so the crop is
 * anchored there instead. The poster is this element's own poster, so
 * it follows the same framing before the video starts.
 */
@media (max-width: 1023px) {

	.ao-hero__video {
		object-position: 82% center;
	}
}

/*
 * A light wash, not a dark one. The heading is a gradient clipped to the
 * text and the lead is dark ink on cream; dimming the footage is what
 * keeps that typography exactly as approved. Weighted to the foot, where
 * the lead and the button sit and the contrast has to hold.
 */
.ao-hero__scrim {
	position: absolute;
	inset: 0;
	background:
		linear-gradient(180deg,
			rgba(253, 251, 248, .40) 0%,
			rgba(253, 251, 248, .48) 42%,
			rgba(253, 251, 248, .48) 100%);
}

/* The copy rides above both. */
.ao-hero__inner {
	position: relative;
	z-index: 1;
}

/*
 * The homepage background layer.
 *
 * One pseudo-element on the page wrapper, painted once and shared by
 * every section, rather than the same gradients repeated per section.
 * Add `ao-home-bg` to any page's body to get the same treatment.
 *
 * WHY A PSEUDO-ELEMENT AT z-index -1
 *
 * No extra DOM, and `isolation: isolate` on the wrapper gives it a
 * stacking context so the negative index lands above the wrapper's own
 * background but below every piece of content - including static,
 * unpositioned content, which a `z-index: 0` layer would paint over.
 * It is inert to the pointer, so nothing on the page changes what it
 * can be clicked through to.
 *
 * WHY THE WASHES CARRY A background-size
 *
 * Both are sized in percent of the painted box. Across a whole document
 * that is thousands of pixels, which spreads them until they are not
 * visible. Confining them to a band keeps them the size the hero showed
 * while the two grid layers tile the full page.
 *
 * Cost: four gradients on one element that nothing animates, so it is
 * rasterised once and only repaints on resize.
 */
.ao-home-bg {
	position: relative;
	isolation: isolate;
	background: var(--ao-hero-bg);
}

.ao-home-bg::before {
	content: "";
	position: absolute;
	z-index: -1;
	inset: 0;
	background-image:
		/* Warm wash, top of the page. */
		radial-gradient(60% 55% at 26% 0%, rgba(255, 214, 158, .34), transparent 70%),
		/* Cool wash, under the hero's call to action. */
		radial-gradient(46% 52% at 8% 96%, rgba(198, 176, 255, .26), transparent 72%),
		/*
		 * The ruled grid: ONE cell each way, tiled by background-repeat.
		 *
		 * These were repeating-linear-gradients sized `auto`, which makes
		 * the pattern span the element and be rasterised across its whole
		 * width in one pass. Rules that then land on a fractional device
		 * pixel are antialiased away completely, so lines went missing and
		 * the cells measured 143px where two had merged - at 1024, 1280 and
		 * 1920, while 390, 768 and 1440 happened to divide evenly and
		 * looked correct.
		 *
		 * Painting a single cell and letting the browser tile it means one
		 * rasterisation repeated at integer offsets, so every cell is
		 * identical at every width. It is also cheaper: one small tile in
		 * the cache instead of a page-wide gradient.
		 */
		linear-gradient(90deg, var(--ao-hero-grid) 0 1px, transparent 1px),
		linear-gradient(180deg, var(--ao-hero-grid) 0 1px, transparent 1px);
	background-repeat: no-repeat, no-repeat, repeat, repeat;
	background-size:
		100% var(--ao-home-wash),
		100% var(--ao-home-wash),
		var(--ao-hero-cell) var(--ao-hero-cell),
		var(--ao-hero-cell) var(--ao-hero-cell);
	background-position: 0 0, 0 0, 0 0, 0 0;
	pointer-events: none;
	opacity: 0.4;
}

/* .ao-hero__inner takes its geometry from layout.css. */

/*
 * The measure is what makes the heading break after "&" and gives the
 * gradient a box to run across. Widening it past this puts the break in
 * the wrong place and leaves the first line short of purple.
 */
.ao-hero__copy {
	max-width: 44.5rem;
}

.ao-hero__title {
	margin: 0;
	/* Shared display scale - see layout.css. */
	font-size: var(--display-size);
	line-height: var(--display-lh);
	font-weight: var(--display-weight);
	letter-spacing: var(--display-ls);
	background: var(--ao-grad-hero);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	/* Not balanced: the reference has a long first line and a short
	   second one, which balancing would even out. */
	text-wrap: pretty;
}
.ao-hero__title span {
	color: #000000;
}
/* A tinted phrase inside an already-tinted heading has nothing to say,
   so <em> only drops the italic and inherits the gradient. */
.ao-hero__title em {
	font-style: normal;
}

.ao-hero__lead {
	margin: 22px 0 0;
	max-width: 38rem;
	font-size: clamp(.98rem, .94rem + 1.4rem, 1.4rem);
	line-height: 1.68;
	color: var(--ao-ink-soft);
}

.ao-hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 14px;
	margin-top: 32px;
}

/* --- Reveal ---------------------------------------------------------
 *
 * The hero is above the fold, so it cannot wait on the scroll observer
 * that the sections below use. It plays once on load instead: a short
 * rise, staggered by line, and nothing after that. No continuous
 * animation anywhere in this section.
 * ------------------------------------------------------------------ */

.ao-hero__title,
.ao-hero__lead,
.ao-hero__actions {
	animation: ao-hero-rise .6s var(--ao-ease, cubic-bezier(.4, 0, .2, 1)) both;
}

.ao-hero__lead {
	animation-delay: .08s;
}

.ao-hero__actions {
	animation-delay: .16s;
}

@keyframes ao-hero-rise {
	from {
		opacity: 0;
		transform: translate3d(0, 14px, 0);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@media (max-width: 767px) {

	.ao-hero {
		padding-block: 36px 44px;
	}

	.ao-hero__lead {
		margin-top: 18px;
	}

	.ao-hero__actions {
		margin-top: 26px;
	}

	/* Full width, because a 10px-radius pill floating in a narrow column
	   reads as an afterthought. */
	.ao-hero__actions .ao-btn {
		justify-content: center;
		width: 100%;
	}
}

/* =====================================================================
 * 5. Service panel
 * ================================================================== */

.ao-service {
	background: #f1f0f5;
	border: 1px solid rgba(20, 18, 28, .07);
	border-radius: var(--ao-r-lg, 20px);
	overflow: hidden;
}

.ao-service__media {
	aspect-ratio: 4 / 3;
	background: #e7e5ee;
}

.ao-service__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.ao-service__body {
	padding: 22px 24px 26px;
}

.ao-service__category {
	margin: 0 0 8px;
	font-size: .74rem;
	font-weight: 700;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--color-border-blue, #8347ec);
}

.ao-service__title {
	margin: 0;
	font-size: 1.15rem;
	font-weight: 700;
	color: var(--ao-ink);
}

.ao-service__desc {
	margin: 8px 0 0;
	font-size: .94rem;
	line-height: 1.55;
	color: var(--ao-ink-soft);
}

/* =====================================================================
 * 6. Marquee
 * ================================================================== */

.ao-marquee {
	position: relative;
	overflow: hidden;
	/* Fades the logos out at both edges instead of cutting them dead. */
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

.ao-marquee__viewport {
	display: flex;
	width: max-content;
}

.ao-marquee__track {
	display: flex;
	align-items: center;
	gap: 22px;
	margin: 0;
	padding: 0 11px;
	list-style: none;
	/* -50% lands the duplicate exactly where the original began, which is
	   what makes the loop seamless. */
	animation: ao-marquee-scroll var(--ao-marquee-speed, 40s) linear infinite;
}

.ao-marquee__item {
	flex: 0 0 auto;
	margin: 0;
}

/* The card - translucent fill, light border, rounded corners - is part of
   each PNG, so nothing is drawn around it here. */
.ao-marquee__item img {
	display: block;
	height: 56px;
	width: auto;
}

@keyframes ao-marquee-scroll {
	from {
		transform: translate3d(0, 0, 0);
	}

	to {
		transform: translate3d(-50%, 0, 0);
	}
}

.ao-marquee--pause:hover .ao-marquee__track,
.ao-marquee--pause:focus-within .ao-marquee__track {
	animation-play-state: paused;
}

/* The shared in-view observer adds .is-visible. Until it does, the track
   is not animating, so an off-screen band costs no compositor time. */
.ao-marquee[data-inview]:not(.is-visible) .ao-marquee__track {
	animation-play-state: paused;
}

@media (max-width: 767px) {
	.ao-marquee__track {
		gap: 14px;
		padding-inline: 7px;
	}

	.ao-marquee__item img {
		height: 44px;
	}
}

/* =====================================================================
 * 7. Category carousel
 * ================================================================== */

.ao-work {
	display: grid;
	/* A grid item defaults to min-width:auto, which lets the very wide
	   track dictate the column size and push the page sideways. Every
	   track in this component is inside overflow:hidden, so the columns
	   are told explicitly that they may shrink. */
	grid-template-columns: minmax(0, 1fr);
	gap: 40px;
	/* --ao-work-per is how many cards are on screen. The track, the slide
	   width and the page count all derive from it, so a breakpoint only
	   has to change this one number. */
	--ao-work-per: 1;
	--ao-work-gap: 24px;
}

@media (min-width: 1024px) {
	.ao-work {
		grid-template-columns: 310px minmax(0, 1fr);
		gap: 56px;
		align-items: start;
		--ao-work-per: 2;
	}
}

/* --- Rail ---------------------------------------------------------- */

.ao-work__tabs {
	display: flex;
	flex-direction: column;
}

.ao-work__tab {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	padding: 18px 20px;
	border: 0;
	border-radius: 10px;
	background: transparent;
	color: var(--ao-ink);
	font-size: 1.12rem;
	font-weight: 700;
	line-height: 1.25;
	text-align: left;
	cursor: pointer;
	transition: background-color var(--ao-dur, .28s) var(--ao-ease, ease);
}

/* A rule between items, but not under the last one and not under the
   active item, whose filled panel already separates it. */
.ao-work__tab+.ao-work__tab {
	border-top: 1px solid rgba(20, 18, 28, .12);
}

.ao-work__tab.is-active,
.ao-work__tab.is-active+.ao-work__tab {
	border-top-color: transparent;
}

.ao-work__tab.is-active {
	background: #f1ecfd;
	color: var(--ao-ink);
}

.ao-work__tab.is-active .ao-work__tab-icon {
	color: var(--color-border-blue, #8347ec);
}

.ao-work__tab:hover:not(.is-active) {
	background: rgba(20, 18, 28, .04);
}

.ao-work__tab:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: -2px;
}

.ao-work__tab-icon {
	display: inline-flex;
	flex: 0 0 auto;
	color: var(--ao-ink);
}

.ao-work__cta {
	margin-top: 28px;
}

/* --- Stage --------------------------------------------------------- */

.ao-work__stage {
	position: relative;
	min-width: 0;
}

.ao-work__rail {
	min-width: 0;
}

.ao-work__tabs {
	min-width: 0;
}

/* overflow:hidden alone is not enough once this is a grid item. */
.ao-work__viewport {
	min-width: 0;
}

/* The viewport clips the track. Arrows sit outside it and overlap the
   edges, which is why the stage is the positioning context, not this. */
.ao-work__viewport {
	overflow: hidden;
}

.ao-work__panel[hidden] {
	display: none;
}

.ao-work__track {
	display: flex;
	gap: var(--ao-work-gap);
	/* Only transform animates, so a slide change never triggers layout. */
	transition: transform .55s var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
	will-change: transform;
}

/* The script adds this for the silent rewind at the loop boundary. */
.ao-work__track.is-instant {
	transition: none;
}

.ao-work__slide {
	flex: 0 0 calc((100% - (var(--ao-work-per) - 1) * var(--ao-work-gap)) / var(--ao-work-per));
	min-width: 0;
}

/* --- Card ---------------------------------------------------------- */

.ao-work-card {
	display: block;
	height: 100%;
	padding: 12px 12px 18px;
	border-radius: 16px;
	background: linear-gradient(158deg, #7b3fe4 0%, #8c45e8 46%, #6f2fd6 100%);
	color: #fff;
	text-decoration: none;
	box-shadow: 0 18px 40px -22px rgba(74, 40, 140, .7);
}

.ao-work-card__media {
	border-radius: 12px;
	overflow: hidden;
	/* Reserves the space before the image arrives, so nothing shifts. */
	aspect-ratio: 668 / 418;
}

.ao-work-card__media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.ao-work-card__body {
	display: flex;
	align-items: center;
	gap: 12px;
	margin-top: 20px;
}

.ao-work-card__badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: 34px;
	height: 34px;
	border-radius: 9px;
	border: 1px solid rgba(255, 255, 255, .28);
	background: rgba(255, 255, 255, .14);
	color: #fff;
}

.ao-work-card__badge svg {
	width: 18px;
	height: 18px;
}

.ao-work-card__title {
	margin: 0;
	font-size: 1.4rem;
	font-weight: 600;
	line-height: 1.2;
	color: #fff;
}

.ao-work-card__desc {
	margin: 12px 0 0;
	font-size: .95rem;
	line-height: 1.5;
	color: rgba(255, 255, 255, .86);
}

/* --- Arrows and dots ----------------------------------------------- */

.ao-work__arrow {
	position: absolute;
	top: 50%;
	z-index: 2;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* 44px keeps the tap target comfortable; the visual box is smaller
	   on desktop via the inner icon, matching the reference. */
	width: 34px;
	height: 44px;
	padding: 0;
	transform: translateY(-50%);
	border: 1px solid rgba(20, 18, 28, .1);
	border-radius: 8px;
	background: #f3f3f5;
	color: #3f3d4d;
	cursor: pointer;
	transition:
		background-color var(--ao-dur, .28s) var(--ao-ease, ease),
		border-color var(--ao-dur, .28s) var(--ao-ease, ease);
}

.ao-work__arrow:hover {
	background: #e7e5ee;
}

.ao-work__arrow:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: 2px;
}

.ao-work__arrow--prev {
	left: -17px;
}

.ao-work__arrow--next {
	right: -17px;
}

.ao-work__dots {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 4px;
	margin-top: 22px;
}

.ao-work__dot {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 20px;
	height: 32px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.ao-work__dot span {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: rgba(20, 18, 28, .22);
	transition: background-color var(--ao-dur, .28s) var(--ao-ease, ease);
}

.ao-work__dot.is-active span {
	background: var(--ao-ink);
}

.ao-work__dot:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: 2px;
}

/* --- Below desktop -------------------------------------------------- */

@media (max-width: 1023px) {

	/* The rail becomes a horizontal snap-scroller so the categories stay
	   one tap away without consuming a column. */
	.ao-work__tabs {
		flex-direction: row;
		gap: 8px;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		scrollbar-width: none;
		padding-bottom: 4px;
	}

	.ao-work__tabs::-webkit-scrollbar {
		display: none;
	}

	.ao-work__tab {
		flex: 0 0 auto;
		width: auto;
		padding: 12px 16px;
		font-size: .98rem;
		border-top: 0;
		border: 1px solid rgba(20, 18, 28, .12);
		scroll-snap-align: start;
	}

	.ao-work__tab+.ao-work__tab {
		border-top: 1px solid rgba(20, 18, 28, .12);
	}

	.ao-work__tab.is-active {
		border-color: transparent;
	}

	.ao-work__cta {
		margin-top: 20px;
	}

	/* Inside the padded section the arrows would sit on top of the cards,
	   so they move under the track beside the dots. */
	.ao-work__arrow {
		position: static;
		transform: none;
	}

	.ao-work__dots {
		gap: 10px;
		margin-top: 18px;
	}

	.ao-work__stage {
		display: grid;
		grid-template-areas:
			"viewport viewport viewport"
			"prev     dots     next";
		grid-template-columns: auto minmax(0, 1fr) auto;
		align-items: center;
		gap: 4px;
	}

	.ao-work__viewport {
		grid-area: viewport;
	}

	.ao-work__arrow--prev {
		grid-area: prev;
	}

	.ao-work__arrow--next {
		grid-area: next;
	}

	.ao-work__dots {
		grid-area: dots;
		margin-top: 0;
	}
}

@media (max-width: 767px) {
	.ao-work {
		gap: 28px;
		--ao-work-gap: 16px;
	}

	.ao-work-card {
		padding: 16px 16px 20px;
	}

	.ao-work-card__title {
		font-size: 1.2rem;
	}

	.ao-work-card__desc {
		font-size: .9rem;
	}
}


/* =====================================================================
 * 8. Split band - claim beside the points that back it
 *
 * Two columns divided by a hairline, the points stacked as equal rows.
 *
 * WHY THE ROWS ARE GRID-SIZED
 *
 * `grid-auto-rows: minmax(0, 1fr)` divides the column by the number of
 * rows rather than by their content, so three points split it in thirds
 * and the two rules land exactly on the third and two-third marks. It
 * also means a fourth point needs no change here: the rows simply become
 * quarters. Sizing them by content would leave the rules at uneven
 * heights and make the column look accidental.
 *
 * The section wrapper is given `space: none` for this band, because the
 * dividing rules have to run the full height of the purple. The vertical
 * rhythm is applied inside the columns instead - same token, one level
 * down - which is what lets the rule reach the top and bottom edges
 * while the copy stays inset.
 * ================================================================== */

.ao-split {
	display: grid;
	/* 52/48 rather than half and half: the claim is the larger type and
	   needs the wider measure, the rows are short lines. */
	grid-template-columns: minmax(0, 52fr) minmax(0, 48fr);
}

/* --- Left column: the claim ---------------------------------------- */

.ao-split__copy {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-width: 0;
	padding-block: var(--ao-space-section);
	padding-inline: 0 clamp(28px, 4vw, 56px);
}

.ao-split__title {
	margin: 0;
	/* Deliberately larger than .ao-section__title: this band carries no
	   section heading above it, so the claim IS the heading. */
	font-size: clamp(2rem, 1.2rem + 2.7vw, 3.25rem);
	line-height: 1.12;
	font-weight: 800;
	letter-spacing: -.025em;
	color: #fff;
	text-wrap: balance;
}

.ao-split__body {
	margin: clamp(18px, 2.2vw, 28px) 0 0;
	max-width: 46ch;
	font-size: clamp(.95rem, .9rem + .15vw, 1.05rem);
	line-height: 1.65;
	color: rgba(255, 255, 255, .82);
}

/* --- Right column: the points -------------------------------------- */

.ao-split__list {
	display: grid;
	grid-auto-rows: minmax(0, 1fr);
	min-width: 0;
	margin: 0;
	padding: 0;
	list-style: none;
	border-left: 1px solid rgba(255, 255, 255, .17);
}

.ao-split__item {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-width: 0;
	padding: clamp(20px, 2.6vw, 30px) 0 clamp(20px, 2.6vw, 30px) clamp(24px, 3.4vw, 46px);
	/* The tint is painted on a pseudo-element rather than the row, so the
	   hover cannot bleed over the hairlines the rows share. */
	position: relative;
	isolation: isolate;
	transition: transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-split__item+.ao-split__item {
	border-top: 1px solid rgba(255, 255, 255, .17);
}

.ao-split__item::before {
	content: "";
	position: absolute;
	z-index: -1;
	inset: 0;
	background: linear-gradient(90deg, rgba(255, 255, 255, .09), rgba(255, 255, 255, 0));
	opacity: 0;
	transition: opacity var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-split__item:hover::before,
.ao-split__item:focus-within::before {
	opacity: 1;
}

.ao-split__head {
	display: flex;
	align-items: center;
	gap: 12px;
	min-width: 0;
}

.ao-split__icon {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	border-radius: 9px;
	border: 1px solid rgba(255, 255, 255, .24);
	background: rgba(255, 255, 255, .12);
	color: #fff;
	transition:
		background var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-split__item:hover .ao-split__icon,
.ao-split__item:focus-within .ao-split__icon {
	border-color: rgba(255, 255, 255, .45);
	background: rgba(255, 255, 255, .2);
}

.ao-split__icon svg {
	width: 18px;
	height: 18px;
}

/*
 * appagentix-icons.css paints .ai-ico with --ai-color, and repaints a
 * hovered [data-ai-trigger] with --ai-color-hover - the brand purple,
 * which is invisible on this band. The icon takes the chip's colour
 * instead; the animation still plays, which is the part worth having.
 */
.ao-split__item .ai-ico,
.ao-split__item:hover .ai-ico,
.ao-split__item:focus-within .ai-ico {
	color: inherit;
}

.ao-split__item-title {
	margin: 0;
	min-width: 0;
	font-size: clamp(1.05rem, .95rem + .45vw, 1.35rem);
	line-height: 1.25;
	font-weight: 700;
	letter-spacing: -.015em;
	color: #fff;
}

.ao-split__item-text {
	margin: 10px 0 0;
	font-size: clamp(.85rem, .82rem + .12vw, .93rem);
	line-height: 1.55;
	color: rgba(255, 255, 255, .78);
}

/* --- Tablet and below: the columns stack --------------------------- */

@media (max-width: 1023px) {

	.ao-split {
		grid-template-columns: minmax(0, 1fr);
	}

	.ao-split__copy {
		padding-inline: 0;
		padding-block: var(--ao-space-section) clamp(24px, 4vw, 36px);
	}

	/*
	 * Stacked, the rule that divided the columns becomes the rule that
	 * separates the claim from the points.
	 */
	.ao-split__list {
		border-left: 0;
		border-top: 1px solid rgba(255, 255, 255, .17);
		padding-bottom: var(--ao-space-section);
	}

	.ao-split__item {
		padding-inline: 0;
	}

	/* Content-sized once stacked: equal thirds of a tall column would
	   stretch three short rows into three tall empty ones. */
	.ao-split__list {
		grid-auto-rows: auto;
	}

	.ao-split__item::before {
		background: linear-gradient(90deg, rgba(255, 255, 255, .09), rgba(255, 255, 255, 0) 70%);
	}
}

@media (max-width: 767px) {

	.ao-split__head {
		gap: 10px;
	}

	.ao-split__icon {
		width: 30px;
		height: 30px;
		border-radius: 8px;
	}

	.ao-split__icon svg {
		width: 16px;
		height: 16px;
	}

	.ao-split__item-text {
		margin-top: 8px;
	}
}

/* =====================================================================
 * 9. Tinted section heading
 *
 * The same warm-to-cool ramp the hero heading uses, so a section title
 * can carry it without the hero's display size. Opt in per section with
 * `'class' => 'ao-section--tinted'`; nothing else changes.
 * ================================================================== */

.ao-section--tinted .ao-section__title {
	/* inline-block so the box shrinks to the text. Left as a block it
	   spans the full 1200px container while the centred text sits in the
	   middle, so the words only sample the middle of the ramp and the
	   heading opens on coral instead of orange. */
	display: inline-block;
	background: var(--ao-grad-hero);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

/* =====================================================================
 * 10. Solution slider
 *
 * One card, N slides, copy on the left and a bordered panel on the
 * right.
 *
 * WHY THE SLIDES ARE GRID-STACKED
 *
 * Every slide sits in the same grid cell, so the stage reserves the
 * height of the tallest one and changing slide cannot move the page
 * beneath it. Slides differ in length here, so a track that sized
 * itself to the active slide would jolt the page on every change.
 *
 * It never advances on its own. The controls are the only way to move.
 * ================================================================== */

.ao-sol {
	position: relative;
	padding: clamp(20px, 2.4vw, 32px) clamp(18px, 2.4vw, 32px) clamp(16px, 1.8vw, 22px);
	border-radius: 18px;
	border: 1px solid rgba(20, 18, 28, .08);
	background: var(--surface, #fff);
	box-shadow: 0 20px 44px -30px rgba(74, 40, 140, .4);
}

.ao-sol__stage {
	display: grid;
	grid-template-areas: "slide";
}

.ao-sol__slide {
	grid-area: slide;
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	align-items: start;
	gap: clamp(20px, 3vw, 44px);
	opacity: 0;
	transform: translateY(10px);
	transition:
		opacity .45s var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		transform .45s var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
	pointer-events: none;
}

.ao-sol__slide.is-active {
	opacity: 1;
	transform: none;
	pointer-events: auto;
}

/* [hidden] must not beat the grid placement, or the stage collapses to
   one slide's height and the page jumps on every change. */
.ao-sol__slide[hidden] {
	display: grid;
	visibility: hidden;
}

/* --- Left: the copy ------------------------------------------------ */

.ao-sol__copy {
	min-width: 0;
	padding-block: clamp(4px, 1vw, 14px);
}

.ao-sol__title {
	margin: 0;
	font-size: clamp(1.45rem, 1.1rem + 1.2vw, 2rem);
	line-height: 1.2;
	font-weight: 700;
	letter-spacing: -.02em;
	color: var(--ao-ink);
}

.ao-sol__text {
	margin: 14px 0 0;
	font-size: 20px;
	line-height: 1.62;
	color: var(--ao-ink-soft);
}

.ao-sol__actions {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 10px;
	margin-top: clamp(18px, 2vw, 26px);
}

/* --- Right: the panel ---------------------------------------------- */

.ao-sol__panel {
	min-width: 0;
	padding: clamp(16px, 1.8vw, 22px);
	border-radius: 14px;
	/* Warm rim on a cool fill - the panel treatment from the reference. */
	border: 1px solid var(--brand-warm, #ffa15c);
	background: linear-gradient(160deg, #fbf7ff 0%, #f6f1ff 100%);
}

.ao-sol__panel-head {
	display: flex;
	align-items: center;
	gap: 11px;
	margin-bottom: clamp(14px, 1.6vw, 20px);
}

.ao-sol__panel-icon {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	border-radius: 9px;
	border: 1px solid #000;
	background: transparent;
	color: var(--brand, #000);
	transition:
		background var(--ao-dur, .28s) var(--ao-ease, ease),
		border-color var(--ao-dur, .28s) var(--ao-ease, ease);
}

.ao-sol__panel:hover .ao-sol__panel-icon {
	border-color: #000;
	background: transparent;
}

.ao-sol__panel-icon svg {
	width: 18px;
	height: 18px;
}

/* appagentix-icons.css repaints a hovered [data-ai-trigger]'s icon with
   --ai-color-hover, which is this same purple - so the colour is pinned
   and only the animation is taken from the trigger. */
.ao-sol__panel .ai-ico,
.ao-sol__panel:hover .ai-ico {
	color: inherit;
}

.ao-sol__panel-title {
	margin: 0;
	min-width: 0;
	font-size: clamp(1.05rem, .95rem + .4vw, 1.25rem);
	line-height: 1.25;
	font-weight: 700;
	letter-spacing: -.015em;
	color: var(--ao-ink);
}

.ao-sol__points {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: clamp(14px, 1.6vw, 20px) clamp(14px, 1.8vw, 24px);
	margin: 0;
	padding: 0;
	list-style: none;
}

.ao-sol__point {
	min-width: 0;
	font-size: 18px;
	line-height: 1.5;
	color: var(--ao-ink-soft);
}

.ao-sol__point-label {
	font-weight: 700;
	color: var(--ao-ink);
}

/* --- Controls ------------------------------------------------------ */

.ao-sol__nav {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	margin-top: clamp(16px, 2vw, 24px);
}

.ao-sol__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* The visible box is small; the button stays 32px so the tap target
	   is never smaller than a fingertip. */
	width: 30px;
	height: 32px;
	padding: 0;
	border: 1px solid rgba(20, 18, 28, .1);
	border-radius: 8px;
	background: #f3f3f5;
	color: #3f3d4d;
	cursor: pointer;
	transition:
		background-color var(--ao-dur, .28s) var(--ao-ease, ease),
		border-color var(--ao-dur, .28s) var(--ao-ease, ease);
}

.ao-sol__arrow:hover {
	border-color: rgba(101, 77, 255, .45);
	background: #ece9ff;
}

.ao-sol__arrow svg {
	width: 15px;
	height: 15px;
}

.ao-sol__arrow .ai-ico {
	color: inherit;
}

.ao-sol__dots {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 3px;
}

.ao-sol__dot {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 18px;
	height: 32px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.ao-sol__dot span {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: rgba(20, 18, 28, .22);
	transition:
		background-color var(--ao-dur, .28s) var(--ao-ease, ease),
		width var(--ao-dur, .28s) var(--ao-ease, ease);
}

.ao-sol__dot.is-active span {
	width: 18px;
	border-radius: 999px;
	background: var(--brand, #654dff);
}

.ao-sol__arrow:focus-visible,
.ao-sol__dot:focus-visible {
	outline: 2px solid var(--brand, #654dff);
	outline-offset: 2px;
}

/* --- Responsive ----------------------------------------------------- */

@media (max-width: 899px) {

	/* Stacked: the panel reads as support for the copy above it rather
	   than a second column competing with it. */
	.ao-sol__slide {
		grid-template-columns: minmax(0, 1fr);
	}

	.ao-sol__slide[hidden] {
		display: grid;
	}
}

@media (max-width: 599px) {

	.ao-sol__points {
		grid-template-columns: minmax(0, 1fr);
	}

	.ao-sol__actions .ao-btn {
		justify-content: center;
		width: 100%;
	}
}

/* =====================================================================
 * 11. Stage cards
 *
 * Auto-fit rather than two fixed columns: a third stage needs no change
 * here, and the cards stack on their own below the track width without
 * a second rule doing it by hand.
 * ================================================================== */

.ao-stages {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
	gap: clamp(18px, 2.2vw, 28px);
}

.ao-stage {
	display: flex;
	flex-direction: column;
	min-width: 0;
	padding: clamp(22px, 2.6vw, 34px);
	border-radius: 18px;
	border: 1px solid rgba(20, 18, 28, .08);
	background: var(--surface, #fff);
	box-shadow: 0 20px 44px -30px rgba(74, 40, 140, .4);
	transition:
		transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		box-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-stage:hover,
.ao-stage:focus-within {
	transform: translateY(-3px);
	border-color: rgba(101, 77, 255, .22);
	box-shadow: 0 26px 52px -28px rgba(74, 40, 140, .5);
}

.ao-stage__title {
	margin: 0;
	font-size: clamp(1.5rem, 1.15rem + 1.3vw, 2.05rem);
	line-height: 1.18;
	font-weight: 700;
	letter-spacing: -.022em;
	color: var(--ao-ink);
}

.ao-stage__text {
	margin: 14px 0 0;
	font-size: clamp(.9rem, .87rem + .12vw, .97rem);
	line-height: 1.66;
	color: var(--ao-ink-soft);
}

.ao-stage__list-title {
	margin: clamp(16px, 1.8vw, 22px) 0 10px;
	font-size: .92rem;
	font-weight: 700;
	color: var(--brand-warm, #ffa15c);
}

.ao-stage__list {
	display: flex;
	flex-direction: column;
	gap: 9px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * The reference marks these with a plain bullet. They are registry
 * icons here instead, per the brief's ask for the theme's icon system -
 * same list, same rhythm, a marker that belongs to the design system.
 */
.ao-stage__item {
	display: flex;
	align-items: flex-start;
	gap: 9px;
	min-width: 0;
	font-size: clamp(.88rem, .85rem + .12vw, .95rem);
	line-height: 1.55;
	color: var(--ao-ink-soft);
}

.ao-stage__item-icon {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* Optically centred on the first line of text rather than its box. */
	margin-top: .18em;
	color: var(--brand, #654dff);
}

.ao-stage__item-icon svg {
	width: 15px;
	height: 15px;
}

.ao-stage .ai-ico,
.ao-stage:hover .ai-ico {
	color: inherit;
}

/* margin-top:auto pins the button to the card's floor, so two cards of
   different lengths still line their buttons up. */
.ao-stage__foot {
	display: flex;
	justify-content: flex-end;
	margin-top: auto;
	padding-top: clamp(18px, 2.2vw, 28px);
}

@media (max-width: 479px) {

	.ao-stage__foot {
		justify-content: stretch;
	}

	.ao-stage__foot .ao-btn {
		justify-content: center;
		width: 100%;
	}
}

/* =====================================================================
 * 12. Capability areas - a disclosure list beside the section's opening
 *
 * The left column states the section - video, heading, lead, button -
 * and the right column lists the six areas. This is the one section on
 * the page whose heading is not the wrapper's centred .ao-section__head:
 * the reference puts it under the video, on the measure of the column,
 * so the wrapper is given no heading and the H2 lives here instead.
 *
 * WHY THE ROWS ARE RULES, NOT CARDS
 *
 * Only the open row is a card. The rest are hairline-separated rows, so
 * the list reads as one column of choices rather than six competing
 * boxes, and opening a row is a visible change of state rather than a
 * card that merely got taller.
 *
 * WHY THE PANEL HEIGHT IS AN fr UNIT
 *
 * The same technique as the FAQ accordion in appagentix-motion.css:
 * `grid-template-rows: 0fr -> 1fr` animates to the copy's real height,
 * survives a resize and needs no measurement in JavaScript. max-height
 * would need a number the stylesheet cannot know.
 * ================================================================== */

.ao-cap {
	display: grid;
	/* 55/45: the left column holds the display heading and the video and
	   needs the wider measure; the rows are single lines. */
	grid-template-columns: minmax(0, 55fr) minmax(0, 45fr);
	gap: clamp(28px, 3.6vw, 60px);
	align-items: start;
}

/* --- Left column: video, heading, lead, button ---------------------- */

.ao-cap__intro {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.ao-cap__title {
	margin: clamp(24px, 3vw, 38px) 0 0;
	/* Display type, like the hero's: this heading opens the section on
	   its own rather than sitting above a centred lead. */
	font-size: clamp(1.9rem, 1.15rem + 2.5vw, 2.85rem);
	line-height: 1.14;
	font-weight: 800;
	letter-spacing: -.022em;
	/* The hero's warm-to-cool ramp, clipped to the glyphs. Same token as
	   .ao-section--tinted, which cannot be used here because that rule
	   targets the wrapper's heading and this one is the component's. */
	background: var(--ao-grad-hero);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	text-wrap: balance;
}

.ao-cap__lead {
	margin: clamp(14px, 1.8vw, 20px) 0 0;
	max-width: 48ch;
	font-size: clamp(.9rem, .87rem + .14vw, .98rem);
	line-height: 1.62;
	color: var(--ao-ink-soft);
}

/* Left-aligned under the lead, not centred under the section: it closes
   this column rather than the whole band. margin-top:auto would pin it
   to the taller column's floor, which is why it is a fixed step. */
.ao-cap__foot {
	display: flex;
	margin-top: clamp(20px, 2.4vw, 30px);
}

/* --- The video ------------------------------------------------------ */

.ao-cap__media {
	/*
	 * One token for the play button and the halo around it, so the two
	 * cannot drift apart at a breakpoint. A viewport clamp was wrong
	 * here: the frame is half the page on desktop and all of it on a
	 * tablet, so the same vw gave the widest frame the smallest button.
	 */
	--ao-cap-play: 64px;

	position: relative;
	/* Set from the poster's intrinsic size in the template, so the box is
	   the right shape before the image decodes and the player lands in a
	   frame that is already reserved. */
	aspect-ratio: var(--ao-cap-ratio, 16 / 9);
	width: 100%;
	overflow: hidden;
	border-radius: 18px;
	background: var(--ao-ink, #14121c);
	isolation: isolate;
}

.ao-cap__poster {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform var(--dur-slow, .45s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/*
 * A scrim only where the button is. The reference leaves the poster its
 * own colour, so this is a soft pool under the ring rather than a wash
 * over the image - enough to keep a white ring legible on a light frame,
 * not enough to darken the picture.
 */
.ao-cap__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background: radial-gradient(circle at 50% 50%, rgba(20, 18, 28, .34), rgba(20, 18, 28, 0) 42%);
	opacity: 1;
	transition: opacity var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
	pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {

	.ao-cap__media:hover .ao-cap__poster {
		transform: scale(1.03);
	}
}

/*
 * The button covers the whole frame, so a click anywhere on the poster
 * plays it - the ring is the affordance, not the hit area. z-index lifts
 * it clear of the scrim, which is a pseudo-element and therefore paints
 * after it in the box tree.
 */
.ao-cap__play {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	border: 0;
	background: transparent;
	color: #fff;
	cursor: pointer;
}

/* A hairline ring, not a filled pill: the reference keeps the poster
   visible through it. */
.ao-cap__play-mark {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--ao-cap-play);
	height: var(--ao-cap-play);
	/* A triangle's visual centre sits left of its bounding box, so the
	   glyph is nudged right to look centred in the circle. */
	padding-left: 4px;
	border-radius: 999px;
	border: 1.5px solid rgba(255, 255, 255, .92);
	background: rgba(255, 255, 255, .06);
	color: #fff;
	backdrop-filter: blur(2px);
	box-shadow: 0 10px 26px -12px rgba(20, 18, 28, .8);
	transition:
		transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		background var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-cap__play .ai-ico,
.ao-cap__play:hover .ai-ico,
.ao-cap__play:focus-visible .ai-ico {
	color: inherit;
}

/*
 * The halo. A ring that breathes outwards, keyed to .is-visible from the
 * shared in-view observer so it costs nothing while the section is off
 * screen - the same gate the marquees and the CTA aurora use.
 */
.ao-cap__play-halo {
	position: absolute;
	top: 50%;
	left: 50%;
	width: var(--ao-cap-play);
	height: var(--ao-cap-play);
	margin: calc(var(--ao-cap-play) / -2) 0 0 calc(var(--ao-cap-play) / -2);
	border-radius: 999px;
	border: 1px solid rgba(255, 255, 255, .45);
	opacity: 0;
	pointer-events: none;
}

.ao-cap__media.is-visible .ao-cap__play-halo {
	animation: ao-cap-halo 2.8s var(--ease, cubic-bezier(.4, 0, .2, 1)) infinite;
}

@keyframes ao-cap-halo {

	0% {
		transform: scale(1);
		opacity: .5;
	}

	70% {
		transform: scale(1.5);
		opacity: 0;
	}

	100% {
		transform: scale(1.5);
		opacity: 0;
	}
}

@media (hover: hover) and (pointer: fine) {

	.ao-cap__play:hover .ao-cap__play-mark {
		transform: scale(1.07);
		background: rgba(255, 255, 255, .16);
	}
}

.ao-cap__play:focus-visible {
	outline: none;
}

.ao-cap__play:focus-visible .ao-cap__play-mark {
	outline: 3px solid #fff;
	outline-offset: 4px;
}

/* Once the player is in, the poster, the scrim and the button are gone
   and the player has the frame to itself. */
.ao-cap__media.is-playing::after {
	opacity: 0;
}

/*
 * The script attaches the player on the first sign of intent, before the
 * click, so the first bytes are already in when play is pressed. Until
 * the click it is present but not shown and not reachable: the frame is
 * still the poster and the one play button, which is the whole point of
 * the facade. It carries the poster as its own `poster` too, so the swap
 * on .is-playing shows the same image until the first frame decodes.
 *
 * object-fit: cover matches the poster's framing - the file is 16:9 and
 * the frame takes its ratio from the poster, so the two are close but
 * not identical, and cropping the difference beats letterboxing it.
 */
.ao-cap__video {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border: 0;
	background: var(--ao-ink, #14121c);
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	z-index: 2;
}

.ao-cap__media.is-playing .ao-cap__video {
	opacity: 1;
	visibility: visible;
	pointer-events: auto;
}

/* --- Right column: the rows ----------------------------------------- */

.ao-cap__list {
	display: flex;
	flex-direction: column;
	min-width: 0;
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * A row is a rule until it opens, and a tinted card once it does. The
 * radius is declared on every row rather than only the open one, so the
 * corners are already the right shape when the background arrives and
 * the change is a fade rather than a snap.
 */
.ao-cap__card {
	position: relative;
	min-width: 0;
	
	border-bottom: 1px solid rgba(20, 18, 28, .12);
	background: rgba(131, 71, 236, 0);
	transition:
		background var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		margin-bottom var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-cap__card.is-open {
	border-radius: 14px;
	background: #EEE3FF;
	/* The card's own edges replace the rule, which would otherwise cut
	   across the tint. */
	border-bottom-color: transparent;
	/* A card needs air under it where a rule needs none. */
	margin-bottom: 10px;
	margin-top: 10px;
}

/* The last row has nothing under it to separate itself from. */
.ao-cap__card:last-child {
	border-bottom-color: transparent;
}

@media (hover: hover) and (pointer: fine) {

	.ao-cap__card:not(.is-open):hover {
		background: rgba(131, 71, 236, .045);
	}
}

/* The heading is structure only - every visual property belongs to the
   button inside it, which is the thing that is hovered and focused. */
.ao-cap__h {
	margin: 0;
	font: inherit;
}

.ao-cap__trigger {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	margin: 0;
	padding: clamp(15px, 1.6vw, 19px) clamp(14px, 1.5vw, 20px);
	border: 0;
	border-radius: 14px;
	background: transparent;
	font: inherit;
	text-align: left;
	cursor: pointer;
}

/* Inset, so a 14px radius does not clip it. */
.ao-cap__trigger:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: -3px;
}

/* The circled chevron. The glyph inside turns through 180 degrees on
   aria-expanded and nudges down on hover, both from
   appagentix-icons.css - there is no state rule for it here. */
.ao-cap__mark {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	border-radius: 999px;
	border: 1.5px solid rgba(20, 18, 28, .38);
	color: var(--ao-ink, #14121c);
	transition:
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-cap__card.is-open .ao-cap__mark,
.ao-cap__trigger:hover .ao-cap__mark,
.ao-cap__trigger:focus-visible .ao-cap__mark {
	border-color: rgba(131, 71, 236, .55);
	color: var(--brand-alt, #8347ec);
}

/* The registry's muted rest colour would read as disabled against the
   ring; the chevron takes the ring's own colour instead. */
.ao-cap__mark .ai-ico {
	color: inherit;
}

/* Registry off: a CSS caret, rotated by the open class rather than by
   the icon system. */
.ao-cap__caret {
	width: 7px;
	height: 7px;
	margin-top: -2px;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate(45deg);
	transition: transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-cap__card.is-open .ao-cap__caret {
	transform: rotate(-135deg);
	margin-top: 2px;
}

/*
 * The title, dark into brand purple across its own width. A gradient
 * rather than a tinted last word, because the copy is plain text in the
 * registry and this must not need markup inside it - and because the
 * ramp then lands in the same place whatever the title's length.
 */
.ao-cap__title-row {
	/*
	 * Shrink-to-fit, not grow. The ramp is clipped to this box, so a
	 * box that filled the row would put its purple end past the last
	 * word and leave the short titles entirely dark - which is what
	 * `flex: 1 1 auto` did. Sized to the text, every title lands its
	 * tail in the same place whatever its length.
	 */
	flex: 0 1 auto;
	min-width: 0;
	font-size: clamp(1.02rem, .92rem + .5vw, 1.24rem);
	line-height: 1.3;
	font-weight: 700;
	letter-spacing: -.015em;
	background: linear-gradient(90deg,
			var(--ao-ink, #14121c) 0%,
			var(--ao-ink, #14121c) 66%,
			var(--brand-alt, #8347ec) 100%);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	text-wrap: pretty;
	transition: font-size var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/* Open, the row is a card of copy rather than a headline in a list, so
   the title steps down to sit with the paragraph under it. */
.ao-cap__card.is-open .ao-cap__title-row {
	font-size: clamp(.95rem, .9rem + .25vw, 1.06rem);
	background: none;
	-webkit-text-fill-color: currentColor;
	color: var(--brand-alt, #8347ec);
}

/* --- The panel ------------------------------------------------------ */

.ao-cap__panel {
	display: grid;
	grid-template-rows: 0fr;
	overflow: hidden;
	/* Removed from the accessibility tree while closed, so a screen
	   reader never reads copy that is not on screen and the link inside
	   it cannot be tabbed to. The delay holds it visible until the
	   collapse has finished. */
	visibility: hidden;
	transition:
		grid-template-rows .4s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		visibility 0s linear .4s;
}

.ao-cap__card.is-open .ao-cap__panel {
	grid-template-rows: 1fr;
	visibility: visible;
	transition:
		grid-template-rows .4s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		visibility 0s;
}

/*
 * The clipping element carries no padding of its own. It is the grid
 * item, and a grid item's padding is added to a `0fr` row rather than
 * collapsed with it - which left every closed row 12px tall. The copy's
 * padding lives on the blocks inside, where it is clipped instead.
 */
.ao-cap__panel-inner {
	min-height: 0;
	overflow: hidden;
}

/*
 * Flush with the card's padding rather than indented under the title:
 * the chevron is an affordance, not a bullet, so there is no hanging
 * indent for the copy to line up with.
 */
.ao-cap__text {
	margin: 0;
	padding: 0 clamp(14px, 1.5vw, 20px);
	font-size: clamp(.82rem, .8rem + .1vw, .88rem);
	line-height: 1.6;
	color: var(--ao-ink-soft);
	opacity: 0;
	transform: translate3d(0, -4px, 0);
	transition:
		opacity .3s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		transform .3s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

/* The copy settles slightly behind the height, so the row reads as
   opening and then filling rather than as one blunt jump. */
.ao-cap__card.is-open .ao-cap__text,
.ao-cap__card.is-open .ao-cap__more {
	opacity: 1;
	transform: none;
	transition-delay: .1s;
}

/* A row with no link leaves the copy holding the card's floor. */
.ao-cap__text:last-child {
	padding-bottom: clamp(12px, 1.4vw, 16px);
}

.ao-cap__more {
	margin: clamp(8px, 1vw, 12px) 0 0;
	padding: 0 clamp(14px, 1.5vw, 20px) clamp(12px, 1.4vw, 16px);
	text-align: right;
	opacity: 0;
	transform: translate3d(0, -4px, 0);
	transition:
		opacity .3s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		transform .3s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

.ao-cap__link {
	font-size: .78rem;
	font-weight: 600;
	color: var(--brand-alt, #8347ec);
	text-decoration: none;
	border-bottom: 1px solid transparent;
	transition: border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-cap__link:hover,
.ao-cap__link:focus-visible {
	border-bottom-color: currentColor;
}

.ao-cap__link:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: 3px;
	border-radius: 3px;
}

/* --- Tablet and below: the columns stack ---------------------------- */

@media (max-width: 1023px) {

	.ao-cap {
		grid-template-columns: minmax(0, 1fr);
	}

	/* Stacked, the heading has the full measure and no longer needs the
	   column's narrower one. */
	.ao-cap__lead {
		max-width: 60ch;
	}
}

@media (max-width: 599px) {

	.ao-cap__media {
		--ao-cap-play: 54px;

		border-radius: 14px;
	}

	.ao-cap__trigger {
		gap: 10px;
	}

	/*
	 * Solid ink, not the ramp. A title that wraps samples the gradient
	 * per line, so the last line's tail falls short of the box and the
	 * tint all but vanishes - which left the one-line titles purple and
	 * the two-line ones black in the same list. At this width most of
	 * them wrap, so none of them carry it.
	 */
	.ao-cap__title-row {
		background: none;
		-webkit-text-fill-color: currentColor;
		color: var(--ao-ink, #14121c);
	}

	.ao-cap__card.is-open .ao-cap__title-row {
		color: var(--brand-alt, #8347ec);
	}

	/* A single full-width button reads as the column's next step; a
	   left-aligned pill at this width reads as an afterthought. */
	.ao-cap__foot .ao-btn {
		justify-content: center;
		width: 100%;
	}
}

/* =====================================================================
 * 13. Service tabs - one panel of cards per discipline
 *
 * A bordered panel with the tablist flush along its top edge and the
 * active category's cards below it.
 *
 * WHY THE PANELS SHARE ONE GRID CELL
 *
 * Every panel is placed in the same grid area, so the stage is as tall
 * as the tallest of them and changing tab moves no layout. `[hidden]`
 * must therefore not be allowed to collapse a panel, which is why it is
 * re-shown below and taken out of the picture with visibility instead -
 * the same arrangement, for the same reason, as .ao-sol__slide.
 *
 * WHY THE TAB COUNT IS A CUSTOM PROPERTY
 *
 * --ao-ais-count comes from the template, so the rail divides itself by
 * however many categories the data holds. A fifth needs no change here.
 * ================================================================== */

.ao-ais {
	overflow: hidden;
	border: 1px solid rgba(20, 18, 28, .09);
	border-radius: 18px;
	background: var(--surface, #fff);
	box-shadow: 0 24px 54px -34px rgba(74, 40, 140, .4);
}

/* --- The tablist ---------------------------------------------------- */

.ao-ais__tabs {
	display: grid;
	grid-template-columns: repeat(var(--ao-ais-count, 4), minmax(0, 1fr));
	border-bottom: 1px solid rgba(20, 18, 28, .1);
}

.ao-ais__tab {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	min-width: 0;
	margin: 0;
	padding: clamp(14px, 1.6vw, 19px) clamp(10px, 1.4vw, 20px);
	border: 0;
	background: transparent;
	font: inherit;
	font-size: clamp(.85rem, .8rem + .22vw, .98rem);
	font-weight: 600;
	line-height: 1.3;
	text-align: center;
	color: var(--ao-ink-soft);
	cursor: pointer;
	transition:
		color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		background var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/* The hairline between segments. On the tab rather than the rail, so it
   follows the count without the rail having to place it. */
.ao-ais__tab+.ao-ais__tab {
	border-left: 1px solid rgba(20, 18, 28, .1);
}

/*
 * The active underline. A gradient bar scaled from the centre rather
 * than a width that animates, so the movement is compositor-only, and
 * on a pseudo-element so it never participates in layout. It sits on
 * the rail's own hairline rather than replacing it.
 */
.ao-ais__tab::after {
	content: "";
	position: absolute;
	right: 0;
	bottom: -1px;
	left: 0;
	height: 2px;
	background: var(--ao-grad-hero);
	transform: scaleX(0);
	transition: transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-ais__tab.is-active {
	color: var(--ao-ink);
}

.ao-ais__tab.is-active::after {
	transform: scaleX(1);
}

@media (hover: hover) and (pointer: fine) {

	.ao-ais__tab:not(.is-active):hover {
		color: var(--ao-ink);
		background: rgba(131, 71, 236, .04);
	}

	/* A hint of the underline under the pointer, at a third of the
	   weight, so hovering reads as "this one next" rather than as a
	   second active tab. */
	.ao-ais__tab:not(.is-active):hover::after {
		transform: scaleX(.34);
	}
}

/* Inset, so the rail's own edges do not clip it. */
.ao-ais__tab:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: -3px;
}

/* --- The stage ------------------------------------------------------ */

.ao-ais__stage {
	display: grid;
	grid-template-areas: "panel";
	padding: clamp(20px, 2.6vw, 34px);
}

.ao-ais__panel {
	grid-area: panel;
	min-width: 0;
}

/*
 * [hidden] must not beat the grid placement, or the stage collapses to
 * one panel's height and the section jumps on every change. The panel is
 * taken out of the picture with visibility instead, and out of the
 * accessibility tree with the inert attribute the template writes
 * alongside it.
 */
.ao-ais__panel[hidden] {
	display: grid;
	visibility: hidden;
	pointer-events: none;
}

/* --- The cards ------------------------------------------------------ */

.ao-ais__grid {
	display: grid;
	/*
	 * auto-fit rather than three fixed columns: a fourth card needs no
	 * change here, and the cards reflow on their own below the track
	 * width without a second rule doing it by hand.
	 *
	 * The 290px floor is what holds the reference's three-across on a
	 * 1200px measure. At 232px a fourth column fitted, so three cards
	 * were laid into a four-column track and left a gap on the right.
	 */
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
	gap: clamp(14px, 2vw, 30px);
	margin: 0;
	padding: 0;
	list-style: none;
}

.ao-ais__card {
	display: flex;
	flex-direction: column;
	min-width: 0;
	padding: clamp(18px, 2vw, 26px) clamp(16px, 1.8vw, 24px);
	border: 1px solid rgba(20, 18, 28, .1);
	border-radius: 10px;
	background: var(--surface, #fff);
	/*
	 * The cards arrive with the panel rather than on their own observer:
	 * the panel is the [data-reveal] element, and .is-in on it releases
	 * these in sequence. The same transition plays on a tab change,
	 * because the panel loses .is-active and takes it back.
	 */
	opacity: 0;
	transform: translate3d(0, 8px, 0);
	transition:
		opacity .42s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		transform .42s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		box-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
	transition-delay: calc(var(--i, 0) * 70ms);
}

/*
 * Both conditions, because there are two ways a card becomes visible:
 * the section scrolling in (.is-in, from the shared observer) and a tab
 * being chosen (.is-active, from the page script).
 */
.ao-ais__panel.is-in .ao-ais__card,
.ao-ais__panel.is-active .ao-ais__card {
	opacity: 1;
	transform: none;
}

@media (hover: hover) and (pointer: fine) {

	.ao-ais__card:hover {
		border-color: rgba(101, 77, 255, .28);
		box-shadow: 0 18px 38px -26px rgba(74, 40, 140, .55);
	}
}

/* The head: the icon tile and the title on one line, as the reference
   has it. Aligned to the top rather than centred, so a title that wraps
   to two lines keeps the tile against its first line. */
.ao-ais__card-head {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	min-width: 0;
}

/* The dark tile the reference draws the glyph in. Square, so the icons
   read as one set across the four panels whatever their own proportions
   are. */
.ao-ais__card-icon {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	border-radius: 10px;
	background: var(--ao-ink, #14121c);
	color: #fff;
	transition: background var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-ais__card-icon svg {
	width: 20px;
	height: 20px;
}

@media (hover: hover) and (pointer: fine) {

	.ao-ais__card:hover .ao-ais__card-icon {
		background: var(--brand, #654dff);
	}
}

.ao-ais__card-title {
	margin: 0;
	/* Beside the tile, so the head reads as one line rather than a
	   centred block with a glyph pushed off to its side. */
	text-align: left;
	font-size: 20px;
	line-height: 1.3;
	font-weight: 700;
	letter-spacing: -.015em;
	color: var(--ao-ink);
	text-wrap: balance;
}

.ao-ais__points {
	display: flex;
	flex-direction: column;
	gap: clamp(10px, 26vw, 26px);
	margin: clamp(14px, 1.8vw, 20px) 0 0;
	padding: 0;
	list-style: none;
}

.ao-ais__point {
	display: flex;
	align-items: flex-start;
	gap: 9px;
	min-width: 0;
	font-size: 18px;
	line-height: 1.55;
	color: var(--ao-ink-soft);
}

/* The registry repaints a hovered [data-ai-trigger] icon with its own
   hover colour - the brand purple, which is the tile's own hover
   background. The glyph takes the tile's colour instead, so only the
   animation changes on hover. */
.ao-ais__card .ai-ico,
.ao-ais__card:hover .ai-ico {
	color: inherit;
}

.ao-ais__point-text {
	min-width: 0;
	text-wrap: pretty;
}

/* The reference's own bullet, drawn rather than typed so it sits on the
   first line's optical centre whatever the row's font size is. */
.ao-ais__point-dot {
	flex: 0 0 auto;
	width: 4px;
	height: 4px;
	margin-top: .58em;
	border-radius: 999px;
	background: currentColor;
}

/* The action line. Weighted a step up and tinted, so the line that names
   the next step is distinguishable from the one that describes the
   service without becoming a second heading. */
.ao-ais__point--action {
	/* font-weight: 600; */
	color: var(--ao-ink);
}

/* --- Tablet: the rail stays a grid, the cards reflow ---------------- */

@media (max-width: 1023px) {

	.ao-ais__tab {
		font-size: .86rem;
	}
}

/* --- Phone: the rail scrolls --------------------------------------- */

@media (max-width: 767px) {

	/*
	 * Four equal segments at this width give each tab a two-character
	 * measure. A scrolling rail keeps the labels whole; scroll-snap
	 * stops it resting between two of them, and the script scrolls the
	 * chosen tab into view so a keyboard never selects one off screen.
	 */
	.ao-ais__tabs {
		display: flex;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	.ao-ais__tabs::-webkit-scrollbar {
		display: none;
	}

	/* scroll-snap-align: start, as the work rail uses - a rail that
	   begins at the panel's left edge should rest against it. */
	.ao-ais__tab {
		flex: 0 0 auto;
		scroll-snap-align: start;
		white-space: nowrap;
	}

	.ao-ais__card {
		/* Stacked, the cards arrive one after another rather than as
		   three rows of one, so the stagger is shortened. */
		transition-delay: calc(var(--i, 0) * 45ms);
	}
}

/* =====================================================================
 * 14. Industry cards - a photograph, a name, and the work behind it
 *
 * Five across on the 1200px measure, two rows of five for the ten
 * sectors. Each tile is a 4:5 photograph with its sector's name over it;
 * opening the tile raises a brand panel that covers the picture and
 * lists what we build there.
 *
 * HOW THE PANEL MOVES
 *
 * The name and the points are one bottom-aligned panel. At rest the
 * points are a grid collapsed to `0fr` - the same technique as the FAQ
 * accordion in appagentix-motion.css - so they take no height and the
 * name sits just above the tile's floor. Opening expands them to `1fr`,
 * which pushes the name up the tile, while the gradient behind them
 * slides up from the bottom on a transform.
 *
 * Nothing measures anything: the rest position is wherever the name
 * lands with the points collapsed, so a two-line name, a one-line name
 * or a fourth point all work without a number changing here.
 *
 * WHY THE PHOTOGRAPHS ARE TINTED
 *
 * In the reference every tile carries the same purple product render,
 * and that shared colour is what makes the grid read as one object.
 * Ten real photographs are ten different palettes, so the cohesion is
 * applied here: the picture is desaturated and a brand wash is laid over
 * it. Both live on .ao-inds__media, so the treatment is one place to
 * change - or to remove, if the photographs should keep their colour.
 * ================================================================== */

.ao-inds {
	display: grid;
	/* auto-fit, so the count follows the data. The 190px floor is what
	   holds five across on the 1200px measure and steps down on its own
	   below it. */
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
	gap: clamp(12px, 1.5vw, 18px);
	margin: 0;
	padding: 0;
	list-style: none;
}

.ao-inds__card {
	position: relative;
	min-width: 0;
	/* Measured off the reference: a 225px tile stands 276px tall. The
	   ratio is on the tile rather than the image, so the grid is its
	   final height before anything has loaded. */
	aspect-ratio: 4 / 5;
	overflow: hidden;
	border-radius: 12px;
	/* background: var(--ao-ink, #14121c); */
	/* box-shadow: 0 16px 36px -28px rgba(74, 40, 140, .55); */
	transition: box-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/* --- The photograph ------------------------------------------------- */

.ao-inds__media {
	position: absolute;
	inset: 0;
	overflow: hidden;
}

.ao-inds__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* The two halves of the treatment. Static, so they rasterise once. */
	/* filter: saturate(.4) contrast(1.06) brightness(.92); */
	transform: scale(1);
	transition: transform var(--dur-slow, .45s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/* The brand wash. Multiply keeps the photograph's own light and shade
   rather than flooding it, which is why ten different pictures still
   come out as one set. */
.ao-inds__media::after {
	content: "";
	position: absolute;
	inset: 0;
	    background: linear-gradient(182deg, #9d6ce900 0%, #7a49d2a6 52%, #6b37c4 100%);
	mix-blend-mode: multiply;
	opacity: 1;
	pointer-events: none;
}

/* --- The name's backing ---------------------------------------------- */

/*
 * Only for the rest state: once the panel's gradient is up it is doing
 * this job, so the scrim fades out rather than doubling the darkness.
 */
.ao-inds__scrim {
	position: absolute;
	right: 0;
	bottom: 0;
	left: 0;
	height: 62%;
	background: linear-gradient(to top, rgba(28, 12, 58, .88) 0%, rgba(28, 12, 58, .45) 42%, rgba(28, 12, 58, 0) 100%);
	pointer-events: none;
	transition: opacity var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/* --- The panel -------------------------------------------------------- */

.ao-inds__panel {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	/* Bottom-aligned, so with the points collapsed the name rests on the
	   tile's floor, and expanding them pushes it up the tile. */
	justify-content: flex-end;
	align-items: center;
	padding: clamp(14px, 1.7vw, 20px);
	text-align: center;
	pointer-events: none;
	/*
	 * Confines the gradient below to this element. Without it the
	 * `z-index: -1` escapes the panel - the card is positioned but has
	 * no stacking context of its own - and the gradient paints behind
	 * the photograph, which then never disappears on hover.
	 */
	isolation: isolate;
}

/*
 * The gradient, on a pseudo-element so it can slide independently of the
 * copy sitting on it. translate3d only - the panel's height never
 * changes, so nothing here touches layout.
 */
.ao-inds__panel::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: linear-gradient(180deg, #9d6ce9 0%, #7a49d2 52%, #6b37c4 100%);
	transform: translate3d(0, 100%, 0);
	transition: transform var(--dur-slow, .45s) var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

.ao-inds__name {
	margin: 0;
	/* Sized so most sector names take two lines on a 226px tile, as they
	   do in the reference - which also lifts the open panel's copy to
	   roughly where the reference sets it. */
	font-size: clamp(.9rem, .84rem + .26vw, 1.06rem);
	line-height: 1.3;
	font-weight: 600;
	letter-spacing: -.01em;
	color: #fff;
	text-wrap: balance;
	/* Survives the moment when the gradient has not quite arrived. */
	text-shadow: 0 1px 10px rgba(20, 8, 44, .45);
	transition: text-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

/* --- The points ------------------------------------------------------- */

.ao-inds__points {
	display: grid;
	grid-template-rows: 0fr;
	width: 100%;
	transition: grid-template-rows var(--dur-slow, .45s) var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

/* The clipping element carries no padding of its own, or a closed card
   keeps that padding as residual height and the name never reaches the
   floor. The list's own spacing is clipped inside it. */
.ao-inds__points-inner {
	min-height: 0;
	overflow: hidden;
}

.ao-inds__list {
	display: flex;
	flex-direction: column;
	gap: clamp(5px, .7vw, 8px);
	/*
	 * The gap between the name and the first line, carried here so the
	 * collapsed state has nothing to contribute.
	 *
	 * It is wide on purpose. In the reference this gap is about 15% of
	 * the tile's height, and it is what lifts the name to the top third
	 * when the card opens - bottom-aligned copy with a tight gap sat a
	 * third of a tile too low. The ceiling is set so the longest card
	 * (a two-line name over three two-line points) still clears the
	 * tile's floor.
	 */
	margin: clamp(16px, 2.6vw, 36px) 0 0;
	padding: 0;
	list-style: none;
}

/*
 * A typed bullet rather than a registry icon. The reference centres each
 * line AND centres the wrap under it; an icon needs a flex row, which
 * hangs the second line off the first. The marker is inline so the whole
 * line, wrap included, stays centred.
 */
.ao-inds__point {
	font-size: clamp(.72rem, .68rem + .16vw, .8rem);
	line-height: 1.45;
	color: rgba(255, 255, 255, .95);
	opacity: 0;
	transform: translate3d(0, 6px, 0);
	transition:
		opacity .34s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		transform .34s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

.ao-inds__point::before {
	content: "\2022";
	margin-right: 6px;
}

/* --- The control ------------------------------------------------------ */

/*
 * Covers the tile so a tap anywhere opens it. Transparent and last in
 * the box tree, so it never paints over the panel.
 */
.ao-inds__toggle {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: inherit;
	background: transparent;
	cursor: pointer;
}

.ao-inds__toggle:focus-visible {
	outline: 2px solid #fff;
	outline-offset: -4px;
}

/* --- The open state ---------------------------------------------------
 *
 * Three ways in, one set of rules: the pointer, the keyboard landing on
 * the button, and the class the script sets on tap.
 * ------------------------------------------------------------------- */

.ao-inds__card:hover .ao-inds__points,
.ao-inds__card:focus-within .ao-inds__points,
.ao-inds__card.is-open .ao-inds__points {
	grid-template-rows: 1fr;
}

.ao-inds__card:hover .ao-inds__panel::before,
.ao-inds__card:focus-within .ao-inds__panel::before,
.ao-inds__card.is-open .ao-inds__panel::before {
	transform: translate3d(0, 0, 0);
}

.ao-inds__card:hover .ao-inds__scrim,
.ao-inds__card:focus-within .ao-inds__scrim,
.ao-inds__card.is-open .ao-inds__scrim {
	opacity: 0;
}

/* Behind the gradient the name needs no shadow, and losing it sharpens
   the type. */
.ao-inds__card:hover .ao-inds__name,
.ao-inds__card:focus-within .ao-inds__name,
.ao-inds__card.is-open .ao-inds__name {
	text-shadow: none;
}

.ao-inds__card:hover .ao-inds__img,
.ao-inds__card:focus-within .ao-inds__img,
.ao-inds__card.is-open .ao-inds__img {
	transform: scale(1.06);
}

.ao-inds__card:hover,
.ao-inds__card:focus-within,
.ao-inds__card.is-open {
	box-shadow: 0 24px 46px -24px rgba(74, 40, 140, .7);
}

/* The lines arrive behind the panel they sit on, so the gradient reads
   as bringing them with it rather than revealing them already there. */
.ao-inds__card:hover .ao-inds__point,
.ao-inds__card:focus-within .ao-inds__point,
.ao-inds__card.is-open .ao-inds__point {
	opacity: 1;
	transform: none;
	transition-delay: .12s;
}

/* --- Narrower: fewer across, and a taller tile ------------------------
 *
 * Three lines of copy in a 180px column wrap to three lines each, which
 * a 4:5 tile cannot hold. The tile lengthens as the columns thin.
 * ------------------------------------------------------------------- */

@media (max-width: 1023px) {

	.ao-inds__card {
		aspect-ratio: 3 / 4;
	}
}

@media (max-width: 767px) {

	.ao-inds {
		grid-template-columns: repeat(auto-fit, minmax(min(100%, 168px), 1fr));
	}

	.ao-inds__card {
		aspect-ratio: 5 / 7;
	}
}

/*
 * One column, and the tile turns landscape: at this width two columns
 * leave each line of copy three words wide, and the reference's
 * proportions matter less than the copy being readable.
 */
@media (max-width: 479px) {

	.ao-inds {
		grid-template-columns: minmax(0, 1fr);
		gap: 12px;
	}

	.ao-inds__card {
		aspect-ratio: 16 / 10;
	}

	.ao-inds__name {
		font-size: 1.05rem;
	}

	.ao-inds__point {
		font-size: .84rem;
	}
}

/* =====================================================================
 * 15. Engagement stages - a numbered rail beside one media panel
 *
 * Seven rows down the left at 47% of the measure, the active stage's
 * photograph filling the 53% beside them. The two columns meet with no
 * gutter, as the reference has them, so the rail's right edge is the
 * panel's left edge.
 *
 * WHY THE PANEL IS SIZED BY THE RAIL
 *
 * The stage is a grid item that stretches, so its height is the rail's
 * height - seven rows of text, which the browser knows before a single
 * photograph has arrived. The panels inside it are absolutely
 * positioned, so an image can never contribute height and can never
 * shift the page. There is no aspect-ratio to guess at on desktop.
 *
 * WHY `[hidden]` IS OVERRIDDEN
 *
 * initServiceTabs() hides the inactive panels with the attribute, which
 * would make the change a cut rather than a cross-fade. They are kept
 * displayed and faded out instead; `inert`, which that function sets
 * alongside, is what keeps them out of the accessibility tree.
 * ================================================================== */

/*
 * The heading, to the brief's own value: black at the left running into
 * deep purple at the right. It overrides the warm ramp every other
 * section title carries, so it is opt-in per section.
 */
.ao-section--ink-title .ao-section__title {
	display: inline-block;
	    background: var(--ao-grad-hero);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

.ao-eng {
	display: grid;
	/* 47/53, measured off the reference. No gutter: the rail's border is
	   the panel's edge. */
	grid-template-columns: minmax(0, 36fr) minmax(0, 53fr);
	align-items: stretch;
}

/* --- The rail -------------------------------------------------------- */

.ao-eng__steps {
	display: flex;
	flex-direction: column;
	min-width: 0;
	border: 1px solid rgba(20, 18, 28, .14);
	/* The panel supplies the edge on this side. */
	border-right: 0;
}

.ao-eng__btn {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	margin: 0;
	/*
	 * The rail is content-sized and the panel beside it stretches to
	 * match, so this padding is what sets the section's height - seven
	 * rows of roughly 74px, as the reference has them.
	 *
	 * The rows were `flex: 1 1 0` to divide the rail evenly, which is
	 * wrong in a column no one has given a height: the items collapsed
	 * to their zero basis and every row became its own padding with the
	 * label clipped out of it.
	 */
	padding: clamp(14px, 1.9vw, 25px) clamp(14px, 1.8vw, 24px);
	border: 0;
	background: transparent;
	font: inherit;
	text-align: left;
	color: var(--ao-ink);
	cursor: pointer;
	transition: background var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-eng__btn+.ao-eng__btn {
	border-top: 1px solid rgba(20, 18, 28, .14);
}

/* The chosen stage takes a brand tint, which is the only thing marking
   it: the rail carries no marker, arrow state or rule of its own. */
.ao-eng__btn.is-active {
	background: rgba(131, 71, 236, .1);
}

@media (hover: hover) and (pointer: fine) {

	.ao-eng__btn:not(.is-active):hover {
		background: rgba(131, 71, 236, .045);
	}
}

/* Inset, so the rail's own edges do not clip it. */
.ao-eng__btn:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: -3px;
}

.ao-eng__num,
.ao-eng__name {
	font-size: clamp(.95rem, .88rem + .3vw, 1.14rem);
	line-height: 1.3;
	font-weight: 600;
	letter-spacing: -.01em;
}

.ao-eng__num {
	flex: 0 0 auto;
}

.ao-eng__name {
	flex: 1 1 auto;
	min-width: 0;
	text-wrap: pretty;
	    background: linear-gradient(270deg, #3F2171 0%, #000000 97.6%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

/* The tinted last word. On the chosen stage it drops back to the ink of
   the rest of the row, so the tint reads as "not this one" rather than
   as decoration. */
.ao-eng__tail {
	color: transparent;
	/* transition: color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)); */
}

.ao-eng__btn.is-active .ao-eng__tail {
	color: inherit;
}

.ao-eng__go {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	border-radius: 999px;
	border: 1.5px solid rgba(20, 18, 28, .38);
	color: var(--ao-ink, #14121c);
	transition:
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-eng__btn.is-active .ao-eng__go,
.ao-eng__btn:hover .ao-eng__go {
	border-color: rgba(131, 71, 236, .55);
	color: var(--brand-alt, #8347ec);
}

/* The registry's muted rest colour would read as disabled against the
   ring; the arrow takes the ring's own colour instead. The slide on
   hover still plays, which is the part worth having. */
.ao-eng__go .ai-ico {
	color: inherit;
}

.ao-eng__go svg {
	width: 14px;
	height: 14px;
}

/* --- The media panel -------------------------------------------------- */

.ao-eng__stage {
	position: relative;
	min-width: 0;
	overflow: hidden;
	background: var(--ao-ink, #14121c);
}

.ao-eng__panel {
	position: absolute;
	inset: 0;
	opacity: 0;
	pointer-events: none;
	transition: opacity .5s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

.ao-eng__panel.is-active {
	opacity: 1;
	pointer-events: auto;
}

/* The attribute must not take the panel out of the flow, or the change
   is a cut rather than a cross-fade. `inert`, set alongside it, is what
   keeps a hidden panel out of the accessibility tree. */
.ao-eng__panel[hidden] {
	display: block;
}

.ao-eng__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transform: scale(1.03);
	transition: transform 1.1s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

/* Settles back to true size as its panel arrives, so the change has a
   little life without anything animating on a loop. */
.ao-eng__panel.is-active .ao-eng__img {
	transform: scale(1);
}

/*
 * The gradient the copy sits on: clear over the top half of the
 * photograph, solid brand purple by its foot. This is the only purple
 * in the panel - the photograph itself is untinted, as the reference
 * shows it.
 */
.ao-eng__scrim {
	position: absolute;
	inset: 0;
	/*
	 * Weighted for the worst photograph in the set, not the best. The
	 * first stop pair was tuned against the reference's own picture,
	 * which is dark where the copy sits; over the whiteboard shot the
	 * same alphas blended to a pale lavender and the white type all but
	 * vanished. It now reaches near-opaque by the title's line, so the
	 * copy reads the same on all seven.
	 */
	background: linear-gradient(180deg,
			rgba(45, 22, 85, 0) 8%,
			rgba(52, 26, 100, .46) 34%,
			rgba(68, 34, 130, .84) 56%,
			rgba(82, 41, 156, .96) 76%,
			#4b2494 100%);
	pointer-events: none;
}

/*
 * z-index, though the copy already follows the gradient in tree order
 * and both are positioned: the photograph above them is transformed, so
 * Chrome promotes this subtree to compositor layers and the gradient was
 * landing on top of the type - white text sampled #AFA2C6 instead of
 * #FFFFFF. Stating the order outright settles it.
 */
.ao-eng__body {
	position: absolute;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 1;
	padding: clamp(20px, 2.6vw, 36px);
}

.ao-eng__title {
	margin: 0;
	/* Light, and large. The reference sets this as a display line rather
	   than a heading, which is why it does not take the h3 ramp. */
	font-size: clamp(1.6rem, 1.1rem + 2.1vw, 2.9rem);
	line-height: 1.12;
	font-weight: 400;
	letter-spacing: -.02em;
	color: #fff;
	text-wrap: balance;
	opacity: 0;
	transform: translate3d(0, 12px, 0);
	transition:
		opacity .46s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		transform .46s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

.ao-eng__text {
	margin: clamp(8px, 1vw, 14px) 0 0;
	max-width: 52ch;
	font-size: clamp(.84rem, .8rem + .18vw, .95rem);
	line-height: 1.55;
	color: rgba(255, 255, 255, .92);
	opacity: 0;
	transform: translate3d(0, 12px, 0);
	transition:
		opacity .46s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		transform .46s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

/* The copy arrives just behind its panel, the paragraph just behind the
   title, so the change reads as one movement rather than three. */
.ao-eng__panel.is-active .ao-eng__title {
	opacity: 1;
	transform: none;
	transition-delay: .1s;
}

.ao-eng__panel.is-active .ao-eng__text {
	opacity: 1;
	transform: none;
	transition-delay: .17s;
}

/* --- Stacked: the panel leads ----------------------------------------
 *
 * Below the two-column width the panel goes above the rail. It holds
 * nothing focusable, so moving it visually does not disturb the tab
 * order, and putting it first keeps the thing being chosen in view
 * while the rail is worked through.
 * ------------------------------------------------------------------- */

@media (max-width: 1023px) {

	.ao-eng {
		grid-template-columns: minmax(0, 1fr);
	}

	.ao-eng__stage {
		order: -1;
		/* Nothing sizes the panel once it is no longer beside the rail,
		   so it takes a ratio of its own - reserved before the image
		   loads, as on desktop. */
		aspect-ratio: 16 / 10;
	}

	/* Nothing sits to the right of the rail now, so it closes its own
	   box. The panel above supplies the edge they used to share. */
	.ao-eng__steps {
		border-right: 1px solid rgba(20, 18, 28, .14);
		border-top: 0;
	}
}

@media (max-width: 599px) {

	.ao-eng__stage {
		aspect-ratio: 4 / 3;
	}

	.ao-eng__btn {
		gap: 8px;
	}

	.ao-eng__go {
		width: 22px;
		height: 22px;
	}

	.ao-eng__go svg {
		width: 12px;
		height: 12px;
	}
}

/* =====================================================================
 * 16. The stack - a rail of areas beside the logos behind each one
 *
 * One white panel, split about 30/70: the areas down the left, the
 * active area's technologies as a grid of logo chips on the right.
 *
 * WHY THE RAIL'S RULES ARE NOT THE ENGAGEMENT RAIL'S
 *
 * Section 15 draws a rail that looks like this one, and the two could
 * share a block. They do not, because sharing means editing section 15's
 * selectors to serve a section that did not exist when they were
 * written, and that band is signed off. The duplication is about forty
 * lines and is worth the isolation; if a third rail turns up, that is
 * the moment to lift one out.
 *
 * HOW THE SIX-ROW LIMIT WORKS
 *
 * The rail is capped at `--ao-stk-visible` rows and scrolls past that.
 * The cap is a multiple of a fixed row height rather than a measured
 * one, so the rail ends on a row's edge instead of halfway through a
 * label. PHP adds `.is-scrollable` when the data actually overflows,
 * which is what gates the fade at the foot - at exactly six rows a fade
 * would dim the last row for no reason.
 * ================================================================== */

.ao-stk {
	--ao-stk-row: 71px;

	display: grid;
	/* 30/70, measured off the reference: the rail holds one short label
	   per row, the panel holds a grid of chips. */
	grid-template-columns: minmax(0, 30fr) minmax(0, 70fr);
	align-items: stretch;
	overflow: hidden;
	border: 1px solid rgba(20, 18, 28, .09);
	border-radius: 12px;
	background: var(--surface, #fff);
	box-shadow: 0 24px 54px -34px rgba(74, 40, 140, .4);
}

/* --- The rail -------------------------------------------------------- */

.ao-stk__rail {
	display: flex;
	flex-direction: column;
	min-width: 0;
	max-height: calc(var(--ao-stk-visible, 6) * var(--ao-stk-row));
	overflow-y: auto;
	/* Rests on a row edge rather than mid-label. `proximity`, not
	   `mandatory`: a short flick should be allowed to settle where it
	   lands. */
	scroll-snap-type: y proximity;
	/*
	 * Capped at six rows, so it does not stretch with the grid row. The
	 * divider between the columns therefore belongs to the panel, which
	 * does stretch - see .ao-stk__stage. Drawn here it stopped short of
	 * the panel's floor whenever the copy made the panel the taller of
	 * the two, which the descriptions now do by about five pixels.
	 */
	align-self: start;
	scrollbar-width: thin;
	scrollbar-color: rgba(131, 71, 236, .32) transparent;
	overscroll-behavior: contain;
}

/* Only when the data overflows, so the last row is never dimmed for
   nothing. The fade is a mask, so it works over any row colour. */
/*
 * Shallow on purpose. At 46px falling to .15 the fade reached a third of
 * the way up the sixth row, and that row is a live control - it dimmed
 * the selected one whenever the last visible area was the chosen one.
 * 24px to .55 still reads as "there is more under this" while leaving
 * every row it touches legible.
 */
.ao-stk__rail.is-scrollable {
	-webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 24px), rgba(0, 0, 0, .55) 100%);
	mask-image: linear-gradient(180deg, #000 calc(100% - 24px), rgba(0, 0, 0, .55) 100%);
}

.ao-stk__rail::-webkit-scrollbar {
	width: 6px;
}

.ao-stk__rail::-webkit-scrollbar-thumb {
	border-radius: 999px;
	background: rgba(131, 71, 236, .32);
}

.ao-stk__rail::-webkit-scrollbar-thumb:hover {
	background: rgba(131, 71, 236, .55);
}

.ao-stk__btn {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	width: 100%;
	/* Fixed, so the cap above lands on a row edge. flex-shrink is off or
	   seven rows would squeeze into six rows' worth of space instead of
	   scrolling. */
	flex: 0 0 var(--ao-stk-row);
	min-height: var(--ao-stk-row);
	margin: 0;
	padding: 0 clamp(14px, 1.6vw, 24px);
	border: 0;
	background: transparent;
	font: inherit;
	text-align: left;
	color: var(--ao-ink);
	cursor: pointer;
	scroll-snap-align: start;
	transition: background var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-stk__btn+.ao-stk__btn {
	border-top: 1px solid rgba(20, 18, 28, .1);
}

/* The chosen area takes a brand tint, which is the only thing marking
   it: the rail carries no marker or rule of its own. */
.ao-stk__btn.is-active {
	background: rgba(131, 71, 236, .1);
}

@media (hover: hover) and (pointer: fine) {

	.ao-stk__btn:not(.is-active):hover {
		background: rgba(131, 71, 236, .045);
	}
}

/* Inset, so the rail's own edges do not clip it. */
.ao-stk__btn:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: -3px;
}

.ao-stk__name {
	min-width: 0;
	font-size: clamp(.95rem, .88rem + .3vw, 1.12rem);
	line-height: 1.3;
	font-weight: 600;
	letter-spacing: -.01em;
	text-wrap: pretty;
	    background: linear-gradient(253deg, #3F2171 0%, #000000 97.6%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

/* The tinted last word. On the chosen area it drops back to the ink of
   the rest of the row, so the tint reads as "not this one" rather than
   as decoration. A one-word name has no tail at all. */
.ao-stk__tail {
	color: transparent;
	}

.ao-stk__btn.is-active .ao-stk__tail {
	color: inherit;
}

.ao-stk__go {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	border-radius: 999px;
	border: 1.5px solid rgba(20, 18, 28, .38);
	color: var(--ao-ink, #14121c);
	transition:
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-stk__btn.is-active .ao-stk__go,
.ao-stk__btn:hover .ao-stk__go {
	border-color: rgba(131, 71, 236, .55);
	color: var(--brand-alt, #8347ec);
}

/* The registry's muted rest colour would read as disabled against the
   ring; the arrow takes the ring's own colour instead. The slide on
   hover still plays, which is the part worth having. */
.ao-stk__go .ai-ico {
	color: inherit;
}

.ao-stk__go svg {
	width: 14px;
	height: 14px;
}

/* --- The panel -------------------------------------------------------- */

.ao-stk__stage {
	display: grid;
	grid-template-areas: "panel";
	min-width: 0;
	/* The divider between the columns. It lives here because this column
	   stretches to the grid row and the capped rail does not. */
	border-left: 1px solid rgba(20, 18, 28, .1);
	padding: clamp(20px, 2.8vw, 40px);
}

.ao-stk__panel {
	grid-area: panel;
	min-width: 0;
	opacity: 0;
	pointer-events: none;
	transition: opacity .42s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

.ao-stk__panel.is-active {
	opacity: 1;
	pointer-events: auto;
}

/*
 * [hidden] must not beat the grid placement, or the stage collapses to
 * one panel's height and the section jumps on every change. `inert`,
 * which initServiceTabs sets alongside, is what keeps a hidden panel out
 * of the accessibility tree.
 */
.ao-stk__panel[hidden] {
	display: block;
	visibility: hidden;
}

.ao-stk__head {
	display: flex;
	align-items: center;
	gap: 12px;
	min-width: 0;
}

.ao-stk__glyph {
	flex: 0 0 auto;
	display: block;
	width: clamp(26px, 2.4vw, 34px);
	height: clamp(26px, 2.4vw, 34px);
	object-fit: contain;
	filter: saturate(.1) contrast(1.5) brightness(0.7);
}

.ao-stk__title {
	margin: 0;
	min-width: 0;
	font-size: clamp(1.15rem, 1rem + .7vw, 1.6rem);
	line-height: 1.25;
	font-weight: 700;
	letter-spacing: -.02em;
	color: var(--ao-ink);
}

.ao-stk__text {
	margin: clamp(10px, 1.4vw, 16px) 0 0;
	max-width: 62ch;
	font-size: clamp(.86rem, .83rem + .14vw, .95rem);
	line-height: 1.62;
	color: var(--ao-ink-soft);
}

/* --- The chips --------------------------------------------------------
 *
 * auto-FILL, not auto-fit: the fifth chip on a four-column row must keep
 * a chip's width rather than stretch across the empty tracks beside it,
 * which is what the reference shows.
 * ------------------------------------------------------------------- */

.ao-stk__grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 140px), 1fr));
	gap: clamp(12px, 1.6vw, 22px);
	margin: clamp(16px, 2.2vw, 28px) 0 0;
	padding: 0;
	list-style: none;
}

.ao-stk__chip {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	min-width: 0;
	padding: clamp(12px, 1.4vw, 18px) 10px;
	border: 1px solid rgba(20, 18, 28, .12);
	border-radius: 8px;
	background: var(--surface, #fff);
	text-align: center;
	/* Arrives with its panel: the stagger is on the index the grid gives
	   it, so nothing here counts the chips. */
	opacity: 0;
	transform: translate3d(0, 8px, 0);
	transition:
		opacity .4s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		transform .4s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1)),
		border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		box-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-stk__panel.is-active .ao-stk__chip {
	opacity: 1;
	transform: none;
}

/* Six steps is enough for the longest list here, and a chip past the
   sixth simply arrives with the sixth rather than needing its own rule. */
.ao-stk__panel.is-active .ao-stk__chip:nth-child(1) {
	transition-delay: .06s;
}

.ao-stk__panel.is-active .ao-stk__chip:nth-child(2) {
	transition-delay: .11s;
}

.ao-stk__panel.is-active .ao-stk__chip:nth-child(3) {
	transition-delay: .16s;
}

.ao-stk__panel.is-active .ao-stk__chip:nth-child(4) {
	transition-delay: .21s;
}

.ao-stk__panel.is-active .ao-stk__chip:nth-child(5) {
	transition-delay: .26s;
}

.ao-stk__panel.is-active .ao-stk__chip:nth-child(n+6) {
	transition-delay: .31s;
}

@media (hover: hover) and (pointer: fine) {

	.ao-stk__chip:hover {
		border-color: rgba(101, 77, 255, .3);
		box-shadow: 0 14px 30px -22px rgba(74, 40, 140, .6);
	}
}

.ao-stk__logo {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: 36px;
}

.ao-stk__logo img {
	display: block;
	width: auto;
	max-width: 100%;
	height: 100%;
	object-fit: contain;
}

/*
 * A chip standing for a capability rather than a product - IoT, AR/VR -
 * has no brand mark to show, so it takes a registry icon in the brand
 * purple. Sized to the same 36px box the logos occupy, so the two kinds
 * of chip sit on one baseline.
 */
.ao-stk__chip--glyph .ao-stk__logo {
	color: var(--brand-alt, #8347ec);
}

/* The registry repaints a hovered [data-ai-trigger] icon with its own
   hover colour; the chip already carries the colour it should be, and
   the animation still plays. */
.ao-stk__chip--glyph .ai-ico,
.ao-stk__chip--glyph:hover .ai-ico {
	color: inherit;
}

.ao-stk__chip--glyph .ai-ico svg {
	width: 28px;
	height: 28px;
}

.ao-stk__label {
	font-size: clamp(.72rem, .7rem + .1vw, .78rem);
	line-height: 1.35;
	font-weight: 500;
	color: var(--ao-ink-soft);
	text-wrap: pretty;
}

/* --- Stacked: the rail goes on top ------------------------------------ */

@media (max-width: 1023px) {

	.ao-stk {
		grid-template-columns: minmax(0, 1fr);
	}

	/*
	 * The cap lifts once the rail is the section's full width: a
	 * scrolling strip stacked above the content is harder to work than
	 * a list that simply runs on, and there is no column beside it whose
	 * height it has to respect.
	 */
	.ao-stk__rail {
		max-height: none;
		overflow-y: visible;
		border-bottom: 1px solid rgba(20, 18, 28, .1);
	}

	/* Stacked, the columns are divided horizontally by the rail's own
	   floor, so the vertical rule has nothing left to separate. */
	.ao-stk__stage {
		border-left: 0;
	}

	.ao-stk__rail.is-scrollable {
		-webkit-mask-image: none;
		mask-image: none;
	}

	.ao-stk {
		--ao-stk-row: 62px;
	}
}

@media (max-width: 599px) {

	.ao-stk {
		--ao-stk-row: 56px;
	}

	.ao-stk__grid {
		grid-template-columns: repeat(auto-fill, minmax(min(100%, 116px), 1fr));
	}

	.ao-stk__logo {
		height: 30px;
	}
}

/* =====================================================================
 * 17. About - the claim and the detail beside four figures
 *
 * Copy on the left at 57% of the measure, a two-by-two grid of counters
 * on the right. Measured off the reference: 672 and 501 on the 1200
 * container with 27px between them.
 *
 * The counting itself is the shared observer's - see
 * .ao-abt__num and assets/js/appagentix-motion.js. Nothing here starts,
 * stops or repeats it.
 * ================================================================== */

.ao-abt {
	display: grid;
	grid-template-columns: minmax(0, 57fr) minmax(0, 43fr);
	gap: clamp(24px, 2.6vw, 80px);
	align-items: start;
}

/* --- Left: the copy --------------------------------------------------- */

.ao-abt__copy {
	min-width: 0;
}

/*
 * inline-block so the box shrinks to the line. Left as a block the
 * gradient would run the full column and land its purple past the last
 * word, leaving the heading entirely dark - the same trap the industry
 * titles hit.
 */
.ao-abt__title {
	display: inline-block;
	margin: 0;
	font-size: clamp(1.1rem, .98rem + .5vw, 2rem);
	line-height: 1.3;
	font-weight: 700;
	letter-spacing: -.018em;
	background: linear-gradient(90deg,
			var(--ao-ink, #14121c) 0%,
			var(--ao-ink, #14121c) 74%,
			var(--brand-alt, #8347ec) 100%);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

.ao-abt__text {
	margin: clamp(14px, 1.8vw, 20px) 0 0;
	font-size: clamp(.88rem, .85rem + .14vw, 1rem);
	line-height: 1.66;
	color: var(--ao-ink-soft);
	text-wrap: pretty;
}

/* Sits at the foot of the copy, against its right edge, as the
   reference has it. */
.ao-abt__more {
	margin: clamp(10px, 1.2vw, 14px) 0 0;
	text-align: right;
}

.ao-abt__link {
	font-size: clamp(.82rem, .8rem + .1vw, .88rem);
	font-weight: 600;
	color: var(--brand-alt, #8347ec);
	text-decoration: none;
	border-bottom: 1px solid transparent;
	transition: border-color var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-abt__link:hover,
.ao-abt__link:focus-visible {
	border-bottom-color: currentColor;
}

.ao-abt__link:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: 3px;
	border-radius: 3px;
}

/* --- Right: the figures ----------------------------------------------- */

.ao-abt__stats {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: clamp(14px, 1.8vw, 24px);
	min-width: 0;
	margin: 0;
	padding: 0;
	list-style: none;
}

.ao-abt__stat {
	display: flex;
	flex-direction: column;
	gap: clamp(6px, .8vw, 10px);
	min-width: 0;
	padding: clamp(18px, 2.2vw, 28px) clamp(16px, 1.8vw, 24px);
	border-radius: 10px;
	background: var(--surface, #fff);
	box-shadow: 0 18px 40px -30px rgba(74, 40, 140, .45);
	transition: box-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

@media (hover: hover) and (pointer: fine) {

	.ao-abt__stat:hover {
		box-shadow: 0 24px 48px -28px rgba(74, 40, 140, .6);
	}
}

.ao-abt__num {
	font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.15rem);
	line-height: 1.1;
	font-weight: 700;
	letter-spacing: -.03em;
	color: var(--ao-ink);
	/* The figure is rewritten frame by frame while it climbs, and
	   proportional digits change width as it does. Tabular ones keep the
	   number from shuffling on the spot. */
	font-variant-numeric: tabular-nums;
}

.ao-abt__label {
	font-size: clamp(.84rem, .81rem + .14vw, .94rem);
	line-height: 1.4;
	color: var(--ao-ink-soft);
	text-wrap: pretty;
}

/* --- Stacked ---------------------------------------------------------- */

@media (max-width: 1023px) {

	.ao-abt {
		grid-template-columns: minmax(0, 1fr);
		gap: clamp(24px, 3.4vw, 34px);
	}

	.ao-abt__text {
		max-width: 68ch;
	}
}

@media (max-width: 479px) {

	.ao-abt__stats {
		grid-template-columns: minmax(0, 1fr);
	}

	/*
	 * Solid ink, not the ramp. A heading that wraps samples the gradient
	 * per line, so the last line's tail falls short of the box and the
	 * tint all but vanishes - and at this width it always wraps.
	 */
	.ao-abt__title {
		display: block;
		background: none;
		-webkit-text-fill-color: currentColor;
		color: var(--ao-ink, #14121c);
	}

	.ao-abt__more {
		text-align: left;
	}
}

/* =====================================================================
 * 18. Testimonials - a draggable track of quote cards
 *
 * Three cards on screen at a time, advancing one at a time, with a
 * sliver of the next one showing past the right edge so the track reads
 * as something that moves.
 *
 * WHY THE COUNT IS A CUSTOM PROPERTY
 *
 * --ao-tst-per is the one place the number on screen is decided. The
 * slide's width is calculated from it, and the script reads the same
 * value back to know how far a step is and where the last stop lies, so
 * a breakpoint change needs no matching edit in JavaScript.
 *
 * WHY THE VIEWPORT IS NOT `overflow: hidden` ALONE
 *
 * touch-action is what makes the drag coexist with the page: `pan-y`
 * hands vertical scrolling back to the browser and keeps the horizontal
 * for the track. Without it a swipe on a phone either scrolls the page
 * or moves the slider, depending on which won the race.
 * ================================================================== */

.ao-tst {
	--ao-tst-per: 3;
	--ao-tst-gap: clamp(14px, 1.6vw, 22px);
	/* How much of the next card shows past the right edge. */
	--ao-tst-peek: clamp(24px, 3vw, 44px);
}

.ao-tst__viewport {
	position: relative;
	overflow: hidden;
	/* Vertical scrolling stays the browser's; horizontal is the track's.
	   This is what stops a swipe fighting the page. */
	touch-action: pan-y;
	cursor: grab;
}

.ao-tst__viewport:active {
	cursor: grabbing;
}

/* The viewport is the only tab stop the design leaves room for, so its
   ring has to be visible against the page rather than inset. */
.ao-tst__viewport:focus-visible {
	outline: 2px solid var(--color-border-blue, #8347ec);
	outline-offset: 4px;
	border-radius: 12px;
}

.ao-tst__track {
	display: flex;
	gap: var(--ao-tst-gap);
	margin: 0;
	padding: 0;
	list-style: none;
	will-change: transform;
	transition: transform .55s var(--ao-m-ease, cubic-bezier(.22, 1, .36, 1));
}

/* While the pointer is down the track has to sit exactly where the
   finger is, so the easing that makes a released card settle would fight
   it. Also set for the instant re-position after a resize. */
.ao-tst__track.is-dragging,
.ao-tst__track.is-instant {
	transition: none;
}

/* Text selection turns a drag into a highlight, so it is off for the
   duration rather than globally - a visitor can still select a quote
   when they are not dragging it. */
.ao-tst__track.is-dragging,
.ao-tst__track.is-dragging * {
	user-select: none;
}

/*
 * Each slide is a share of the viewport less the gaps and the peek, so
 * three sit across it with the fourth's edge showing. flex-shrink is off
 * or they would compress to fit instead of overflowing, and the track
 * would have nothing to slide.
 */
.ao-tst__slide {
	flex: 0 0 calc((100% - (var(--ao-tst-per) - 1) * var(--ao-tst-gap) - var(--ao-tst-peek)) / var(--ao-tst-per));
	min-width: 0;
}

/* --- The card ---------------------------------------------------------- */

.ao-tst__card {
	display: flex;
	flex-direction: column;
	height: 100%;
	min-width: 0;
	margin: 0;
	overflow: hidden;
	border-radius: 12px;
	/* Lighter at the top-left, deeper at the foot, as the reference has
	   it - the band under the divider is the same ramp continuing, not a
	   second colour. */
	background: linear-gradient(145deg, #8f5ce8 0%, #7440d4 46%, #5a23a8 100%);
	box-shadow: 0 20px 44px -30px rgba(74, 40, 140, .7);
}

.ao-tst__mark {
	font-size: 1.9rem;
	line-height: 1;
	font-weight: 700;
	color: rgba(255, 255, 255, .5);
}

.ao-tst__mark--open {
	display: block;
	padding: clamp(16px, 1.8vw, 22px) clamp(16px, 1.8vw, 22px) 0;
}

/* Trails the last word rather than sitting on its own line, which is
   where the reference puts it. */
.ao-tst__mark--close {
	display: inline;
	margin-left: 4px;
	font-size: 1.4rem;
	vertical-align: -.24em;
}

.ao-tst__quote {
	flex: 1 1 auto;
	margin: 0;
	padding: clamp(8px, 1vw, 12px) clamp(16px, 1.8vw, 22px) clamp(16px, 1.8vw, 22px);
	font-size: clamp(.8rem, .77rem + .14vw, .88rem);
	line-height: 1.6;
	color: rgba(255, 255, 255, .95);
	text-wrap: pretty;
}

/* --- The attribution ---------------------------------------------------- */

.ao-tst__by {
	display: flex;
	align-items: center;
	gap: 12px;
	min-width: 0;
	padding: clamp(12px, 1.4vw, 16px) clamp(16px, 1.8vw, 22px);
	border-top: 1px solid rgba(255, 255, 255, .18);
	/* A touch darker than the card's foot, so the footer reads as its own
	   band without a second gradient. */
	background: rgba(32, 10, 72, .22);
}

.ao-tst__avatar {
	flex: 0 0 auto;
	width: 44px;
	height: 44px;
	border-radius: 999px;
	border: 2px solid rgba(255, 255, 255, .55);
	object-fit: cover;
	background: rgba(255, 255, 255, .15);
}

.ao-tst__cite {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
	font-style: normal;
}

.ao-tst__role {
	font-size: clamp(.88rem, .84rem + .16vw, .98rem);
	line-height: 1.25;
	font-weight: 700;
	letter-spacing: -.01em;
	color: #fff;
}

.ao-tst__org {
	font-size: clamp(.68rem, .66rem + .1vw, .74rem);
	line-height: 1.3;
	color: rgba(255, 255, 255, .78);
	text-wrap: pretty;
}

/* --- The hover hint -----------------------------------------------------
 *
 * Pointer-inert and hidden from assistive technology: it says what the
 * cursor already implies, and nothing lives in it. Off entirely where
 * there is no hover, so a touch screen never shows a label about an
 * interaction it does not have.
 * ------------------------------------------------------------------- */

.ao-tst__drag {

	position: absolute;
	top: 50%;
	left: 50%;
	display: none;
	align-items: center;
	gap: 6px;
	padding: 8px 14px;
	border-radius: 999px;
	border: 1px solid rgba(255, 255, 255, .5);
	background: rgba(32, 10, 72, .62);
	backdrop-filter: blur(3px);
	font-size: .76rem;
	font-weight: 600;
	letter-spacing: .02em;
	color: #fff;
	white-space: nowrap;
	pointer-events: none;
	opacity: 0;
	transform: translate3d(-50%, -50%, 0) scale(.94);
	transition:
		opacity var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-tst__drag .ai-ico {
	color: inherit;
}

.ao-tst__drag svg {
	width: 13px;
	height: 13px;
}

@media (hover: hover) and (pointer: fine) {

	.ao-tst__drag {
		display: none;
	}

	.ao-tst__viewport:hover .ao-tst__drag {
		opacity: 1;
		transform: translate3d(-50%, -50%, 0) scale(1);
	}

	/* Once the drag is under way the hint has done its job, and leaving
	   it up would sit it over the card being moved. */
	.ao-tst__viewport:has(.is-dragging) .ao-tst__drag {
		opacity: 0;
	}
}

/* --- Narrower ----------------------------------------------------------- */

@media (max-width: 1023px) {

	.ao-tst {
		--ao-tst-per: 2;
	}
}

@media (max-width: 767px) {

	.ao-tst {
		--ao-tst-per: 1;
	}
}

/* =====================================================================
 * 19. Audit badges - what we are certified against, and how we work
 *
 * Two white cards, 48/52, the left holding five third-party marks in
 * their own colours and the right four of our own practices as purple
 * discs. Neither carries a heading: the artwork is what tells them
 * apart, which is how the reference does it.
 *
 * WHY THE COLUMN COUNT IS A CUSTOM PROPERTY
 *
 * --ao-aud-cols is written onto each list by the template as the number
 * of items it holds, so five across and four across fall out of the
 * data. Neither five nor four appears below, and a sixth badge changes
 * nothing here.
 *
 * WHY THE BADGES ARE SIZED BY HEIGHT
 *
 * The marks are different shapes - PCI is landscape, the CCPA bear is
 * portrait - so a fixed width would squash some and strand others. A
 * fixed-height box with `width: auto` keeps each one's proportions and
 * still lines them all up on one baseline. The box has that height with
 * or without its artwork, so the row never resizes as the SVGs arrive.
 * ================================================================== */

.ao-aud {
	display: grid;
	/* 48/52: the right card carries four two-line captions where the
	   left carries five of one line, so it needs marginally more room. */
	grid-template-columns: minmax(0, 48fr) minmax(0, 52fr);
	gap: clamp(14px, 1.8vw, 24px);
	align-items: stretch;
}

/*
 * Tight on purpose. The reference sets the badges close to the card's
 * edges - roughly a fifth of its height above and below - and a more
 * generous pad left the artwork stranded in the middle of a tall card.
 */
.ao-aud__card {
	min-width: 0;
	padding: clamp(16px, 1.8vw, 24px) clamp(14px, 1.8vw, 24px);
	border-radius: 12px;
	background: var(--surface, #fff);
	box-shadow: 0 20px 44px -32px rgba(74, 40, 140, .45);
	transition: box-shadow var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

@media (hover: hover) and (pointer: fine) {

	.ao-aud__card:hover {
		box-shadow: 0 26px 52px -30px rgba(74, 40, 140, .6);
	}
}

.ao-aud__grid {
	display: grid;
	grid-template-columns: repeat(var(--ao-aud-cols, 4), minmax(0, 1fr));
	gap: clamp(8px, 1.2vw, 16px);
	align-items: start;
	margin: 0;
	padding: 0;
	list-style: none;
}

.ao-aud__item {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: clamp(6px, .8vw, 10px);
	min-width: 0;
	text-align: center;
}

/*
 * Fixed height, so the row is its final size before a single SVG has
 * loaded and the marks share a baseline whatever shape they are.
 */
.ao-aud__badge {
	display: flex;
	align-items: center;
	justify-content: center;
	height: clamp(42px, 4.4vw, 56px);
	transition: transform var(--ao-dur, .28s) var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
}

.ao-aud__badge img {
	display: block;
	width: auto;
	max-width: 100%;
	height: 100%;
	object-fit: contain;
}

@media (hover: hover) and (pointer: fine) {

	.ao-aud__item:hover .ao-aud__badge {
		transform: translate3d(0, -2px, 0) scale(1.05);
	}
}

.ao-aud__label {
	font-size: clamp(.72rem, .69rem + .14vw, .82rem);
	line-height: 1.32;
	font-weight: 600;
	letter-spacing: -.005em;
	color: var(--ao-ink);
	text-wrap: pretty;
}

/* --- Stacked: each card takes the full measure ----------------------- */

@media (max-width: 899px) {

	.ao-aud {
		grid-template-columns: minmax(0, 1fr);
	}
}

/*
 * Five badges across a phone leaves each one about sixty pixels wide
 * with a caption under it, so the rows rewrap instead - three, then two.
 * The count is overridden rather than the template changed.
 */
@media (max-width: 599px) {

	.ao-aud__grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: clamp(12px, 3vw, 18px);
	}
}

@media (max-width: 379px) {

	.ao-aud__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* =====================================================================
 * 20. Contact band - the lead form beside the office on a map
 *
 * Two balanced columns on a solid brand panel, the form on the left and
 * the map on the right.
 *
 * WHY A MODIFIER AND NOT A NEW TONE
 *
 * lp/section.php offers 'light', 'purple' and 'plain', and 'purple'
 * paints the gradient band the promise section uses. This band is a flat
 * colour instead, so rather than teach the shared wrapper a fourth tone -
 * and change a file six other sections depend on - the section takes
 * 'plain' and this class paints it. Same full-bleed behaviour: the
 * wrapper carries the colour, the inner keeps the measure.
 *
 * WHY THE MAP IS ABSOLUTE INSIDE ITS WRAPPER
 *
 * The form decides the row's height. An iframe is a replaced element
 * with its own intrinsic size, so left in flow it would either stretch
 * the row or fall short of it; taken out of flow it fills whatever the
 * form left behind, and the map ends up exactly as tall as the card
 * beside it at any width.
 * ================================================================== */

.ao-section--contact {
	background: radial-gradient(circle at 12% 10%,rgba(91,61,245,0.58) 0,rgba(91,61,245,0) 42%),radial-gradient(ellipse 60% 55% at 55% 112%,rgba(226,63,159,0.3) 0,rgba(226,63,159,0) 60%),radial-gradient(circle at 94% 96%,rgba(255,138,0,0.3) 0,rgba(255,138,0,0) 24%),#100C24;
	color: #fff;
}

.ao-cmap {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: clamp(24px, 3vw, 44px);
	/* Both columns take the row's full height, which is what lets the map
	   match the form rather than sit in the middle of a gap. */
	align-items: stretch;
}

.ao-cmap__form {
	min-width: 0;
}

/*
 * The form component brings its own card, its own colours and its own
 * glow rim. Nothing is restyled here: a near-black card on this purple
 * is the contrast the design wants, and overriding it would mean
 * maintaining a second look for one placement.
 */
.ao-cmap__form .lp-form-card {
	height: 100%;
}

.ao-cmap__map {
	position: relative;
	min-width: 0;
	/* A floor, so the map is never a sliver if the form is unusually
	   short - and the height the ratio below starts from once stacked. */
	min-height: 320px;
	overflow: hidden;
	border-radius: 18px;
	box-shadow: 0 30px 70px -40px rgba(18, 13, 38, .8);
}

/*
 * Absolute, so the embed's own 600x450 never contributes to layout. The
 * attributes stay on the tag as the browser's pre-load ratio; these two
 * rules are what actually size it.
 */
.ao-cmap__map iframe {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

/* --- Stacked ---------------------------------------------------------- */

@media (max-width: 767px) {

	.ao-cmap {
		grid-template-columns: minmax(0, 1fr);
	}

	/*
	 * Nothing sets the map's height once it is no longer beside the form,
	 * so it takes a ratio of its own. Reserved before the embed loads, so
	 * the stack does not jump.
	 */
	.ao-cmap__map {
		aspect-ratio: 4 / 3;
		min-height: 0;
		border-radius: 14px;
	}
}


	.ao-sol__panel-icon {
		color: #120d26;
	}

	.ao-sol__panel-icon .ai-svg {
		stroke: #000;
	}

/* =====================================================================
 * 21. Case studies - the copy beside the photograph, one at a time
 *
 * One bordered card holding all five studies, the copy on the left and
 * the photograph on the right, with the arrows and dots along its foot.
 *
 * WHY THE STUDIES ARE GRID-STACKED
 *
 * Every study sits in the same grid cell, so the card reserves the
 * height of the longest one and moving between them cannot shift the
 * page beneath it. The studies differ by a line or two, so a card that
 * sized itself to the visible one would jolt on every change. The same
 * arrangement, for the same reason, as .ao-sol__slide.
 *
 * WHY THE CATEGORY IS A CHIP AND NOT A BUTTON
 *
 * It is a label. There is no category archive to send anyone to, so it
 * is styled as the pale pill the reference shows without being a control
 * that takes focus and then does nothing - see case-studies.php.
 *
 * It never advances on its own. The controls are the only way to move.
 * ================================================================== */

.ao-cs {
	position: relative;
	box-sizing: border-box;
	padding: clamp(18px, 2.2vw, 28px) clamp(18px, 2.2vw, 28px) clamp(14px, 1.6vw, 20px);
	border-radius: 18px;
	border: 1px solid rgba(20, 18, 28, .08);
	background: var(--surface, #fff);
	box-shadow: 0 20px 44px -30px rgba(74, 40, 140, .4);
}

.ao-cs__stage {
	display: grid;
	grid-template-areas: "case";
}

.ao-cs__item {
	grid-area: case;
	box-sizing: border-box;
	display: grid;
	/* Slightly narrower copy than media, so the photograph carries the
	   row and the text keeps a comfortable measure at 1200px. */
	grid-template-columns: minmax(0, .92fr) minmax(0, 1fr);
	/* Stretch, so the photograph fills the card's full height. The copy
	   opts back out below - it is centred against the photograph rather
	   than stretched to match it. */
	align-items: stretch;
	gap: clamp(20px, 3vw, 44px);
	opacity: 0;
	transform: translateY(10px);
	transition:
		opacity .45s var(--ao-ease, cubic-bezier(.4, 0, .2, 1)),
		transform .45s var(--ao-ease, cubic-bezier(.4, 0, .2, 1));
	pointer-events: none;
}

.ao-cs__item.is-active {
	opacity: 1;
	transform: none;
	pointer-events: auto;
}

/* [hidden] must not beat the grid placement, or the card collapses to
   one study's height and the page jumps on every change. The attribute
   still hides it from assistive tech, and `inert` on the same element
   keeps it out of the tab order - see case-studies.php. */
.ao-cs__item[hidden] {
	display: grid;
	visibility: hidden;
}

/* --- Left: the copy ------------------------------------------------- */

.ao-cs__copy {
	min-width: 0;
	/* Centred against the photograph beside it. The card is as tall as
	   the longest study, so a shorter one would otherwise sit at the top
	   of its column with the space all below it. */
	align-self: center;
	padding-block: clamp(4px, 1vw, 12px);
}

.ao-cs__title {
	margin: 0;
	font-size: clamp(1.45rem, 1.1rem + 1.2vw, 2rem);
	line-height: 1.2;
	font-weight: 700;
	letter-spacing: -.02em;
	color: var(--ao-ink);
}

/*
 * Only one study carries this: the concept, which has to say it is not
 * a client project. A tinted line rather than a loud badge - it is a
 * qualification, not a label to lead with.
 */
.ao-cs__badge {
	display: inline-flex;
	align-items: center;
	margin: 10px 0 0;
	padding: 5px 11px;
	border-radius: var(--radius-pill, 999px);
	border: 1px solid rgba(101, 77, 255, .22);
	background: rgba(101, 77, 255, .07);
	font-size: .76rem;
	font-weight: 600;
	line-height: 1.35;
	letter-spacing: .01em;
	color: var(--brand-deep, #6a34d4);
}

.ao-cs__meta {
	margin: 12px 0 0;
	font-size: clamp(.86rem, .84rem + .1vw, .93rem);
	line-height: 1.55;
	color: #7B7B7B;
}

.ao-cs__meta-label {
	font-weight: 700;
	color: #7B7B7B;
}

.ao-cs__text {
	margin: 14px 0 0;
	font-size: clamp(.95rem, .92rem + .15vw, 1.02rem);
	line-height: 1.7;
	color: var(--ao-ink-soft);
}

/* The label in the reference's pill. Inline-flex on the span rather than
   the paragraph, so the pill is as wide as its words and the paragraph
   keeps the block rhythm around it. */
.ao-cs__cat {
	margin: 16px 0 0;
}

.ao-cs__chip {
	display: inline-flex;
	align-items: center;
	padding: 8px 15px;
	border-radius: var(--radius-pill, 999px);
	border: 1px solid rgba(20, 18, 28, .14);
	background: rgba(101, 77, 255, .05);
	font-size: .82rem;
	font-weight: 600;
	line-height: 1.3;
	color: var(--ao-ink);
}

.ao-cs__foot {
	display: flex;
	margin-top: clamp(18px, 2.2vw, 26px);
}

/* --- Right: the photograph ------------------------------------------ */

/*
 * The ratio is on the box, so the row is its final height before any
 * photograph has decoded and the card cannot resize as they arrive.
 */
/*
 * No ratio here: the column is stretched, so the photograph takes the
 * card's full height and the copy beside it decides what that is. The
 * stacked breakpoint puts a ratio back, because there is no second
 * column to take the height from once they are one above the other.
 *
 * The photographs carry no shadow of their own. The supplied PNGs each
 * held a 17px soft drop shadow in a transparent margin; the WebPs beside
 * them are cropped to the photograph itself, so the only edge here is
 * this radius.
 */
.ao-cs__media {
	position: relative;
	min-width: 0;
	overflow: hidden;
	border-radius: 14px;
	background: var(--surface-soft, #faf9ff);
}

.ao-cs__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	user-select: none;
}

/* --- Controls ------------------------------------------------------- */

.ao-cs__nav {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	margin-top: clamp(16px, 2vw, 22px);
}

.ao-cs__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* The visible box is small; the button stays 32px so the tap target
	   is never smaller than a fingertip. */
	width: 30px;
	height: 32px;
	padding: 0;
	border: 1px solid rgba(20, 18, 28, .1);
	border-radius: 8px;
	background: #f3f3f5;
	color: #3f3d4d;
	cursor: pointer;
	transition:
		background-color var(--ao-dur, .28s) var(--ao-ease, ease),
		border-color var(--ao-dur, .28s) var(--ao-ease, ease);
}

.ao-cs__arrow:hover {
	border-color: rgba(101, 77, 255, .45);
	background: #ece9ff;
}

.ao-cs__arrow svg {
	width: 15px;
	height: 15px;
}

.ao-cs__arrow .ai-ico {
	color: inherit;
}

.ao-cs__dots {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 3px;
}

.ao-cs__dot {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 18px;
	height: 32px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.ao-cs__dot span {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: rgba(20, 18, 28, .22);
	transition:
		background-color var(--ao-dur, .28s) var(--ao-ease, ease),
		width var(--ao-dur, .28s) var(--ao-ease, ease);
}

.ao-cs__dot.is-active span {
	width: 18px;
	border-radius: 999px;
	background: var(--brand, #654dff);
}

.ao-cs__arrow:focus-visible,
.ao-cs__dot:focus-visible {
	outline: 2px solid var(--brand, #654dff);
	outline-offset: 2px;
}

/* While a drag is in progress the pointer owns the card: no text
   selection, and the photograph does not offer itself for dragging. */
.ao-cs.is-dragging {
	cursor: grabbing;
	user-select: none;
}

/* --- Responsive ----------------------------------------------------- */

@media (max-width: 899px) {

	/* Stacked: the photograph reads as support for the copy above it
	   rather than a second column competing with it. */
	.ao-cs__item {
		grid-template-columns: minmax(0, 1fr);
		align-items: start;
	}

	.ao-cs__item[hidden] {
		display: grid;
	}

	/* Nothing is beside the photograph now, so it takes a ratio of its
	   own rather than a height from the column. Reserved before it
	   decodes, so the stack does not jump. */
	.ao-cs__media {
		aspect-ratio: 817 / 416;
	}
}

@media (max-width: 599px) {

	.ao-cs {
		border-radius: 14px;
	}

	.ao-cs__foot .ao-btn {
		justify-content: center;
		width: 100%;
	}
}

/* =====================================================================
 * 22. Reduced motion
 * ================================================================== */

@media (prefers-reduced-motion: reduce) {

	/* The track still moves between cards - that is navigation, not
	   decoration - but it arrives immediately rather than sliding. */
	.ao-work__track {
		transition-duration: .01ms;
	}

	.ao-work__tab,
	.ao-work__arrow,
	.ao-work__dot span {
		transition-duration: .01ms;
	}


	.ao-btn,
	.ao-btn::before,
	.ao-btn__icon {
		transition-duration: .01ms;
	}

	/* The slide still changes - that is navigation, not decoration -
	   but it arrives immediately rather than fading in. */
	.ao-sol__slide,
	.ao-sol__arrow,
	.ao-sol__dot span,
	.ao-sol__panel-icon,
	.ao-stage {
		transition-duration: .01ms;
	}

	/* Same for the case studies: the study still changes, because that is
	   the content, but it is there rather than fading in. */
	.ao-cs__item,
	.ao-cs__arrow,
	.ao-cs__dot span {
		transition-duration: .01ms;
	}


	.ao-stage:hover,
	.ao-stage:focus-within {
		transform: none;
	}

	/* The split band's hover tint and lift are decoration, so they go.
	   The rows stay legible either way - nothing here is carried by
	   motion alone. */
	.ao-split__item,
	.ao-split__item::before,
	.ao-split__icon {
		transition-duration: .01ms;
	}

	.ao-btn:hover,
	.ao-btn:focus-visible {
		transform: none;
	}

	/* The loop stops, and the row becomes scrollable so every logo is
	   still reachable rather than frozen off screen. */
	.ao-marquee {
		-webkit-mask-image: none;
		mask-image: none;
		overflow-x: auto;
	}

	.ao-marquee__track {
		animation: none;
	}

	.ao-marquee__viewport>.ao-marquee__track[aria-hidden="true"] {
		display: none;
	}

	/*
	 * The capability rows still open - that is the content, not
	 * decoration - but they arrive rather than unfold, and the poster's
	 * zoom, the button's scale and the halo around it go.
	 */
	.ao-cap__card,
	.ao-cap__trigger,
	.ao-cap__mark,
	.ao-cap__caret,
	.ao-cap__title-row,
	.ao-cap__panel,
	.ao-cap__poster,
	.ao-cap__play-mark,
	.ao-cap__media::after,
	.ao-cap__link {
		transition-duration: .01ms;
	}

	.ao-cap__panel,
	.ao-cap__text,
	.ao-cap__more {
		transition-delay: 0s;
	}

	.ao-cap__media:hover .ao-cap__poster,
	.ao-cap__play:hover .ao-cap__play-mark {
		transform: none;
	}

	.ao-cap__media.is-visible .ao-cap__play-halo {
		animation: none;
		opacity: 0;
	}

	/*
	 * The panel still changes - that is navigation, not decoration - but
	 * it arrives whole rather than dealing its cards out. The stagger
	 * goes with the travel: a delay with nothing left to delay would
	 * only hold an already-visible card back.
	 */
	.ao-ais__tab,
	.ao-ais__tab::after,
	.ao-ais__card {
		transition-duration: .01ms;
	}

	.ao-ais__card {
		transition-delay: 0s;
		transform: none;
	}

	.ao-ais__card:hover {
		box-shadow: none;
	}

	/*
	 * The industry panel still opens - the three lines are content, not
	 * decoration - but it arrives rather than rising, and the photograph
	 * holds still. The gradient is snapped into place rather than
	 * transitioned, because a sliding backdrop is the one part of this
	 * that is purely motion.
	 */
	.ao-inds__card,
	.ao-inds__panel::before,
	.ao-inds__points,
	.ao-inds__point,
	.ao-inds__scrim,
	.ao-inds__img,
	.ao-inds__name {
		transition-duration: .01ms;
	}

	.ao-inds__point {
		transition-delay: 0s;
		transform: none;
	}

	.ao-inds__card:hover .ao-inds__img,
	.ao-inds__card:focus-within .ao-inds__img,
	.ao-inds__card.is-open .ao-inds__img {
		transform: none;
	}

	/*
	 * The stage still changes - that is navigation - but it cuts rather
	 * than cross-fades, the copy arrives in place, and the photograph's
	 * slow settle back to true size goes entirely.
	 */
	.ao-eng__btn,
	.ao-eng__tail,
	.ao-eng__go,
	.ao-eng__panel,
	.ao-eng__title,
	.ao-eng__text,
	.ao-eng__img {
		transition-duration: .01ms;
	}

	.ao-eng__title,
	.ao-eng__text {
		transition-delay: 0s;
		transform: none;
	}

	.ao-eng__img,
	.ao-eng__panel.is-active .ao-eng__img {
		transform: none;
	}

	/*
	 * The stack panel still changes - that is navigation - but it cuts
	 * rather than cross-fades and the chips arrive together rather than
	 * dealing themselves out. The rail keeps its scrolling; it just
	 * stops smoothing it.
	 */
	.ao-stk__btn,
	.ao-stk__tail,
	.ao-stk__go,
	.ao-stk__panel,
	.ao-stk__chip {
		transition-duration: .01ms;
	}

	.ao-stk__panel.is-active .ao-stk__chip,
	.ao-stk__panel.is-active .ao-stk__chip:nth-child(1),
	.ao-stk__panel.is-active .ao-stk__chip:nth-child(2),
	.ao-stk__panel.is-active .ao-stk__chip:nth-child(3),
	.ao-stk__panel.is-active .ao-stk__chip:nth-child(4),
	.ao-stk__panel.is-active .ao-stk__chip:nth-child(5),
	.ao-stk__panel.is-active .ao-stk__chip:nth-child(n+6) {
		transition-delay: 0s;
	}

	.ao-stk__chip {
		transform: none;
	}

	.ao-stk__rail {
		scroll-behavior: auto;
	}

	/*
	 * The figures are not animated here at all - appagentix-motion.js
	 * writes the final value and skips the climb under this query, so
	 * there is nothing to shorten. Only the card's hover lift goes.
	 */
	.ao-abt__stat,
	.ao-abt__link {
		transition-duration: .01ms;
	}

	.ao-abt__stat:hover {
		box-shadow: 0 18px 40px -30px rgba(74, 40, 140, .45);
	}

	/*
	 * The testimonial track still moves - dragging it is the point, and
	 * the keys have to go somewhere - but it arrives rather than glides,
	 * and the timer never starts, so it only ever moves when asked. The
	 * hover hint stops fading in and simply appears.
	 */
	.ao-tst__track,
	.ao-tst__drag {
		transition-duration: .01ms;
	}

	.ao-tst__drag {
		transform: translate3d(-50%, -50%, 0);
	}

	@media (hover: hover) and (pointer: fine) {

		.ao-tst__viewport:hover .ao-tst__drag {
			transform: translate3d(-50%, -50%, 0);
		}
	}

	/* The badges are static anyway; only the hover lift is motion. */
	.ao-aud__card,
	.ao-aud__badge {
		transition-duration: .01ms;
	}

	.ao-aud__item:hover .ao-aud__badge {
		transform: none;
	}
}

/* =====================================================================
 * 23. custom properties
 * ================================================================== */

 
/* Small Mobile: 320px - 374px */
@media (max-width: 374px) {
  .ao-hero__title {
    font-size: 25px;
  }
.ao-hero__lead {
    font-size: 16px;
}
.ao-section__title {
	    font-size: 25px;
        text-align: left;
        line-height: unset;
  }
.ao-section__lead {
    font-size: 16px;
    text-align: left;
}
.ao-sol__title{
	font-size: 25px;
	text-align: left;
	line-height: unset;
  }
.ao-sol__text{
	font-size: 16px;
	text-align: left;
	line-height: unset;
  }
  .ao-split__title {
    font-size: 25px;
    text-align: left;
    line-height: unset;
}
.ao-split__body {
    font-size: 16px;
    text-align: left;
    line-height: unset;
}
.ao-cap__title {
    font-size: 25px;
    text-align: left;
    line-height: unset;
}
.ao-ais__points {
    gap: 9px;
}
.ao-inds__panel {
    justify-content: center;
    align-items: center;
    padding: 56px 22px;
    text-align: left;
}

}

/* Mobile: 375px - 767px */
@media (min-width: 375px) and (max-width: 767px) {
    .ao-hero__title {
    font-size: 25px;
  }
.ao-hero__lead {
    font-size: 16px;
}
.ao-section__title {
	    font-size: 25px;
        text-align: left;
        line-height: unset;
  }
  .ao-section__lead {
    font-size: 16px;
    text-align: left;
}
.ao-sol__title{
	font-size: 25px;
	text-align: left;
	line-height: unset;
  }
  .ao-sol__text{
	font-size: 16px;
	text-align: left;
	line-height: unset;
  }
  .ao-split__title {
    font-size: 25px;
    text-align: left;
    line-height: unset;
}
.ao-split__body {
    font-size: 16px;
    text-align: left;
    line-height: unset;
}
.ao-cap__title {
    font-size: 25px;
    text-align: left;
    line-height: unset;
}
.ao-ais__points {
    gap: 9px;
}
.ao-inds__panel {
    justify-content: center;
    align-items: center;
    padding: 56px 22px;
    text-align: left;
}

}

/* Tablet: 768px - 1023px */
@media (min-width: 768px) and (max-width: 1023px) {
   .ao-hero__title {
    font-size: 25px;
  }
.ao-hero__lead {
    font-size: 16px;
}
.ao-section__title {
	    font-size: 25px;
        text-align: left;
        line-height: unset;
  }
  .ao-section__lead {
    font-size: 16px;
    text-align: left;
}
.ao-sol__title{
	font-size: 25px;
	text-align: left;
	line-height: unset;
  }
  .ao-sol__text{
	font-size: 16px;
	text-align: left;
	line-height: unset;
  }
  .ao-split__title {
    font-size: 25px;
    text-align: left;
    line-height: unset;
}
.ao-split__body {
    font-size: 16px;
    text-align: left;
    line-height: unset;
}
.ao-cap__title {
    font-size: 25px;
    text-align: left;
    line-height: unset;
}
.ao-ais__points {
    gap: 9px;
}
.ao-inds__panel {
    justify-content: center;
    align-items: center;
    padding: 56px 22px;
    text-align: left;
}
}

/* Desktop / Laptop: 1024px - 1399px */
@media (min-width: 1024px) and (max-width: 1399px) {
 .tta-nav > ul > li > a {
    font-size: 14px;
 }
}

/* Large Desktop: 1400px - 1599px */
@media (min-width: 1400px) and (max-width: 1599px) {
  /* Large desktop styles */
}

/* Screen: 1600px - 1799px */
@media (min-width: 1600px) and (max-width: 1799px) {
  /* 1600px screen styles */
}

/* Screen: 1800px - 2199px */
@media (min-width: 1800px) and (max-width: 2199px) {
  /* 1800px screen styles */
}

/* Extra Large Screen: 2200px and above */
@media (min-width: 2200px) {
  /* Extra-large screen styles */
}
