/*
	Theme Name: Hello Elementor Child
	Description: Site-specific customisations for iotocean.com on top of Hello Elementor.
	Author: IoTOcean
	Template: hello-elementor
	Version: 1.9.5
	Requires PHP: 8.1
	Text Domain: hello-elementor-child
*/

/*
 * Presentation only (constitution Principle IV). Colours come from the Elementor Kit
 * globals as CSS variables (contracts/design-tokens.md); no hex values here, so each
 * colour is defined once (Principle VI).
 */

/* Guard against sideways scrolling on small screens. */
html {
	overflow-x: clip;
}

/* "Ocean" in the header and footer wordmark. */
.wordmark-accent {
	color: var(--e-global-color-accent);
}

/* The Kit's default container padding (40 px left and right, 24 px on mobile) is meant for
 * top-level sections. Elementor also applies it to every nested container without padding of
 * its own, so each level lost 80 px (the footer logo's box was about 90 px wide). Nested
 * containers get no default side padding; a container's own padding setting still applies,
 * since Elementor sets it directly, not through these defaults. ".e-con .e-con" outranks
 * the Kit's ".e-con" at every breakpoint. */
.e-con .e-con {
	--container-default-padding-left: 0px;
	--container-default-padding-right: 0px;
}

/* Focus ring: 2 px Aqua outline with a 2 px offset, never removed. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
	outline: 2px solid var(--e-global-color-accent);
	outline-offset: 2px;
}

/* Hello Elementor's skip link, visible when focused. */
.skip-link.screen-reader-text:focus {
	position: fixed;
	top: 8px;
	left: 8px;
	z-index: 100000;
	clip: auto;
	clip-path: none;
	width: auto;
	height: auto;
	padding: 12px 20px;
	border-radius: 8px;
	background-color: var(--e-global-color-accent);
	color: var(--e-global-color-primary);
	font-weight: 600;
	text-decoration: none;
	outline: 2px solid var(--e-global-color-text);
	outline-offset: 2px;
}

/* Status badge (contracts/design-tokens.md, Status Badge): a small tag, tinted in its status
 * colour. In Development is Amber with a traffic-cone icon (owner, 2026-10-25). */
.status-badge,
.status-concept,
.status-in-development,
.status-testing,
.status-beta,
.status-available {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 3px 9px;
	border: 1px solid transparent;
	border-radius: 6px;
	font-size: 12px;
	font-weight: 600;
	line-height: 1.4;
	letter-spacing: 0.02em;
}

.status-concept {
	border-color: var(--e-global-color-accent);
	color: var(--e-global-color-accent);
	background-color: transparent;
}

.status-in-development {
	border-color: color-mix(in srgb, var(--e-global-color-amber) 45%, transparent);
	color: var(--e-global-color-amber);
	background-color: color-mix(in srgb, var(--e-global-color-amber) 10%, transparent);
}

/* Traffic cone, drawn in the label colour through a mask (decorative, no text). */
.status-in-development::before {
	flex: none;
	width: 1.15em;
	height: 1.15em;
	background-color: currentColor;
	-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.6 2h2.8l1 3H5.6zM5.2 6.5h5.6l.7 2H4.5zM4 10h8l.6 2H3.4zM1.5 12.6h13v1.6h-13z'/%3E%3C/svg%3E") center / contain no-repeat;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.6 2h2.8l1 3H5.6zM5.2 6.5h5.6l.7 2H4.5zM4 10h8l.6 2H3.4zM1.5 12.6h13v1.6h-13z'/%3E%3C/svg%3E") center / contain no-repeat;
	content: "";
}

.status-testing,
.status-beta {
	border-color: var(--e-global-color-electric);
	color: var(--e-global-color-text);
	background-color: transparent;
}

.status-available {
	border-color: var(--e-global-color-accent);
	color: var(--e-global-color-primary);
	background-color: var(--e-global-color-accent);
}

/* Product feature list (iotocean-features dynamic tag): a card grid, 2 columns from
 * tablet up, 1 on mobile (T061). */
ul.iotocean-features {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 24px;
	margin: 0;
	padding: 0;
	list-style: none;
}

@media (max-width: 767px) {

	ul.iotocean-features {
		grid-template-columns: minmax(0, 1fr);
	}
}

ul.iotocean-features li {
	padding: 24px;
	border: 1px solid var(--e-global-color-hairline);
	border-radius: 16px;
	background-color: var(--e-global-color-secondary);
}

/* The feature title in the H3 style (Space Grotesk 600, the H3 global's size). */
ul.iotocean-features strong {
	display: block;
	margin-bottom: 8px;
	color: var(--e-global-color-text);
	font-family: var(--e-global-typography-h3-font-family), sans-serif;
	font-size: var(--e-global-typography-h3-font-size);
	font-weight: var(--e-global-typography-h3-font-weight, 600);
	line-height: var(--e-global-typography-h3-line-height);
}

ul.iotocean-features span {
	color: var(--e-global-color-mist);
}

/* Technology stack list (iotocean-tech-stack dynamic tag). On product pages
 * assets/css/tech-icons.css adds each technology's icon before its name. */
ul.iotocean-tech-stack {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
}

ul.iotocean-tech-stack li {
	padding: 4px 12px;
	border: 1px solid var(--e-global-color-hairline);
	border-radius: 999px;
	color: var(--e-global-color-mist);
	font-size: 15px;
}

/* Footer product list ([iotocean_product_list]). */
ul.iotocean-product-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

ul.iotocean-product-list li + li {
	margin-top: 8px;
}

/* ---------------------------------------------------------------------------------------
 * Phase 3: header, footer, cards and sections (contracts/design-tokens.md, Components).
 * ------------------------------------------------------------------------------------- */

/* Wordmark: Space Grotesk (the H3 global) at weight 700. */
.wordmark .elementor-heading-title {
	font-weight: 700;
}

.wordmark .elementor-heading-title a {
	color: inherit;
}

/* Header: Deep Navy, then Ocean Blue with a hairline border once the page scrolls
 * (Elementor Pro adds .elementor-sticky--effects past the sticky offset). */
.iotocean-header {
	border-bottom: 1px solid transparent;
	background-color: var(--e-global-color-primary);
	transition: background-color 0.2s ease, border-color 0.2s ease;
}

.iotocean-header.elementor-sticky--effects {
	border-bottom-color: var(--e-global-color-hairline);
	background-color: var(--e-global-color-secondary);
}

/* "Contact" in the main menu as a Secondary button (menu item class menu-cta). */
.site-nav .elementor-nav-menu--main .menu-cta > a.elementor-item {
	margin-left: 8px;
	padding: 10px 20px;
	border: 1.5px solid var(--e-global-color-electric);
	border-radius: 999px;
	color: var(--e-global-color-text);
}

.site-nav .elementor-nav-menu--main .menu-cta > a.elementor-item:hover,
.site-nav .elementor-nav-menu--main .menu-cta > a.elementor-item:focus-visible {
	border-color: var(--e-global-color-accent);
	color: var(--e-global-color-text);
}

/* The menu toggle is a div with role="button"; give it the same focus ring. */
.elementor-menu-toggle:focus-visible {
	outline: 2px solid var(--e-global-color-accent);
	outline-offset: 2px;
}

/* Secondary button: transparent, Cloud White label, Electric Blue border; Aqua on hover. */
.elementor-widget-button.button-secondary .elementor-button {
	border: 1.5px solid var(--e-global-color-electric);
	background-color: transparent;
	color: var(--e-global-color-text);
}

.elementor-widget-button.button-secondary .elementor-button:hover,
.elementor-widget-button.button-secondary .elementor-button:focus-visible {
	border-color: var(--e-global-color-accent);
	background-color: transparent;
	color: var(--e-global-color-text);
}

/* Text links: Aqua, underlined on hover. */
.text-link .elementor-heading-title a {
	color: inherit;
	text-decoration: none;
}

.text-link .elementor-heading-title a:hover,
.text-link .elementor-heading-title a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 4px;
}

/* The hero art fades into the page on all four edges: two linear masks, intersected, so
   only the middle stays fully opaque. The art has its own Deep Navy margin, so a 7 % fade is
   enough to hide its edge. Mask colours are alpha only, so no Kit colour is needed. */
.hero-illustration img {
	width: 100%;
	height: auto;
	-webkit-mask-image:
		linear-gradient(to right, transparent, black 7%, black 93%, transparent),
		linear-gradient(to bottom, transparent, black 7%, black 93%, transparent);
	-webkit-mask-composite: source-in;
	mask-image:
		linear-gradient(to right, transparent, black 7%, black 93%, transparent),
		linear-gradient(to bottom, transparent, black 7%, black 93%, transparent);
	mask-composite: intersect;
}

/* Status badge text inside a Heading widget: the tag sets the font and colour. The extra
 * .elementor-widget raises specificity above Elementor's own
 * ".elementor-widget-heading .elementor-heading-title" rule, which loads later and would
 * otherwise give the label the Display (H1) size. */
.elementor-widget.status-badge .elementor-heading-title {
	color: inherit;
	font-family: var(--e-global-typography-text-font-family), sans-serif;
	font-size: inherit;
	font-weight: inherit;
	letter-spacing: normal;
	line-height: inherit;
}

/* Product card: 16:10 image, lift 4 px with an Electric Blue border on hover or focus. */
.product-card {
	height: 100%;
	transition: transform 0.18s ease, border-color 0.18s ease;
}

.product-card:hover,
.product-card:focus-within {
	border-color: var(--e-global-color-electric);
	transform: translateY(-4px);
}

.product-card__image img,
.journal-card__image img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 16 / 10;
	object-fit: cover;
}

.product-card__link {
	margin-top: auto;
}

/* Rows line up across cards: where cards sit side by side (768 px and up), the tagline (Body,
 * 1.65 line height) keeps room for two lines, three in the four-column home grid, so every
 * short description starts at the same height; the link sits at the bottom of the card. */
@media (min-width: 768px) {
	.product-card__tagline .elementor-heading-title {
		min-height: calc(2 * 1.65em);
	}
}

@media (min-width: 1025px) {
	.elementor-grid-4 .product-card__tagline .elementor-heading-title {
		min-height: calc(3 * 1.65em);
	}
}

/* ---------------------------------------------------------------------------------------
 * Card carousels (Home "Our products" and "Latest from the journal", prompts/our_products_
 * section_into_infinite_card_carousel.md): Elementor Pro's Loop Carousels with the product or
 * journal cards, class .iotocean-carousel (plus .product-carousel or .journal-carousel);
 * assets/js/carousel.js adds keys, breakpoints and autoplay rules. Three cards from
 * 1200 px, two from 768 px, one below, 24 px apart, all as tall as the tallest. The arrows are
 * 44 px circles in the back-to-top style, centred on the carousel's side edges (half over the
 * section's 40 px side padding, 24 px on phones, so the page never scrolls sideways); the
 * dots sit below, the current one a wider Aqua bar. When every card fits on screen (few
 * journal articles) the carousel stands still: no arrows, dots, loop or autoplay (.is-static,
 * set by the script, or a single card, where Elementor doesn't start Swiper at all).
 * ".elementor-element" and the widget class in front outrank Elementor's own carousel rules.
 * --------------------------------------------------------------------------------------- */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel {
	/* The dots' 24 px tap targets and 8 px above them: 32 px under the cards. */
	--swiper-pagination-size: 24px;
	--swiper-pagination-spacing: 8px;
	--arrow-size: 18px;
	--arrow-normal-color: var(--e-global-color-accent);
	--arrow-hover-color: var(--e-global-color-primary);
	--arrow-prev-left-align: 0px;
	--arrow-prev-left-position: -22px;
	--arrow-next-right-align: 0px;
	--arrow-next-right-position: -22px;
	/* Centred on the cards: the frame has 8 px above them and the dots' 32 px below. */
	--arrow-prev-top-align: 50%;
	--arrow-next-top-align: 50%;
	--arrow-prev-caption-spacing: 12px;
	--arrow-next-caption-spacing: 12px;
	--arrow-prev-translate-y: -50%;
	--arrow-next-translate-y: -50%;
	/* The 8 px room above the cards (for their hover lift) comes out of the section gap. */
	margin-top: -8px;
}

/* Elementor's breakpoint for two cards is 1024 px; the carousel keeps two up to 1199 px, also
 * before Swiper starts, so nothing moves when it does. */
@media (min-width: 1025px) and (max-width: 1199px) {
	.elementor-element.elementor-widget-loop-carousel.iotocean-carousel {
		--swiper-slides-to-display: 2;
	}
}

/* The frame clips at its padding edge: 8 px on top keeps the hover lift visible. */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper {
	padding-top: 8px;
	cursor: grab;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper:active {
	cursor: grabbing;
}

/* The carousel fades in as a whole (its own Fade In Up, as the cards did together in the
 * grid). The card templates' per-card entrance stays off here: Swiper's loop copies are made
 * before it runs and would stay invisible. The cards show only once the carousel itself is no
 * longer invisible: a visible child of a hidden element would show before its section fades
 * in (and the card titles would come before their section heading for assistive technology). */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel:not(.elementor-invisible) .product-card.elementor-invisible,
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel:not(.elementor-invisible) .journal-card.elementor-invisible {
	visibility: visible;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .product-card.animated,
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .journal-card.animated {
	animation: none;
}

/* Every card as tall as the tallest (the slides stretch; the card fills its slide). */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-slide {
	height: auto;
}

/* Before Swiper starts, and without JavaScript: the slides take their final width (Elementor's
 * own rule targets a class these slides don't have) and the row scrolls sideways with snap
 * points, so every card stays reachable by touch, trackpad or Tab. */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper:not(.swiper-initialized) > .swiper-wrapper {
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper:not(.swiper-initialized) > .swiper-wrapper::-webkit-scrollbar {
	display: none;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper:not(.swiper-initialized) > .swiper-wrapper > .swiper-slide {
	width: calc((100% - (var(--swiper-slides-to-display, 1) - 1) * var(--swiper-slides-gap, 0px)) / var(--swiper-slides-to-display, 1));
	scroll-snap-align: start;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel:not(:has(.swiper-initialized)) .elementor-swiper-button {
	display: none;
}

/* Home journal: the "First articles are on their way" message (.journal-empty) shows only when
 * the carousel has no article (the Loop Carousel has no "nothing found" setting). The Elementor
 * editor keeps it visible so it can be edited. */
body:not(.elementor-editor-active) :is(.e-con, .e-con-inner):has(> .journal-carousel .swiper-slide) > .journal-empty {
	display: none;
}

/* Every card fits: no room kept for the dots, no arrows, dots or grab cursor. */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel.is-static,
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel:has(.swiper-slide:only-child) {
	--swiper-pagination-size: 0px;
	--swiper-pagination-spacing: 0px;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel.is-static .elementor-swiper-button,
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel.is-static .swiper-pagination {
	display: none;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel.is-static .swiper {
	cursor: auto;
}

/* Arrows: Ocean Blue circles with an Electric Blue ring and an Aqua chevron, solid Aqua on
 * hover, the site-wide Aqua focus ring. */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .elementor-swiper-button {
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border: 1.5px solid var(--e-global-color-electric);
	border-radius: 50%;
	background-color: var(--e-global-color-secondary);
	box-shadow: 0 8px 24px color-mix(in srgb, var(--e-global-color-primary) 70%, transparent);
	transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
	-webkit-tap-highlight-color: transparent;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .elementor-swiper-button:hover {
	border-color: var(--e-global-color-accent);
	background-color: var(--e-global-color-accent);
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .elementor-swiper-button:focus-visible,
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel:focus-visible,
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-pagination-bullet:focus-visible {
	outline: 2px solid var(--e-global-color-accent);
	outline-offset: 2px;
}

/* Dots: 24 px tap targets (WCAG 2.5.8) side by side, each drawing an 8 px Slate dot (6:1 or more
 * on both section backgrounds, WCAG 1.4.11), Mist on hover; the current one a 24 px Aqua bar. */
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-pagination-bullet {
	position: relative;
	display: inline-block;
	width: 24px;
	height: 24px;
	margin: 0;
	border-radius: 0;
	background: transparent;
	opacity: 1;
	vertical-align: top;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-pagination-bullet::after {
	position: absolute;
	top: 8px;
	left: 8px;
	width: 8px;
	height: 8px;
	border-radius: 4px;
	background: var(--e-global-color-slate);
	transition: left 0.2s ease, width 0.2s ease, background-color 0.2s ease;
	content: "";
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-pagination-bullet:hover {
	background: transparent;
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-pagination-bullet:hover::after {
	background: var(--e-global-color-mist);
}

.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-pagination-bullet-active::after,
.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .swiper-pagination-bullet-active:hover::after {
	left: 0;
	width: 24px;
	background: var(--e-global-color-accent);
}

@media (max-width: 767px) {
	.elementor-element.elementor-widget-loop-carousel.iotocean-carousel {
		--arrow-size: 16px;
		--arrow-prev-left-position: -20px;
		--arrow-next-right-position: -20px;
	}

	.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .elementor-swiper-button {
		width: 40px;
		height: 40px;
	}
}

/* Touch screens have no hover: keep the resting look after a tap. */
@media (hover: none) {
	.elementor-element.elementor-widget-loop-carousel.iotocean-carousel .elementor-swiper-button:hover {
		border-color: var(--e-global-color-electric);
		background-color: var(--e-global-color-secondary);
	}
}

/* Journal card: same surface, the article's featured image on top at 16:10 (as the product
 * cards; the IoTOcean mark when an article has none), Electric Blue border on hover or focus. */
.journal-card {
	height: 100%;
	transition: border-color 0.18s ease;
}

.journal-card:hover,
.journal-card:focus-within {
	border-color: var(--e-global-color-electric);
}

.journal-card .elementor-heading-title a {
	color: inherit;
}

/* Card text lines up across a row (owner, 2026-10-25).
 * Point cards (.why-point: Why IoTOcean on Home, Engineering philosophy and How we build
 * products on About) use CSS subgrid: each card spans two rows of its grid (title, text), so
 * every title in a visual row takes the height of the longest one, however many lines it
 * wraps to, and all the text below starts on the same line. The grid drops its fixed
 * "repeat(1, 1fr)" rows so the rows can be created as needed. */
/* ".elementor" in front outranks the page's own "repeat(1, 1fr)" rule (0,3,0), whatever the
 * order the stylesheets load in. */
.elementor .e-con.e-grid:has(> .why-point) {
	--e-con-grid-template-rows: none;
	grid-template-rows: none;
	grid-auto-rows: auto;
}

.e-con.e-grid > .e-con.why-point {
	display: grid;
	grid-row: span 2;
	grid-template-rows: subgrid;
	row-gap: 12px;
	align-content: start;
}

/* Journal cards sit inside Elementor's loop grid, which subgrid can't reach, so where they sit
 * side by side (tablet and up) the title keeps room for two lines (H3, 1.25 line height); a
 * longer title still shows in full. */
@media (min-width: 768px) {
	.journal-card__title .elementor-heading-title {
		min-height: calc(2 * 1.25em);
	}
}

/* Whole-card links (owner, 2026-10-25): each card keeps its one real link ("Explore <product>"
 * on product cards, the title on journal cards), stretched over the whole card, so a click
 * anywhere opens the page while keyboard and screen-reader users still meet one link per
 * card. Elementor makes inner containers and widgets position: relative, which would limit
 * the overlay to the link's own box, so inside the cards they are static and the card is the
 * positioned box. */
.product-card,
.journal-card {
	position: relative;
	cursor: pointer;
}

.product-card .e-con,
.product-card .elementor-widget,
.journal-card .e-con,
.journal-card .elementor-widget {
	--position: static;
	position: static;
}

.product-card .product-card__link a::after,
.journal-card .journal-card__title a::after {
	position: absolute;
	z-index: 1;
	inset: 0;
	border-radius: inherit;
	content: "";
}

/* CTA band (owner, 2026-10-25): an Ocean Blue to Deep Navy surface inside a 1 px Electric
 * Blue to Aqua gradient border, a chat icon above the centred title, and the buttons centred.
 * The border is two background layers (padding-box and border-box) on a transparent border,
 * so it follows the 16 px radius; the element's own Ocean Blue stays as the colour under them. */
.e-con.cta-band {
	position: relative;
	overflow: hidden;
	border: 1px solid transparent;
	background:
		linear-gradient(150deg, var(--e-global-color-secondary) 0%, color-mix(in srgb, var(--e-global-color-secondary) 45%, var(--e-global-color-primary)) 100%) padding-box,
		linear-gradient(135deg, var(--e-global-color-electric) 0%, var(--e-global-color-accent) 100%) border-box;
}

/* Centred text at a comfortable reading width. */
.cta-band .elementor-widget-text-editor {
	max-width: 640px;
}

/* The chat icon: a thin Aqua ring on a faint Aqua tint. */
.cta-band__icon .elementor-icon {
	background-color: color-mix(in srgb, var(--e-global-color-accent) 10%, transparent);
}

/* Footer link lists ([iotocean_product_list] and the Footer Site menu). */
.footer-links a {
	color: var(--e-global-color-mist);
	font-size: 15px;
	text-decoration: none;
}

.footer-links a:hover,
.footer-links a:focus-visible {
	color: var(--e-global-color-accent);
	text-decoration: underline;
}

/* "All products →" after the first four products in the footer (iotocean-core 1.1.0). */
.footer-links .iotocean-product-list__more a {
	color: var(--e-global-color-accent);
	font-weight: 600;
}

/* ---------------------------------------------------------------------------------------
 * Phase 4: products directory and product page (T060, T061, T063).
 * ------------------------------------------------------------------------------------- */

/* Product page sections whose record field is empty are hidden, so no empty heading is
 * left (FR-016c). The Elementor editor keeps them visible so they can still be edited. */
body:not(.elementor-editor-active) .product-features:not(:has(ul.iotocean-features)),
body:not(.elementor-editor-active) .product-stack:not(:has(ul.iotocean-tech-stack)),
body:not(.elementor-editor-active) .product-links:not(:has(a.iotocean-product-link)) {
	display: none;
}

/* The lists come through a Heading widget (as a div); keep the body text style. */
.product-features__list .elementor-heading-title,
.product-stack__list .elementor-heading-title {
	font-weight: 400;
}

/* Product links ([iotocean_product_link]) as Secondary buttons in a row. */
.product-links__list .elementor-shortcode {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
}

a.iotocean-product-link {
	display: inline-block;
	padding: 14px 28px;
	border: 1.5px solid var(--e-global-color-electric);
	border-radius: 999px;
	color: var(--e-global-color-text);
	font-family: var(--e-global-typography-accent-font-family), sans-serif;
	font-size: var(--e-global-typography-accent-font-size);
	font-weight: var(--e-global-typography-accent-font-weight, 600);
	line-height: 1;
	text-decoration: none;
	transition: border-color 0.18s ease;
}

a.iotocean-product-link:hover,
a.iotocean-product-link:focus-visible {
	border-color: var(--e-global-color-accent);
	color: var(--e-global-color-text);
}

/* Long description: headings inside the 760 px reading column use the H3 scale. */
.product-content .elementor-widget-theme-post-content h2,
.product-content .elementor-widget-theme-post-content h3 {
	margin: 48px 0 16px;
	color: var(--e-global-color-text);
	font-family: var(--e-global-typography-h3-font-family), sans-serif;
	font-size: var(--e-global-typography-h3-font-size);
	font-weight: var(--e-global-typography-h3-font-weight, 600);
	line-height: var(--e-global-typography-h3-line-height);
}

.product-content .elementor-widget-theme-post-content ul {
	padding-left: 1.25em;
}

/* Step diagram in a long description (2026-10-26): an ordered list with the class product-flow,
 * from "1. Title | description" lines in the copy deck (PXEfAI's provisioning workflow,
 * BACOcean's network layers). Each step is a card like the feature cards, with an Aqua
 * numbered node; an Electric Blue to Aqua line joins the nodes from one step to the next, and
 * the last step, the outcome, has an Aqua border. One column at every width, so it reads the
 * same in the reading column on a phone and on a desktop. */
.product-content ol.product-flow {
	display: grid;
	gap: 16px;
	margin: 32px 0;
	padding: 0;
	counter-reset: product-flow;
}

/* The browser's own number is hidden but kept: "list-style: none" would make Safari drop the
 * list semantics (VoiceOver no longer announces an ordered list). */
.product-content ol.product-flow li::marker {
	font-size: 0;
}

.product-content ol.product-flow li {
	position: relative;
	margin: 0;
	padding: 20px 24px 20px 80px;
	border: 1px solid var(--e-global-color-hairline);
	border-radius: 16px;
	background-color: var(--e-global-color-secondary);
	counter-increment: product-flow;
}

.product-content ol.product-flow li:last-child {
	border-color: color-mix(in srgb, var(--e-global-color-accent) 55%, transparent);
}

/* The numbered node. Its number is drawn only: the list item's own number is the one read out
 * (the second declaration, with empty alternative text, is skipped where unsupported). */
.product-content ol.product-flow li::before {
	content: counter(product-flow);
	content: counter(product-flow) / "";
	position: absolute;
	top: 20px;
	left: 24px;
	display: grid;
	place-items: center;
	box-sizing: border-box;
	width: 36px;
	height: 36px;
	border: 1.5px solid var(--e-global-color-accent);
	border-radius: 50%;
	background-color: color-mix(in srgb, var(--e-global-color-accent) 10%, var(--e-global-color-secondary));
	color: var(--e-global-color-accent);
	font-family: var(--e-global-typography-h3-font-family), sans-serif;
	font-size: 16px;
	font-weight: 600;
	line-height: 1;
}

/* The line to the next node: from under this node (20 + 36 px) through the 16 px gap to the
 * next node's top (20 px into the next card). Above the next card, which comes later. */
.product-content ol.product-flow li:not(:last-child)::after {
	content: "";
	position: absolute;
	z-index: 1;
	top: 56px;
	bottom: -36px;
	left: 41px;
	width: 2px;
	background: linear-gradient(var(--e-global-color-electric), var(--e-global-color-accent));
}

.product-content ol.product-flow strong {
	display: block;
	margin-bottom: 4px;
	color: var(--e-global-color-text);
	font-family: var(--e-global-typography-h3-font-family), sans-serif;
	font-size: 18px;
	font-weight: 600;
	line-height: 1.4;
}

.product-content ol.product-flow span {
	display: block;
	color: var(--e-global-color-mist);
}

@media (max-width: 767px) {

	.product-content ol.product-flow li {
		padding: 16px 16px 16px 64px;
	}

	.product-content ol.product-flow li::before {
		top: 16px;
		left: 16px;
		width: 32px;
		height: 32px;
		font-size: 15px;
	}

	/* 16 + 32 px under the node, then the gap and 16 px into the next card. */
	.product-content ol.product-flow li:not(:last-child)::after {
		top: 48px;
		bottom: -32px;
		left: 31px;
	}
}

/* /products/ heading: the label and H1 stay at the start; the intro is centred on the page
 * at a reading width (owner, 2026-10-25), in line with the grid below. */
.products-heading .reading-column {
	max-width: none;
}

.products-heading .products-intro {
	align-self: center;
	max-width: 760px;
}

/* Contact page heading: the label and H1 stay at the start; the intro is centred on the page
 * at a reading width (owner, 2026-10-26). */
.contact-heading .reading-column {
	max-width: none;
}

.contact-heading .contact-intro {
	align-self: center;
	max-width: 760px;
}

/* Journal heading (owner, 2026-10-25): the label and H1 stay at the start; the intro and the
 * category dropdown are centred on the page, the intro at a reading width. */
.journal-heading .reading-column {
	max-width: none;
}

.journal-heading .journal-intro {
	align-self: center;
	max-width: 760px;
}

/* The category filter spans the content width, in line with the article grid (owner). */
.journal-heading .journal-filter {
	align-self: stretch;
}

/* Category dropdown ([iotocean_category_filter], iotocean-core 1.2.0): the label, then a
 * pill select in the secondary button style filling the rest of the line, with a chevron
 * drawn in the label colour. Up to 600 px the label sits above a full-width select. */
.iotocean-category-filter {
	display: flex;
	align-items: center;
	gap: 16px;
	width: 100%;
}

.iotocean-category-filter label {
	flex: none;
	color: var(--e-global-color-slate);
	font-size: 15px;
	white-space: nowrap;
}

.iotocean-category-filter__control {
	position: relative;
	display: block;
	flex: 1 1 auto;
	min-width: 0;
}

.iotocean-category-filter__control::after {
	position: absolute;
	top: 50%;
	right: 18px;
	width: 8px;
	height: 8px;
	border-right: 2px solid var(--e-global-color-accent);
	border-bottom: 2px solid var(--e-global-color-accent);
	transform: translateY(-70%) rotate(45deg);
	content: "";
	pointer-events: none;
}

.iotocean-category-filter select {
	width: 100%;
	padding: 10px 44px 10px 20px;
	border: 1.5px solid var(--e-global-color-electric);
	border-radius: 999px;
	background-color: var(--e-global-color-secondary);
	color: var(--e-global-color-text);
	font-family: inherit;
	font-size: 16px;
	cursor: pointer;
	appearance: none;
}

.iotocean-category-filter select:hover,
.iotocean-category-filter select:focus-visible {
	border-color: var(--e-global-color-accent);
}

@media (max-width: 600px) {
	.iotocean-category-filter {
		flex-direction: column;
		align-items: stretch;
		gap: 8px;
	}

	.iotocean-category-filter__control {
		flex: none;
	}
}

.iotocean-category-filter__links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 8px 20px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Journal post meta: date and reading time on one line, in Slate (T073). */
.journal-meta .elementor-heading-title,
.journal-reading-time p {
	margin: 0;
}

.journal-image img {
	width: 100%;
	border-radius: 16px;
}

/* Article and default page body in the reading column: headings, lists, quotes and code (T073, T074). */
.journal-content h2,
.journal-content h3 {
	margin: 48px 0 16px;
	color: var(--e-global-color-text);
	font-family: var(--e-global-typography-h3-font-family), sans-serif;
	font-size: var(--e-global-typography-h3-font-size);
	font-weight: var(--e-global-typography-h3-font-weight, 600);
	line-height: var(--e-global-typography-h3-line-height);
}

.journal-content ul,
.journal-content ol {
	margin: 0 0 24px;
	padding-left: 1.25em;
}

.journal-content li + li {
	margin-top: 8px;
}

.journal-content strong {
	color: var(--e-global-color-text);
}

.journal-content a,
.contact-privacy-note a {
	color: var(--e-global-color-accent);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.journal-content blockquote {
	margin: 32px 0;
	padding: 4px 0 4px 24px;
	border-left: 3px solid var(--e-global-color-electric);
	color: var(--e-global-color-text);
}

.journal-content code {
	padding: 2px 6px;
	border-radius: 4px;
	background-color: var(--e-global-color-secondary);
	color: var(--e-global-color-text);
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.9em;
}

.journal-content pre {
	overflow-x: auto;
	margin: 0 0 24px;
	padding: 20px 24px;
	border: 1px solid var(--e-global-color-hairline);
	border-radius: 8px;
	background-color: var(--e-global-color-secondary);
}

.journal-content pre code {
	padding: 0;
	background: none;
}

/* Previous and next links under an article. */
.journal-nav {
	padding-top: 32px;
	border-top: 1px solid var(--e-global-color-hairline);
}

/* Contact form: help text under the label, inside the label element (T076). Elementor adds the
 * required mark as the label's ::after, which would land after the help text; as flex items the
 * mark keeps its place right after the field name and the help text wraps to its own line. */
.contact-form .elementor-field-label:has(.field-help) {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
}

.contact-form .field-help {
	display: block;
	flex-basis: 100%;
	order: 1;
	margin-top: 4px;
	color: var(--e-global-color-mist);
	font-family: var(--e-global-typography-small-font-family), sans-serif;
	font-size: var(--e-global-typography-small-font-size);
	font-weight: 400;
}

/* Field errors from the server: kept visible next to the field. */
.contact-form .elementor-field-group .elementor-message-danger {
	margin-top: 8px;
	color: var(--e-global-color-text);
}

.contact-form .elementor-field-group:has(.elementor-message-danger) .elementor-field {
	border-color: var(--e-global-color-accent);
}

/* Reduced motion: no animation or transition, and content visible straight away. */
@media (prefers-reduced-motion: reduce) {

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}

	.elementor-invisible {
		visibility: visible !important;
		opacity: 1 !important;
	}
}

/* ---------------------------------------------------------------------------------------
 * Back to top (prompts/scroll_to_top_prompt.md): a 48 px circle at the bottom right, in the
 * secondary button style (Ocean Blue, Electric Blue ring, Aqua arrow; solid Aqua on hover).
 * Hidden until 300 px of scrolling (assets/js/back-to-top.js adds .is-visible), with a short
 * fade and lift. "body button" outranks the Kit's ".elementor-kit-7 button" rules. The site
 * has no cookie banner or bottom bar, so the corner is free; safe-area insets keep it clear
 * of the iPhone home indicator. Focus uses the site-wide Aqua ring.
 * --------------------------------------------------------------------------------------- */
body button.back-to-top {
	position: fixed;
	right: calc(24px + env(safe-area-inset-right, 0px));
	bottom: calc(24px + env(safe-area-inset-bottom, 0px));
	z-index: 999;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 48px;
	height: 48px;
	padding: 0;
	border: 1.5px solid var(--e-global-color-electric);
	border-radius: 50%;
	background-color: var(--e-global-color-secondary);
	box-shadow: 0 8px 24px color-mix(in srgb, var(--e-global-color-primary) 70%, transparent);
	color: var(--e-global-color-accent);
	cursor: pointer;
	opacity: 0;
	visibility: hidden;
	transform: translateY(8px);
	transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s, background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
	-webkit-tap-highlight-color: transparent;
}

body button.back-to-top.is-visible {
	opacity: 1;
	visibility: visible;
	transform: none;
	transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s, background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}

body button.back-to-top:focus {
	border-color: var(--e-global-color-electric);
	background-color: var(--e-global-color-secondary);
	color: var(--e-global-color-accent);
}

body button.back-to-top:hover {
	border-color: var(--e-global-color-accent);
	background-color: var(--e-global-color-accent);
	color: var(--e-global-color-primary);
}

body button.back-to-top svg {
	display: block;
}

@media (max-width: 767px) {
	body button.back-to-top {
		right: calc(16px + env(safe-area-inset-right, 0px));
		bottom: calc(16px + env(safe-area-inset-bottom, 0px));
		width: 44px;
		height: 44px;
	}
}

/* Touch screens have no hover: keep the resting look after a tap. */
@media (hover: none) {
	body button.back-to-top:hover {
		border-color: var(--e-global-color-electric);
		background-color: var(--e-global-color-secondary);
		color: var(--e-global-color-accent);
	}
}

/* WCAG 2.4.11: the footer's bottom bar ends before the button's corner, so a focused link
 * there (Privacy Policy, at the far right) is never covered by the button. */
.e-con.footer-bottom {
	padding-inline-end: 72px;
}

@media (max-width: 767px) {
	.e-con.footer-bottom {
		padding-inline-end: 60px;
	}
}

/* The 300 px marker the script observes (no box of its own on screen). */
.back-to-top-marker {
	position: absolute;
	top: 0;
	left: 0;
	width: 1px;
	pointer-events: none;
}

/* The main landmark (functions.php) takes focus from the "Skip to content" link; the focus
 * lands on the whole page region, so no ring is drawn around it. */
.iotocean-main:focus {
	outline: none;
}
