/* ==========================================================================
   Ambientech custom styles
   Loaded after theme + skin, so anything here wins.
   ========================================================================== */

/* --- Overscroll canvas ---------------------------------------------------
   `html` has no background of its own, so the canvas takes body's white. That
   is invisible while the page is at rest -- body covers the full document -- but
   Chrome's elastic overscroll on Windows drags the content away from the edge
   and paints the canvas in the gap: a white band above the hero when you pull
   down at the top, and above the footer when you pull up at the bottom.
   Measured: html backgroundColor rgba(0,0,0,0), body rgb(255,255,255).

   Note this is the overscroll gutter only -- it is NOT what caused the white
   line above the hero. That was the header flow box; see its block below.

   Both ends of every page are now brand-dark -- the hero and the solid header
   at the top, the footer at the bottom -- so the canvas is set to match. Giving
   `html` a background stops body's from propagating to the canvas, but body's
   box already spans the whole document, so nothing inside the page changes. */
html {
	background-color: #1F1F33;
}

/* --- Brand lockup in the header ---------------------------------------- */
.abt-logo {
	display: flex;
	align-items: center;
	gap: .6rem;
	text-decoration: none;
}

.abt-logo img {
	width: 42px;
	height: 42px;
	/* The logo mark is deep indigo on transparent; on the dark header a soft
	   white disc keeps it legible without altering the artwork. */
	background: #fff;
	border-radius: 50%;
	padding: 2px;
}

.abt-logo-text {
	display: flex;
	flex-direction: column;
	line-height: 1;
}

.abt-logo-name {
	color: #fff;
	font-weight: 700;
	font-size: 1.25rem;
	letter-spacing: .06em;
}

.abt-logo-tag {
	color: var(--secondary);
	font-size: .62rem;
	font-weight: 500;
	letter-spacing: .18em;
	text-transform: uppercase;
	margin-top: .18rem;
}

/* TH / EN language switch in the header. Sits on the dark header, so links are
   light with the current language picked out in the brand's secondary colour. */
.abt-lang {
	display: flex;
	align-items: center;
	gap: .3rem;
	margin: 0 1rem 0 1.25rem;
	white-space: nowrap;
}
.abt-lang-link {
	color: rgba(255, 255, 255, .6);
	font-size: .82rem;
	font-weight: 600;
	letter-spacing: .03em;
	text-decoration: none;
	transition: color .2s ease;
}
.abt-lang-link:hover {
	color: #fff;
}
.abt-lang-link.active {
	color: var(--secondary);
}
.abt-lang-sep {
	color: rgba(255, 255, 255, .3);
	font-size: .78rem;
}


/* --- Header flow box (hero layout) ---------------------------------------
   The one-page demo parks the header at the bottom of the hero with
   `#header { position: relative; top: calc(100vh - 78px) }`. Relative offsets do
   not free up space, so the header still occupies 78px at the TOP of the
   document, and the demo cancels exactly that with `.main { margin-top: -78px }`.
   Two hardcoded numbers that have to agree to the pixel.

   They stop agreeing the moment the header's content renders taller than 78px.
   Porto's sticky plugin measures the header on build and writes the result back
   as an inline height -- observed in the wild as `height: 80.7188px`. Inline
   beats the stylesheet, so the flow box grows to 80.72 while the cancellation
   stays at 78, and the difference opens a gap above the hero at the very top of
   the page. Measured with that exact inline value: header box 80.7188, hero top
   2.7188, and elementFromPoint(x, 1) returns div.body -- the white page wrapper
   showing through above the hero. That is the white line.

   The fix is to pin the flow box rather than chase the measurement. !important
   is what beats the plugin's inline style, and every number now reads one
   token, so the two halves cannot drift apart again. Raising the token is the
   supported way to make the bar taller -- editing one of these values alone is
   what reopens the gap.

   Scoped away from .abt-header-solid: those pages keep the header in normal
   flow at the top of the page, with no negative margin to disagree with, so a
   taller header there simply pushes the page down as it should. */
:root {
	--abt-header-h: 78px;
}

#header:not(.abt-header-solid) {
	height: var(--abt-header-h) !important;
	top: calc(100vh - var(--abt-header-h));
}

#header:not(.abt-header-solid) ~ .main {
	margin-top: calc(-1 * var(--abt-header-h));
}


/* --- Solid header (pages without a hero) ---------------------------------
   The header is built to sit ON the home hero: white wordmark, white nav,
   translucent language links. On a page with no photograph behind it those
   land on the theme's white header body and the brand name renders white on
   white -- measured at 1.0:1, i.e. invisible.

   Rather than restyle every child for a light ground, the header keeps its own
   dark ground on those pages. Everything inside is then correct as-is, and the
   bar reads as deliberate site chrome instead of an empty white strip.

   Set from a view with ViewData["SolidHeader"] = true. */
/* Selectors are ID-qualified on purpose. The theme styles this element as
   `#header .header-body` (specificity 1,1,0), so a plain `.abt-header-solid
   .header-body` (0,2,0) loses and the override silently does nothing -- which is
   how the 3px #EDEDED border-top below survived and drew a pale line across the
   top of the bar.

   border-bottom keeps !important because the sticky script adds
   .header-body-bottom-border-fixed, and the theme declares that one !important
   too; specificity alone cannot beat it. */
#header.abt-header-solid,
#header.abt-header-solid .header-body {
	background-color: #1F1F33;
	border-top: 0;
	border-bottom: 0 !important;
}

/* The one-page demo parks the header at the bottom of the 100vh hero. With no
   hero to sit at the bottom of, it belongs at the top -- stated in CSS so it is
   right on first paint rather than after the sticky script runs. */
#header.abt-header-solid {
	top: 0 !important;
}

/* A hairline under the bar so the dark chrome and the dark page-head below it
   read as two bands rather than one tall block -- on the Knowledge index the two
   are the same colour and would otherwise merge. Drawn as an inset shadow rather
   than a border so it cannot reintroduce the layout the border above caused. */
#header.abt-header-solid .header-body {
	box-shadow: inset 0 -1px 0 rgba(255, 255, 255, .1);
}

/* --- Hero ---------------------------------------------------------------- */
/* Slide 1 carries a photograph; a flat brand colour sits behind it so there is
   no white flash before the image decodes. */
.abt-hero-1 {
	background: #2b2b48;
}

/* Brand-tinted scrim, two gradients stacked. The 100deg layer is the one that
   does the work: near-opaque indigo where the headline sits, easing to almost
   clear on the right so the people in the photograph stay visible instead of
   being flattened under a single grey veil. The vertical layer on top only
   darkens the extreme top and bottom, which keeps the header nav and the CTA
   from floating on a bright patch of the shot.
   This band is the one place the page keeps a gradient - the flat-UI pass below
   is about solid brand fills for UI chrome, not about the photographic hero. */
.abt-hero-scrim {
	z-index: 2;
	background:
		linear-gradient(to bottom, rgba(31, 31, 51, .45) 0%, rgba(31, 31, 51, 0) 28%, rgba(31, 31, 51, 0) 72%, rgba(31, 31, 51, .55) 100%),
		linear-gradient(100deg, rgba(31, 31, 51, .95) 0%, rgba(31, 31, 51, .92) 30%, rgba(46, 46, 79, .82) 48%, rgba(72, 72, 127, .45) 70%, rgba(72, 72, 127, .15) 100%);
}

@media (max-width: 991px) {
	/* Narrow viewports centre the text over the subjects rather than keeping it
	   left, so a left-to-right ramp would leave the headline half-lit. Top-to-
	   bottom instead, and denser throughout. */
	.abt-hero-scrim {
		background: linear-gradient(to bottom, rgba(31, 31, 51, .82) 0%, rgba(46, 46, 79, .78) 60%, rgba(31, 31, 51, .88) 100%);
	}
}

.abt-hero-2 {
	background: linear-gradient(135deg, #1F1F33 0%, #48487F 60%, #70C3D9 100%);
}

/* Faint grid, so the flat gradient reads as "industrial" rather than empty. */
.abt-hero-grid::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background-image:
		linear-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px),
		linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
	background-size: 64px 64px;
	pointer-events: none;
}

/* --- Service / solution cards -------------------------------------------- */
.abt-service-num {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: 62px;
	height: 62px;
	border-radius: 50%;
	background: var(--primary);
	color: #fff;
	font-size: 1.5rem;
	font-weight: 700;
}

.abt-pillar-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 76px;
	height: 76px;
	border-radius: 50%;
	background: var(--primary-rgba-10);
	color: var(--primary);
	font-size: 1.9rem;
	margin-bottom: 1.25rem;
}

/* --- Who We Are: proof rows ----------------------------------------------- */
.abt-proof {
	display: flex;
	align-items: flex-start;
	gap: 1.1rem;
	margin-bottom: 1.5rem;
}

.abt-proof-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: 48px;
	height: 48px;
	border-radius: 50%;
	background: var(--primary-rgba-10);
	color: var(--primary);
	font-size: 1.05rem;
}

/* --- Who We Are: before / after output chart ------------------------------ */
.abt-gain {
	display: block;
	width: 100%;
	max-width: 340px;
	height: auto;
	margin: 0 auto 1.25rem;
}

/* This chart carries information, so unlike the decorative loops elsewhere it
   plays ONCE into its final state and holds there. A loop would leave "4.0" and
   "+60%" invisible for part of every cycle, and would briefly show a full-height
   bar labelled with the "before" figure.

   The resting styles in the SVG are already the finished state, so if the theme's
   scroll-into-view JS never fires the reader still sees the correct chart - the
   animation is added only by .appear-animation-visible, and runs from the
   "before" state forward. */
.abt-gain-bar { transform-box: fill-box; transform-origin: 50% 100%; }
.abt-gain-pill { transform-box: fill-box; transform-origin: 50% 50%; }

.appear-animation-visible .abt-gain-bar { animation: abtGainGrow 1.5s .15s cubic-bezier(.25,.8,.3,1) both; }
.appear-animation-visible .abt-gain-val { animation: abtGainVal .7s .95s ease-out both; }
.appear-animation-visible .abt-gain-arrow { animation: abtGainArrow .8s .8s ease-out both; }
.appear-animation-visible .abt-gain-pill { animation: abtGainPill .7s 1.25s cubic-bezier(.34,1.56,.64,1) both; }

/* .615 is the before/after ratio: a 2.5 t/day bar against a 4.0 t/day bar. */
@keyframes abtGainGrow { from { transform: scaleY(.615); } to { transform: scaleY(1); } }
@keyframes abtGainVal { from { opacity: 0; transform: none; } to { opacity: 1; } }
@keyframes abtGainArrow { from { stroke-dashoffset: 60; } to { stroke-dashoffset: 0; } }
@keyframes abtGainPill {
	from { transform: scale(.5); opacity: 0; }
	to { transform: scale(1); opacity: 1; }
}

/* --- Animated stat icons (counter band) ----------------------------------- */
/* NB: never put a CSS transform animation on an element that already carries a
   `transform` attribute - in SVG they are the same property and the CSS rule
   wins, silently discarding the attribute's translate. Where both are needed,
   an outer <g> holds the translate and an inner <g> takes the animation. */
.abt-stat-icon {
	display: block;
	width: 78px;
	height: 78px;
	margin: 0 auto .9rem;
	overflow: visible;
}

.abt-stat-icon .s { fill: none; stroke: #fff; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.abt-stat-icon .p { fill: #fff; }

/* Years: medal with a sweeping highlight and a check that draws itself */
.abt-shine {
	fill: none;
	stroke: rgba(255, 255, 255, .95);
	stroke-width: 4.5;
	stroke-linecap: round;
	stroke-dasharray: 12 90;
	animation: abtShine 3.4s ease-in-out infinite;
}
@keyframes abtShine {
	0% { stroke-dashoffset: 102; opacity: 0; }
	15% { opacity: 1; }
	55% { stroke-dashoffset: 0; opacity: 0; }
	100% { stroke-dashoffset: 0; opacity: 0; }
}
.abt-ribbon { transform-box: fill-box; transform-origin: 50% 0; animation: abtSway 4.2s ease-in-out infinite; }
@keyframes abtSway { 0%, 100% { transform: rotate(-2.5deg); } 50% { transform: rotate(2.5deg); } }
.abt-medal-check { stroke-dasharray: 28; animation: abtDrawCheck 3.4s ease-in-out infinite; }
@keyframes abtDrawCheck { 0% { stroke-dashoffset: 28; } 22%, 100% { stroke-dashoffset: 0; } }

/* Customers: three figures nodding in sequence */
.abt-person { transform-box: fill-box; transform-origin: 50% 100%; animation: abtPop 3s ease-in-out infinite; }
.abt-p2 { animation-delay: .2s; }
.abt-p3 { animation-delay: .4s; }
@keyframes abtPop { 0%, 55%, 100% { transform: translateY(0) scale(1); } 16% { transform: translateY(-3px) scale(1.05); } }

/* Industries: smoke rising from the stacks */
.abt-smoke .p { transform-box: fill-box; transform-origin: 50% 50%; animation: abtPuff 2.8s ease-out infinite; }
.abt-smoke .p:nth-child(2) { animation-delay: .55s; }
.abt-smoke .p:nth-child(3) { animation-delay: 1.1s; }
@keyframes abtPuff {
	0% { transform: translateY(7px) scale(.45); opacity: 0; }
	30% { opacity: .95; }
	100% { transform: translateY(-13px) scale(1.3); opacity: 0; }
}

/* Capacity gain: bars growing under a rising trend line */
.abt-bar { transform-box: fill-box; transform-origin: 50% 100%; animation: abtBarGrow 3s ease-in-out infinite; }
.abt-b2 { animation-delay: .18s; }
.abt-b3 { animation-delay: .36s; }
@keyframes abtBarGrow { 0%, 100% { transform: scaleY(.35); } 45%, 72% { transform: scaleY(1); } }
.abt-arrow { transform-box: fill-box; transform-origin: 0 100%; animation: abtArrowRise 3s ease-in-out infinite; }
@keyframes abtArrowRise { 0%, 100% { opacity: .4; transform: translateY(5px); } 50%, 72% { opacity: 1; transform: translateY(0); } }

/* --- Animated service illustrations (What We Do) -------------------------- */
.abt-service-art {
	display: block;
	width: 100%;
	max-width: 340px;
	height: auto;
	margin: 0 auto 1.75rem;
}

.abt-service-art .ln { fill: none; stroke: var(--primary); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.abt-service-art .axis { fill: none; stroke: var(--grey-500); stroke-width: 2; stroke-linecap: round; }
.abt-service-art .track { fill: var(--primary-rgba-10); }
.abt-service-art .fillp { fill: var(--primary); }
.abt-service-art .fills { fill: var(--secondary); }
.abt-service-art .lbl { font: 600 9.5px Degular, Anuphan, Arial, sans-serif; fill: #7a7f8c; letter-spacing: .08em; }

/* 1 - every job level fills up, then the assessment badge stamps in */
.abt-fill { transform-box: fill-box; transform-origin: 50% 100%; animation: abtFillUp 3.6s ease-in-out infinite; }
.abt-f2 { animation-delay: .16s; }
.abt-f3 { animation-delay: .32s; }
.abt-f4 { animation-delay: .48s; }
@keyframes abtFillUp { 0% { transform: scaleY(.08); } 40%, 82% { transform: scaleY(1); } 100% { transform: scaleY(.08); } }
.abt-badge { transform-box: fill-box; transform-origin: 50% 50%; animation: abtBadgeIn 3.6s ease-in-out infinite; }
@keyframes abtBadgeIn { 0%, 45% { transform: scale(.7); opacity: .35; } 58% { transform: scale(1.14); opacity: 1; } 70%, 100% { transform: scale(1); opacity: 1; } }

/* 2 - cost bars settle down and the trend line draws along their tops */
.abt-cost { transform-box: fill-box; transform-origin: 50% 100%; animation: abtCostDown 3.8s ease-in-out infinite; }
@keyframes abtCostDown { 0% { transform: scaleY(1); } 50%, 84% { transform: scaleY(var(--to, .5)); } 100% { transform: scaleY(1); } }
.abt-trend { stroke-dasharray: 185; animation: abtTrendDraw 3.8s ease-in-out infinite; }
@keyframes abtTrendDraw { 0% { stroke-dashoffset: 185; } 50%, 84% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 185; } }
.abt-gear { transform-box: fill-box; transform-origin: 50% 50%; animation: abtGearSpin 9s linear infinite; }
@keyframes abtGearSpin { to { transform: rotate(360deg); } }

/* 3 - data moving along the collect / manage / visualize / analyse pipeline */
.abt-node { transform-box: fill-box; transform-origin: 50% 50%; animation: abtNodeBeat 2.8s ease-in-out infinite; }
.abt-n2 { animation-delay: .35s; }
.abt-n3 { animation-delay: .7s; }
.abt-n4 { animation-delay: 1.05s; }
@keyframes abtNodeBeat { 0%, 60%, 100% { transform: scale(1); } 18% { transform: scale(1.13); } }
.abt-ring { transform-box: fill-box; transform-origin: 50% 50%; animation: abtRingBeat 2.8s ease-in-out infinite; }
.abt-ring2 { animation-delay: .35s; }
.abt-ring3 { animation-delay: .7s; }
.abt-ring4 { animation-delay: 1.05s; }
@keyframes abtRingBeat { 0%, 60%, 100% { opacity: .35; transform: scale(1); } 18% { opacity: 1; transform: scale(1.06); } }
.abt-flowdot { transform-box: view-box; transform-origin: 0 0; animation: abtFlowDot 2.8s linear infinite; }
.abt-fd2 { animation-delay: .35s; }
.abt-fd3 { animation-delay: .7s; }
@keyframes abtFlowDot {
	0% { transform: translateX(0); opacity: 0; }
	20% { opacity: 1; }
	80% { opacity: 1; }
	100% { transform: translateX(20px); opacity: 0; }
}
.abt-chartbar { transform-box: fill-box; transform-origin: 50% 100%; animation: abtChartGrow 2.8s ease-in-out infinite; }
.abt-cb2 { animation-delay: .1s; }
.abt-cb3 { animation-delay: .2s; }
@keyframes abtChartGrow { 0%, 100% { transform: scaleY(.3); } 45%, 75% { transform: scaleY(1); } }

@media (prefers-reduced-motion: reduce) {
	.abt-shine, .abt-ribbon, .abt-medal-check, .abt-person, .abt-smoke .p, .abt-bar, .abt-arrow,
	.abt-fill, .abt-badge, .abt-cost, .abt-trend, .abt-gear, .abt-node, .abt-ring, .abt-flowdot, .abt-chartbar,
	.appear-animation-visible .abt-gain-bar, .appear-animation-visible .abt-gain-val,
	.appear-animation-visible .abt-gain-arrow, .appear-animation-visible .abt-gain-pill {
		animation: none;
	}
	/* Leave every element in its resolved, fully-drawn state. */
	.abt-shine, .abt-flowdot { opacity: 0; }
	.abt-medal-check, .abt-trend { stroke-dashoffset: 0; }
	.abt-ring { opacity: .6; }
}

/* --- ABT Factory OS: platform, solutions, engine -------------------------- */
/* Every rule below is scoped to the platform section's own classes. The three
   colour hooks - --acc, --acc-soft - are set inline on each solution's wrapper
   so one set of rules paints four differently accented cards. */

/* Shared motion vocabulary for this section. Deliberately its own keyframes
   rather than reusing the ones declared inside the product-stack SVG: those
   live in that section's markup, and this section must not break if it moves. */
@keyframes abtOsDash { to { stroke-dashoffset: -14; } }
@keyframes abtOsSpin { to { transform: rotate(360deg); } }

/* ---- the architecture diagram ---- */
/* The diagram is this section's centrepiece, and the page container crops it to
   roughly 1110px - at which its product labels render under 12px and the
   whole thing reads cramped. Breaking it out to a wider stage scales the
   drawing and every label inside it up together. */
.abt-os-stage {
	width: min(1440px, calc(100vw - 2.5rem));
	max-width: none;
	margin-left: 50%;
	transform: translateX(-50%);
}

.abt-os {
	display: block;
	width: 100%;
	height: auto;
	/* Below this the 14px product labels stop being readable, so the wrapper
	   scrolls instead of scaling the whole diagram down. */
	min-width: 1060px;
}

.abt-os-kick { font: 600 10.5px Degular, Anuphan, Arial, sans-serif; fill: rgba(255, 255, 255, .72); letter-spacing: .2em; }
.abt-os-name { font: 700 29px Degular, Anuphan, Arial, sans-serif; fill: #fff; }
.abt-os-sub { font: 600 11px Degular, Anuphan, Arial, sans-serif; fill: #cfe6f0; letter-spacing: .18em; }

.abt-os-card { fill: #fff; stroke: #e3e6ef; stroke-width: 1.5; }
.abt-os-cap { fill: var(--acc); }
.abt-os-tile { fill: var(--acc); fill-opacity: .1; }
.abt-os-glyph { fill: none; stroke: var(--acc); stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; }
.abt-os-sname { font: 700 19px Degular, Anuphan, Arial, sans-serif; fill: #1F1F33; }
.abt-os-snum { font: 600 10px Degular, Anuphan, Arial, sans-serif; fill: var(--acc); letter-spacing: .16em; }
.abt-os-rule { fill: none; stroke: #eef0f6; stroke-width: 1.5; }
.abt-os-chip rect { fill: var(--acc); fill-opacity: .08; }
.abt-os-chip circle { fill: var(--acc); }
.abt-os-chiptxt { font: 600 14px Degular, Anuphan, Arial, sans-serif; fill: #3d4250; }

/* Each product lights in turn, so the eye is walked across all eight of them
   instead of being asked to read a static list. */
.abt-os-chip rect { animation: abtOsChip 3.2s ease-in-out infinite; animation-delay: var(--d, 0s); }
@keyframes abtOsChip { 0%, 100% { fill-opacity: .08; } 45% { fill-opacity: .22; } }
.abt-os-chip circle { transform-box: fill-box; transform-origin: 50% 50%; animation: abtOsChipDot 3.2s ease-in-out infinite; animation-delay: var(--d, 0s); }
@keyframes abtOsChipDot { 0%, 100% { transform: scale(1); } 45% { transform: scale(1.55); } }

/* Platform branching down into the four solutions */
.abt-os-link { fill: none; stroke: #c9cfe0; stroke-width: 2; stroke-dasharray: 7 7; animation: abtOsDash 1.1s linear infinite; }
.abt-os-link:nth-of-type(2) { animation-duration: 1.25s; }
.abt-os-link:nth-of-type(3) { animation-duration: 1.35s; }
.abt-os-link:nth-of-type(4) { animation-duration: 1.2s; }

/* ABT Agent feeding every column from underneath - the arrows point up because
   the engine powers the products, not the other way round. */
.abt-os-feed { fill: none; stroke: rgba(112, 195, 217, .6); stroke-width: 2; stroke-dasharray: 6 6; animation: abtOsDash 1s linear infinite; }
.abt-os-rise { fill: var(--secondary); transform-box: view-box; transform-origin: 0 0; animation: abtOsRise 2.4s linear infinite; }
@keyframes abtOsRise {
	0% { transform: translateY(0); opacity: 0; }
	15% { opacity: 1; }
	85% { opacity: 1; }
	100% { transform: translateY(-76px); opacity: 0; }
}

.abt-os-ring { transform-box: view-box; animation: abtOsSpin 14s linear infinite; }
.abt-os-ring-b { animation-duration: 18s; animation-direction: reverse; }
.abt-os-ring-c { animation-duration: 10s; }
.abt-os-halo { transform-box: view-box; transform-origin: 118px 562px; animation: abtOsHalo 3.4s ease-in-out infinite; }
@keyframes abtOsHalo { 0%, 100% { transform: scale(.86); opacity: .55; } 50% { transform: scale(1.06); opacity: 1; } }

.abt-os-akick { font: 600 10.5px Degular, Anuphan, Arial, sans-serif; fill: var(--secondary); letter-spacing: .2em; }
.abt-os-aname { font: 700 30px Degular, Anuphan, Arial, sans-serif; fill: #fff; }
.abt-os-asub { font: 600 10.5px Degular, Anuphan, Arial, sans-serif; fill: rgba(255, 255, 255, .62); letter-spacing: .14em; }
.abt-os-steptxt { font: 600 13px Degular, Anuphan, Arial, sans-serif; fill: #fff; letter-spacing: .12em; }
.abt-os-arrow { fill: none; stroke: rgba(255, 255, 255, .5); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.abt-os-step rect { fill: #fff; fill-opacity: .08; stroke: rgba(255, 255, 255, .3); stroke-width: 1.5; animation: abtOsStep 2.7s ease-in-out infinite; animation-delay: var(--d, 0s); }
@keyframes abtOsStep { 0%, 100% { fill-opacity: .08; } 30% { fill-opacity: .26; } }

/* ---- the four solution cards ---- */
.abt-sol-card {
	height: 100%;
	background: #fff;
	border: 1px solid #e9ebf2;
	border-top: 3px solid var(--acc);
	border-radius: 12px;
	padding: 1.75rem 1.5rem 1.5rem;
	box-shadow: 0 6px 24px rgba(31, 31, 51, .06);
	transition: transform .25s ease, box-shadow .25s ease;
}

.abt-sol-card:hover {
	transform: translateY(-6px);
	box-shadow: 0 18px 40px rgba(31, 31, 51, .14);
}

.abt-sol-art {
	display: block;
	width: 100%;
	max-width: 180px;
	height: auto;
	margin: 0 auto 1.25rem;
}

.abt-sol-art .ax { fill: none; stroke: #dfe3ec; stroke-width: 2; stroke-linecap: round; }
.abt-sol-art .ln { fill: none; stroke: var(--acc); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.abt-sol-art .bar { fill: var(--acc); fill-opacity: .18; }
.abt-sol-art .pane { fill: var(--acc-soft); stroke: var(--acc); stroke-width: 1.8; }
.abt-sol-art .ring { fill: none; stroke: var(--acc); stroke-width: 2; opacity: .35; }
.abt-sol-art .dot { fill: var(--secondary); }
.abt-sol-art .fillp { fill: var(--acc); }
.abt-sol-art .trend { fill: none; stroke: var(--secondary); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.abt-sol-art .pf { fill: var(--acc); opacity: .45; }

.abt-sol-art .bar { transform-box: fill-box; transform-origin: 50% 100%; animation: abtSolBar 3.4s ease-in-out infinite; }
.abt-sol-art .abt-sb2 { animation-delay: .14s; }
.abt-sol-art .abt-sb3 { animation-delay: .28s; }
@keyframes abtSolBar { 0%, 100% { transform: scaleY(.35); } 45%, 78% { transform: scaleY(1); } }

.abt-sol-trend { stroke-dasharray: 60; animation: abtSolTrend 3.4s ease-in-out infinite; }
@keyframes abtSolTrend { 0% { stroke-dashoffset: 60; } 45%, 78% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 60; } }

.abt-sol-puff .pf { transform-box: fill-box; transform-origin: 50% 50%; animation: abtSolPuff 2.4s ease-out infinite; }
@keyframes abtSolPuff {
	0% { transform: translateY(6px) scale(.4); opacity: 0; }
	30% { opacity: .7; }
	100% { transform: translateY(-14px) scale(1.35); opacity: 0; }
}

.abt-sol-flow { transform-box: view-box; transform-origin: 0 0; animation: abtSolFlow 2.2s linear infinite; }
@keyframes abtSolFlow {
	0% { transform: translateX(0); opacity: 0; }
	20% { opacity: 1; }
	80% { opacity: 1; }
	100% { transform: translateX(16px); opacity: 0; }
}

.abt-sol-stamp { transform-box: fill-box; transform-origin: 50% 50%; animation: abtSolStamp 2.2s ease-in-out infinite; }
@keyframes abtSolStamp { 0%, 45% { transform: scale(.72); opacity: .3; } 60% { transform: scale(1.12); opacity: 1; } 72%, 100% { transform: scale(1); opacity: 1; } }

.abt-sol-art .ring { transform-box: fill-box; transform-origin: 50% 50%; animation: abtSolRing 2.8s ease-in-out infinite; }
.abt-sol-ring2 { animation-delay: .5s; }
.abt-sol-ring3 { animation-delay: 1s; }
@keyframes abtSolRing { 0%, 65%, 100% { opacity: .3; transform: scale(1); } 20% { opacity: 1; transform: scale(1.08); } }

.abt-sol-pkt { transform-box: view-box; transform-origin: 0 0; animation: abtSolPkt 2.8s linear infinite; }
@keyframes abtSolPkt {
	0% { transform: translateY(0); opacity: 0; }
	20% { opacity: 1; }
	80% { opacity: 1; }
	100% { transform: translateY(26px); opacity: 0; }
}

.abt-sol-cap { transform-box: fill-box; transform-origin: 50% 100%; animation: abtSolCap 3.4s ease-in-out infinite; }
@keyframes abtSolCap { 0%, 100% { transform: translateY(3px); } 50% { transform: translateY(-3px); } }

.abt-sol-name {
	color: var(--dark);
	font-weight: 700;
	font-size: 1.15rem;
	text-transform: none;
	margin-bottom: .15rem;
}

.abt-sol-kick {
	color: var(--acc);
	font-size: .68rem;
	font-weight: 600;
	letter-spacing: .16em;
	text-transform: uppercase;
	margin-bottom: .7rem;
}

.abt-sol-body {
	color: #5b6070;
	font-size: .92rem;
	line-height: 1.65;
	margin-bottom: 1.1rem;
}

.abt-sol-list {
	list-style: none;
	padding: 0;
	margin: 0;
}

.abt-sol-list li {
	position: relative;
	background: var(--acc-soft);
	border-radius: 8px;
	color: #33384a;
	font-size: .83rem;
	font-weight: 600;
	padding: .5rem .8rem .5rem 1.9rem;
	margin-bottom: .45rem;
}

.abt-sol-list li:last-child { margin-bottom: 0; }

.abt-sol-list li::before {
	content: "";
	position: absolute;
	left: .8rem;
	top: 50%;
	width: 6px;
	height: 6px;
	margin-top: -3px;
	border-radius: 50%;
	background: var(--acc);
}

/* ---- the engine underneath ---- */
.abt-agent {
	position: relative;
	display: flex;
	align-items: center;
	gap: 2.75rem;
	overflow: hidden;
	border-radius: 16px;
	padding: 2.75rem 3rem;
	background: linear-gradient(120deg, #1F1F33 0%, #2E2E4F 45%, #48487F 100%);
}

/* Light bleeding out of the core, so the panel reads as powered rather than
   as a flat dark box. Behind the content, and never intercepts a click. */
.abt-agent::after {
	content: "";
	position: absolute;
	inset: 0;
	background: radial-gradient(circle at 13% 50%, rgba(112, 195, 217, .22), transparent 46%);
	pointer-events: none;
}

.abt-agent-core {
	position: relative;
	z-index: 1;
	flex: 0 0 232px;
}

.abt-agent-core svg { display: block; width: 100%; height: auto; }

.abt-agent-copy { position: relative; z-index: 1; }

.abt-agent-kick {
	color: var(--secondary);
	font-size: .7rem;
	font-weight: 600;
	letter-spacing: .2em;
	text-transform: uppercase;
	margin-bottom: .4rem;
}

.abt-agent-title {
	color: #fff;
	font-weight: 700;
	font-size: 1.9rem;
	line-height: 1.22;
	text-transform: none;
	margin-bottom: .85rem;
}

.abt-agent-body {
	color: rgba(255, 255, 255, .78);
	font-size: 1rem;
	line-height: 1.7;
	max-width: 760px;
	margin-bottom: 1.75rem;
}

.abt-agent-steps {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 1.5rem;
}

.abt-agent-step { display: flex; gap: .8rem; }

.abt-agent-step-n {
	color: var(--secondary);
	font-size: .85rem;
	font-weight: 700;
	line-height: 1.5;
	opacity: .8;
}

.abt-agent-step h4 {
	color: #fff;
	font-size: .8rem;
	font-weight: 600;
	letter-spacing: .14em;
	text-transform: uppercase;
	margin-bottom: .3rem;
}

.abt-agent-step p {
	color: rgba(255, 255, 255, .62);
	font-size: .85rem;
	line-height: 1.6;
	margin-bottom: 0;
}

.abt-agent-onprem {
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	margin: 1.75rem 0 0;
	padding: .45rem 1rem;
	border: 1px solid rgba(112, 195, 217, .45);
	border-radius: 999px;
	color: var(--secondary);
	font-size: .72rem;
	font-weight: 600;
	letter-spacing: .14em;
	text-transform: uppercase;
}

.abt-ag-halo { fill: rgba(112, 195, 217, .1); transform-box: view-box; transform-origin: 110px 110px; animation: abtAgHalo 4s ease-in-out infinite; }
@keyframes abtAgHalo { 0%, 100% { transform: scale(.9); opacity: .5; } 50% { transform: scale(1.04); opacity: 1; } }
.abt-ag-orbit { fill: none; stroke: rgba(112, 195, 217, .38); stroke-width: 1.6; stroke-dasharray: 5 9; transform-box: view-box; transform-origin: 110px 110px; animation: abtOsSpin 18s linear infinite; }
.abt-ag-orbit-b { stroke: rgba(255, 255, 255, .22); animation-duration: 12s; animation-direction: reverse; }
.abt-ag-lane path { fill: none; stroke: rgba(112, 195, 217, .45); stroke-width: 2; stroke-dasharray: 4 5; animation: abtOsDash 1s linear infinite; }
.abt-ag-node { fill: rgba(255, 255, 255, .28); }
.abt-ag-disc { fill: rgba(255, 255, 255, .1); }
.abt-ag-pulse { fill: none; stroke: rgba(112, 195, 217, .6); stroke-width: 2; transform-box: view-box; transform-origin: 110px 110px; animation: abtAgPulse 2.6s ease-out infinite; }
@keyframes abtAgPulse { 0% { transform: scale(.9); opacity: .9; } 100% { transform: scale(1.55); opacity: 0; } }
.abt-ag-glyph { fill: none; stroke: #fff; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.abt-ag-check { stroke: var(--secondary); stroke-width: 3; stroke-dasharray: 18; animation: abtAgCheck 2.6s ease-in-out infinite; }
@keyframes abtAgCheck { 0%, 20% { stroke-dashoffset: 18; } 45%, 85% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 18; } }

/* Signals travelling in along the four lanes. One keyframe per direction
   because each lane points a different way at the core. */
.abt-ag-in { fill: var(--secondary); transform-box: view-box; transform-origin: 0 0; }
.abt-ag-in-d { animation: abtAgInD 2.4s linear infinite; }
.abt-ag-in-u { animation: abtAgInU 2.4s linear infinite; }
.abt-ag-in-l { animation: abtAgInL 2.4s linear infinite; }
.abt-ag-in-r { animation: abtAgInR 2.4s linear infinite; }
@keyframes abtAgInD { 0% { transform: translateY(0); opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { transform: translateY(32px); opacity: 0; } }
@keyframes abtAgInU { 0% { transform: translateY(0); opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { transform: translateY(-32px); opacity: 0; } }
@keyframes abtAgInL { 0% { transform: translateX(0); opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { transform: translateX(-32px); opacity: 0; } }
@keyframes abtAgInR { 0% { transform: translateX(0); opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { transform: translateX(32px); opacity: 0; } }

@media (max-width: 991px) {
	.abt-agent {
		flex-direction: column;
		gap: 1.75rem;
		padding: 2.25rem 1.5rem;
		text-align: center;
	}
	.abt-agent-core { flex: 0 0 auto; width: 176px; }
	.abt-agent-title { font-size: 1.5rem; }
	.abt-agent-steps { grid-template-columns: 1fr; gap: 1.1rem; text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
	.abt-os-chip rect, .abt-os-chip circle, .abt-os-link, .abt-os-feed, .abt-os-rise,
	.abt-os-ring, .abt-os-halo, .abt-os-step rect,
	.abt-sol-art .bar, .abt-sol-trend, .abt-sol-puff .pf, .abt-sol-flow, .abt-sol-stamp,
	.abt-sol-art .ring, .abt-sol-pkt, .abt-sol-cap,
	.abt-ag-halo, .abt-ag-orbit, .abt-ag-lane path, .abt-ag-pulse, .abt-ag-check, .abt-ag-in {
		animation: none;
	}
	/* Leave every element in its resolved, fully-drawn state. */
	.abt-os-rise, .abt-sol-puff .pf, .abt-sol-flow, .abt-sol-pkt, .abt-ag-in, .abt-ag-pulse { opacity: 0; }
	.abt-sol-trend, .abt-ag-check { stroke-dashoffset: 0; }
	.abt-sol-art .ring { opacity: .45; }
}

/* --- Product stack diagram ------------------------------------------------ */
.abt-stack-scroll {
	overflow-x: auto;
	overflow-y: hidden;
	-webkit-overflow-scrolling: touch;
}

.abt-stack {
	display: block;
	width: 100%;
	height: auto;
	/* Below this the 11-14px labels inside the diagram stop being readable, so
	   the wrapper scrolls instead of scaling the whole thing down. */
	min-width: 820px;
}

/* --- System integration diagram ------------------------------------------- */
.abt-integration {
	display: block;
	width: 100%;
	height: auto;
	min-width: 860px;
}

.abt-int-cap { font: 600 10.5px Degular, Anuphan, Arial, sans-serif; fill: #9aa0ad; letter-spacing: .12em; }
.abt-int-src rect { fill: #fff; stroke: #e3e6ef; stroke-width: 1.5; }
.abt-int-dot { fill: var(--secondary); }
.abt-int-lbl { font: 600 13.5px Degular, Anuphan, Arial, sans-serif; fill: #3d4250; }
.abt-int-hub { font: 700 15px Degular, Anuphan, Arial, sans-serif; fill: #fff; }
.abt-int-hubsub { font: 600 9.5px Degular, Anuphan, Arial, sans-serif; fill: #cfe6f0; letter-spacing: .12em; }
.abt-int-panel { fill: #fff; stroke: var(--primary); stroke-width: 2; }
.abt-int-result { font: 700 27px Degular, Anuphan, Arial, sans-serif; fill: var(--primary); }
.abt-int-bullet text { font: 400 13px Degular, Anuphan, Arial, sans-serif; fill: #5b6070; }
.abt-int-bullet circle { fill: var(--secondary); }
.abt-int-halo { fill: var(--primary-rgba-10); }
.abt-int-out { fill: none; stroke: var(--primary); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }

/* Reuses abtDash / abtSpin from the product-stack diagram so both diagrams on
   the page share one motion vocabulary. */
.abt-int-line {
	fill: none;
	stroke: #c9cfe0;
	stroke-width: 2;
	stroke-dasharray: 7 7;
	animation: abtDash 1.1s linear infinite;
}
.abt-int-line:nth-of-type(2) { animation-duration: 1.25s; }
.abt-int-line:nth-of-type(4) { animation-duration: 1.35s; }
.abt-int-line:nth-of-type(5) { animation-duration: 1.2s; }

.abt-int-ring { transform-box: view-box; transform-origin: 500px 199px; animation: abtSpin 16s linear infinite; }
.abt-int-halo { animation: abtGlow 3.6s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
	.abt-int-line, .abt-int-ring, .abt-int-halo { animation: none; }
}

/* --- Results / cost-down cards -------------------------------------------- */
.abt-result-card {
	height: 100%;
	background: #fff;
	border-top: 3px solid var(--secondary);
	padding: 2rem 1.75rem;
	box-shadow: 0 6px 24px rgba(31, 31, 51, .08);
	transition: transform .25s ease, box-shadow .25s ease;
}

.abt-result-card:hover {
	transform: translateY(-4px);
	box-shadow: 0 14px 36px rgba(31, 31, 51, .14);
}

.abt-result-value {
	color: var(--primary);
	font-size: 2.1rem;
	font-weight: 700;
	line-height: 1;
}

.abt-result-value-lg {
	font-size: 3.1rem;
}

/* The headline result gets a tinted ground and a heavier rule rather than a
   different colour scheme, so it leads without breaking the set. */
.abt-result-card-featured {
	border-top-width: 5px;
	border-top-color: var(--primary);
	background: #f7f9fd;
}

.abt-result-badge {
	display: inline-block;
	padding: .3rem .7rem;
	border-radius: 999px;
	background: var(--primary-rgba-10);
	color: var(--primary);
	font-size: .62rem;
	font-weight: 700;
	letter-spacing: .16em;
}

.abt-industry-icon {
	display: block;
	flex: 0 0 auto;
}

/* Relative-scale bar: fill width is the project's share of the largest saving. */
.abt-result-bar {
	height: 6px;
	border-radius: 999px;
	background: var(--grey-300);
	overflow: hidden;
	margin-top: auto;
}

.abt-result-bar span {
	display: block;
	height: 100%;
	border-radius: 999px;
	background: var(--primary);
}

/* Cards are flex columns so the bar can sit flush at the bottom regardless of
   how many lines the industry name wraps to. */
.abt-result-card {
	display: flex;
	flex-direction: column;
}

/* --- Solution screenshots ------------------------------------------------- */
/* Shots are dark UI captures; a dark mat stops them floating on white.
   The source screenshots have differing aspect ratios, so the tiles are pinned
   to one ratio and the image is contained inside it - that keeps every caption
   in the grid on the same baseline. `contain` rather than `cover` because
   cropping a dashboard cuts off the numbers that make it worth showing. */
.abt-shot {
	display: block;
	aspect-ratio: 16 / 10;
	background: #1F1F33;
	padding: .5rem;
	box-shadow: 0 10px 30px rgba(31, 31, 51, .18);
}

.abt-shot img {
	width: 100%;
	height: 100%;
	display: block;
	object-fit: contain;
}

/* --- Product showcase ----------------------------------------------------- */
/* Each product gets a full-bleed band. ABT SCADA keeps the brand indigo on a
   cool light ground; Otto sits on near-black with its own orange, which is the
   palette of the application itself - so the band reads as the product, not as
   a differently coloured page section. Everything below is driven off
   --abt-accent, so a third product only needs a new band class. */
.abt-product {
	--abt-accent: var(--primary);
	--abt-accent-soft: rgba(72, 72, 127, .12);
	--abt-accent-ink: #fff;
	position: relative;
}

/* --- Tab change ----------------------------------------------------------- */
/* The panes deliberately do not use Bootstrap's `.fade`. That parks the panel
   at opacity 0 and relies on a *transition* to bring it back, so the resting
   state of a shown panel is invisible. These are animations with no forwards
   fill instead: the resting state is the visible one, and the motion is purely
   an entrance, so a panel can never be left stranded at opacity 0.
   The band fades first, then its columns rise in sequence, which reads as the
   new product arriving rather than the page swapping underneath you. */
.abt-product.tab-pane.active {
	animation: abtPaneIn 260ms ease-out;
}

.abt-product.tab-pane.active > .container > .row > [class*="col-"] {
	animation: abtPaneRise 520ms cubic-bezier(.22, .61, .36, 1) backwards;
}

/* Row 1 is the identity copy and the product-page shot, row 2 the capability
   cards, row 3 the closing line. */
.abt-product.tab-pane.active > .container > .row:nth-of-type(1) > [class*="col-"]:nth-child(1) { animation-delay: 60ms; }
.abt-product.tab-pane.active > .container > .row:nth-of-type(1) > [class*="col-"]:nth-child(2) { animation-delay: 130ms; }
.abt-product.tab-pane.active > .container > .row:nth-of-type(2) > [class*="col-"]:nth-child(1) { animation-delay: 200ms; }
.abt-product.tab-pane.active > .container > .row:nth-of-type(2) > [class*="col-"]:nth-child(2) { animation-delay: 260ms; }
.abt-product.tab-pane.active > .container > .row:nth-of-type(2) > [class*="col-"]:nth-child(3) { animation-delay: 320ms; }
.abt-product.tab-pane.active > .container > .row:nth-of-type(3) > [class*="col-"] { animation-delay: 380ms; }

@keyframes abtPaneIn {
	from { opacity: 0; }
	to { opacity: 1; }
}

@keyframes abtPaneRise {
	from { opacity: 0; transform: translateY(18px); }
	to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
	.abt-product.tab-pane.active,
	.abt-product.tab-pane.active > .container > .row > [class*="col-"] {
		animation: none;
	}
}

/* Matched to the #portfolio fill rather than set a single value away from it:
   the intro copy, the tab strip and the band belong to one product block, and
   two near-identical greys only muddied the edge between them. Otto keeps its
   own near-black ground, so switching tabs still reads as switching product. */
.abt-product-iot {
	background: #f4f6fb;
}

.abt-product-otto {
	--abt-accent: #F97316;
	--abt-accent-soft: rgba(249, 115, 22, .16);
	--abt-accent-ink: #16162a;
	background: #16162a;
	color: #b9bfd2;
}

.abt-product-otto h4,
.abt-product-otto strong {
	color: #fff;
}

.abt-product-otto .list-icons li > i {
	color: var(--abt-accent);
}

/* --- "Visit the site" link ------------------------------------------------
   Each product also lives at its own domain, and this is the way out to it.
   Outlined in the band's own --abt-accent rather than filled, so it reads as a
   secondary route off the page and not as the band's main action.

   Filling on hover needs an ink colour per band: white on Otto's #F97316 is
   2.6:1, so --abt-accent-ink carries the readable pairing instead of a single
   hardcoded value -- #fff on the indigo (8.0:1), the near-black ground on the
   orange (6.5:1). Resting state measures 7.6:1 on IoT's #f4f6fb and 6.5:1 on
   Otto's #16162a. */
.abt-product-visit {
	display: inline-flex;
	align-items: center;
	gap: .6rem;
	padding: .7rem 1.5rem;
	border: 1px solid var(--abt-accent);
	border-radius: 999px;
	color: var(--abt-accent);
	font-weight: 700;
	line-height: 1.4;
	text-decoration: none;
	transition: background-color .2s ease, color .2s ease;
}

.abt-product-visit:hover,
.abt-product-visit:focus {
	background: var(--abt-accent);
	color: var(--abt-accent-ink);
	text-decoration: none;
}

/* The glyph is the "leaves this site" cue, so it sizes off the label rather
   than carrying a size of its own -- see the +2px scale block. */
.abt-product-visit i {
	font-size: .82em;
}

/* --- Product tabs --------------------------------------------------------- */
/* Buttons rather than links: these switch a panel in place, they do not
   navigate. The selected one fills with its product's accent, so the tab strip
   and the band below it always agree on which product you are looking at. */
.abt-product-nav {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: .75rem;
}

.abt-product-pill {
	--abt-pill: var(--primary);
	display: inline-flex;
	align-items: center;
	gap: .55rem;
	padding: .7rem 1.4rem;
	border: 1px solid var(--grey-300);
	border-radius: 999px;
	background: #fff;
	color: var(--dark);
	font-family: inherit;
	font-weight: 700;
	font-size: .95rem;
	line-height: 1.4;
	text-decoration: none;
	box-shadow: 0 4px 14px rgba(31, 31, 51, .06);
	/* Short on the colours so the selected pill keeps up with the panel rather
	   than trailing it. */
	transition: transform .2s ease, box-shadow .2s ease, background-color .18s ease, border-color .18s ease, color .18s ease;
}

.abt-product-pill-otto { --abt-pill: #F97316; }

.abt-product-pill em {
	font-style: normal;
	font-weight: 500;
	font-size: .78rem;
	color: var(--grey-600, #6c757d);
	letter-spacing: .04em;
}

.abt-product-pill:hover {
	transform: translateY(-2px);
	box-shadow: 0 10px 24px rgba(31, 31, 51, .12);
	border-color: var(--abt-pill);
	color: var(--dark);
}

.abt-product-pill:focus-visible {
	outline: 2px solid var(--abt-pill);
	outline-offset: 3px;
}

.abt-product-pill.active {
	background: var(--abt-pill);
	border-color: var(--abt-pill);
	color: #fff;
	box-shadow: 0 10px 26px rgba(31, 31, 51, .18);
}

.abt-product-pill.active em { color: rgba(255, 255, 255, .78); }
.abt-product-pill.active .abt-product-pill-dot { background: #fff; }

.abt-product-pill-dot {
	width: 10px;
	height: 10px;
	border-radius: 50%;
	flex: 0 0 auto;
	background: var(--abt-pill);
}

/* --- Product identity block ----------------------------------------------- */
.abt-product-badge {
	display: inline-block;
	margin-bottom: 1rem;
	padding: .35rem .85rem;
	border-radius: 999px;
	background: var(--abt-accent-soft);
	color: var(--abt-accent);
	font-size: .64rem;
	font-weight: 700;
	letter-spacing: .16em;
}

.abt-product-name {
	font-size: 2.75rem;
	font-weight: 700;
	line-height: 1.05;
	color: var(--dark);
	text-transform: none;
}

.abt-product-otto .abt-product-name {
	color: #fff;
}

.abt-product-tagline {
	font-size: 1.35rem;
	font-weight: 600;
	line-height: 1.3;
	color: var(--abt-accent);
}

/* Three short proof points under the copy; the rules between them come from
   borders rather than separate elements so they collapse cleanly when the
   column narrows. */
.abt-product-stats {
	display: flex;
	flex-wrap: wrap;
}

.abt-product-stats > div {
	padding: 0 1.4rem;
	border-left: 1px solid var(--grey-300);
}

.abt-product-stats > div:first-child {
	padding-left: 0;
	border-left: 0;
}

.abt-product-otto .abt-product-stats > div {
	border-left-color: rgba(255, 255, 255, .14);
}

.abt-product-stats strong {
	display: block;
	font-size: 1.5rem;
	font-weight: 700;
	line-height: 1.1;
	color: var(--abt-accent);
}

.abt-product-otto .abt-product-stats strong {
	color: var(--abt-accent);
}

.abt-product-stats span {
	font-size: .68rem;
	font-weight: 600;
	letter-spacing: .12em;
	text-transform: uppercase;
	opacity: .75;
}

/* --- Kicker: the one-line reason the product matters ---------------------- */
/* An accent rule rather than a fourth paragraph of body copy, so the identity
   column reads name -> promise -> reason -> detail at four distinct weights
   instead of as one grey block. */
.abt-product-kicker {
	margin: 0 0 1.1rem;
	padding-left: .9rem;
	border-left: 3px solid var(--abt-accent);
	font-size: 1.05rem;
	font-weight: 600;
	line-height: 1.55;
	color: var(--dark);
}

.abt-product-otto .abt-product-kicker { color: #fff; }

/* --- Capability cards ----------------------------------------------------- */
/* Three cards across the band rather than a list inside the copy column: each
   capability is an English term the reader scans for plus a sentence about it,
   and stacked as list items they run together into one wall of text. The top
   rule and the icon chip both take --abt-accent, so the same markup works on
   either product's ground. */
.abt-feature {
	display: flex;
	flex-direction: column;
	height: 100%;
	padding: 1.6rem 1.5rem;
	border: 1px solid rgba(31, 31, 51, .08);
	border-top: 3px solid var(--abt-accent);
	border-radius: 14px;
	background: #fff;
	box-shadow: 0 8px 24px rgba(31, 31, 51, .06);
	transition: transform .25s ease, box-shadow .25s ease;
}

.abt-feature:hover {
	transform: translateY(-4px);
	box-shadow: 0 18px 40px rgba(31, 31, 51, .12);
}

.abt-product-otto .abt-feature {
	background: rgba(255, 255, 255, .04);
	border-color: rgba(255, 255, 255, .1);
	box-shadow: 0 10px 30px rgba(0, 0, 0, .3);
}

.abt-feature-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 46px;
	margin-bottom: 1rem;
	border-radius: 12px;
	background: var(--abt-accent-soft);
	color: var(--abt-accent);
	font-size: 1.15rem;
}

.abt-feature-title {
	margin-bottom: .6rem;
	font-size: 1.05rem;
	font-weight: 700;
	line-height: 1.35;
	color: var(--dark);
	text-transform: none;
}

.abt-product-otto .abt-feature-title { color: #fff; }

.abt-feature-body {
	margin: 0;
	font-size: .95rem;
	line-height: 1.7;
}

/* --- Closing line --------------------------------------------------------- */
/* The payoff sentence, given the accent tint so it reads as the band's
   conclusion rather than as one more paragraph. */
.abt-product-close {
	display: flex;
	align-items: center;
	gap: .85rem;
	margin: 0;
	padding: 1.15rem 1.5rem;
	border-left: 4px solid var(--abt-accent);
	border-radius: 14px;
	background: var(--abt-accent-soft);
	color: var(--abt-accent);
	font-size: 1.1rem;
	font-weight: 700;
	line-height: 1.5;
}

.abt-product-close i {
	flex: 0 0 auto;
	font-size: 1.15rem;
}

/* --- Contact form panel --------------------------------------------------- */
/* Every other panel on the page is a rounded, lifted card; the form was the one
   square-cornered slab, which read as unstyled rather than deliberate. */
.abt-contact-card {
	border-radius: 16px;
	box-shadow: 0 18px 44px rgba(31, 31, 51, .18);
}

/* --- Browser frame for the product page captures -------------------------- */
/* The two hero captures are whole marketing pages; the chrome tells the reader
   at a glance that this is a real web application, not a rendered mockup. */
.abt-browser {
	display: block;
	border-radius: 10px;
	overflow: hidden;
	background: #2A2A45;
	box-shadow: 0 18px 48px rgba(15, 15, 30, .32);
	transition: transform .3s ease, box-shadow .3s ease;
}

.abt-browser:hover {
	transform: translateY(-4px);
	box-shadow: 0 26px 60px rgba(15, 15, 30, .42);
}

.abt-browser-bar {
	display: flex;
	align-items: center;
	gap: 7px;
	height: 30px;
	padding: 0 14px;
}

.abt-browser-bar i {
	width: 10px;
	height: 10px;
	border-radius: 50%;
	background: rgba(255, 255, 255, .28);
}

.abt-browser-bar i:first-child { background: #FF5F57; }
.abt-browser-bar i:nth-child(2) { background: #FEBC2E; }
.abt-browser-bar i:nth-child(3) { background: #28C840; }

.abt-browser img {
	display: block;
	width: 100%;
	height: auto;
}

/* Screenshot tiles sit on a dark ground already; on the Otto band they need a
   hairline to separate the mat from the page. */
.abt-product-otto .abt-shot {
	border: 1px solid rgba(255, 255, 255, .1);
	box-shadow: 0 10px 30px rgba(0, 0, 0, .38);
}

.abt-result-card-dark {
	background: rgba(255, 255, 255, .04);
	border-top-color: var(--abt-accent);
	box-shadow: 0 10px 30px rgba(0, 0, 0, .3);
}

.abt-result-card-dark:hover {
	box-shadow: 0 18px 42px rgba(0, 0, 0, .42);
}

@media (max-width: 575px) {
	.abt-product-name { font-size: 2.1rem; }
	.abt-product-tagline { font-size: 1.1rem; }
	.abt-product-kicker { font-size: .98rem; }
	.abt-product-close { font-size: 1rem; }
	.abt-product-stats > div { padding: 0 1rem; }
}

/* ==========================================================================
   Thai typography
   Degular carries no Thai glyphs, so the browser falls through to Anuphan per
   character -- Latin stays Degular, Thai renders properly (see fonts.css). Thai
   also needs looser leading (tone marks stack above and below) and far tighter
   tracking than the uppercase Latin labels this theme is built around.
   ========================================================================== */
body,
h1, h2, h3, h4, h5, h6,
.btn, input, textarea, select,
.abt-logo-name,
.abt-logo-tag,
.abt-lang-link,
.abt-product-name,
.abt-product-tagline,
.abt-product-badge,
.abt-product-pill,
.abt-product-stats,
.abt-result-badge,
.abt-result-value {
	font-family: 'Degular', 'Anuphan', Arial, sans-serif;
}

html[lang="th"] body { line-height: 1.9; }
html[lang="th"] h1, html[lang="th"] h2, html[lang="th"] h3,
html[lang="th"] h4, html[lang="th"] h5, html[lang="th"] h6 { line-height: 1.4; }

/* Tracking meant for Latin small-caps ruins Thai -- neutralise it. Porto's
   positive-ls-* utilities are !important, so the override has to be too.
   negative-ls-2 is left alone: it only sits on the counter digits, which are
   Latin in both languages. */
html[lang="th"] .positive-ls-1,
html[lang="th"] .positive-ls-2,
html[lang="th"] .positive-ls-3,
html[lang="th"] #header .header-nav-main nav > ul > li > a,
html[lang="th"] .abt-logo-tag,
html[lang="th"] .abt-lang-link,
html[lang="th"] .abt-footer-heading,
html[lang="th"] .abt-product-badge,
html[lang="th"] .abt-product-pill em,
html[lang="th"] .abt-product-stats span,
html[lang="th"] .abt-result-badge {
	letter-spacing: .3px !important;
}

/* Same treatment for the labels inside the inline SVG diagrams, which carry
   translated text and set their own tracking. Only the classes whose every
   instance is translated are listed: .abt-stack-full is hardcoded English
   product names, so it keeps the wide Latin tracking. */
html[lang="th"] .abt-service-art .lbl,
html[lang="th"] .abt-int-cap,
html[lang="th"] .abt-int-hubsub,
html[lang="th"] .abt-rpa-sub-i18n {
	letter-spacing: .3px;
}

/* The hero's line-height-1 leaves no room for Thai tone marks, which stack above
   and below the baseline -- they get clipped against the line box. Needs
   !important to beat Porto's .line-height-1 utility, which is itself !important.
   Font sizes are deliberately left alone: Porto's text-* utilities already fit
   the translated headlines at every breakpoint, so shrinking them would only
   make Thai smaller than the English it sits beside. */
html[lang="th"] #home h1 { line-height: 1.32 !important; }
html[lang="th"] .abt-product-name { line-height: 1.25; }
html[lang="th"] .abt-product-tagline { line-height: 1.45; }

/* .custom-font-secondary is the demo's Lora, a Latin-only serif that is no
   longer loaded at all -- and that Thai could never render, falling back to a
   serif Thai face which clashed with the sans used everywhere else. Both
   languages now take the primary stack. */
.custom-font-secondary { font-family: "Degular", "Anuphan", Arial, sans-serif !important; }

/* --- Odometer: the Results figures roll upward like a mechanical counter ------
   Each digit is a vertical strip of 0-9 (plus a trailing 0 for a seamless wrap)
   inside a one-glyph-tall window; JS translates the strip so the digit rolls up.
   tabular-nums keeps every digit one ch wide so nothing jitters as they spin. */
.odo {
	display: inline-flex;
	overflow: hidden;
	line-height: 1;
	font-variant-numeric: tabular-nums;
	font-feature-settings: "tnum";
}
.odo-d {
	display: inline-block;
	width: 1ch;
	height: 1em;
	overflow: hidden;
}
.odo-strip {
	display: block;
	will-change: transform;
}
.odo-strip > span {
	display: block;
	width: 1ch;
	height: 1em;
	line-height: 1;
	text-align: center;
}
.odo-sep { display: inline-block; }

/* ==========================================================================
   +2px text scale
   Every text size on this page raised by exactly two pixels. Kept as one block
   at the end of the file so the whole adjustment can be lifted out again in a
   single edit rather than hunted through the rules above.

   Porto's text-* utilities are em-based (relative to body, which is 14px and
   13px under 576px) and declared !important. They are re-declared here as
   calc(<original em> + 2px): the em half preserves the existing scale and the
   nesting behaviour, and the +2px is flat -- so every element grows by exactly
   two pixels at both breakpoints instead of being multiplied up. Writing fixed
   px instead would have made the largest headings grow by six pixels or more.

   Deliberately NOT touched:
   - Inline SVG diagram labels. Their sizes are viewBox user units placed on
     hardcoded coordinates, so bigger type would spill out of the cards drawn
     around it, and the whole diagram already scales with its container.
   - .abt-pillar-icon / .abt-proof-icon. Those size a Font Awesome glyph inside
     a fixed-diameter circle -- an icon, not text.
   ========================================================================== */

/* --- Porto text utilities used on this page ------------------------------- */
.text-1 { font-size: calc(.8em + 2px) !important; }
.text-2 { font-size: calc(.9em + 2px) !important; }
.text-3 { font-size: calc(1em + 2px) !important; }
.text-3-5 { font-size: calc(1.1em + 2px) !important; }
.text-4 { font-size: calc(1.2em + 2px) !important; }
.text-5 { font-size: calc(1.5em + 2px) !important; }
.text-5-5 { font-size: calc(1.65em + 2px) !important; }
.text-6 { font-size: calc(1.8em + 2px) !important; }
.text-9 { font-size: calc(2.5em + 2px) !important; }
.text-10 { font-size: calc(2.75em + 2px) !important; }
.text-11 { font-size: calc(3em + 2px) !important; }

/* Responsive variants have to stay inside their own media queries -- declared
   unconditionally they would beat the plain .text-* rules below 576px too. */
@media (min-width: 576px) {
	.text-sm-4 { font-size: calc(1.2em + 2px) !important; }
	.text-sm-13 { font-size: calc(4em + 2px) !important; }
}

@media (min-width: 768px) {
	.text-md-14 { font-size: calc(4.5em + 2px) !important; }
}

/* --- Site classes that set their own size --------------------------------- */
.abt-logo-name { font-size: calc(1.25rem + 2px); }
.abt-footer-heading { font-size: calc(.72rem + 2px); }
.abt-footer-legal { font-size: calc(.8rem + 2px); }
.abt-logo-tag { font-size: calc(.62rem + 2px); }
.abt-lang-link { font-size: calc(.82rem + 2px); }
.abt-lang-sep { font-size: calc(.78rem + 2px); }
.abt-service-num { font-size: calc(1.5rem + 2px); }
.abt-result-value { font-size: calc(2.1rem + 2px); }
.abt-result-value-lg { font-size: calc(3.1rem + 2px); }
.abt-result-badge { font-size: calc(.62rem + 2px); }
.abt-product-pill { font-size: calc(.95rem + 2px); }
.abt-product-pill em { font-size: calc(.78rem + 2px); }
.abt-product-badge { font-size: calc(.64rem + 2px); }
.abt-product-name { font-size: calc(2.75rem + 2px); }
.abt-product-tagline { font-size: calc(1.35rem + 2px); }
.abt-product-stats strong { font-size: calc(1.5rem + 2px); }
.abt-product-stats span { font-size: calc(.68rem + 2px); }
.abt-product-kicker { font-size: calc(1.05rem + 2px); }
.abt-product-close { font-size: calc(1.1rem + 2px); }
.abt-product-visit { font-size: calc(1rem + 2px); }
.abt-feature-title { font-size: calc(1.05rem + 2px); }
.abt-feature-body { font-size: calc(.95rem + 2px); }

/* Re-stated because the small-screen sizes they override live earlier in this
   file; without the media query the desktop rule above would win everywhere. */
@media (max-width: 575px) {
	.abt-product-name { font-size: calc(2.1rem + 2px); }
	.abt-product-tagline { font-size: calc(1.1rem + 2px); }
	.abt-product-kicker { font-size: calc(.98rem + 2px); }
	.abt-product-close { font-size: calc(1rem + 2px); }
}

/* --- Text that sizes off the theme rather than a utility ------------------ */
/* The main nav links, and the two runs of copy that inherit straight from body
   (header phone number, contact-form result alerts) -- without these the page
   would have a handful of items left at their old size. */
#header .header-nav-main nav > ul > li > a { font-size: calc(.85rem + 4px); }
/* Thai falls back to the system Thai font, whose smaller x-height makes it read
   a touch smaller than the Latin at the same size. Nudge the Thai nav up 2px so
   the two languages look visually matched. */
html[lang="th"] #header .header-nav-main nav > ul > li > a { font-size: calc(.85rem + 6px); }
#header a[href^="tel:"],
#footer .abt-footer-hours,
.contact-form .alert { font-size: calc(1em + 2px); }

/* At the larger size the number no longer fits its header column and broke
   across three lines. It is one token to a reader, so keep it on one. */
#header a[href^="tel:"] { white-space: nowrap; }

/* The alert above is a parent of a .text-1 detail line, and growing the parent
   also grows what that child's em resolves against -- it would gain 3.6px
   rather than 2. The parent is now (body + 2px), so .8em is .8*body + 1.6px;
   the extra .4px lands it back on exactly .8*body + 2px, at either breakpoint. */
.contact-form .alert .text-1 { font-size: calc(.8em + .4px) !important; }

/* --- Body copy contrast --------------------------------------------------- */
/* Overrides the skin's --default (#777). See the note in the section rhythm
   block below for why this token, rather than a pile of per-element rules. */
:root {
	--default: #565b6b;
}


/* ==========================================================================
   Knowledge library — the home band, the index, and the article page
   Built on the same tokens as the rest of the site: 14px radius, the soft card
   shadow, the accent chip, and the white / #f4f6fb section rhythm.
   ========================================================================== */

/* --- Article card ---------------------------------------------------------
   One card serves the home band, the index grid and the related row, so an
   article looks the same wherever it appears. */
.abt-kb-card {
	display: flex;
	flex-direction: column;
	height: 100%;
	border: 1px solid rgba(31, 31, 51, .08);
	border-radius: 14px;
	background: #fff;
	box-shadow: 0 8px 24px rgba(31, 31, 51, .06);
	overflow: hidden;
	text-decoration: none;
	transition: transform .25s ease, box-shadow .25s ease;
}

.abt-kb-card:hover {
	transform: translateY(-4px);
	box-shadow: 0 18px 40px rgba(31, 31, 51, .12);
	text-decoration: none;
}

/* Fixed ratio so every card in a row is the same height whatever the headline
   does. A ragged bottom edge is what kills a three-across grid. */
.abt-kb-card-media {
	display: block;
	aspect-ratio: 16 / 9;
	background: linear-gradient(135deg, #3d3d6b 0%, #48487F 45%, #70C3D9 100%);
	overflow: hidden;
}

.abt-kb-card-media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.abt-kb-card-body {
	display: flex;
	flex-direction: column;
	flex: 1;
	padding: 1.3rem 1.4rem 1.5rem;
}

.abt-kb-card h3 {
	margin: 0 0 .5rem;
	font-size: calc(1.08rem + 2px);
	line-height: 1.45;
	color: var(--dark);
	text-transform: none;
}

.abt-kb-card p {
	margin: 0 0 1rem;
	font-size: calc(.93rem + 2px);
	line-height: 1.7;
}

/* Pushed to the bottom so the meta line sits on the same baseline across a row
   even when one excerpt runs a line longer. */
.abt-kb-card-meta {
	display: flex;
	align-items: center;
	gap: .5rem;
	margin-top: auto;
	font-size: calc(.8rem + 2px);
	color: #646b7c;
}

.abt-kb-chip {
	display: inline-block;
	align-self: flex-start;
	margin-bottom: .7rem;
	padding: .28rem .75rem;
	border-radius: 999px;
	background: rgba(72, 72, 127, .1);
	color: var(--primary);
	font-size: calc(.72rem + 2px);
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
}

/* Thai has no case, and the wide Latin tracking ruins it. */
html[lang="th"] .abt-kb-chip { letter-spacing: .02em; text-transform: none; }

/* --- Index page ----------------------------------------------------------- */
.abt-kb-head {
	padding: 3.4rem 0 2.6rem;
	background: #1F1F33;
	color: #cfd3e0;
}

.abt-kb-eyebrow {
	margin: 0 0 .4rem;
	color: var(--secondary);
	font-size: calc(.78rem + 2px);
	font-weight: 600;
	letter-spacing: .18em;
	text-transform: uppercase;
}

html[lang="th"] .abt-kb-eyebrow { letter-spacing: .04em; text-transform: none; }

.abt-kb-title {
	margin: 0 0 .6rem;
	color: #fff;
	font-size: calc(2.5rem + 2px);
	font-weight: 700;
	line-height: 1.25;
	text-transform: none;
}

html[lang="th"] .abt-kb-title { line-height: 1.35; }

.abt-kb-lead { max-width: 640px; margin: 0; font-size: calc(1.02rem + 2px); }

.abt-kb-filters { display: flex; flex-wrap: wrap; gap: .55rem; padding: 1.5rem 0 0; }

.abt-kb-filters a {
	padding: .5rem 1.05rem;
	border: 1px solid rgba(31, 31, 51, .1);
	border-radius: 999px;
	background: #fff;
	color: var(--default);
	font-size: calc(.88rem + 2px);
	font-weight: 600;
	text-decoration: none;
}

.abt-kb-filters a span { opacity: .55; margin-left: .2rem; }
.abt-kb-filters a:hover { border-color: var(--primary); color: var(--primary); }
.abt-kb-filters a.on { background: var(--primary); border-color: var(--primary); color: #fff; }
.abt-kb-filters a.on span { opacity: .8; }

.abt-kb-body { padding: 2.6rem 0 4rem; }

/* One featured article at the top of page one. "Featured" means "most recent",
   so there is no flag to maintain and it can never go stale. */
.abt-kb-lead-post {
	display: grid;
	grid-template-columns: 1.15fr 1fr;
	border: 1px solid rgba(31, 31, 51, .08);
	border-radius: 14px;
	background: #fff;
	box-shadow: 0 8px 24px rgba(31, 31, 51, .06);
	overflow: hidden;
	margin-bottom: 2.6rem;
}

@media (max-width: 860px) { .abt-kb-lead-post { grid-template-columns: 1fr; } }

.abt-kb-lead-art {
	display: block;
	min-height: 320px;
	background: linear-gradient(135deg, #1F1F33 0%, #48487F 55%, #70C3D9 100%);
}

.abt-kb-lead-art img { display: block; width: 100%; height: 100%; object-fit: cover; }

.abt-kb-lead-txt { display: flex; flex-direction: column; justify-content: center; padding: 2.4rem; }
.abt-kb-lead-txt h2 { margin: 0 0 .7rem; font-size: calc(1.75rem + 2px); line-height: 1.35; text-transform: none; }
.abt-kb-lead-txt h2 a { color: var(--dark); text-decoration: none; }
.abt-kb-lead-txt h2 a:hover { color: var(--primary); }
.abt-kb-lead-txt p { margin: 0 0 1.3rem; font-size: calc(1rem + 2px); line-height: 1.75; }

.abt-kb-badge {
	display: inline-flex;
	align-self: flex-start;
	margin-bottom: .9rem;
	padding: .3rem .85rem;
	border-radius: 999px;
	background: rgba(112, 195, 217, .2);
	color: #2b7c93;
	font-size: calc(.74rem + 2px);
	font-weight: 700;
}

.abt-kb-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .7rem; font-size: calc(.86rem + 2px); color: #646b7c; }
.abt-kb-meta .abt-kb-chip { margin: 0; }

.abt-kb-subhead { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin: 2.4rem 0 1.4rem; }
.abt-kb-subhead h2 { margin: 0; font-size: calc(1.05rem + 2px); text-transform: none; }
.abt-kb-subhead span { font-size: calc(.86rem + 2px); color: #646b7c; }

.abt-kb-pager { display: flex; justify-content: center; gap: .4rem; margin-top: 2.4rem; }

.abt-kb-pager a {
	min-width: 44px; height: 44px;
	display: inline-flex; align-items: center; justify-content: center;
	border: 1px solid rgba(31, 31, 51, .1);
	border-radius: 10px;
	background: #fff;
	color: var(--default);
	font-size: calc(.92rem + 2px);
	font-weight: 600;
	text-decoration: none;
}

.abt-kb-pager a:hover { border-color: var(--primary); color: var(--primary); }
.abt-kb-pager a.on { background: var(--primary); border-color: var(--primary); color: #fff; }

/* Day one has no articles. Say so plainly rather than rendering an empty grid
   that reads as a broken feature. */
.abt-kb-empty { max-width: 560px; margin: 2rem auto 3rem; text-align: center; }
.abt-kb-empty h2 { font-size: calc(1.5rem + 2px); text-transform: none; }
.abt-kb-empty p { font-size: calc(1rem + 2px); margin-bottom: 1.6rem; }

/* --- Article page --------------------------------------------------------- */
.abt-art-head { padding: 3rem 0 2.2rem; background: #f4f6fb; }
.abt-art-crumbs { font-size: calc(.85rem + 2px); color: #646b7c; margin-bottom: .8rem; }
.abt-art-crumbs a { color: #646b7c; text-decoration: none; }
.abt-art-crumbs a:hover { color: var(--primary); }
.abt-art-head h1 { max-width: 820px; margin: 0 0 1rem; font-size: calc(2.35rem + 2px); line-height: 1.3; text-transform: none; }
html[lang="th"] .abt-art-head h1 { line-height: 1.4; }
.abt-art-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .8rem; font-size: calc(.9rem + 2px); color: #646b7c; }
.abt-art-meta .abt-kb-chip { margin: 0; }

.abt-art-cover {
	aspect-ratio: 16 / 7;
	margin-top: -1.4rem;
	border-radius: 14px;
	overflow: hidden;
	box-shadow: 0 18px 48px rgba(15, 15, 30, .22);
	background: linear-gradient(135deg, #1F1F33 0%, #48487F 55%, #70C3D9 100%);
}

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

/* Both rails on the left, stacked, with the article taking everything else.
   Splitting them put share buttons on one side and contents on the other, which
   asked the reader to track two margins; and it left a tall empty column beside
   the opening paragraphs. One rail, one place to look. */
.abt-art-grid { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 3.5rem; padding: 3rem 0 4rem; align-items: start; }

@media (max-width: 1080px) {
	.abt-art-grid { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
	.abt-art-rail { display: none; }
}

/* One sticky block holds both, so they travel together down a long article. */
.abt-art-rail { position: sticky; top: 90px; align-self: start; }
.abt-art-share { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(31, 31, 51, .12); }

.abt-art-toc h2, .abt-art-share h2 {
	font-size: calc(.74rem + 2px); letter-spacing: .14em; text-transform: uppercase;
	color: #646b7c; margin-bottom: .8rem;
}

html[lang="th"] .abt-art-toc h2, html[lang="th"] .abt-art-share h2 { letter-spacing: .04em; text-transform: none; }

.abt-art-toc a {
	display: block; padding: .42rem 0 .42rem .9rem;
	border-left: 2px solid rgba(31, 31, 51, .16);
	color: var(--default); text-decoration: none; font-size: calc(.89rem + 2px);
	font-weight: 500; line-height: 1.5;
}

.abt-art-toc a:hover { color: var(--primary); }
.abt-art-toc a.on { border-left-color: var(--primary); color: var(--primary); font-weight: 600; }

/* Firmer border and a darker label: these were a 10%-alpha outline holding grey
   text, which reads as disabled rather than as a button. */
.abt-art-share a, .abt-art-share button {
	display: flex; align-items: center; gap: .6rem;
	width: 100%; margin-bottom: .45rem; padding: .65rem .9rem;
	border: 1px solid rgba(31, 31, 51, .18); border-radius: 9px; background: #fff;
	color: var(--dark); font: inherit; font-size: calc(.86rem + 2px); font-weight: 600;
	text-align: left; text-decoration: none; cursor: pointer;
	transition: border-color .2s ease, color .2s ease, background-color .2s ease;
}

.abt-art-share a:hover, .abt-art-share button:hover {
	border-color: var(--primary); color: var(--primary); background: rgba(72, 72, 127, .06);
}

/* Long-form Thai needs more leading than the 1.7 the rest of the site uses --
   tone marks stack above and below the x-height and collide at normal spacing.
   The measure is capped in ch so it holds at any font size. */
.abt-art-prose { max-width: 68ch; font-size: calc(1.06rem + 2px); line-height: 1.95; }
.abt-art-prose h2 { font-size: calc(1.5rem + 2px); margin: 2.6rem 0 .9rem; text-transform: none; scroll-margin-top: 90px; }
.abt-art-prose h3 { font-size: calc(1.15rem + 2px); margin: 2rem 0 .6rem; text-transform: none; }
.abt-art-prose p { margin: 0 0 1.3rem; }
.abt-art-prose ul, .abt-art-prose ol { margin: 0 0 1.3rem; padding-left: 1.3rem; }
.abt-art-prose li { margin-bottom: .5rem; }
/* The editor stores list items as <li><p>...</p></li> — valid HTML, and what
   ProseMirror's list schema produces. Without this the paragraph's own bottom
   margin is added inside every bullet and the list falls apart vertically. */
.abt-art-prose li > p { margin: 0; }
.abt-art-prose img { max-width: 100%; height: auto; border-radius: 10px; }
.abt-art-prose figure { margin: 2rem 0; }
.abt-art-prose figcaption { font-size: calc(.85rem + 2px); color: #646b7c; margin-top: .55rem; }

/* --- Video embeds --------------------------------------------------------
   The editor stores a bare <iframe> inside a <figure> and no inline style,
   because style="" is stripped by the sanitiser on save. The 16:9 box has to
   come from here, or the embed renders at the iframe default of 300x150. */
.abt-art-prose figure:has(iframe) { position: relative; aspect-ratio: 16 / 9; }
.abt-art-prose figure iframe { width: 100%; height: 100%; border: 0; border-radius: 10px; display: block; }
/* Fallback for browsers without :has() — the ratio box is lost but the video
   is still a usable size rather than a 300x150 postage stamp. */
@supports not selector(figure:has(iframe)) {
	.abt-art-prose figure iframe { aspect-ratio: 16 / 9; height: auto; }
}
/* An article image is stored with real width/height attributes, so it reserves
   its space before it loads. width:100% alone would undo that. */
.abt-art-prose figure img { width: 100%; height: auto; }

/* --- Tables, rules and units ---------------------------------------------
   Everything the editor can now produce needs a matching rule here, or a spec
   table written in the admin arrives on the page as unstyled browser default. */
.abt-art-prose table {
	width: 100%; border-collapse: collapse; margin: 0 0 1.6rem;
	font-size: calc(.97rem + 2px); line-height: 1.6;
}
.abt-art-prose th, .abt-art-prose td {
	border: 1px solid rgba(31, 31, 51, .14); padding: .65rem .8rem;
	text-align: left; vertical-align: top;
}
.abt-art-prose th { background: #f4f6fb; color: var(--dark); font-weight: 700; }
.abt-art-prose tr:nth-child(even) td { background: #fbfcfe; }
.abt-art-prose th > p, .abt-art-prose td > p { margin: 0; }
/* A wide spec table scrolls inside itself rather than pushing the article out
   sideways on a phone. */
.abt-art-prose table { display: block; overflow-x: auto; }

.abt-art-prose hr { border: 0; border-top: 2px solid rgba(31, 31, 51, .12); margin: 2.4rem 0; }
.abt-art-prose sup, .abt-art-prose sub { line-height: 0; font-size: .75em; }
.abt-art-prose code {
	background: #f4f6fb; padding: .12em .4em; border-radius: 5px;
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .9em;
}

.abt-art-prose blockquote {
	margin: 1.8rem 0; padding: 1.1rem 1.4rem;
	border-left: 4px solid var(--secondary);
	background: rgba(112, 195, 217, .09);
	font-size: calc(1.02rem + 2px);
}

/* One call to action, at the end. A consulting article earns the ask by being
   useful first; a mid-article CTA interrupts the reader who was about to convert. */
.abt-art-cta { margin: 2.8rem 0 0; padding: 1.8rem 2rem; border-radius: 14px; background: #1F1F33; color: #cfd3e0; }
.abt-art-cta h3 { color: #fff; font-size: calc(1.2rem + 2px); margin-bottom: .5rem; text-transform: none; }
.abt-art-cta p { margin: 0 0 1.2rem; font-size: calc(.95rem + 2px); }
.abt-art-cta .btn { background: var(--secondary); border-color: var(--secondary); color: #10202a; }
.abt-art-cta .btn:hover { background: var(--secondary-100); border-color: var(--secondary-100); color: #10202a; }

/* Related articles come after the ending, where a finished reader is genuinely
   looking for the next thing -- not alongside, competing with the article. */
.abt-art-related { padding: 3.5rem 0 4rem; background: #f4f6fb; }
.abt-art-related h2 { font-size: calc(1.5rem + 2px); margin-bottom: 1.6rem; text-transform: none; }

/* ==========================================================================
   Flat UI pass — no gradients
   Every gradient fill is replaced with a solid brand colour, and the page is
   given a calm white / light-grey section rhythm so each band reads as its own
   block without any gradient wash.
   ========================================================================== */

/* Buttons: solid brand fill instead of the theme's gradient. The theme sets the
   gradient with `background: … !important`, so the flat fill has to answer in
   kind — shorthand + !important. */
.btn-gradient:not(.btn-outline),
.btn-gradient:not(.btn-outline):focus,
.btn-gradient:not(.btn-outline).focus {
	background: var(--primary) !important;
	border-color: var(--primary);
	color: #fff;
}
.btn-gradient:not(.btn-outline):hover,
.btn-gradient:not(.btn-outline).hover,
.btn-gradient:not(.btn-outline):active,
.btn-gradient:not(.btn-outline).active {
	background: var(--primary-100) !important;
	border-color: var(--primary-100);
	color: #fff;
}

/* The CTA band used .bg-gradient — make it a single solid brand indigo. */
.bg-gradient {
	background-image: none !important;
	background-color: var(--primary) !important;
}

/* Section rhythm: alternate plain white and a whisper-grey so neighbouring
   bands separate cleanly without any gradient. IDs beat the theme's
   .bg-transparent utility, so the fills take without !important gymnastics.
   The order the reader actually sees is hero photo -> indigo counters ->
   PLATFORM white -> PRODUCTS grey -> RESULTS white -> KNOWLEDGE grey ->
   CONTACT white, so no two neighbouring bands share a ground. #who-we-are and
   #what-we-do are hidden but keep their fills, so unhiding either one drops it
   back into the alternation.

   #knowledge needs an explicit fill for the same reason every other band does:
   Porto's .section default is #f4f4f4, which is neither white nor this page's
   grey — near enough to #f4f6fb to read as a rendering artefact against the
   Contact band rather than as a section boundary. */
#who-we-are,
#what-we-do,
#platform,
#cases,
#contact {
	background-color: #ffffff !important;
}
#portfolio,
#knowledge {
	background-color: #f4f6fb !important;
}

/* ==========================================================================
   Footer
   The theme already paints #footer with --dark (#1F1F33) -- the same tone as the
   solid header -- and the old markup covered that up with .bg-color-light. Letting
   it through is what turns the strip into brand chrome and book-ends the page, so
   nothing here sets a ground of its own. CONTACT above is white, so the two bands
   separate on tone and the hairline the light footer needed is gone with it.

   The wordmark and logo disc need no footer override any more either: on this
   ground the header's own white treatment is already right.
   ========================================================================== */

/* Anchors are classed rather than selected by position. The theme ships
   `#footer a:not(.btn):not(.no-footer-css)` at specificity (1,2,1) painting
   --default (#565b6b), which measures 2.2:1 here. `#footer .abt-footer-links a`
   is only (1,1,1) and would silently lose, so every footer link carries
   .abt-footer-link and this matches at (1,3,1).
   .72 white blends to ~#C0C0C4 on this ground: 8.9:1. */
#footer a.abt-footer-link:not(.btn) {
	color: rgba(255, 255, 255, .72);
	text-decoration: none;
	transition: color .2s ease;
}

#footer a.abt-footer-link:not(.btn):hover,
#footer a.abt-footer-link:not(.btn):focus {
	color: #fff;
	text-decoration: none;
}

/* Column headings. Small, tracked out, and in the brand's secondary blue (8.1:1
   here) so the columns read as labelled groups without competing with the
   wordmark next to them. */
#footer .abt-footer-heading {
	color: var(--secondary);
	font-weight: 700;
	letter-spacing: .16em;
	text-transform: uppercase;
	margin-bottom: 1.1rem;
}

/* Capped so the sentence wraps after a comfortable measure instead of running
   the full width of a large screen, which at this size is unreadable. */
#footer .abt-footer-blurb {
	color: rgba(255, 255, 255, .62);
	max-width: 34rem;
}

#footer .abt-footer-links li + li {
	margin-top: .6rem;
}

#footer .abt-footer-contact li {
	display: flex;
	align-items: flex-start;
	gap: .7rem;
	color: rgba(255, 255, 255, .72);
}

#footer .abt-footer-contact li + li {
	margin-top: .7rem;
}

/* Fixed width, not just a margin: it puts the three labels on one left edge
   whatever glyph sits in front of them, and the phone and clock icons are
   different widths. Decorative and aria-hidden, so the low contrast is fine. */
#footer .abt-footer-contact i {
	flex: 0 0 1rem;
	width: 1rem;
	margin-top: .28rem;
	color: rgba(255, 255, 255, .38);
	text-align: center;
}

/* The theme's btn-outline btn-light paints --default on a white fill, which is
   invisible on this ground. Outlined in the footer's own light instead, and
   ID-qualified so it beats `html body .btn-outline.btn-light` (0,3,2). */
#footer .abt-footer-cta {
	border: 1px solid rgba(255, 255, 255, .3);
	border-radius: 8px;
	background: transparent;
	color: #fff;
	letter-spacing: .04em;
	transition: background-color .2s ease, border-color .2s ease;
}

#footer .abt-footer-cta:hover,
#footer .abt-footer-cta:focus {
	background: var(--secondary);
	border-color: var(--secondary);
	color: var(--dark);
}

/* Legal bar. A hairline of the footer's own light rather than a second ground
   colour, so the fine print separates without banding the footer in two. */
#footer .abt-footer-bottom {
	border-top: 1px solid rgba(255, 255, 255, .1);
}

/* .55 white blends to ~#9A9AA0: 5.8:1, which small print needs. */
#footer .abt-footer-legal {
	color: rgba(255, 255, 255, .55);
}

/* The switch was built for the dark header and its palette is right for this
   ground too -- but its own selectors are (0,1,0) and (0,2,0), so down here the
   theme's `#footer a:not(.btn):not(.no-footer-css)` (1,2,1) repainted both links
   --default (#565b6b): 2.2:1, measured. Restated at (1,3,1)/(1,4,1) so the
   header's colours survive the move. */
#footer .abt-lang {
	margin: 0;
}

#footer a.abt-lang-link:not(.btn) {
	color: rgba(255, 255, 255, .6);
}

#footer a.abt-lang-link:not(.btn):hover,
#footer a.abt-lang-link:not(.btn):focus {
	color: #fff;
}

#footer a.abt-lang-link.active:not(.btn) {
	color: var(--secondary);
}

/* Porto styles .tab-content as a bordered white panel - the leftover of a
   small tabbed widget. On a full-bleed product band it drew a white frame with
   15px of padding around the panel, which read as an accidental gap rather than
   a design. The band supplies its own ground. */
#portfolio .tab-content {
	padding: 0;
	border: 0;
	border-radius: 0;
	background: transparent;
	box-shadow: none;
}

/* ==========================================================================
   Article presentation classes (public side)
   The closed set HtmlSanitizerService allows. Must stay in step with the same
   block in admin.css — if these two drift, what the writer sees in the editor
   is not what the reader gets.
   ========================================================================== */
.abt-art-prose .a-left   { text-align: left; }
.abt-art-prose .a-center { text-align: center; }
.abt-art-prose .a-right  { text-align: right; }

/* left/right on a figure floats it so the article text wraps around. */
.abt-art-prose figure.a-left  { float: left;  max-width: 48%; margin: .4rem 1.8rem 1.2rem 0; }
.abt-art-prose figure.a-right { float: right; max-width: 48%; margin: .4rem 0 1.2rem 1.8rem; }
.abt-art-prose figure.a-center { margin-inline: auto; }
.abt-art-prose h2, .abt-art-prose h3, .abt-art-prose h4 { clear: both; }
.abt-art-prose::after { content: ""; display: block; clear: both; }
/* A floated picture at 48% is unreadable next to a phone-width column. */
@media (max-width: 680px) {
	.abt-art-prose figure.a-left,
	.abt-art-prose figure.a-right { float: none; max-width: 100%; margin-inline: 0; }
}

.abt-art-prose .s-sm { font-size: .85em; }
.abt-art-prose .s-lg { font-size: 1.25em; }

.abt-art-prose .c-primary { color: var(--primary); }
.abt-art-prose .c-accent  { color: #26788f; }   /* measured 5.04:1 on white — the first cut, #2b8ba6, was 3.93:1 */
.abt-art-prose .c-muted   { color: #69707e; }
.abt-art-prose .c-ok      { color: #178050; }   /* measured 4.95:1 — --ok (#1f9d6b) was 3.45:1 */
.abt-art-prose .c-warn    { color: #9a6a15; }   /* darkened for AA on white */
.abt-art-prose .c-danger  { color: #C0392B; }
.abt-art-prose .f-mono {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .93em;
}
