/*
 * Telecom industry page — template-telecom.php.
 *
 * Source of truth: Figma "Telecom Page Thinkpalm" frame 1:479 (1440 wide).
 * The frame starts at y=-2, so every figure quoted in a comment below is a
 * PAGE coordinate (Figma y + 2) and the two agree at the top of the hero.
 *
 * Section rhythm on the 1440 frame:
 *   hero      0    - 600
 *   intro     701  - 1043   (+101)
 *   logos     1093 - 1188   (+50)
 *   outcomes  1289 - 1980   (+101)
 *   quotes    2080 - 2656   (+100)
 *   deliver   2756 - 3346   (+100)
 *   cta       3446 - 3676   (+100)
 *   whats-new 3776 - 4403   (+100)
 *   footer    4503          (+100)
 * The gaps are set per boundary rather than by one shared rule, because the
 * logo strip's is half the rest — see the end of this file for the two shared
 * sections (What's New, Get In Touch) whose own padding has to be re-based.
 *
 * Breakpoints: the Figma file has only the 1440 frame, so everything below
 * 1025 is an interpolation of it, not a second design.
 */

/* ----------------------------------------------------------------
 * Shared icon pill — Figma 585:848 (cards) / 1:891 (banner).
 *
 * 44px tall, radius 55, 1px INSIDE white stroke, transparent fill; a 28px
 * white disc holds the 21px arrow, with the label to its right.
 *
 * Hover is the component's own variant, not the homepage banner's: every
 * instance of this pill (1:891 on the CTA banner, 7:162/7:177/7:191/7:205 on
 * the outcome cards) carries an ON_HOVER that swaps 1:197 "Default" for 1:204
 * "Variant2". In 1:204 the disc stays exactly where it is and the colours
 * invert instead — the disc fills #0D6EFC and the arrow inside it goes white.
 * The label/disc side-swap belongs to a different component, "Submit Now"
 * (1:456 -> 1:451), which .tp-git__submit implements in its own stylesheet.
 *
 * Client QA has since asked for that side-swap on the disc-bearing pills too,
 * which overrides 1:204 deliberately -- see the hover rule below, and do not
 * "restore" the still disc as drift.
 * ---------------------------------------------------------------- */
.tp-tele-pill {
	position: relative;
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
	min-height: 44px;
	/* 12 inset + 28 disc + 10 gap = 50 */
	padding: 10px 28px 10px 50px;
	border: 0;
	border-radius: 55px;
	background: transparent;
	font: var(--tp-btn);
	color: var(--tp-white);
	text-decoration: none;
	cursor: pointer;
	/* Inside stroke as an inset shadow: a real border would add 2px to the
	   44px box and push the disc off its 8px inset. */
	box-shadow: inset 0 0 0 1px var(--tp-white);
	transition: background-color var(--tp-speed-fast) var(--tp-ease),
		color var(--tp-speed-fast) var(--tp-ease),
		padding var(--tp-speed-spring) var(--tp-ease-spring);
}

.tp-tele-pill__icon {
	position: absolute;
	left: 12px;
	top: 8px;
	/* (44 - 28) / 2 */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	background: var(--tp-white);
	color: #0e397e;
	transition: background-color var(--tp-speed-fast) var(--tp-ease),
		color var(--tp-speed-fast) var(--tp-ease),
		left var(--tp-speed-spring) var(--tp-ease-spring);
}

.tp-tele-pill__label {
	display: block;
}

/* 1:204, taken whole and shared by every disc-bearing instance of the pill —
   1:891 on the CTA banner and 7:162/7:177/7:191/7:205 on the outcome cards:
   the pill fills white, the label drops to #080A0B and the disc inverts to
   #0D6EFC with a white arrow. The outcome cards used to keep the label white
   on the assumption their pill sat on photography; it sits on the card's own
   solid navy body, where #080A0B reads fine, so the two now match.
   .tp-tele-pill--plain (the discless 1:552 variant both hero templates use)
   keeps its own hover and is excluded here. */
.tp-tele-pill:not(.tp-tele-pill--plain):hover,
.tp-tele-pill:not(.tp-tele-pill--plain):focus-visible {
	background: var(--tp-white);
	color: #080a0b;
	/* Client QA override of 1:204, which holds the disc still and only inverts
	   the colours: the disc is to travel from the left inset to the right one
	   on hover and slide back on mouse-out, the way the homepage banner pill
	   does (.tp-btn--white in base.css). The inversion above stays; this adds
	   the geometry, on the same spring the banner uses. Both disc-bearing
	   instances take it -- the four outcome cards (7:162/7:177/7:191/7:205)
	   and the CTA banner (1:891) -- so the two never behave differently on a
	   page that shows both. The discless hero variant is excluded by the
	   :not() it already carried.

	   The label is moved by animating the pill's own padding rather than the
	   label, so the 78px of horizontal padding -- and with it the pill's width
	   -- is identical in both states and neither the card nor the banner row
	   reflows mid-hover. */
	padding: 10px 50px 10px 28px;
	/* the 28/50 default, mirrored */
}

.tp-tele-pill:hover .tp-tele-pill__icon,
.tp-tele-pill:focus-visible .tp-tele-pill__icon {
	background: #0d6efc;
	color: var(--tp-white);
	left: calc(100% - 40px);
	/* 28 disc + 12 inset */
}

/* Figma 1:552 — the hero's CTA is the same pill with no disc: 237x44,
   radius 59, 10/24 padding. */
.tp-tele-pill--plain {
	padding: 10px 24px;
	border-radius: 59px;
}

/* The hover state is a real variant, not an invention: 1:552 carries an
   ON_HOVER interaction that swaps the component set 1:102 from
   "Property 1=Default" (1:103, both fills hidden) to "Property 1=Variant2"
   (1:105), which turns the white fill on. The label goes blue with it —
   #0D6EFC, sampled from the variant's own render, which is a shade off
   --tp-blue (#1977f3) and so is kept literal rather than tokenised. */
.tp-tele-pill--plain:hover,
.tp-tele-pill--plain:focus-visible {
	background: var(--tp-white);
	color: #0d6efc;
}

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

	.tp-tele-pill,
	.tp-tele-pill__icon {
		transition: none;
	}
}

/* ----------------------------------------------------------------
 * FR-002 — Hero, Figma 1:543 (1440x600).
 *
 * 1:543 stacks two full-bleed rects plus a COLOR_DODGE texture. Sampling the
 * Figma render against the top rect's gradient at eight points put every
 * channel within 1/255, so the lower rect and the dodge layer are provably
 * invisible and the band is this one gradient over #000740 — no image.
 *
 * The gradient handles run (0.0076, 0.4829) -> (0.9910, 0.5550), i.e. 91.75deg
 * over a line 1416.8px long that is NOT the box diagonal CSS would use. The
 * stop percentages below are each Figma stop re-projected onto the 1457.6px
 * line that `linear-gradient(91.75deg, …)` draws on a 1440x600 box, so the
 * two gradients coincide at the design width.
 *
 * Figma's last two stops fade alpha 1 -> 0.213 -> 0 over the navy. Figma
 * interpolates straight (non-premultiplied) alpha and CSS premultiplies, so
 * transcribing the four stops verbatim darkened the whole right half by up to
 * 8/255 per channel. The stops are therefore pre-composited over #000740 and
 * emitted opaque, sampled densely enough that the two curves agree to 1/255.
 * ---------------------------------------------------------------- */
.tp-tele-hero {
	position: relative;
	box-sizing: border-box;
	/* 1:543 is a 600-tall Container, but it is placed at y=-2 and clipped by the
	   page frame, so 598 is what actually renders and what every section below
	   is measured from. Its children are positioned by their absolute y, which
	   already accounts for the -2, so this trims the band and nothing else. */
	min-height: 598px;
	padding-top: 213px;
	/* 1:549 title top, y=213 */
	padding-bottom: 72px;
	overflow: hidden;
	background-color: #000740;
	background-image: linear-gradient(91.75deg,
			#01163b 0%,
			#01163b 5.72%,
			#041c47 12%,
			#062252 18%,
			#09275d 24%,
			#0b2d68 30%,
			#0e3273 36%,
			#10387e 42.01%,
			#11377f 48%,
			#11357d 54%,
			#10317a 60%,
			#0f2c74 66%,
			#0d266b 72%,
			#0a1e61 78%,
			#08195a 81.26%,
			#061554 86%,
			#05114e 90%,
			#030c48 94%,
			#000740 98.55%,
			#000740 100%);
}

/* ------------------------------------------------------------------
 * Full-bleed hero artwork (LP 1 - Dynamic, "hero_bg" field).
 *
 * The telecom hero paints a flat navy gradient and floats its artwork in a
 * fixed 751.763x459 slot. Some industry pages instead bleed one image across
 * the whole band with the gradient laid over it -- Figma 1:545 on the
 * Maritime page. That is a different composite, not a different image, so it
 * gets a modifier rather than a second hero partial.
 *
 * Stop positions are Figma's own, remapped from its gradient handles
 * (p0 13.612%, p1 98.5493% of the CSS gradient line) onto CSS's centred line.
 * The 41.37% -> 78.91% fade is sampled densely because Figma interpolates
 * straight alpha and CSS interpolates premultiplied; at this density the two
 * curves agree to 0.19/255 once composited over the artwork.
 * ------------------------------------------------------------------ */
.tp-tele-hero--bg {
	background-image:
		linear-gradient(91.75deg,
			#01163B 5.7148%,
			#10387E 41.3714%,
			rgba(19, 61, 134, 0.9016) 44.6461%,
			rgba(21, 65, 141, 0.8032) 47.9209%,
			rgba(24, 70, 149, 0.7047) 51.1956%,
			rgba(26, 74, 157, 0.6063) 54.4703%,
			rgba(29, 79, 164, 0.5079) 57.745%,
			rgba(32, 83, 172, 0.4095) 61.0198%,
			rgba(34, 88, 180, 0.311) 64.2945%,
			rgba(37, 92, 187, 0.2126) 67.5692%,
			rgba(40, 95, 191, 0.1701) 69.8367%,
			rgba(43, 99, 194, 0.1276) 72.1041%,
			rgba(46, 102, 197, 0.0851) 74.3716%,
			rgba(49, 105, 200, 0.0425) 76.6391%,
			rgba(52, 108, 204, 0) 78.9065%),
		var(--tp-tele-hero-bg);
	background-repeat: no-repeat, no-repeat;
	background-position: center, center;
	background-size: 100% 100%, cover;
}


/* Figma 54:138 at x=637 on a 1440 frame — 83px left of centre, which is what
   keeps the artwork tied to the headline rather than to the viewport edge. */
.tp-tele-hero__art {
	position: absolute;
	left: 50%;
	top: 118px;
	margin-left: -83px;
	width: 751.763px;
	height: 459px;
	pointer-events: none;
}

.tp-tele-hero__art-main,
.tp-tele-hero__art-glow {
	position: absolute;
	display: block;
}

/* 54:139 — a glow plate whose dark pixels have to drop out against the navy;
   that is why Figma blends it SCREEN instead of flattening it into 54:140. */
.tp-tele-hero__art-glow {
	left: 10.159px;
	top: 10.621px;
	width: 716.668px;
	height: 437.758px;
	mix-blend-mode: screen;
}

.tp-tele-hero__art-main {
	left: 0;
	top: 0;
	width: 751.763px;
	height: 459px;
}

.tp-tele-hero__inner {
	position: relative;
	z-index: 1;
}

/* Figma 1:558 — Ellipse 197, 224x224 of #008cff at x=241 y=-150 with a
   LAYER_BLUR of 307.8. Anchored to the centre line like the artwork, not to
   the viewport edge, so it keeps its offset from the content column at every
   width. z-index puts it over the headline, as the Figma layer order does,
   which is why the Figma render's white glyphs come out at #f9fcff.

   The blur is fitted, not converted: a LAYER_BLUR radius does not map to a CSS
   blur by the quarter-radius rule that holds for Figma's drop shadows. Two
   independent fits against the Figma render — a Gaussian model of the disc,
   and an in-browser sweep of the CSS value — both land on ~131px for radius
   307.8, i.e. about radius/2.33. */
.tp-tele-hero__glow {
	position: absolute;
	left: 50%;
	top: -150px;
	z-index: 2;
	margin-left: -479px;
	width: 224px;
	height: 224px;
	border-radius: 50%;
	background: #008cff;
	filter: blur(132px);
	pointer-events: none;
}

/* Figma 1:549 — Bold 52/65.52, white, measure 688px. The 688 matters: it is
   what breaks "Telecom Engineering Solutions For A" over two lines, giving the
   three-line block the design draws.

   No `text-transform`: textCase is TITLE on telecom/maritime but None on the
   Fintech hero ("...of Finance"), so the casing is authored into each post's
   copy rather than coerced here for all of them. */
.tp-tele-hero__title {
	margin: 0;
	max-width: 688px;
	font: 700 52px/65.52px var(--tp-font-heading);
	color: var(--tp-white);
}

/* Client request: the headline may lead with an emphasised phrase set against
   the rest of the line in regular weight ("**Telecom** Engineering Solutions
   For A Connected World"). The base above stays bold so every headline that
   carries no emphasis renders exactly as before -- only a title that actually
   contains one drops to 400. Without :has() support the whole line simply
   stays bold, i.e. the pre-existing design. */
.tp-tele-hero__title:has(strong) {
	font-weight: 400;
}

.tp-tele-hero__title strong {
	font-weight: 700;
}

/* 1:547 is a VERTICAL auto-layout with itemSpacing 32. The button itself is
   the discless 1:552 variant -- see .tp-tele-pill--plain above. */
.tp-tele-hero__cta {
	margin-top: 32px;
}

/* ----------------------------------------------------------------
 * FR-003 — Intro, Figma 1:583 (row height 342, vertically centred).
 * ---------------------------------------------------------------- */
.tp-tele-intro {
	margin-top: 101px;
}

.tp-tele-intro__inner {
	display: flex;
	align-items: center;
	gap: 40px;
}

/* The heading and the body carry their own measures: Data Center's 1:597 is
   554 wide while every page's body node (1:587 / 1:598) stays at 520. Sharing
   one 520 column broke "Data Centers Are Rebuilding" onto a third line. */
.tp-tele-intro__copy {
	flex: 0 0 554px;
	max-width: 554px;
}

/* 1:586 — SemiBold 40/50.4, #080a0b. */
.tp-tele-intro__title {
	margin: 0;
	font: 600 40px/50.4px var(--tp-font-heading);
	color: #080a0b;
}

/* 1:587 — Regular 16/26, #383838, 24px below the heading, measure 520. */
.tp-tele-intro__text {
	margin: 24px 0 0;
	max-width: 560px;
	font: 400 16px/26px var(--tp-font-body);
	color: #383838;
}

/* Figma separates the two intro paragraphs with one empty line of body text,
   so the gap is the line box itself — 26px, not the 24px that sets the whole
   block off from the heading. */
.tp-tele-intro__text+.tp-tele-intro__text {
	margin-top: 26px;
}

/* 1:584 — 363x342, radius 12, 1px INSIDE #eeefff.
 *
 * The width is a variable because the row gives the copy a fixed column and
 * then pins the card to the container's right edge, so whatever is left over
 * reads as a hole down the middle rather than as the 40px `gap` declared
 * above — 195px of it at the 1112 container. A page whose intro does not fill
 * the row sets `--tp-tele-intro-card-w` (Intro → "Artwork panel width") to
 * container − copy − gap and the panel absorbs that slack instead.
 *
 * Shrink is enabled so an override sized for the 1152 container cap narrows
 * instead of overflowing between 1025 and 1151, where the container is still
 * fluid. It never fires on the 363 default: 554 + 40 + 363 clears even the
 * narrowest container in that range. */
.tp-tele-intro__card {
	position: relative;
	flex-grow: 0;
	flex-shrink: 1;
	flex-basis: var(--tp-tele-intro-card-w, 363px);
	width: var(--tp-tele-intro-card-w, 363px);
	aspect-ratio: 363 / 342;
	margin-left: auto;
	border-radius: 12px;
	background: linear-gradient(180deg, #f4f5ff 0%, #fbfdff 100%);
	box-shadow: inset 0 0 0 1px #eeefff;
}

/* 1:588 — 330x307 at +16, +23 inside the card, stated against the card so the
   artwork keeps Figma's placement at any --tp-tele-intro-card-w. At the 363x342
   default these resolve to exactly 16 / 23 / 330 / 307. */
.tp-tele-intro__art {
	position: absolute;
	left: 4.4077%;
	top: 6.7251%;
	display: block;
	width: 90.9091%;
	height: 89.7661%;
	object-fit: contain;
}

/* ----------------------------------------------------------------
 * FR-004 — Client logo marquee, Figma 1:589.
 *
 * Cards are 210.912 x 95.1348, radius 16.155, 0.8975 INSIDE #e3efff, 14.36
 * apart. The spacing is a margin rather than a `gap` on purpose: with `gap`
 * the row is 10 gaps short of two whole sets, so translateX(-50%) lands off
 * by half a gap and the loop shows a seam. A trailing margin on every card
 * makes one set exactly half the row.
 * ---------------------------------------------------------------- */
.tp-tele-logos {
	margin-top: 50px;
}

.tp-tele-logos__marquee {
	overflow: hidden;
	/* The strip is wider than the column, so it has to fade rather than stop
	   dead at the container edge. */
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
}

.tp-tele-logos__row {
	display: flex;
	width: max-content;
	animation: tp-tele-logos-scroll 45s linear infinite;
}

.tp-tele-logos__marquee:hover .tp-tele-logos__row,
.tp-tele-logos__marquee:focus-within .tp-tele-logos__row {
	animation-play-state: paused;
}

.tp-tele-logos__card {
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	flex: 0 0 210.912px;
	height: 95.1348px;
	margin-right: 14.359968px;
	border-radius: 16.154964px;
	background: var(--tp-white);
	box-shadow: inset 0 0 0 0.897498px #e3efff;
}

.tp-tele-logos__wrap {
	display: flex;
	align-items: center;
	justify-content: center;

	/* An LP1 mark is an upload, so its ink box comes from the post rather
	   than from a --<slug> rule below, and arrives as --tp-logo-w/-h on the
	   element (template-parts/lp1-dynamic/client-logos.php). It is read here
	   rather than written straight into the inline style so that a media
	   query can still override it; an inline width would out-specify every
	   rule in this file. auto for the slug-classed strips, which set their
	   own sizes further down and win on source order. */
	width: var(--tp-logo-w, auto);
	height: var(--tp-logo-h, auto);
}

.tp-tele-logos__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
}

/* Ink-box sizes carried over from the Matter CSA strip FR-004 points at, so
   the ten marks keep the optical weight they were balanced at there. */
.tp-tele-logos__wrap--net2edge {
	width: 135px;
	height: 38.75px;
}

.tp-tele-logos__wrap--celfinet {
	width: 129.5px;
	height: 32.5px;
}

.tp-tele-logos__wrap--ribbon {
	width: 135.25px;
	height: 37.25px;
}

.tp-tele-logos__wrap--edgecore {
	width: 158px;
	height: 43.5px;
}

.tp-tele-logos__wrap--silentium {
	width: 128.25px;
	height: 28.25px;
}

.tp-tele-logos__wrap--cms {
	width: 163.25px;
	height: 29.25px;
}

.tp-tele-logos__wrap--ppc {
	width: 96.47px;
	height: 40.76px;
}

.tp-tele-logos__wrap--celeno {
	width: 144.5px;
	height: 38px;
}

.tp-tele-logos__wrap--rada {
	width: 135px;
	height: 32.75px;
}

.tp-tele-logos__wrap--rad {
	width: 72px;
	height: 39px;
}

/* Figma 1:589 is five tiles spanning the whole 1112 column -- 5 x 210.912 plus
   4 x 14.359968 -- so there is nothing to scroll into and the set is centred
   instead. `gap` is safe here precisely because there is no second pass to line
   up, and it lets a longer list wrap rather than run off the column. */
.tp-tele-logos--static .tp-tele-logos__marquee {
	-webkit-mask-image: none;
	mask-image: none;
}

.tp-tele-logos--static .tp-tele-logos__row {
	width: auto;
	flex-wrap: wrap;
	justify-content: center;
	gap: 14.359968px;
	animation: none;
}

.tp-tele-logos--static .tp-tele-logos__card {
	margin-right: 0;
}

@keyframes tp-tele-logos-scroll {
	from {
		transform: translateX(0);
	}

	to {
		transform: translateX(-50%);
	}
}

@media (prefers-reduced-motion: reduce) {
	.tp-tele-logos__row {
		animation: none;
	}
}

/* ----------------------------------------------------------------
 * FR-005 — Proven Outcomes, Figma 1:590.
 * ---------------------------------------------------------------- */
/* The heading row and the card track share one grid purely so the nav can span
   both: a sticky element may only travel inside its own grid area, so parking
   the nav in row 1 alone would give it 100px of travel and nothing more. See
   template-parts/lp1-dynamic/outcomes.php for why it must travel at all. One
   full-width column — the head is still boxed by its own .tp2026-container and
   the track still bleeds off the right. */
.tp-tele-outcomes {
	/* The column gutter, restated from .tp2026-container--narrow's maths. Lives
	   here rather than on the scroller so the nav can line up with it too: both
	   read it inside a grid area spanning the section's full width, so the
	   percentage resolves to the same number for each. */
	--tp-tele-gutter: max(var(--tp-gutter), calc((100% - var(--tp-container-narrow)) / 2 + var(--tp-gutter)));

	display: grid;
	grid-template-columns: minmax(0, 1fr);

	/* 101px is the page's section rhythm and what Figma 1:589 -> 1:590 draws.
	   It is a variable because the strip above this section is sized for the
	   five tiles that fill the column: a page carrying fewer marks keeps the
	   full 101 under a part-empty row, and the two pockets read as one void.
	   Such a page sets --tp-tele-outcomes-gap (Section Headings -> "Space
	   below the client logo strip") and tightens it. Desktop only — the
	   <=1024 rule below restates margin-top flat, so the override cannot
	   follow the layout into the stacked breakpoints it was not judged at. */
	margin-top: var(--tp-tele-outcomes-gap, 101px);
}

/* Every child's placement is explicit. Leaving any of them to auto-placement
   lets it spill into an implicit second column, which would also shrink the
   grid area --tp-tele-gutter's percentage resolves against. */
.tp-tele-outcomes>.tp2026-container {
	grid-row: 1;
	grid-column: 1;
}

/* 1:592 — HORIZONTAL, counter-axis CENTER, height 100. The nav that made this
   a space-between row now sits outside it; the row's height is what still
   places the nav, so the 100 floor is load-bearing. */
.tp-tele-outcomes__head {
	display: flex;
	align-items: center;
	min-height: 100px;
}

/* 1:593 — SemiBold 40/50.4, #080a0b, measure 517. */
/* 517 was telecom's own two-line measure; Data Center's 23:239 sits on one
   line in a 76-tall heading row, so the cap has to clear its 557. Widening to
   560 leaves telecom, maritime and fintech breaking exactly where they did. */
.tp-tele-outcomes__title {
	margin: 0;
	max-width: 560px;
	font: 600 40px/50.4px var(--tp-font-heading);
	color: #080a0b;
}

/* 1:595 — 146x76 pill, radius 60, #e8f1ff, 1px INSIDE white. The discs sit at
   +13 and +78 inside it: 13 left, 5 between, 8 right. */
.tp-tele-outcomes__nav {
	/* Spans both grid rows so it stays in reach for as long as the cards it
	   drives are on screen: it pins clear of the scrolled bar and releases at
	   the bottom of the track, so the section's end is the end of the stick.
	   At rest it is still drawn exactly where 1:592 puts it. */
	grid-row: 1 / 3;
	grid-column: 1;
	justify-self: end;
	align-self: start;
	position: sticky;
	top: calc(var(--tp-header-height-scrolled) + 16px);
	z-index: 2;
	/* (100px row - 76px pill) / 2 — 1:592's counter-axis CENTER, restated
	   because the pill is no longer a flex child of that row. A heading that
	   wrapped to three lines would out-grow the 100 floor and leave the pill
	   sitting high; all four LP 1 headings and telecom's fit two. */
	margin-top: 12px;
	margin-right: var(--tp-tele-gutter);

	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 5px;
	box-sizing: border-box;
	flex: 0 0 auto;
	/* 13 + 60 + 5 + 60 + 8 — 1:595's 146 stated outright rather than left to
	   shrink-to-fit. With the box exactly full, space-between resolves to the
	   same 5px gap. */
	width: 146px;
	height: 76px;
	padding: 8px 8px 8px 13px;
	border-radius: 60px;
	background: #e8f1ff;
	box-shadow: inset 0 0 0 1px var(--tp-white);
}

.tp-tele-outcomes__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 60px;
	height: 60px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: #0e65ea;
	color: var(--tp-white);
	cursor: pointer;
	transition: background var(--tp-speed-fast) var(--tp-ease),
		color var(--tp-speed-fast) var(--tp-ease),
		box-shadow var(--tp-speed-fast) var(--tp-ease);
}

/* Figma only ever draws one arrow enabled at a time, and an unfilled disc is
   exactly how it draws the other — so the disabled state IS the design, not a
   dimmed version of it. */
.tp-tele-outcomes__arrow[disabled] {
	background: transparent;
	color: #b1c6e5;
	cursor: default;
}

/* FR-004 — the scroller matches Matter CSA "Already Have Hardware?", which
   lifts the enabled disc a step on hover. The disabled disc is a design state
   (see above), not an interactive one, so it is excluded rather than dimmed.
   The ring is drawn only for keyboard focus; a mouse click already has the
   hover colour as its feedback. */
.tp-tele-outcomes__arrow:hover:not([disabled]) {
	background: #1977f3;
}

.tp-tele-outcomes__arrow:focus-visible:not([disabled]) {
	background: #1977f3;
	outline: 2px solid #0d6efd;
	outline-offset: 2px;
}

.tp-tele-outcomes__chevron {
	display: block;
}

/* One chevron-up glyph rotated both ways, as in the Figma component. */
.tp-tele-outcomes__chevron--prev {
	transform: rotate(-90deg);
}

.tp-tele-outcomes__chevron--next {
	transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
	.tp-tele-outcomes__arrow {
		transition: none;
	}
}

/* 7:144 is 1928 wide inside a 1112 column: the track deliberately runs off the
   right of the viewport so the fourth card is cut and the row reads as
   scrollable. Sitting outside the container is what lets it do that; the
   padding below puts the first card back on the 164px gutter. */
.tp-tele-outcomes__scroller {
	grid-row: 2;
	grid-column: 1;

	/* Heading box bottom 1387 to card row top 1409. */
	margin-top: 22px;
	padding-left: var(--tp-tele-gutter);
	/* Without this the first card's snap-align:start pulls the padding out from
	   under it on load: the snapport would start at scroll offset 0, so the
	   browser scrolls to 164 to line the card up and the gutter disappears. */
	scroll-padding-left: var(--tp-tele-gutter);
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
}

.tp-tele-outcomes__scroller::-webkit-scrollbar {
	display: none;
}

.tp-tele-outcomes__track {
	display: flex;
	gap: 24px;
	margin: 0;
	padding: 0;
	list-style: none;
	width: max-content;
}

/* 7:145 — 464x569, radius 24, padding 24/32/40/32 on the content and a 16px
   inset on the media. The two fills are the tiled texture and a vertical
   gradient over it; Figma interpolates gradient stops with STRAIGHT alpha
   while CSS premultiplies, which on a stop that goes 0.44 -> 1 is a visible
   difference, so the gradient is sampled into five stops instead of two. */
.tp-tele-outcomes__card {
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	flex: 0 0 464px;
	height: 569px;
	padding: 16px 16px 40px;
	border-radius: 24px;
	scroll-snap-align: start;
	background-color: #000d1d;
	background-image:
		linear-gradient(180deg,
			rgba(0, 8, 22, 0.44) -13.11%,
			rgba(4, 23, 53, 0.58) 15.17%,
			rgba(8, 37, 84, 0.72) 43.44%,
			rgba(12, 52, 114, 0.86) 71.72%,
			rgb(16, 66, 145) 100%),
		url('../images/telecom/outcome-card-bg.jpg');
	background-size: auto, 367.5px 654px;
	background-repeat: no-repeat, repeat;
}

/* 7:146 — 432x236, radius 24. */
.tp-tele-outcomes__media {
	display: block;
	width: 432px;
	height: 236px;
	border-radius: 24px;
	object-fit: cover;
}

/* The card frame (7:163) pads 32 left and right, but its media rectangle
   (7:164) sits at a 16 inset and breaks out of that padding, so the padding is
   reproduced here rather than on the card. Bottom-aligned, so cards with
   shorter copy still line their CTAs up. */
.tp-tele-outcomes__body {
	display: flex;
	flex-direction: column;
	gap: 24px;
	margin-top: auto;
	padding: 0 16px;
	width: 432px;
	box-sizing: border-box;
}

/* Frame 7:167 is FIXED at 393, not the full 400 the card's padding leaves —
   a hand-set measure, and the one that decides where every card title and
   description breaks. At 368 the first card's description loses "Fi" off
   "Wi-Fi" and the second card's title breaks a word early. */
.tp-tele-outcomes__copy {
	display: flex;
	flex-direction: column;
	gap: 12px;
	width: 393px;
}

.tp-tele-outcomes__card-title {
	margin: 0;
	font: 700 20px/26px var(--tp-font-heading);
	color: var(--tp-white);
}

.tp-tele-outcomes__desc {
	margin: 0;
	font: 400 15px/24px var(--tp-font-body);
	color: var(--tp-white);
}

.tp-tele-outcomes__stats {
	display: flex;
	gap: 16px;
	margin: 0;
}

.tp-tele-outcomes__stat {
	display: flex;
	align-items: center;
	gap: 8px;
	min-height: 48px;
}

.tp-tele-outcomes__stat-value {
	font: 600 30px/45px var(--tp-font-heading);
	color: var(--tp-white);
	white-space: nowrap;
}

/* Three of the six stats are words rather than numerals, and Figma drops them
   to the body size so they still fit the 48px row. The word variant is also
   spaced differently — 16 either side of the rule instead of 8 — and its value
   column is the one piece of this section that has to be read off the node
   tree rather than inferred: frame 7:172 is FIXED at 117 but the TEXT inside
   it (7:173) is FIXED at 141, so the text wraps at 141 while the rule is
   placed 117 + 16 along. Letting it wrap at 117 instead costs card 4 a third
   line ("More Roadmap Focus" breaks after "More"), so the overflow is real and
   the negative margin is what reproduces it: 141 wide, pulled back 24 so the
   16px gap still lands the rule at +133, which is where the Figma render puts
   it (card 2 684 -> 817, card 3 1172 -> 1305, card 4 1660 -> 1793). */
.tp-tele-outcomes__stat--text {
	gap: 16px;
}

.tp-tele-outcomes__stat--text .tp-tele-outcomes__stat-value {
	margin-right: -24px;
	width: var(--tp-stat-value-w, 141px);
	font-size: 16px;
	line-height: 24px;
	white-space: normal;
}

/* A per-card width override (stat1_value_w) positions the rule, not the text.
   Telecom's 141 is allowed to run 8px past its rule because its copy is known
   to clear it; an override row's copy is not, and Fintech 37:676 proves it —
   the rule sits at +183 but the TEXT node is only 171, so wrapping at the full
   191 puts "Continuous Compliance" (186) across the rule. Padding shortens the
   measure without moving the box, so the rule stays put and the text wraps
   12px short of it: 191 - 20 = 171, the Figma measure. */
.tp-tele-outcomes__stat--text .tp-tele-outcomes__stat-value--sized {
	padding-right: 20px;
}

/* Semibold, unlike the numeral variant's label: Figma's word-variant label
   nodes (7:176, 7:190, 7:204) are weight 600 while 7:157 is 400. It is not
   only a colour difference — at 400 "Roaming & reconnection" fits the 188
   measure and the line breaks a word later than the design. */
.tp-tele-outcomes__stat--text .tp-tele-outcomes__stat-label {
	padding-left: 15px;
	width: var(--tp-stat-label-w, 141px);
	font-weight: 600;
}

/* The rule between value and label is decoration, so it is this element's
   border rather than a glyph a screen reader would have to skip.

   content-box, and 1px shaved off the padding, so `width` can be the Figma
   text measure as written (113 here, 141/188 for the word variant). Figma's
   rule is a zero-width LINE and its 8px gap is measured from that zero, while
   a border has real thickness starting at the same place — 1 + 7 keeps the
   glyphs where Figma puts them instead of a pixel to the right of it. */
.tp-tele-outcomes__stat-label {
	display: flex;
	align-items: center;
	box-sizing: content-box;
	margin: 0;
	padding-left: 7px;
	width: var(--tp-stat-label-w, 113px);
	height: 46px;
	border-left: 1px solid var(--tp-white);
	font: 400 16px/24px var(--tp-font-body);
	color: var(--tp-white);
}

/* A one-sentence outcome line (Figma's Maritime cards) has no second column
   to rule off against, so the value takes the whole card width and wraps
   there instead of inside the 141px measure the two-column variant needs. */
.tp-tele-outcomes__stat--solo .tp-tele-outcomes__stat-value,
.tp-tele-outcomes__stat--text.tp-tele-outcomes__stat--solo .tp-tele-outcomes__stat-value {
	margin-right: 0;
	width: auto;
}

.tp-tele-outcomes__cta {
	align-self: flex-start;
}

/* ----------------------------------------------------------------
 * FR-006 — What Our Clients Say, Figma 1:892.
 *
 * The band's own fill is #f1f8ff; the navy is ellipse 1:893 (#10377d, LAYER
 * BLUR 271.3) sitting at -333,1759 at 2106x1323, clipped by the frame. Its
 * feather does reach the section — sampling the Figma render at the band's
 * left edge gives 0.88 alpha over the #f1f8ff, which no flat fill produces —
 * so the ellipse is reproduced rather than flattened. See
 * .tp-tele-quotes__wash below.
 * ---------------------------------------------------------------- */
.tp-tele-quotes {
	position: relative;
	margin-top: 100px;
	padding: 68px 0 72px;
	overflow: hidden;
	/* Frame 1:892's own fill; the blue is the wash below, not this. */
	background: #f1f8ff;
}

/* Figma 1:893 — Ellipse 206, 2106x1323 of #10377d starting 319px above the
   section, LAYER_BLUR 271.3, clipped by the frame. Fitted the same way as the
   hero glow: 116px is where an in-browser sweep against the Figma render
   bottoms out. Sampling the band's left edge at y=2080 gives 0.88 alpha over
   #f1f8ff, which a flat fill cannot produce — hence the ellipse. */
.tp-tele-quotes__wash {
	position: absolute;
	left: 50%;
	top: -319px;
	z-index: 0;
	margin-left: -1053px;
	width: 2106px;
	height: 1323px;
	border-radius: 50%;
	background: #10377d;
	filter: blur(116px);
	pointer-events: none;
}

/* 1:894 — 72 white hairlines, 5px wide on a 21px pitch, inside a frame at
   opacity 0.07 whose own stroke gradient fades out downwards. Net: ~0.546 of
   that 7% at the top of the band, gone by 199px. The first line sits at page
   x=-34.5, which is x=7.5 once reduced modulo the 21px pitch. */
.tp-tele-quotes__comb {
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	z-index: 1;
	height: 311px;
	pointer-events: none;
	background-image: repeating-linear-gradient(90deg,
			transparent 0 5px,
			rgba(255, 255, 255, 0.07) 5px 10px,
			transparent 10px 21px);
	-webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.546) 0, rgba(0, 0, 0, 0) 199px);
	mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.546) 0, rgba(0, 0, 0, 0) 199px);
}

/* 1:968 — SemiBold 40/50.4, white, centred. */
.tp-tele-quotes__title {
	position: relative;
	margin: 0;
	font: 600 40px/50.4px var(--tp-font-heading);
	color: var(--tp-white);
	text-align: center;
}

/* The ribbon is full-bleed and taller than one slide: the active panel is
   centred in the viewport and its neighbours are simply whatever falls either
   side, which is how the design shows cards running off both edges. */
.tp-tele-quotes__viewport {
	position: relative;
	margin-top: 42px;
	overflow: hidden;
}

.tp-tele-quotes__ribbon {
	display: flex;
	align-items: center;
	gap: 25px;
	margin: 0;
	padding: 0;
	list-style: none;
	width: max-content;
	transition: transform var(--tp-speed-slow) var(--tp-ease-gentle);
}

/* Inactive: the person card alone, 233x274. Active (1:969): the same card
   with the quote column added to its left inside a 709x298 panel. */
.tp-tele-quotes__slide {
	display: flex;
	align-items: center;
	box-sizing: border-box;
	flex: 0 0 auto;
	width: 233px;
	padding: 0;
	border-radius: 20px;
	background: transparent;
	/* Client request: a card off to the side is a control -- clicking it brings
	   it to the centre and opens it (see initQuotes() in section-telecom.js).
	   The whole band is JS-driven, so there is no no-JS state to mislead. */
	cursor: pointer;
}

.tp-tele-quotes__slide.is-active {
	width: 709px;
	gap: 42px;
	padding: 12px 12px 12px 42px;
	background: #f1f8ff;
	box-shadow: inset 0 0 0 1px #e4efff;
	/* Already centred and open, so there is nothing for a click to do. */
	cursor: default;
}

/* 1:971 — 380 wide, itemSpacing 24. Hidden rather than collapsed on the
   inactive slides: at 380px it cannot share a 233px box with the card, and
   animating between the two states would mean cloning the card mid-flight for
   no gain the design asks for. The ribbon's slide is the motion. */
.tp-tele-quotes__quote {
	display: none;
	flex-direction: column;
	gap: 24px;
	flex: 0 0 380px;
	width: 380px;
}

.tp-tele-quotes__slide.is-active .tp-tele-quotes__quote {
	display: flex;
}

.tp-tele-quotes__stars {
	display: block;
	width: 144px;
	height: 24px;
}

.tp-tele-quotes__text {
	margin: 0;
	font: 500 20px/26px var(--tp-font-body);
	color: #131313;
}

.tp-tele-quotes__card {
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	flex: 0 0 233px;
	width: 233px;
	height: 274px;
	padding: 24px;
	border-radius: 12px;
	background: var(--tp-white);
	box-shadow: inset 0 0 0 1px #eaedef;
}

/* A fixed 161x60 box with the mark contained inside it, so four marks of
   different proportions still align with each other. */
.tp-tele-quotes__logo {
	display: flex;
	align-items: center;
	width: 161px;
	height: 60px;
}

.tp-tele-quotes__logo img {
	display: block;
	max-width: 100%;
	width: auto;
	object-fit: contain;
}

.tp-tele-quotes__person {
	display: flex;
	align-items: center;
	gap: 12px;
	margin-top: auto;
	/* 24 top + 60 logo + auto + 50 row + 24 = 274 */
	height: 50px;
}

.tp-tele-quotes__avatar {
	display: block;
	flex: 0 0 50px;
	width: 50px;
	height: 50px;
	border-radius: 10px;
	object-fit: cover;
}

.tp-tele-quotes__who {
	display: flex;
	flex-direction: column;
	min-width: 0;

	/* The 50px headshot leaves the attribution 123px inside the 233px card,
	   which is too narrow for a two-word discipline such as "Embedded
	   Engineering" (135px) and breaks it across two lines. Roles are bound
	   with a non-breaking space where they must stay together, so the column
	   is allowed to take 16px of the card's right gutter rather than shrink
	   the type; the longest line still clears the card edge by ~12px. */
	margin-right: -16px;
}

.tp-tele-quotes__name {
	font: 700 16px/20px var(--tp-font-heading);
	color: #124189;
}

.tp-tele-quotes__role {
	font: 400 12px/20px var(--tp-font-body);
	color: rgba(8, 10, 11, 0.6);
}

/* 1:1022 — 14px dots on a 20px pitch; the active one filled, the rest a 1px
   white outline. */
.tp-tele-quotes__dots {
	position: relative;
	display: flex;
	justify-content: center;
	gap: 6px;
	margin-top: 32px;
}

.tp-tele-quotes__dot {
	position: relative;
	width: 14px;
	height: 14px;
	padding: 0;
	border: 1px solid var(--tp-white);
	border-radius: 50%;
	background: transparent;
	cursor: pointer;
	transition: background var(--tp-speed-fast) var(--tp-ease);
}

.tp-tele-quotes__dot.is-active {
	background: var(--tp-white);
}

/* ----------------------------------------------------------------
 * FR-007 — Everything We Deliver, Figma 1:656.
 *
 * Open/close is the Matter CSA "What Sets ThinkPalm Apart" behaviour the FRD
 * points at: hover opens, and the reveal animates a grid row from 0fr to 1fr
 * so it runs at the panel's real height with nothing measured in JS. Hover is
 * pointer-only, so section-telecom.js adds a click/Enter toggle on top and
 * .is-open mirrors :hover here — without it the descriptions and their Learn
 * More links are unreachable on a phone.
 * ---------------------------------------------------------------- */
.tp-tele-deliver {
	margin-top: 100px;
}

/* 1:690 — Bold 36/40, #080a0b, centred. */
.tp-tele-deliver__title {
	margin: 0;
	font: 700 36px/40px var(--tp-font-heading);
	color: #080a0b;
	text-align: center;
}

/* flex-start, NOT stretch. The photo is painted with background-size: cover,
   so any change to its box height rescales the image — opening a panel read
   as the photo zooming (client QA, 2026-09-23: "when I open NavitasAI, the
   image is zooming"). Measured on maritime at >=1025: the box went 276 -> 300
   the moment NavitasAI opened, an 8.7% rescale. Telecom had the same defect
   at 900-1024 (+44 to +68) and only looked clean at >=1100 because its floor
   already equalled its open-list height.

   No accordion state may change the photo or its box in any way. Same rule,
   same reason and same three-pass history as .mcsa-compare — see that section
   before re-introducing stretch here. */
.tp-tele-deliver__row {
	display: flex;
	align-items: flex-start;
	gap: 42px;
	margin-top: 42px;
}

/* 1:691 draws the photo as a frame fill, not an image node, at a literal
   508px against Figma's own 6-row list (484px open). The design's premise is
   that the photo's bottom edge lands on the open panel's bottom edge — but a
   fixed height only holds for one exact row count and one exact description
   length, and this section is per-post editable (LP 1 - Dynamic), so any edit
   left the photo hanging below the last row.

   So the floor stays per-post (min-height + the field below) and the LIST is
   what closes the gap — see the :has(.is-open) rules under .tp-tele-deliver__list.
   Only one of the two columns ever has to move, and it can never be the photo.
   The floor must therefore clear the tallest open-list height for that post,
   or the list overhangs instead: telecom 508 at >=1025, maritime 300.

   The floor is also what frames the photo. The box is painted background-size:
   cover, so any floor whose aspect differs from the asset's crops the design's
   framing off the top and bottom — telecom sat at 426 against a 535x508 asset
   and lost 82px of it, clipping the mast and most of the skyline (client QA,
   2026-09-30: "the placement of the featured image needs to be fixed"). Match
   the floor to the height 1:691 draws and cover becomes a no-op. */
/* 535 is the basis, not a lock: 535 + 42 + 535 is exactly the 1112 content
   width at 1440, but between 1025 and ~1152 the container is narrower than
   that and a rigid basis pushed the list past the right gutter, taking the
   whole page into horizontal scroll. Shrinking keeps both columns equal and
   inside the gutters; the 1024 breakpoint below still re-bases them to 0. */
.tp-tele-deliver__photo {
	flex: 1 1 535px;
	min-width: 0;
	/* 426 was telecom's open-list height and stays the default for a post that
	   sets nothing, but the floor is per-post: a page with two accordion rows
	   wants a shorter photo than one with five, or the photo hangs below the
	   last row. Telecom itself now carries 508 — 1:691's own height, so its
	   asset is shown whole. See the "photo height" field in
	   inc/lp1-dynamic-config.php. */
	min-height: var(--tp-tele-deliver-photo-min, 426px);
	border-radius: 24px;
	background-position: center;
	background-size: cover;
	background-repeat: no-repeat;
}

.tp-tele-deliver__list {
	display: flex;
	flex-direction: column;
	align-self: flex-start;
	gap: 12px;
	flex: 1 1 535px;
	min-width: 0;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* The photo is locked, so the list is what lands the two bottom edges on the
   same line. Only while a panel is open: unconditional stretch would drag the
   closed list down over the photo's full floor and wreck the rest state, where
   the list is deliberately much shorter.

   The slack goes into the LAST item, not justify-content: space-between —
   space-between re-spaces every gap, so all the rows drift for the whole
   reveal and a row can slide out from under the pointer mid-open. Growing one
   item moves nothing: the slack is invisible whitespace under a closed last
   row, or slightly deeper bottom padding when the last row is the open one.

   Self-limiting: if the open list is already the taller column, stretch
   resolves to its own content height and both rules are inert. Guarded to the
   side-by-side widths — below 769 the row is stacked and the list's height is
   content-driven, so there is no free space to distribute. */
@media (min-width: 769px) {
	.tp-tele-deliver__list:has(.tp-tele-deliver__item.is-open) {
		align-self: stretch;
	}

	.tp-tele-deliver__list:has(.tp-tele-deliver__item.is-open)>.tp-tele-deliver__item:last-child {
		flex-grow: 1;
	}
}

/* The hover half of the pair above. A separate block because a nested media
   query is not valid in a plain stylesheet, and the pointer guard matters:
   :hover sticks after a tap on touch, which would pin a row open against the
   click toggle. */
@media (min-width: 769px) and (hover: hover) and (pointer: fine) {
	.tp-tele-deliver__list:has(.tp-tele-deliver__item:hover) {
		align-self: stretch;
	}

	.tp-tele-deliver__list:has(.tp-tele-deliver__item:hover)>.tp-tele-deliver__item:last-child {
		flex-grow: 1;
	}
}

/* When the LAST row is the open one, the stretch above would hand all the
   slack to that row's own card and leave a tall empty panel. Let the list end
   at its content instead; the bottom edges then only align while some other
   row is open. */
@media (min-width: 769px) {
	.tp-tele-deliver__list:has(> .tp-tele-deliver__item:last-child.is-open) {
		align-self: flex-start;
	}
}

@media (min-width: 769px) and (hover: hover) and (pointer: fine) {
	.tp-tele-deliver__list:has(> .tp-tele-deliver__item:last-child:hover) {
		align-self: flex-start;
	}
}

/* Closed 535x46 (6px block padding on a 34px row); open 1:665 pads to 20 all
   round and picks up the panel skin. */
.tp-tele-deliver__item {
	box-sizing: border-box;
	padding: 6px 20px;
	border-radius: 12px;
	background: transparent;
	box-shadow: inset 0 0 0 1px transparent;
	transition: padding 0.35s cubic-bezier(0.4, 0, 0.2, 1),
		background 0.35s cubic-bezier(0.4, 0, 0.2, 1),
		box-shadow 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

.tp-tele-deliver__item.is-open {
	padding: 20px;
	background: #fafcff;
	box-shadow: inset 0 0 0 1px #ecf1f9;
}

/* FR-005 — hover is the Matter CSA "What Sets ThinkPalm Apart" behaviour the
   FRD cites. Its own block, not a comma list, so a browser that chokes on the
   :has() selector below still gets hover, and vice versa. Pointer-guarded:
   :hover sticks after a tap on touch, which would pin the row open against
   the click toggle section-telecom.js adds for exactly that case. */
@media (hover: hover) and (pointer: fine) {
	.tp-tele-deliver__item:hover {
		padding: 20px;
		background: #fafcff;
		box-shadow: inset 0 0 0 1px #ecf1f9;
	}
}

/* Keyboard focus has to open the row too, or Tab lands on a Learn More link
   inside a clipped panel. It is :has(:focus-visible) and not :focus-within
   because a mouse click leaves focus on the head button: under :focus-within
   that pinned the row open, so the second click cleared .is-open and
   aria-expanded while the card stayed visibly open. Split into its own rule
   so a browser without :has() still gets the hover and .is-open states. */
.tp-tele-deliver__item:has(:focus-visible) {
	padding: 20px;
	background: #fafcff;
	box-shadow: inset 0 0 0 1px #ecf1f9;
}

.tp-tele-deliver__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 7px;
	width: 100%;
	/* The design draws every title on one line, so 34px is the row. It is a
	   floor rather than a fixed height because a long title wraps at narrow
	   widths, and a fixed 34 let the second line spill over the row below. */
	min-height: 34px;
	padding: 0;
	border: 0;
	background: transparent;
	text-align: left;
	cursor: pointer;
}

.tp-tele-deliver__item-title {
	font: 600 20px/34px var(--tp-font-heading);
	color: #131313;
}

@media (max-width: 768px) {
	.tp-tele-deliver__item-title {
		line-height: 1.3;
	}
}

.tp-tele-deliver__chevron {
	display: inline-flex;
	flex: 0 0 32px;
	color: #131313;
	transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

.tp-tele-deliver__item.is-open .tp-tele-deliver__chevron {
	transform: rotate(180deg);
}

.tp-tele-deliver__item:has(:focus-visible) .tp-tele-deliver__chevron {
	transform: rotate(180deg);
}

@media (hover: hover) and (pointer: fine) {
	.tp-tele-deliver__item:hover .tp-tele-deliver__chevron {
		transform: rotate(180deg);
	}
}

/* 0fr -> 1fr on the wrapper; the inner element is what gets clipped while the
   row grows, which is why the padding lives there and not here. */
.tp-tele-deliver__panel {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

.tp-tele-deliver__item.is-open .tp-tele-deliver__panel {
	grid-template-rows: 1fr;
}

.tp-tele-deliver__item:has(:focus-visible) .tp-tele-deliver__panel {
	grid-template-rows: 1fr;
}

@media (hover: hover) and (pointer: fine) {
	.tp-tele-deliver__item:hover .tp-tele-deliver__panel {
		grid-template-rows: 1fr;
	}
}

.tp-tele-deliver__panel-inner {
	min-height: 0;
	/* overflow:hidden is what lets the 0fr/1fr row clip, but it clips on both
	   axes and it clips to the PADDING box. The Learn More hover ring below is
	   an outline, so it paints 2px outside the pill's border box on every side;
	   the padding grows the clip box enough to let it through while the
	   matching negative margin keeps the content box — and therefore the
	   description's top-left corner and the panel's height — exactly where
	   they were. */
	margin: -4px;
	padding: 4px;
	overflow: hidden;
	opacity: 0;
	/* Trails the 0fr->1fr row growth by a beat so the copy arrives into space
	   that already exists rather than sliding up through the clip edge. */
	transition: opacity 0.3s ease 0.05s;
}

.tp-tele-deliver__item.is-open .tp-tele-deliver__panel-inner,
.tp-tele-deliver__item:has(:focus-visible) .tp-tele-deliver__panel-inner {
	opacity: 1;
}

@media (hover: hover) and (pointer: fine) {
	.tp-tele-deliver__item:hover .tp-tele-deliver__panel-inner {
		opacity: 1;
	}
}

.tp-tele-deliver__desc {
	margin: 12px 0 0;
	font: 400 16px/24px var(--tp-font-body);
	color: var(--tp-gray-700);
}

/* I1:665;1634:15837 — 119x44, radius 59, 8px gap, and zero horizontal padding:
   the frame hugs its content exactly (label 87 + gap 8 + arrow 24 = 119), so
   its left edge sits flush with the description above it. The frame carries a
   2px #1977f3 stroke and two fills, but all three are visible:false, so at
   rest this is blue text and an arrow and the box is only the hit target.

   The 119 is left to the content rather than hard-set: at the shipped Plus
   Jakarta Sans the label measures 86.5px, so a literal width:119px leaves
   0.5px of slack that any font-metric difference turns into a clipped glyph. */
.tp-tele-deliver__more {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	box-sizing: border-box;
	margin-top: 16px;
	height: 44px;
	border-radius: 59px;
	font: var(--tp-btn);
	color: var(--tp-blue);
	white-space: nowrap;
	text-decoration: none;
	outline: 2px solid transparent;
	transition: outline-color var(--tp-speed-fast) var(--tp-ease);
}

/* Client asked for no hover stroke on this link, so hover is left unstyled.
   The ring stays on :focus-visible only — keyboard users still need to see
   where focus is, and :focus-visible never fires on a mouse hover or click.
   It has to be an outline and not an inside stroke: the frame has no
   horizontal padding, so a 2px inside stroke would run straight through the
   L and the arrow. An outline paints outside the border box and leaves the
   119x44 geometry untouched. */
.tp-tele-deliver__more:focus-visible {
	outline-color: var(--tp-blue);
}

.tp-tele-deliver__more svg {
	flex: 0 0 24px;
}

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

	.tp-tele-deliver__item,
	.tp-tele-deliver__panel,
	.tp-tele-deliver__panel-inner,
	.tp-tele-deliver__chevron,
	.tp-tele-quotes__ribbon {
		transition: none;
	}
}

/* ----------------------------------------------------------------
 * FR-008 — Mid-page CTA banner, Figma 1:885 (1114x230, radius 24).
 *
 * The card's navy, a COLOR_BURN texture burnt into it, then two blurred
 * ellipses, then the copy — see the paint-order note in cta-banner.php.
 *
 * LAYER_BLUR radius converts to a CSS blur at about radius / 2.33, not the
 * radius / 4 that holds for drop shadows: 145 -> 62px. Fitted in-browser
 * against the Figma render, the same way the hero and quotes glows were.
 * ---------------------------------------------------------------- */
.tp-tele-cta {
	margin-top: 100px;
}

.tp-tele-cta__panel {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 40px;
	box-sizing: border-box;
	min-height: 230px;
	padding: 70px;
	border-radius: 24px;
	overflow: hidden;
	background: #131635;
	/* Confines the texture's blend to this card's own fill, as in Figma, so it
	   cannot reach for whatever the page happens to put behind the card. */
	isolation: isolate;
}

.tp-tele-cta__glow {
	position: absolute;
	width: 767px;
	height: 256px;
	border-radius: 50%;
	background: rgba(25, 119, 243, 0.5);
	filter: blur(62px);
	pointer-events: none;
}

/* 1:886 */
.tp-tele-cta__glow--top {
	left: -172px;
	top: -171px;
}

/* 1:887 */
.tp-tele-cta__glow--bottom {
	left: 458px;
	top: 160px;
	width: 869px;
}

/* The fill is STRETCH with an imageTransform that rotates the source a quarter
   turn and crops a 5.9%-43.6% vertical slice of it, so the asset ships already
   transformed and is stretched edge to edge here rather than cropped again by
   `cover`. 0.27 is the fill's own opacity — at full strength the burn takes the
   navy to black. */
.tp-tele-cta__texture {
	position: absolute;
	inset: 0;
	opacity: 0.27;
	background-position: center;
	background-size: 100% 100%;
	background-repeat: no-repeat;
	mix-blend-mode: color-burn;
	pointer-events: none;
}

/* 1:888 — 587 wide, itemSpacing 12. */
.tp-tele-cta__copy {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 12px;
	flex: 0 1 587px;
	max-width: 587px;
}

.tp-tele-cta__title {
	margin: 0;
	font: 700 24px/30.24px var(--tp-font-heading);
	color: var(--tp-white);
}

.tp-tele-cta__text {
	margin: 0;
	font: 400 16px/24px var(--tp-font-body);
	color: var(--tp-white);
}

.tp-tele-cta__btn {
	position: relative;
	flex: 0 0 auto;
}

.tp-tele-cta__btn .tp-tele-pill__icon {
	color: #0442cb;
}

/* ----------------------------------------------------------------
 * Shared sections re-based to this page's rhythm.
 *
 * What's New and the footer's Get In Touch band are the site-wide partials
 * (FR-009, FR-010) and carry their own vertical padding — 64px from
 * .tp-section and 100px from .tp-git. The Figma frame puts 100px above each,
 * so What's New trades its padding for a margin on both edges.
 *
 * The bottom one cannot be left to .tp-git's own 100px top padding: that
 * padding sits INSIDE the band, and the band is a navy gradient, so it
 * produces 100px of navy rather than the 100px of page background the frame
 * shows between the View More row (4403) and the footer's top edge (4503).
 * Without this the band cuts the View More pill off at its baseline.
 * ---------------------------------------------------------------- */
.tp-telecom-page .tp2026-whats-new {
	margin-top: 100px;
	margin-bottom: 100px;
	padding-top: 0;
	padding-bottom: 0;
}

/* LP 1 - Dynamic carries .tp-telecom-page too, so the rule above already gives
   its What's New section the same 100px on both edges. This is the safety net
   for when that section drops out: every LP1 section skips its own render when
   its fields are empty, and What's New skips itself when nothing is published,
   so whichever one ends up last still needs the 100px of page background before
   the Get In Touch band's navy. Both rules set the same value, so they agree
   when What's New is the last child. */
.tp-lp1-page .site-content> :last-child {
	margin-bottom: 100px;
}

/* ----------------------------------------------------------------
 * Below 1025 there is no second Figma frame to work from, so the desktop
 * boxes give up their fixed widths in the order that keeps the design's
 * reading intact: the two-column rows stack first, then the type steps down.
 * ---------------------------------------------------------------- */
@media (max-width: 1240px) {

	/* The card is 464 + a 24 gap; below this width the right bleed stops
	   reading as "there is more" and starts reading as a broken column. */
	.tp-tele-outcomes__scroller {
		padding-right: var(--tp-gutter);
	}
}

@media (max-width: 1024px) {
	.tp-tele-hero {
		min-height: 520px;
		padding-top: 172px;
		padding-bottom: 56px;
	}

	.tp-tele-hero__title {
		font-size: 42px;
		line-height: 1.26;
		max-width: 560px;
	}

	.tp-tele-hero__art {
		left: auto;
		right: -120px;
		margin-left: 0;
		width: 560px;
		height: 342px;
		opacity: 0.55;
	}

	.tp-tele-hero__art-glow {
		left: 7.6px;
		top: 7.9px;
		width: 534px;
		height: 326px;
	}

	.tp-tele-hero__art-main {
		width: 560px;
		height: 342px;
	}

	.tp-tele-intro,
	.tp-tele-outcomes {
		margin-top: 72px;
	}

	.tp-tele-intro__inner {
		gap: 32px;
	}

	/* The 554 heading measure is a desktop-only widening; below 1024 the copy
	   column keeps the 520 it has always had. */
	.tp-tele-intro__copy {
		flex: 1 1 auto;
		max-width: 520px;
	}

	.tp-tele-intro__card {
		flex: 0 0 300px;
		height: 283px;
	}

	.tp-tele-intro__art {
		left: 13px;
		top: 19px;
		width: 273px;
		height: 254px;
	}

	.tp-tele-intro__title,
	.tp-tele-outcomes__title {
		font-size: 32px;
		line-height: 40px;
	}

	.tp-tele-quotes,
	.tp-tele-deliver,
	.tp-tele-cta,
	.tp-telecom-page .tp2026-whats-new {
		margin-top: 72px;
	}

	.tp-telecom-page .tp2026-whats-new,
	.tp-lp1-page .site-content> :last-child {
		margin-bottom: 72px;
	}

	.tp-tele-quotes__title {
		font-size: 32px;
		line-height: 40px;
	}

	.tp-tele-deliver__title {
		font-size: 30px;
		line-height: 36px;
	}

	.tp-tele-deliver__row {
		gap: 32px;
	}

	.tp-tele-deliver__photo,
	.tp-tele-deliver__list {
		flex: 1 1 0;
		min-width: 0;
	}

	/* 450, not 382. The floor has to clear the tallest open-list height at this
	   breakpoint or the list overhangs the locked photo: telecom's rows wrap to
	   two lines here and open to 426-450 (measured 1024 and 900), where 382 left
	   it 44-68px short. Maritime's open states are 300-348 and fit either way. */
	.tp-tele-deliver__photo {
		min-height: 450px;
	}

	.tp-tele-cta__panel {
		padding: 48px;
	}
}

@media (max-width: 768px) {
	/*
	 * Paint the band flat and drop the wash ellipse entirely at phone widths.
	 *
	 * .tp-tele-quotes__wash is 2106x1323 under filter: blur(116px). The blur
	 * forces a rasterised layer of roughly 2800x2020 CSS px (~5.7Mpx), which
	 * on a 3x phone measures ~51Mpx of device pixels - past what iOS Safari
	 * will rasterise for a filtered layer, and it is further clipped by the
	 * section's own overflow: hidden. Safari then renders the layer degraded
	 * rather than dropping it, so the navy came out as a pale lavender wash
	 * on a real iPhone (reported 2026-10-07) while Chrome was correct. It is
	 * the only blurred layer on the page anywhere near that budget: the next
	 * largest, .tp-tele-hero__glow, measures 9.3Mpx.
	 *
	 * Nothing is being approximated here. The ellipse is so much larger than
	 * the band that its feather never reaches the frame at these widths: a
	 * per-pixel sweep of the blurred render against a flat #10377d is an
	 * exact match at 375/414/576 and differs by at most 1/255 at 768. The
	 * feather only becomes visible past ~1024px (5/255) and is genuine design
	 * at desktop, so the ellipse is left in place there.
	 */
	.tp-tele-quotes {
		background: #10377d;
	}

	.tp-tele-quotes__wash {
		display: none;
	}

	.tp-tele-hero {
		min-height: 460px;
		padding-top: 152px;
	}

	.tp-tele-hero__title {
		font-size: 34px;
	}

	/* The artwork has no content of its own and at this width it only fights
	   the headline for contrast. */
	.tp-tele-hero__art {
		display: none;
	}

	.tp-tele-intro__inner {
		flex-direction: column;
		align-items: stretch;
	}

	.tp-tele-intro__copy,
	.tp-tele-intro__text {
		max-width: none;
	}

	.tp-tele-intro__card {
		flex: 0 0 auto;
		margin-left: 0;
		width: 100%;
		height: 320px;
	}

	.tp-tele-intro__art {
		left: 50%;
		top: 50%;
		width: auto;
		height: 274px;
		max-width: calc(100% - 32px);
		transform: translate(-50%, -50%);
	}

	/* Figma 1:589 is five tiles filling the 1112 column, and --static keeps
	   that 210.912px tile at every width. Two of them want 436.18px, so from
	   425px down -- where the narrow column measures 385, 335, 280 -- not
	   even a pair fits and flex-wrap drops the second mark onto a line of its
	   own, so a two-logo strip reads as a stacked list (QA 2026-10-07,
	   Mobile). Wrapping is decided on the flex basis, not on the shrunk size,
	   so permitting shrink changes nothing: the basis itself has to become a
	   share of the row. Half of it less half the gap puts exactly two marks
	   on a line at any phone width and wraps a third to the next, and the
	   aspect-ratio keeps the tile the shape it was drawn rather than letting
	   a narrower one go square. Scoped to --static: the marquee scrolls a
	   max-content row and needs the designed tile for its seamless -50%
	   loop, and it has the whole page width to scroll through anyway. */
	.tp-tele-logos--static .tp-tele-logos__card {
		/* Capped at the designed tile, because this breakpoint reaches up to
		   768px where half the column is wider than 210.912px and the pair
		   would be blown up past the size it was drawn at. */
		flex: 0 0 min(210.912px, calc((100% - 14.359968px) / 2));
		min-width: 0;
		min-height: 0;
		height: auto;
		aspect-ratio: 210.912 / 95.1348;
	}

	/* The ink box was cut for the full tile, so in one half that wide it would
	   spill out. --tp-logo-wp/-hp are the same box as a share of the tile, so
	   the mark shrinks with its tile and keeps the optical weight it was
	   balanced at, rather than being clamped to some flat cap. */
	.tp-tele-logos--static .tp-tele-logos__wrap {
		width: var(--tp-logo-wp, auto);
		height: var(--tp-logo-hp, auto);
	}

	/* Stacked, the nav drops to a row of its own under the heading. It lands
	   right on top of the cards there, and the shorter header leaves enough
	   page to hold both, so it comes out of sticky and back into flow. */
	.tp-tele-outcomes__head {
		min-height: 0;
	}

	.tp-tele-outcomes__nav {
		grid-row: 2;
		justify-self: start;
		position: static;
		margin: 24px 0 0 var(--tp-tele-gutter);
	}

	.tp-tele-outcomes__scroller {
		grid-row: 3;
	}

	.tp-tele-deliver__row {
		flex-direction: column;
	}

	/* Stacked here, so there is no bottom edge to meet — min-height, not
	   height, purely so it overrides the wider breakpoints' floor. */
	.tp-tele-deliver__photo {
		flex: 0 0 auto;
		width: 100%;
		min-height: 320px;
	}

	.tp-tele-deliver__list {
		flex: 0 0 auto;
		width: 100%;
	}

	.tp-tele-cta__panel {
		flex-direction: column;
		align-items: flex-start;
		gap: 24px;
		padding: 40px 32px;
	}

	.tp-tele-cta__copy {
		flex: 0 0 auto;
		max-width: none;
	}
}

@media (max-width: 575px) {
	.tp-tele-hero {
		min-height: 420px;
		padding-top: 140px;
	}

	.tp-tele-hero__title {
		font-size: 28px;
		line-height: 1.3;
	}

	.tp-tele-intro__title,
	.tp-tele-outcomes__title,
	.tp-tele-quotes__title,
	.tp-tele-deliver__title {
		font-size: 26px;
		line-height: 32px;
	}

	/* One card per screen, with the 20px gutter kept on both sides. */
	.tp-tele-outcomes__card {
		flex-basis: min(464px, calc(100vw - 40px));
		width: min(464px, calc(100vw - 40px));
		height: auto;
		padding-bottom: 32px;
	}

	.tp-tele-outcomes__media {
		width: 100%;
		height: auto;
		aspect-ratio: 432 / 236;
	}

	.tp-tele-outcomes__body {
		margin-top: 32px;
		width: 100%;
	}

	/* The copy column and the word-variant stat both carry hand-set Figma
	   measures (393, and the 141-in-a-117-frame overflow) that are wider than
	   a phone card and would clip the title. Below this breakpoint the card
	   sets the measure instead. */
	.tp-tele-outcomes__copy {
		width: 100%;
	}

	.tp-tele-outcomes__stats {
		flex-wrap: wrap;
		gap: 16px 24px;
	}

	.tp-tele-outcomes__stat--text .tp-tele-outcomes__stat-value {
		margin-right: 0;
		width: auto;
		flex: 1 1 0;
		min-width: 0;
	}

	.tp-tele-outcomes__stat--text .tp-tele-outcomes__stat-value--sized {
		padding-right: 0;
	}

	.tp-tele-outcomes__stat-label,
	.tp-tele-outcomes__stat--text .tp-tele-outcomes__stat-label {
		box-sizing: border-box;
		width: auto;
		flex: 1 1 0;
		min-width: 0;
	}

	.tp-tele-quotes__slide,
	.tp-tele-quotes__slide.is-active {
		width: min(320px, calc(100vw - 40px));
		flex-direction: column;
		align-items: stretch;
		gap: 20px;
		padding: 16px;
	}

	/* Stacked, the quote reads before the attribution — which is the order the
	   desktop panel puts them in left-to-right. */
	.tp-tele-quotes__quote {
		flex: 0 0 auto;
		width: auto;
		order: -1;
	}

	.tp-tele-quotes__card {
		flex: 0 0 auto;
		width: 100%;
		height: auto;
	}

	.tp-tele-quotes__person {
		margin-top: 24px;
	}

	.tp-tele-quotes__text {
		font-size: 17px;
		line-height: 24px;
	}

	.tp-tele-cta__panel {
		padding: 32px 20px;
	}

	.tp-tele-cta__title {
		font-size: 22px;
		line-height: 28px;
	}
}