/**
 * Orion Global Trade — front end.
 *
 * =============================================================================
 * FROZEN SELECTORS — the plugin's inquiry.js binds to these by name.
 * Restyle them freely; NEVER rename them. Breaking one means the modal silently
 * stops opening, with no console error.
 *
 *   #ogt-inquiry-modal   #ogt-inquiry-form   #ogt-newsletter-form
 *   .ogt-modal__panel    .ogt-form__status   .ogt-form__submit
 *   .ogt-newsletter__status                 .has-error
 *   [data-ogt-open-inquiry]  [data-ogt-close-inquiry]
 *   body.ogt-modal-open      .is-open
 *   .ogt-hp  — honeypot. Must stay visually hidden but NOT display:none:
 *            some bots treat display:none as a tell, and screen readers skip it.
 * =============================================================================
 *
 * There are no brand colours, font names or sizes in this file. They arrive as
 * custom properties from inc/tokens.php (settings-derived) and tokens.css
 * (static). If you are about to type a hex value here, add a setting instead.
 * The only literals are neutral scaffolding and WhatsApp's brand green, which
 * WhatsApp fixes, not us.
 *
 * Layer order is declared once, below. Note the UNLAYERED TAIL at the very
 * bottom: unlayered CSS always beats layered CSS regardless of source order, and
 * WordPress core block styles are unlayered. Block-editor overrides must live
 * there or core silently outranks every component here.
 *
 * Naming:
 *   .ogt-block__element--modifier   structure  (CSS owns this)
 *   .is-* / .has-*                 state      (JS → CSS channel)
 *   [data-ogt-*]                    behaviour  (JS owns this; CSS never styles it)
 *   .ogt-u-*                        utility
 */

@layer reset, base, layout, components, utilities;

/* ============================================================================
   RESET
   ========================================================================== */

@layer reset {

	*,
	*::before,
	*::after {
		box-sizing: border-box;
	}

	html {
		-webkit-text-size-adjust: 100%;
		scroll-behavior: smooth;
		/* The whole rem scale — spacing included — hangs off this. */
		font-size: var(--ogt-base);
		scroll-padding-top: calc(var(--ogt-header-h) + 1rem);
	}

	/*
	 * The single most valuable accessibility block in the file. It neutralises
	 * every transition and animation defined below, so no component needs to
	 * think about reduced motion individually.
	 */
	@media (prefers-reduced-motion: reduce) {

		html {
			scroll-behavior: auto;
		}

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

	body {
		margin: 0;
		background: var(--ogt-ground);
		color: var(--ogt-text);
		font-family: var(--ogt-font-body);
		font-size: var(--ogt-fs-base);
		line-height: var(--ogt-lh);
		-webkit-font-smoothing: antialiased;
		-moz-osx-font-smoothing: grayscale;

		/*
		 * clip, NOT hidden. `overflow-x: hidden` forces overflow-y to compute
		 * to auto, which makes <body> a scroll container — but the page
		 * actually scrolls on <html>, so body's scrollHeight equals its
		 * clientHeight and it never moves. Every `position: sticky`
		 * descendant then resolves against that frozen scrollport and can
		 * never activate. Measured: the article sidebar, and the asides on
		 * the contact, product and service templates, all sat still.
		 *
		 * `clip` blocks the same horizontal overflow without establishing a
		 * scroll container, so sticky resolves against <html> and works.
		 * Unsupported in Safari < 16, where the declaration is simply dropped
		 * — no horizontal guard, which the layout audit says nothing needs.
		 */
		overflow-x: clip;
	}

	img,
	svg,
	video,
	iframe {
		max-width: 100%;
		height: auto;
		display: block;
	}

	/* <figure> is normalised in the unlayered tail — see the note there. It cannot
	   be done from inside a layer. */

	button,
	input,
	select,
	textarea {
		font: inherit;
		color: inherit;
	}
}

/* ============================================================================
   BASE — element defaults
   ========================================================================== */

@layer base {

	h1, h2, h3, h4, h5, h6 {
		margin: 0;
		font-family: var(--ogt-font-head);
		font-weight: var(--ogt-head-weight);
		letter-spacing: var(--ogt-head-track);
		line-height: var(--ogt-lh-tight);
		color: var(--ogt-ink);
		text-wrap: balance;
	}

	h1 { font-size: var(--ogt-fs-h1); }
	h2 { font-size: var(--ogt-fs-h2); }
	h3 { font-size: var(--ogt-fs-h3); }
	h4 { font-size: var(--ogt-fs-h4); }

	p { margin: 0; }

	a {
		color: var(--ogt-primary);
		text-underline-offset: 0.18em;
	}

	a:hover { color: var(--ogt-primary-hover); }

	/* Visible, generous, and never removed. */
	:focus-visible {
		outline: 2px solid var(--ogt-primary);
		outline-offset: 3px;
		border-radius: var(--ogt-radius-sm);
	}

	::selection {
		background: rgb(var(--ogt-primary-rgb) / 15%);
	}

	.screen-reader-text {
		position: absolute !important;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
		border: 0;
	}

	.ogt-skip {
		position: absolute;
		left: -9999px;
		top: 0;
		z-index: var(--ogt-z-skip);
		padding: 0.75rem 1.25rem;
		background: var(--ogt-primary);
		color: var(--ogt-on-primary);
		border-radius: 0 0 var(--ogt-radius-sm) 0;
		text-decoration: none;
		font-weight: 600;
	}

	.ogt-skip:focus {
		left: 0;
		top: 0;
		color: var(--ogt-on-primary);
	}

	/* Icons inherit colour and take their stroke from the token. */
	.ogt-icon {
		stroke-width: var(--ogt-icon-stroke);
		flex: 0 0 auto;
	}
}

/* ============================================================================
   LAYOUT
   ========================================================================== */

@layer layout {

	.ogt-wrap {
		width: min(100% - (var(--ogt-gutter) * 2), var(--ogt-container));
		margin-inline: auto;
	}

	.ogt-wrap--wide {
		max-width: var(--ogt-container-wide);
	}

	.ogt-wrap--narrow {
		max-width: 820px;
	}

	.ogt-section {
		padding-block: var(--ogt-section-y);
	}

	.ogt-section--sm { padding-block: var(--ogt-section-y-sm); }
	.ogt-section--flush-top { padding-block-start: 0; }
	.ogt-section--flush-bottom { padding-block-end: 0; }

	/* Alternating grounds. --dark re-themes its descendants via the card chassis. */
	.ogt-section--muted { background: var(--ogt-muted); }
	.ogt-section--surface { background: var(--ogt-surface); }

	.ogt-section--dark {
		background: var(--ogt-deep);
		color: var(--ogt-on-dark-muted);
	}

	/* The --dark text overrides are NOT here. They set the same properties as
	   rules in the components layer, and layer order beats specificity, so a
	   layout-layer override loses no matter how specific it is. They live at
	   the end of the components layer instead — search "dark-ground text". */

	/*
	 * Generic gradient surface. Currently unused — the hero, CTA and newsletter
	 * each carry their own version — but kept as the shared definition, and kept
	 * on the SAME vivid ramp as those three so a future section that reaches for
	 * it does not reintroduce the near-black gradient the others were moved off.
	 */
	.ogt-section--gradient {
		position: relative;
		background:
			radial-gradient(65% 85% at 88% 88%, rgb(var(--ogt-accent-rgb) / 30%) 0%, transparent 62%),
			radial-gradient(60% 80% at 8% 4%, rgb(255 255 255 / 12%) 0%, transparent 58%),
			linear-gradient(125deg, var(--ogt-primary-hover) 0%, var(--ogt-primary) 45%, var(--ogt-secondary) 100%);
		color: var(--ogt-on-dark-muted);
		isolation: isolate;
	}

	/*
	 * Prose — long-form editor content.
	 *
	 * 68ch is the readable measure, but a 68ch column pinned to the left of a
	 * 1280px container leaves ~600px of dead space beside it, which is what the
	 * About page looked like. Centre it by default; the split layouts below opt
	 * out, because there the column sits beside something.
	 */
	.ogt-prose {
		max-width: 68ch;
		margin-inline: auto;
		display: flex;
		flex-direction: column;
		gap: var(--ogt-para-space);
	}

	/* Inside a split or a card, prose is one column of two — do not centre it.
	   The product page's main column is the same situation: centring pushed its
	   copy 63px right of the page h1 it sits under, so nothing lined up. */
	.ogt-u-split .ogt-prose,
	.ogt-card .ogt-prose,
	.ogt-product__main .ogt-prose {
		margin-inline: 0;
		max-width: none;
	}

	/*
	 * When prose IS the page container — page.php and page-about.php put both
	 * .ogt-wrap and .ogt-prose on one element — fill the container like the
	 * homepage instead of collapsing to a 68ch strip with dead side margins
	 * (the "not the same container as homepage" complaint). The container width
	 * comes back; only the text RUNS keep a readable measure (left-anchored), so
	 * grids, columns, wide blocks and the product shortcode span full width.
	 *
	 * Scoped to .ogt-wrap.ogt-prose so a standalone reading column (single.php's
	 * blog prose, nested inside .ogt-wrap--narrow) still gets its 68ch measure.
	 */
	.ogt-wrap.ogt-prose { max-width: none; }

	.ogt-wrap.ogt-prose > :not(.alignwide):not(.alignfull):not(.wp-block-columns):not(.ogt-prose__bleed):not([class*="ogt-u-grid"]) {
		max-width: 72ch;
	}

	/* Explicit full-container breakout for shortcode/grid output inside prose. */
	.ogt-prose__bleed { max-width: none; width: 100%; }

	.ogt-prose > h2 { margin-block-start: 1.5em; }
	.ogt-prose > h3 { margin-block-start: 1em; }
	.ogt-prose > :first-child { margin-block-start: 0; }

	.ogt-prose ul,
	.ogt-prose ol {
		margin: 0;
		padding-inline-start: 1.3rem;
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}

	.ogt-prose img { border-radius: var(--ogt-radius-img); }
}

/* ============================================================================
   UTILITIES
   ========================================================================== */

@layer utilities {

	/*
	 * One auto-fit grid replaces the five bespoke grid classes the old CSS had
	 * (cards-grid, offer-grid, quotes, certgrid, statband__grid).
	 */
	.ogt-u-grid {
		display: grid;
		gap: var(--ogt-gap);
		grid-template-columns: repeat(auto-fit, minmax(min(var(--min, 260px), 100%), 1fr));
	}

	.ogt-u-grid--min-200 { --min: 200px; }
	.ogt-u-grid--min-240 { --min: 240px; }
	.ogt-u-grid--min-280 { --min: 280px; }
	.ogt-u-grid--min-320 { --min: 320px; }

	/*
	 * A fixed three-up, for content where the column count is a design decision
	 * rather than whatever happens to fit.
	 *
	 * auto-fit is right when the item count varies and you just want a tidy
	 * wrap. It is wrong for a set of six: at a 1200px container minmax(240px)
	 * resolves to four columns, so six items land as 4 + 2 — a full row and a
	 * stranded pair. Three gives 3 + 3.
	 *
	 * minmax(0, 1fr) rather than 1fr: a grid track's default minimum is
	 * auto, so one long unbreakable word (a botanical name, an HS code) can push
	 * a column wider than its share and make the row unequal.
	 */
	.ogt-u-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

	@media (max-width: 1000px) {
		.ogt-u-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	}

	@media (max-width: 640px) {
		.ogt-u-grid--3 { grid-template-columns: minmax(0, 1fr); }
	}

	/*
	 * auto-fill keeps the empty tracks that auto-fit collapses, so a grid with a
	 * single item leaves it at column width instead of stretching it across the
	 * whole row. Used by search/blog results, where a 1-result page otherwise
	 * rendered one enormous full-width card.
	 */
	.ogt-u-grid--fill {
		grid-template-columns: repeat(auto-fill, minmax(min(var(--min, 260px), 100%), 1fr));
		justify-content: start;
	}

	/*
	 * Equal card height in a row.
	 *
	 * .ogt-u-grid relies on the default align-items:stretch, so a card that is a
	 * direct grid child already fills its row. But where a card is wrapped (the
	 * product archive wraps each in a data-ogt-reveal div for the stagger), the
	 * WRAPPER stretches and the card inside sits at content height. Make the
	 * wrapper a flex column and let the card fill it, so tiles line up whether or
	 * not they are wrapped.
	 */
	/*
	 * :not(.ogt-card) is essential. This rule exists for a plain WRAPPER div that
	 * holds a card — but some templates put data-ogt-reveal on the card itself
	 * (the certification list does). Without the guard this forced those cards to
	 * flex-direction:column and stretched their children, which silently turned
	 * the row-layout cert card into a centred stack with an orphaned tick.
	 */
	.ogt-u-grid > [data-ogt-reveal]:not(.ogt-card),
	.ogt-u-cols-2 > [data-ogt-reveal]:not(.ogt-card),
	.ogt-u-cols-3 > [data-ogt-reveal]:not(.ogt-card),
	.ogt-u-cols-4 > [data-ogt-reveal]:not(.ogt-card) {
		display: flex;
		flex-direction: column;
	}

	.ogt-u-grid > [data-ogt-reveal]:not(.ogt-card) > *,
	.ogt-u-cols-2 > [data-ogt-reveal]:not(.ogt-card) > *,
	.ogt-u-cols-3 > [data-ogt-reveal]:not(.ogt-card) > *,
	.ogt-u-cols-4 > [data-ogt-reveal]:not(.ogt-card) > * { flex: 1 1 auto; }

	.ogt-u-grid > .ogt-card,
	.ogt-u-cols-2 > .ogt-card,
	.ogt-u-cols-3 > .ogt-card,
	.ogt-u-cols-4 > .ogt-card,
	.ogt-u-grid > [data-ogt-reveal] > .ogt-card,
	.ogt-u-cols-2 > [data-ogt-reveal] > .ogt-card,
	.ogt-u-cols-3 > [data-ogt-reveal] > .ogt-card,
	.ogt-u-cols-4 > [data-ogt-reveal] > .ogt-card { height: 100%; }

	/* Fixed column counts, collapsing on small screens. */
	.ogt-u-cols-2,
	.ogt-u-cols-3,
	.ogt-u-cols-4 {
		display: grid;
		gap: var(--ogt-gap);
	}

	.ogt-u-cols-2 { grid-template-columns: repeat(2, 1fr); }
	.ogt-u-cols-3 { grid-template-columns: repeat(3, 1fr); }
	.ogt-u-cols-4 { grid-template-columns: repeat(4, 1fr); }

	@media (max-width: 900px) {
		.ogt-u-cols-3,
		.ogt-u-cols-4 { grid-template-columns: repeat(2, 1fr); }
	}

	@media (max-width: 560px) {
		.ogt-u-cols-2,
		.ogt-u-cols-3,
		.ogt-u-cols-4 { grid-template-columns: 1fr; }
	}

	/* Split layouts — the alternating image/text rhythm. */
	.ogt-u-split {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: clamp(2rem, 5vw, var(--ogt-gap-lg));
		align-items: center;
	}

	.ogt-u-split--wide-left { grid-template-columns: 1.15fr 0.85fr; }
	.ogt-u-split--wide-right { grid-template-columns: 0.85fr 1.15fr; }

	@media (max-width: 900px) {
		.ogt-u-split,
		.ogt-u-split--wide-left,
		.ogt-u-split--wide-right { grid-template-columns: 1fr; }
	}

	/* Reverse visual order without touching DOM order (keeps reading order sane). */
	@media (min-width: 901px) {
		.ogt-u-split--reverse > :first-child { order: 2; }
	}

	.ogt-u-stack {
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap);
		align-items: flex-start;
	}

	.ogt-u-stack--sm { gap: var(--ogt-gap-sm); }
	.ogt-u-stack--center { align-items: center; text-align: center; }

	.ogt-u-row {
		display: flex;
		flex-wrap: wrap;
		gap: var(--ogt-gap-sm);
		align-items: center;
	}

	/* Anything wide must scroll inside itself, never the page. */
	.ogt-u-scroll-x {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
	}

	/*
	 * The utilities below exist because the same eight declarations were being
	 * repeated as inline style="" attributes across 22 call sites. Inline styles
	 * cannot be kept consistent, cannot be overridden by a modifier, and cannot
	 * be audited — which is exactly how the spacing drifted apart.
	 */

	/* Strip list chrome. A <ul> used for layout still needs semantics. */
	.ogt-u-bare {
		list-style: none;
		margin: 0;
		padding: 0;
	}

	.ogt-u-full { width: 100%; }

	/* Rhythm helpers — the ONLY sanctioned way to add space outside a section. */
	.ogt-u-mb { margin-block-end: var(--ogt-gap-lg); }
	.ogt-u-mt { margin-block-start: var(--ogt-gap-lg); }

	/*
	 * Top-align a split. The default is centre, which is right for image+text
	 * but wrong whenever one column is much taller than the other.
	 */
	.ogt-u-split--top { align-items: start; }

	/* A column that follows the reader, clearing the sticky header. */
	.ogt-u-sticky {
		position: sticky;
		top: calc(var(--ogt-header-h) + var(--ogt-space-4));
	}

	@media (max-width: 960px) {
		.ogt-u-sticky { position: static; }
	}

	/* A sub-heading inside a section — smaller than the section title. */
	.ogt-u-h4 {
		font-size: var(--ogt-fs-h4);
		font-family: var(--ogt-font-head);
		font-weight: var(--ogt-head-weight);
		letter-spacing: var(--ogt-head-track);
		line-height: var(--ogt-lh-tight);
		color: var(--ogt-ink);
	}

	.ogt-section--dark .ogt-u-h4,
	.ogt-card--dark .ogt-u-h4 { color: var(--ogt-on-dark); }

	/* --- scroll reveal ---
	 * Gated on html.js, set by an inline script before paint. Without that gate,
	 * a JS failure would hide the entire page. */
	html.js [data-ogt-reveal] {
		opacity: 0;
		transform: translateY(12px);
	}

	html.js [data-ogt-reveal].is-in {
		opacity: 1;
		transform: none;
		transition:
			opacity var(--ogt-dur-slow) var(--ogt-ease-out),
			transform var(--ogt-dur-slow) var(--ogt-ease-out);
	}

	/* --- skeleton --- */
	.ogt-skeleton {
		background: linear-gradient(
			90deg,
			var(--ogt-muted) 25%,
			rgb(var(--ogt-ink-rgb) / 5%) 37%,
			var(--ogt-muted) 63%
		);
		background-size: 400% 100%;
		animation: ogt-shimmer 1.4s ease infinite;
		border-radius: var(--ogt-radius-sm);
	}

	@keyframes ogt-shimmer {
		0% { background-position: 100% 50%; }
		100% { background-position: 0 50%; }
	}
}

/* ============================================================================
   COMPONENTS
   ========================================================================== */

@layer components {

	/* ------------------------------------------------------------------ CARD
	 * The chassis. Every card-shaped component composes this and re-themes via
	 * LOCAL custom properties rather than re-declaring rules — which is why a
	 * --dark variant costs five lines instead of ninety, and why the two
	 * parallel testimonial-card systems this replaces cannot re-fork.
	 */

	.ogt-card {
		--card-bg: var(--ogt-surface);
		--card-fg: var(--ogt-text);
		--card-head: var(--ogt-ink);
		--card-bd: var(--ogt-border);
		--card-pad: var(--ogt-card-pad);
		--card-radius: var(--ogt-radius-card);
		--card-shadow: var(--ogt-shadow-sm);
		--card-shadow-hover: var(--ogt-shadow-lg);
		--card-accent: var(--ogt-primary);

		position: relative;
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap-sm);
		background: var(--card-bg);
		color: var(--card-fg);
		border: 1px solid var(--card-bd);
		border-radius: var(--card-radius);
		padding: var(--card-pad);
		box-shadow: var(--card-shadow);
		transition:
			transform var(--ogt-dur) var(--ogt-ease-out),
			box-shadow var(--ogt-dur) var(--ogt-ease-out),
			border-color var(--ogt-dur) var(--ogt-ease-out);
	}

	.ogt-card h3,
	.ogt-card h4 { color: var(--card-head); }

	.ogt-card--dark {
		--card-bg: var(--ogt-on-dark-surface);
		--card-fg: var(--ogt-on-dark-muted);
		--card-head: var(--ogt-on-dark);
		--card-bd: var(--ogt-on-dark-border);
		--card-shadow: none;
		--card-shadow-hover: none;
		--card-accent: var(--ogt-accent);
		backdrop-filter: blur(6px);
	}

	.ogt-card--flat {
		--card-shadow: none;
		--card-shadow-hover: none;
	}

	.ogt-card--ghost {
		--card-bg: transparent;
		--card-bd: transparent;
		--card-shadow: none;
		--card-pad: 0;
	}

	/*
	 * Media-led card: the image runs to the card edge, and the body carries its
	 * own padding. Replaces style="--card-pad:0" at the call sites.
	 */
	.ogt-card--bleed {
		--card-pad: 0;
		overflow: hidden;
	}

	/* A card whose content is laid out as a row rather than a stack. */
	.ogt-card--row {
		flex-direction: row;
		align-items: center;
	}

	/*
	 * Interactive cards.
	 *
	 * There used to be a three-pixel navy-to-gold gradient that wiped across
	 * the top edge on hover. It was removed because it is the single most
	 * recognisable template flourish on the web: the same wiping gradient rule
	 * ships on thousands of generated and off-the-shelf themes, and a buyer who
	 * has seen it on ten other supplier sites reads it as "this site came out
	 * of a box" — which is the opposite of what a card describing a graded
	 * commodity should say.
	 *
	 * The affordance is now carried by three quieter signals working together:
	 * the lift, a deeper shadow, and the border warming to the brand navy. None
	 * of them is decorative, and the last one is the load-bearing part, because
	 * a border change reads at a glance even for someone who cannot perceive
	 * the shadow.
	 */
	.ogt-card--interactive {
		position: relative;
		overflow: hidden;
	}

	.ogt-card--interactive:hover,
	.ogt-card--interactive:focus-within {
		transform: translateY(var(--ogt-lift));
		box-shadow: var(--card-shadow-hover);
		border-color: rgb(var(--ogt-primary-rgb) / 45%);
	}

	/* The icon tile answers the hover too, so the whole card feels connected. */
	.ogt-card--interactive .ogt-icon-tile {
		transition:
			background-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease),
			transform var(--ogt-dur-slow) var(--ogt-ease-spring);
	}

	.ogt-card--interactive:hover .ogt-icon-tile {
		background: var(--ogt-primary);
		color: var(--ogt-on-primary);
		transform: translateY(-2px) rotate(-6deg);
	}

	.ogt-card--dark.ogt-card--interactive:hover {
		border-color: rgb(var(--ogt-accent-rgb) / 45%);
	}

	/* Stretch a link over the whole card without nesting interactive elements. */
	.ogt-card__link::after {
		content: "";
		position: absolute;
		inset: 0;
		border-radius: inherit;
	}

	.ogt-card__link {
		text-decoration: none;
		color: inherit;
	}

	/* The tinted icon tile every card type shares. */
	.ogt-card__icon,
	.ogt-icon-tile {
		display: grid;
		place-items: center;
		width: 48px;
		height: 48px;
		border-radius: var(--ogt-radius-sm);
		background: rgb(var(--ogt-primary-rgb) / 9%);
		color: var(--card-accent, var(--ogt-primary));
		flex: 0 0 auto;
	}

	.ogt-card--dark .ogt-icon-tile,
	.ogt-section--dark .ogt-icon-tile {
		background: rgb(255 255 255 / 10%);
		color: var(--ogt-accent);
	}

	.ogt-icon-tile--sm { width: 36px; height: 36px; }
	.ogt-icon-tile--lg { width: 56px; height: 56px; }

	.ogt-icon-tile--round { border-radius: 50%; }

	.ogt-icon-tile--solid {
		background: var(--ogt-primary);
		color: var(--ogt-on-primary);
	}

	/*
	 * For use on the deep/gradient grounds, where the tinted primary tile has no
	 * contrast to work against. Replaces an inline rgb() blob in the partners
	 * banner.
	 */
	.ogt-icon-tile--on-dark {
		background: rgb(255 255 255 / 12%);
		border: 1px solid var(--ogt-on-dark-border);
		color: var(--ogt-on-dark);
	}

	/* ---------------------------------------------------------------- BUTTON */

	.ogt-btn {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 0.5rem;
		padding: 0.85rem 1.5rem;
		border: 1px solid transparent;
		border-radius: var(--ogt-radius-pill);
		font-family: var(--ogt-font-head);
		font-weight: 600;
		font-size: 0.9375rem;
		line-height: 1.2;
		text-decoration: none;
		white-space: nowrap;
		cursor: pointer;
		transition:
			background-color var(--ogt-dur) var(--ogt-ease),
			border-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease),
			box-shadow var(--ogt-dur) var(--ogt-ease),
			transform var(--ogt-dur-fast) var(--ogt-ease);
	}

	.ogt-btn:active { transform: translateY(1px); }

	/* The arrow nudges; other icons stay put. */
	.ogt-btn:hover .ogt-icon--arrow,
	.ogt-btn:hover .ogt-icon--arrow-up-right { transform: translateX(3px); }
	.ogt-btn .ogt-icon { transition: transform var(--ogt-dur) var(--ogt-ease-out); }

	.ogt-btn--primary {
		background: var(--ogt-primary);
		color: var(--ogt-on-primary);
		box-shadow: var(--ogt-shadow-sm);
	}

	.ogt-btn--primary:hover {
		background: var(--ogt-primary-hover);
		color: var(--ogt-on-primary);
		box-shadow: var(--ogt-shadow-md);
	}

	.ogt-btn--secondary {
		background: var(--ogt-secondary);
		color: var(--ogt-on-secondary);
	}

	/* Hover darkens secondary in its OWN hue — not a jump to primary, which read
	   as the button changing identity mid-interaction. */
	.ogt-btn--secondary:hover {
		background: var(--ogt-secondary-hover);
		color: var(--ogt-on-secondary);
	}

	.ogt-btn--ghost {
		background: transparent;
		color: var(--ogt-primary);
		border-color: var(--ogt-border);
	}

	.ogt-btn--ghost:hover {
		background: rgb(var(--ogt-primary-rgb) / 6%);
		border-color: rgb(var(--ogt-primary-rgb) / 35%);
		color: var(--ogt-primary);
	}

	/* Translucent — for use on the deep/gradient grounds. */
	.ogt-btn--glass {
		background: rgb(255 255 255 / 10%);
		color: #fff;
		border-color: rgb(255 255 255 / 26%);
		backdrop-filter: blur(6px);
	}

	.ogt-btn--glass:hover {
		background: rgb(255 255 255 / 18%);
		color: #fff;
	}

	/*
	 * WhatsApp green is fixed by WhatsApp's brand guidelines, not our palette —
	 * same two colours as the floating button below, so the site has one
	 * WhatsApp treatment rather than several.
	 *
	 * The label is near-black on purpose: measured, #04310F on #25D366 is
	 * 7.30:1, where the obvious white-on-green is 1.98:1 and fails badly.
	 */
	.ogt-btn--whatsapp {
		background: #25d366;
		color: #04310f;
		border: 1px solid transparent;
	}

	.ogt-btn--whatsapp:hover {
		background: #1ebe5a;   /* 6.05:1 with the same label colour */
		color: #04310f;
	}

	/* Inverted — a light pill on a deep ground. */
	.ogt-btn--on-dark {
		background: #fff;
		color: var(--ogt-deep);
	}

	/* On a deep ground the white pill fills with primary and its text goes white
	   on hover — a solid, high-contrast, on-brand press. The old hover flipped to
	   accent cyan, which left the button palette and computed to borderline
	   contrast. (A translucent tint would read dark-on-dark here, so it must be a
	   solid fill.) */
	.ogt-btn--on-dark:hover {
		background: var(--ogt-primary);
		color: var(--ogt-on-primary);
	}

	.ogt-btn--sm { padding: 0.6rem 1.1rem; font-size: 0.86rem; }
	.ogt-btn--lg { padding: 1rem 1.85rem; font-size: 1rem; }
	.ogt-btn--block { width: 100%; }

	.ogt-btn[disabled],
	.ogt-btn[aria-disabled="true"] {
		opacity: 0.55;
		pointer-events: none;
	}

	/* ------------------------------------------------------------- EYEBROW */

	/*
	 * Section eyebrow.
	 *
	 * Was a bare line of small caps with a hairline rule — legible, but it read
	 * as a caption rather than a deliberate label, and it was the first thing on
	 * every section, so every section opened flat. A tinted pill with a live
	 * status dot gives each section a confident start and one small point of
	 * motion above the fold.
	 */
	/*
	 * Section eyebrow — a quiet tinted pill.
	 *
	 * align-self/width are load-bearing, not cosmetic: .ogt-secthead is a flex
	 * column, so a flex item stretches across the whole column by default and
	 * `display: inline-flex` does NOT prevent it. Without these the pill ran the
	 * full width of the section and read as a broken bar.
	 */
	.ogt-eyebrow {
		display: inline-flex;
		align-self: flex-start;
		width: fit-content;
		max-width: 100%;
		align-items: center;
		gap: 0.5rem;
		padding: 0.35rem 0.8rem 0.35rem 0.65rem;
		border-radius: var(--ogt-radius-pill);
		background: rgb(var(--ogt-primary-rgb) / 7%);
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-xs);
		font-weight: 700;
		letter-spacing: 0.1em;
		text-transform: var(--ogt-eyebrow-transform);
		color: var(--ogt-primary);
	}

	/* Centred section headers centre their own children. */
	.ogt-secthead--center .ogt-eyebrow { align-self: center; }

	/* A static marker dot. It was a pulsing halo; with an eyebrow on every
	   section that meant five things blinking at once, which is noise. */
	.ogt-eyebrow::before {
		content: "";
		flex: 0 0 auto;
		width: 6px;
		height: 6px;
		border-radius: 50%;
		background: currentColor;
	}

	.ogt-section--dark .ogt-eyebrow,
	.ogt-hero .ogt-eyebrow {
		background: rgb(255 255 255 / 10%);
		color: var(--ogt-accent);
	}

	/* The highlighted phrase inside a split heading. */
	.ogt-hl { color: var(--ogt-secondary); }
	.ogt-section--dark .ogt-hl,
	.ogt-section--gradient .ogt-hl,
	.ogt-hero .ogt-hl { color: var(--ogt-accent); }

	/* --------------------------------------------------- 2. SECTION HEADER */

	.ogt-secthead {
		display: flex;
		flex-direction: column;
		gap: 0.85rem;
		margin-block-end: var(--ogt-gap-lg);
		max-width: 62ch;
	}

	.ogt-secthead--center {
		align-items: center;
		text-align: center;
		margin-inline: auto;
	}

	.ogt-secthead__title { max-width: 22ch; }
	.ogt-secthead--center .ogt-secthead__title { max-width: 26ch; }

	.ogt-secthead__intro {
		color: var(--ogt-text-muted);
		font-size: var(--ogt-fs-lg);
	}

	/* Header + action on one line. */
	.ogt-secthead--split {
		flex-direction: row;
		align-items: flex-end;
		justify-content: space-between;
		max-width: none;
		gap: var(--ogt-gap);
	}

	@media (max-width: 720px) {
		.ogt-secthead--split {
			flex-direction: column;
			align-items: flex-start;
		}
	}

	/* ---------------------------------------------------- 1. HERO BANNER */

	.ogt-hero {
		position: relative;
		padding-block: clamp(3.5rem, 8vw, 6.5rem);
		overflow: hidden;
	}

	/*
	 * A saturated blue field, not the near-black navy this used to be. The old
	 * ramp started at --ogt-deep (#0F172A), which is almost black, so the hero
	 * read heavy and the blue brand colour never actually appeared. Running
	 * primary-hover → primary → secondary keeps it unmistakably brand blue while
	 * still darkening behind the headline for contrast.
	 */
	.ogt-hero--home {
		background:
			radial-gradient(65% 85% at 88% 88%, rgb(var(--ogt-accent-rgb) / 30%) 0%, transparent 62%),
			radial-gradient(60% 80% at 8% 4%, rgb(255 255 255 / 12%) 0%, transparent 58%),
			linear-gradient(125deg, var(--ogt-primary-hover) 0%, var(--ogt-primary) 45%, var(--ogt-secondary) 100%);
		color: var(--ogt-on-dark-muted);
		isolation: isolate;
	}

	.ogt-hero--home h1 { color: var(--ogt-on-dark); }

	/* A faint grid — reads as engineering, not decoration. */
	.ogt-hero--home::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		background-image:
			linear-gradient(rgb(255 255 255 / 4%) 1px, transparent 1px),
			linear-gradient(90deg, rgb(255 255 255 / 4%) 1px, transparent 1px);
		background-size: 64px 64px;
		mask-image: radial-gradient(circle at 70% 40%, #000 0%, transparent 70%);
		pointer-events: none;
	}

	.ogt-hero__inner {
		display: grid;
		grid-template-columns: 1.05fr 0.95fr;
		gap: clamp(2rem, 5vw, 4rem);
		align-items: center;
	}

	@media (max-width: 960px) {
		.ogt-hero__inner { grid-template-columns: 1fr; }
	}

	/*
	 * Two columns of equal height rather than two centred blocks.
	 *
	 * Centring left the text column ~30px taller than the image, so neither
	 * edge lined up with anything and the media floated in loose blue. Stretching
	 * makes the image share the text block's top and bottom edge, which is what
	 * makes the split read as deliberate.
	 */
	@media (min-width: 961px) {
		.ogt-hero__inner { align-items: stretch; }

		.ogt-hero__media {
			display: flex;
			flex-direction: column;
			min-height: 26rem;   /* floor, for pages where the copy is short */
		}

		.ogt-hero__figure { flex: 1; min-height: 0; }

		.ogt-hero__figure > img,
		.ogt-hero__figure > video,
		.ogt-hero__figure > .ogt-ph {
			height: 100%;
			aspect-ratio: auto;
			object-fit: cover;
		}
	}

	/*
	 * Rhythm is set per gap, not by one uniform stack gap.
	 *
	 * At a flat 24px the badge, headline, intro, buttons and credentials all read
	 * as five equally-weighted items. Grouping them — label tight to headline,
	 * headline tight to intro, then a real break before the CTA and another
	 * before the credentials — is what gives the column a shape.
	 */
	.ogt-hero__content {
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		gap: 0;
	}

	.ogt-hero__content > .ogt-hero__title { margin-block-start: 1.25rem; }
	.ogt-hero__content > .ogt-hero__intro { margin-block-start: 1rem; }

	.ogt-hero__badge {
		display: inline-flex;
		align-items: center;
		gap: 0.55rem;
		padding: 0.45rem 1rem;
		border-radius: var(--ogt-radius-pill);
		background: rgb(255 255 255 / 10%);
		border: 1px solid rgb(255 255 255 / 18%);
		font-size: var(--ogt-fs-sm);
		font-weight: 500;
		color: rgb(255 255 255 / 90%);
		backdrop-filter: blur(6px);
	}

	.ogt-hero__dot {
		width: 7px;
		height: 7px;
		border-radius: 50%;
		background: var(--ogt-accent);
		box-shadow: 0 0 0 3px rgb(var(--ogt-accent-rgb) / 25%);
		flex: 0 0 auto;
	}

	.ogt-hero__title { max-width: 16ch; }

	.ogt-hero__intro {
		font-size: var(--ogt-fs-lg);
		max-width: 52ch;
		color: var(--ogt-on-dark-muted);
	}

	/* Scoped to the home hero — the compact page hero shares .ogt-hero__intro and
	   wants the wider measure. 46ch keeps the intro visibly narrower than the
	   headline block so the two do not form one grey slab. */
	.ogt-hero--home .ogt-hero__intro { max-width: 46ch; }

	.ogt-hero--compact .ogt-hero__intro { color: var(--ogt-text-muted); }

	.ogt-hero__actions {
		display: flex;
		flex-wrap: wrap;
		align-items: stretch;   /* both pills share a height even if one wraps */
		gap: 0.75rem;
		margin-block-start: 2rem;
	}

	.ogt-hero__stats {
		display: flex;
		flex-wrap: wrap;
		gap: 0.6rem;
		list-style: none;
		margin: 1.75rem 0 0;
		padding: 0;
		width: 100%;
	}

	/*
	 * Trust markers as chips rather than a bare row of text: each gets its own
	 * translucent panel and icon tile, so they read as credentials instead of
	 * running together as a caption under the buttons.
	 */
	/*
	 * Pill, to match the badge above and the buttons between them. The section
	 * previously ran four different corner radii — 999px badge, 999px buttons,
	 * 14px chips, 20px frame, 10px floating cards — which is what made a tidy
	 * layout still feel unresolved. Now the left column is one pill family and
	 * the media column is nested rounded rectangles.
	 */
	.ogt-hero__chip {
		display: flex;
		align-items: center;
		gap: 0.6rem;
		padding: 0.5rem 1.1rem 0.5rem 0.5rem;
		border-radius: var(--ogt-radius-pill);
		background: rgb(255 255 255 / 10%);
		border: 1px solid rgb(255 255 255 / 16%);
		backdrop-filter: blur(6px);
	}

	.ogt-hero__chip-text {
		display: flex;
		flex-direction: column;
		gap: 0.05rem;
		line-height: 1.25;
	}

	.ogt-hero__stat-v {
		font-family: var(--ogt-font-head);
		font-weight: 700;
		font-size: 1.05rem;
		color: var(--ogt-on-dark);
	}

	.ogt-hero__stat-l {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-on-dark-subtle);
	}

	.ogt-hero__media { position: relative; }

	/*
	 * The frame sat on the blue with only a shadow, which the saturated ground
	 * swallows. A hairline highlight on top of the shadow gives it an edge to
	 * read against without introducing another colour.
	 */
	.ogt-hero__figure {
		margin: 0;
		border-radius: var(--ogt-radius-img);
		overflow: hidden;
		border: 1px solid rgb(255 255 255 / 14%);
		box-shadow:
			var(--ogt-shadow-xl),
			0 0 0 1px rgb(255 255 255 / 6%),
			0 24px 60px -20px rgb(2 6 23 / 55%);
	}

	.ogt-hero__img,
	.ogt-hero__video {
		width: 100%;
		aspect-ratio: 4 / 3;
		object-fit: cover;
	}

	/* display:block removes the inline-element baseline gap that would otherwise
	   show as a hairline of figure background under the video. */
	.ogt-hero__video {
		display: block;
		background: var(--ogt-deep);
	}

	/*
	 * The two cards are a matched pair on a diagonal: same overhang, same inset,
	 * opposite corners. They previously used different offsets (-16px vs -12px)
	 * and different vertical insets, so they read as two accidents rather than a
	 * composition. 14px sits inside the frame's 20px — an inner radius should be
	 * smaller than the one it nests in, or the corners fight.
	 */
	.ogt-hero__card {
		--card-overhang: 1rem;
		--card-inset: 1.5rem;

		position: absolute;
		left: calc(var(--card-overhang) * -1);
		bottom: var(--card-inset);
		display: flex;
		align-items: center;
		gap: 0.75rem;
		background: var(--ogt-surface);
		color: var(--ogt-ink);
		border-radius: var(--ogt-radius-input);
		padding: 0.75rem 1.1rem;
		box-shadow: var(--ogt-shadow-lg);
		max-width: 78%;
	}

	/*
	 * Second card, over the top-right of the image. Dark glass rather than the
	 * white of the lower card, so the two read as a pair without competing —
	 * and so it stays legible over a bright image.
	 */
	.ogt-hero__card--top {
		left: auto;
		bottom: auto;
		right: calc(var(--card-overhang) * -1);
		top: var(--card-inset);
		background: rgb(var(--ogt-deep-rgb) / 78%);
		border: 1px solid rgb(255 255 255 / 14%);
		backdrop-filter: blur(10px);
		color: var(--ogt-on-dark);
		padding: 0.75rem 1.1rem;
	}

	.ogt-hero__card--top .ogt-hero__card-value {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		color: var(--ogt-on-dark);
		font-size: 1.25rem;
	}

	.ogt-hero__card--top .ogt-hero__card-value .ogt-icon { color: var(--ogt-accent); }
	.ogt-hero__card--top .ogt-hero__card-label { color: var(--ogt-on-dark-muted); }

	/* Single column: the media spans the full width, so an overhang would push
	   the cards against the viewport edge. Tuck them inside instead. */
	@media (max-width: 960px) {
		.ogt-hero__card { left: 1rem; }
		.ogt-hero__card--top { left: auto; right: 1rem; }
	}

	/* Below ~520px two floating cards over a full-bleed image crowd it and start
	   covering the subject. Keep the headline stat, drop the second. */
	@media (max-width: 520px) {
		.ogt-hero__card { max-width: calc(100% - 2rem); }
		.ogt-hero__card--top { display: none; }
	}

	.ogt-hero__card-text {
		display: flex;
		flex-direction: column;
		line-height: 1.3;
	}

	.ogt-hero__card-label {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-subtle);
	}

	.ogt-hero__card-value {
		font-family: var(--ogt-font-head);
		font-weight: 700;
		font-size: 0.9rem;
		color: var(--ogt-ink);
	}

	/* ------------------------------------------------ hero slider variant
	 *
	 * Only applies when two or more slides are configured. With one slide the
	 * track is a plain block and every rule here is inert, so the static hero
	 * renders byte-for-byte as it did before the slider existed.
	 *
	 * The track is a native scroll-snap scroller: swipeable on touch and fully
	 * navigable with no JS at all. ui.js only adds the arrows, dots, autoplay
	 * and the pause control.
	 * -------------------------------------------------------------------- */
	.ogt-hero--slider .ogt-hero__track {
		display: flex;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		scroll-behavior: smooth;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;

		/*
		 * The slides carry the section's own horizontal padding via .ogt-wrap, so
		 * the track itself must not add any — otherwise slide 2 starts one gutter
		 * short and the snap points drift.
		 */
		margin: 0;
		padding: 0;
		list-style: none;
	}

	.ogt-hero--slider .ogt-hero__track::-webkit-scrollbar { display: none; }

	.ogt-hero--slider .ogt-hero__track:focus-visible {
		outline: 2px solid var(--ogt-on-dark);
		outline-offset: -4px;
		border-radius: var(--ogt-radius-sm);
	}

	.ogt-hero--slider .ogt-hero__slide {
		flex: 0 0 100%;
		min-width: 0;          /* long words must wrap, not widen the slide */
		scroll-snap-align: start;
	}

	/*
	 * Equal-height slides.
	 *
	 * Without this the track is only as tall as the tallest slide's content, and
	 * every shorter slide leaves the arrows jumping up as it scrolls in. A fixed
	 * min-height would be worse on mobile, so the slides simply stretch.
	 */
	.ogt-hero--slider .ogt-hero__track { align-items: stretch; }
	.ogt-hero--slider .ogt-hero__slide > .ogt-hero__inner { height: 100%; }

	/*
	 * Slides 2+ title at H2.
	 *
	 * .ogt-hero--home only brightens h1, because until now the hero had exactly
	 * one heading. The later slides are H2 so the page keeps a single H1, and
	 * without this they would render in the muted body colour while slide 1
	 * renders white — the same text, two different colours.
	 */
	.ogt-hero--home .ogt-hero__title { color: var(--ogt-on-dark); }

	/* Nav sits under the track, inside the section's tint. */
	.ogt-hero__nav {
		margin-block-start: clamp(1.5rem, 3vw, 2.25rem);
	}

	/*
	 * Arrows on a saturated blue ground.
	 *
	 * The shared .ogt-slider__arrow is a white pill for light sections; dropped
	 * on the hero it reads as two bright blobs competing with the CTA. Glass
	 * instead — and the disabled state stays visible rather than fading to
	 * nothing against the blue.
	 */
	.ogt-hero__arrow {
		background: rgb(255 255 255 / 10%);
		border-color: rgb(255 255 255 / 28%);
		color: var(--ogt-on-dark);
		box-shadow: none;
		-webkit-backdrop-filter: blur(8px);
		backdrop-filter: blur(8px);
	}

	.ogt-hero__arrow:hover:not(:disabled) {
		background: var(--ogt-on-dark);
		border-color: var(--ogt-on-dark);
		color: var(--ogt-primary);
		box-shadow: var(--ogt-shadow-md);
	}

	.ogt-hero__arrow:disabled {
		background: rgb(255 255 255 / 6%);
		border-color: rgb(255 255 255 / 15%);
		color: rgb(255 255 255 / 45%);
		opacity: 1;              /* the shared 0.35 is invisible on this ground */
	}

	.ogt-hero__arrow:focus-visible {
		outline: 2px solid var(--ogt-on-dark);
		outline-offset: 3px;
	}

	/*
	 * The dot on the hero's dark ground. Targets ::after, not the button:
	 * the button is now a transparent 24px hit area and the dot is drawn by
	 * the pseudo-element, so colouring the button would paint a 24px square.
	 */
	.ogt-hero__dots .ogt-slider__dot::after {
		background: rgb(255 255 255 / 30%);
	}

	.ogt-hero__dots .ogt-slider__dot:hover::after { background: rgb(255 255 255 / 55%); }

	.ogt-hero__dots .ogt-slider__dot.is-active::after {
		background: var(--ogt-accent);
	}

	.ogt-hero__dots .ogt-slider__dot:focus-visible {
		outline: 2px solid var(--ogt-on-dark);
		outline-offset: 3px;
	}

	/*
	 * Pause / play toggle.
	 *
	 * Drawn in CSS rather than shipped as two more SVGs: it is two rectangles and
	 * a triangle, and the icon registry is for icons the client can pick, which
	 * this is not.
	 */
	.ogt-hero__toggle-icon {
		display: block;
		width: 14px;
		height: 14px;
		background: currentcolor;

		/* Pause: two bars. */
		-webkit-mask: linear-gradient(#000 0 0) 0 0 / 4px 14px no-repeat,
			linear-gradient(#000 0 0) 10px 0 / 4px 14px no-repeat;
		mask: linear-gradient(#000 0 0) 0 0 / 4px 14px no-repeat,
			linear-gradient(#000 0 0) 10px 0 / 4px 14px no-repeat;
	}

	/* Paused: a play triangle, because the button now resumes. */
	.ogt-hero__toggle.is-paused .ogt-hero__toggle-icon {
		-webkit-mask: none;
		mask: none;
		background: none;
		width: 0;
		height: 0;
		border-style: solid;
		border-width: 7px 0 7px 12px;
		border-color: transparent transparent transparent currentcolor;
	}

	@media (max-width: 600px) {
		/* Arrows are redundant where the track is swipeable; dots still show position. */
		.ogt-hero__nav .ogt-hero__arrow:not(.ogt-hero__toggle) { display: none; }
	}

	/* ------------------------------------------------ warmth & material
	 *
	 * The palette shift (cool grey-blue surfaces -> warm bone and sand) happens
	 * in Site Settings, so it needs no CSS. What needs CSS is the MATERIAL:
	 * flat colour reads as software, and this is a business selling something
	 * you can hold.
	 * -------------------------------------------------------------------- */

	/*
	 * Grain on the DARK BANDS only — not on the whole page.
	 *
	 * A large area of perfectly flat colour is what reads as "rendered by a
	 * computer"; a little noise across it reads as printed. But the obvious
	 * implementation — one fixed, viewport-sized layer with mix-blend-mode over
	 * everything — makes the browser re-blend the full viewport on every scroll
	 * frame, which is exactly the kind of jank that shows up on a mid-range
	 * Android and in Core Web Vitals. Not worth it for a texture nobody can
	 * consciously see.
	 *
	 * Scoped to the hero, the CTA banner and the footer instead: the three
	 * places with enough uninterrupted colour for flatness to be noticeable.
	 * No blend mode, no fixed positioning, no effect on scrolling.
	 *
	 * Inline SVG turbulence rather than a tiled PNG: no extra request, no cache
	 * entry, resolution-independent, ~300 bytes.
	 */
	.ogt-hero--home,
	.ogt-footer,
	.ogt-cta {
		position: relative;
		isolation: isolate;
	}

	.ogt-hero--home::after,
	.ogt-footer::after,
	.ogt-cta::after {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		pointer-events: none;
		opacity: 0.18;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
	}


	/*
	 * The hero loses its graph-paper grid.
	 *
	 * A 64px technical grid reads as engineering or fintech. This is an
	 * agricultural product — the ground should feel like light through dust, not
	 * like a CAD backdrop. Two soft warm pools instead, with the gold pulled up
	 * so the brand's second colour actually appears in the hero rather than only
	 * on one highlighted word.
	 */
	.ogt-hero--home::before {
		background-image:
			radial-gradient(38% 55% at 78% 18%, rgb(var(--ogt-accent-rgb) / 22%) 0%, transparent 70%),
			radial-gradient(45% 60% at 12% 88%, rgb(201 122 59 / 18%) 0%, transparent 72%);
		background-size: auto;
		mask-image: none;
	}

	/* ---------------------------------------------- product colour swatch
	 *
	 * With no photography, 37 identical grey tiles is what makes a catalogue
	 * feel dead. Each product carries the colour the material actually is, which
	 * for chilli and turmeric is also its grade.
	 * -------------------------------------------------------------------- */
	.ogt-pcard { position: relative; }

	.ogt-pcard__swatch {
		position: absolute;
		inset-block-start: 0;
		inset-inline: 0;
		height: 4px;
		background: var(--swatch, var(--ogt-accent));
		border-start-start-radius: inherit;
		border-start-end-radius: inherit;
		/* Above the stretched card link, or it is painted over. */
		z-index: 1;
	}

	/* The tinted placeholder modifier lives with the other .ogt-ph rules, further
	 * down — a modifier placed before its base loses to it on source order. */

	/* ------------------------------------------- botanical card artwork
	 *
	 * The drawing sits on a wash of the product's own colour. Both come from
	 * the same hex, so a card is one idea rather than two decorations.
	 * -------------------------------------------------------------------- */
	.ogt-pcard__art {
		aspect-ratio: 16 / 9;
		display: grid;
		place-items: center;
		background: var(--ogt-muted);
		color: var(--ogt-primary);
		overflow: hidden;
		position: relative;
	}

	.ogt-pcard__art.is-tinted {
		background:
			radial-gradient(
				110% 130% at 28% 12%,
				color-mix(in srgb, var(--swatch) 22%, #fff) 0%,
				color-mix(in srgb, var(--swatch) 10%, #fff) 58%,
				color-mix(in srgb, var(--swatch) 17%, #fff) 100%
			);
		/* The drawing darkens its own tint, so it never fights the wash. */
		color: color-mix(in srgb, var(--swatch) 82%, #000);
	}

	/*
	 * A faint oversized repeat of the same drawing, bled off the corner.
	 *
	 * Gives the panel depth without a second asset — it is the same inline SVG
	 * scaled up and dropped to a whisper.
	 */
	.ogt-pcard__art .ogt-bot { position: relative; z-index: 1; }

	.ogt-pcard__art::after {
		content: "";
		position: absolute;
		inset-block-start: -18%;
		inset-inline-end: -14%;
		width: 62%;
		aspect-ratio: 1;
		border-radius: 50%;
		background: currentcolor;
		opacity: 0.06;
	}

	.ogt-pcard:hover .ogt-bot { transform: scale(1.04) rotate(-2deg); }

	.ogt-bot {
		transition: transform var(--ogt-dur-slow) var(--ogt-ease-out);
	}

	@media (prefers-reduced-motion: reduce) {
		.ogt-bot { transition: none; }
		.ogt-pcard:hover .ogt-bot { transform: none; }
	}

	/* ------------------------------------------------ section artwork
	 *
	 * Backgrounds drawn rather than photographed. Each is an inline SVG tile as
	 * a data URI: no request, no cache entry, resolution-independent, and a few
	 * hundred bytes. They are set very low so they read as paper stock rather
	 * than as wallpaper — if you notice the pattern, it is too strong.
	 *
	 * Applied to the alternating bands only. A pattern behind every section is
	 * the same mistake as a card around every block: uniform texture is just a
	 * different flavour of flat.
	 * -------------------------------------------------------------------- */

	/* Scattered seeds — for the sand-coloured bands. */
	.ogt-section--muted { position: relative; isolation: isolate; }

	.ogt-section--muted::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		pointer-events: none;
		opacity: 0.5;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150' viewBox='0 0 150 150'%3E%3Cg fill='none' stroke='%238E4B24' stroke-width='1.1' opacity='.24'%3E%3Cellipse cx='22' cy='26' rx='4' ry='7.5' transform='rotate(24 22 26)'/%3E%3Cellipse cx='96' cy='18' rx='3.6' ry='7' transform='rotate(-38 96 18)'/%3E%3Cellipse cx='130' cy='62' rx='4' ry='7.5' transform='rotate(12 130 62)'/%3E%3Cellipse cx='58' cy='74' rx='3.4' ry='6.5' transform='rotate(-16 58 74)'/%3E%3Cellipse cx='16' cy='104' rx='3.8' ry='7' transform='rotate(52 16 104)'/%3E%3Cellipse cx='110' cy='120' rx='3.6' ry='7' transform='rotate(-28 110 120)'/%3E%3Cellipse cx='74' cy='134' rx='3.4' ry='6.5' transform='rotate(8 74 134)'/%3E%3Ccircle cx='46' cy='44' r='2.4'/%3E%3Ccircle cx='136' cy='96' r='2.2'/%3E%3Ccircle cx='86' cy='96' r='2.4'/%3E%3C/g%3E%3C/svg%3E");
	}

	/*
	 * Contour lines for the dark process band — the visual shorthand for terrain
	 * and growing regions, and it suits a section about where material comes
	 * from far better than the graph-paper grid it replaces.
	 */
	.ogt-section--dark { position: relative; isolation: isolate; }

	.ogt-section--dark::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		pointer-events: none;
		opacity: 0.4;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='170' viewBox='0 0 260 170'%3E%3Cg fill='none' stroke='%23C99A3B' stroke-width='1' opacity='.2'%3E%3Cpath d='M-10 30c40-22 84-22 126 0s86 22 130 0'/%3E%3Cpath d='M-10 52c40-22 84-22 126 0s86 22 130 0'/%3E%3Cpath d='M-10 74c40-22 84-22 126 0s86 22 130 0'/%3E%3Cpath d='M-10 96c40-22 84-22 126 0s86 22 130 0'/%3E%3Cpath d='M-10 118c40-22 84-22 126 0s86 22 130 0'/%3E%3Cpath d='M-10 140c40-22 84-22 126 0s86 22 130 0'/%3E%3C/g%3E%3C/svg%3E");
	}

	/*
	 * A single oversized botanical, bled off the edge of a band.
	 *
	 * Added via a class on the section rather than markup, so it can be moved
	 * around without touching templates. Hidden on narrow screens, where a
	 * half-metre of decorative leaf would crowd the copy.
	 */
	.ogt-art-bleed { position: relative; isolation: isolate; overflow: hidden; }

	.ogt-art-bleed > .ogt-bot--bleed {
		position: absolute;
		z-index: -1;
		inset-block-start: -4%;
		inset-inline-end: -6%;
		width: clamp(220px, 28vw, 420px);
		height: auto;
		color: currentcolor;
		opacity: 0.07;
		pointer-events: none;

		/*
		 * Rotation and parallax compose here. ui.js writes --ogt-par; if it never
		 * runs the fallback is 0px and the artwork simply sits still, rotated.
		 */
		transform: translate3d(0, var(--ogt-par, 0px), 0) rotate(-12deg);
		will-change: transform;
	}

	@media (max-width: 900px) {
		.ogt-art-bleed > .ogt-bot--bleed { display: none; }
	}

	/* ------------------------------------------------ botanical divider
	 *
	 * A rule with a drawing sitting in the gap — an old printing convention, and
	 * a far better section break than another 1px line or another card edge.
	 * -------------------------------------------------------------------- */
	.ogt-divider {
		display: flex;
		align-items: center;
		gap: 1.25rem;
		color: var(--ogt-accent);
		margin-block: clamp(2rem, 5vw, 3.5rem);
	}

	.ogt-divider::before,
	.ogt-divider::after {
		content: "";
		flex: 1;
		height: 1px;
		background: linear-gradient(90deg, transparent, currentcolor, transparent);
		opacity: 0.45;
	}

	.ogt-divider .ogt-bot { flex: 0 0 auto; }

	/* --------------------------------------------------- stroke drawing
	 *
	 * The line draws itself as the section arrives. Uses the existing
	 * [data-ogt-reveal] .is-in hook, so it needs no new JavaScript and it
	 * inherits the failsafe that already guarantees content becomes visible
	 * even if the observer never fires.
	 *
	 * pathLength="1" on every stroke would be the tidy way to do this, but it
	 * has to be set per element; using a large dash figure works on any path
	 * regardless of its true length, at the cost of the timing varying slightly
	 * between drawings. For a decorative flourish that is the right trade.
	 *
	 * Entirely inside a reduced-motion guard: under that preference the art is
	 * simply present, which is what someone asking for less motion wants.
	 */
	@media (prefers-reduced-motion: no-preference) {
		[data-ogt-reveal] .ogt-bot--draw [stroke],
		[data-ogt-reveal] .ogt-bot--draw path,
		[data-ogt-reveal] .ogt-bot--draw circle,
		[data-ogt-reveal] .ogt-bot--draw ellipse,
		[data-ogt-reveal] .ogt-bot--draw line {
			stroke-dasharray: 400;
			stroke-dashoffset: 400;
		}

		[data-ogt-reveal].is-in .ogt-bot--draw path,
		[data-ogt-reveal].is-in .ogt-bot--draw circle,
		[data-ogt-reveal].is-in .ogt-bot--draw ellipse,
		[data-ogt-reveal].is-in .ogt-bot--draw line {
			stroke-dashoffset: 0;
			transition: stroke-dashoffset 1.4s var(--ogt-ease-out);
		}
	}

	/* ------------------------------------------------- scroll progress
	 *
	 * A pre-sized bar scaled with a transform, not a width animation. Width
	 * changes force layout on every scroll frame; a transform is composited.
	 * -------------------------------------------------------------------- */
	.ogt-progress {
		position: fixed;
		inset-block-start: 0;
		inset-inline: 0;
		height: 3px;
		z-index: 200;
		pointer-events: none;
		background: transparent;
	}

	.ogt-progress__bar {
		display: block;
		height: 100%;
		width: 100%;
		transform: scaleX(0);
		transform-origin: 0 50%;
		background: linear-gradient(90deg, var(--ogt-accent), #EFD292, var(--ogt-accent));
		will-change: transform;
	}

	/* ------------------------------------------------- catalogue ticker
	 *
	 * The loop works by translating a track that contains the list twice (the
	 * second copy is cloned by JS and aria-hidden). Moving -50% therefore lands
	 * exactly where it started, so the seam is invisible.
	 *
	 * Without JS, or under reduced motion, no clone exists and no animation
	 * runs: the strip stays an ordinary horizontal scroller, which is still a
	 * usable list of products.
	 * -------------------------------------------------------------------- */
	.ogt-ticker {
		background: var(--ogt-primary);
		border-block: 1px solid rgb(255 255 255 / 10%);
		overflow: hidden;
		position: relative;
	}

	/* Fade the ends so items enter and leave rather than being chopped off. */
	.ogt-ticker::before,
	.ogt-ticker::after {
		content: "";
		position: absolute;
		inset-block: 0;
		width: clamp(40px, 8vw, 120px);
		z-index: 2;
		pointer-events: none;
	}

	.ogt-ticker::before {
		inset-inline-start: 0;
		background: linear-gradient(90deg, var(--ogt-primary), transparent);
	}

	.ogt-ticker::after {
		inset-inline-end: 0;
		background: linear-gradient(270deg, var(--ogt-primary), transparent);
	}

	.ogt-ticker__viewport {
		display: flex;
		overflow-x: auto;
		scrollbar-width: none;
	}

	.ogt-ticker__viewport::-webkit-scrollbar { display: none; }

	.ogt-ticker__track {
		display: flex;
		align-items: center;
		gap: clamp(1.5rem, 4vw, 3rem);
		margin: 0;
		padding: 0.85rem clamp(0.75rem, 2vw, 1.5rem) 0.85rem 0;
		list-style: none;
		flex: 0 0 auto;
	}

	.ogt-ticker.is-running .ogt-ticker__viewport { overflow: hidden; }

	.ogt-ticker.is-running .ogt-ticker__track {
		animation: ogt-marquee 52s linear infinite;
	}

	.ogt-ticker.is-running.is-paused .ogt-ticker__track {
		animation-play-state: paused;
	}

	@keyframes ogt-marquee {
		from { transform: translate3d(0, 0, 0); }
		to   { transform: translate3d(-100%, 0, 0); }
	}

	.ogt-ticker__item { flex: 0 0 auto; }

	.ogt-ticker__link {
		display: inline-flex;
		align-items: center;
		gap: 0.6rem;
		color: var(--ogt-on-dark-muted);
		text-decoration: none;
		font-size: var(--ogt-fs-sm);
		white-space: nowrap;
		transition: color var(--ogt-dur) var(--ogt-ease);

		/*
		 * 24px, measured at 23. WCAG 2.2 SC 2.5.8 sets the minimum target at
		 * 24x24 CSS pixels at Level AA, and these were one pixel under across
		 * all 36 of them — the kind of miss that never shows up in a design
		 * review and fails an accessibility audit outright.
		 *
		 * min-height rather than padding, so the strip's height is unchanged.
		 */
		min-height: 24px;
	}

	/* The product's own colour, as a leading dot. */
	.ogt-ticker__item::marker { content: ""; }

	.ogt-ticker__link::before {
		content: "";
		width: 7px;
		height: 7px;
		border-radius: 50%;
		background: var(--dot, var(--ogt-accent));
		flex: 0 0 auto;
	}

	.ogt-ticker__link .ogt-bot { opacity: 0.55; transition: opacity var(--ogt-dur) var(--ogt-ease); }

	.ogt-ticker__link:hover { color: var(--ogt-on-dark); }
	.ogt-ticker__link:hover .ogt-bot { opacity: 1; }

	.ogt-ticker__link:focus-visible {
		outline: 2px solid var(--ogt-accent);
		outline-offset: 4px;
		border-radius: var(--ogt-radius-sm);
	}

	@media (prefers-reduced-motion: reduce) {
		.ogt-ticker.is-running .ogt-ticker__track { animation: none; }
		.ogt-ticker.is-running .ogt-ticker__viewport { overflow-x: auto; }
	}

	/* ------------------------------------------------- sticky quote bar
	 *
	 * Bottom-right on desktop, a full-width bar on mobile where a floating card
	 * would cover content. Translated out of view rather than display:none so it
	 * can animate, and it is removed from the DOM entirely once dismissed.
	 * -------------------------------------------------------------------- */
	.ogt-stickycta {
		position: fixed;
		inset-block-end: 1.25rem;
		inset-inline-end: 1.25rem;
		z-index: 150;

		display: flex;
		align-items: center;
		gap: 1rem;
		max-width: min(440px, calc(100vw - 2.5rem));
		padding: 0.9rem 1rem 0.9rem 1.25rem;

		background: var(--ogt-surface);
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-lg);
		box-shadow: var(--ogt-shadow-lg);

		opacity: 0;
		visibility: hidden;
		transform: translateY(18px);
		transition:
			opacity var(--ogt-dur-slow) var(--ogt-ease-out),
			transform var(--ogt-dur-slow) var(--ogt-ease-out),
			visibility 0s linear var(--ogt-dur-slow);
	}

	.ogt-stickycta.is-in {
		opacity: 1;
		visibility: visible;
		transform: none;
		transition-delay: 0s;
	}

	.ogt-stickycta__text {
		margin: 0;
		font-size: var(--ogt-fs-sm);
		line-height: 1.4;
		color: var(--ogt-text);
	}

	.ogt-stickycta__text strong {
		display: block;
		color: var(--ogt-ink);
		font-family: var(--ogt-font-head);
	}

	.ogt-stickycta .ogt-btn { flex: 0 0 auto; }

	.ogt-stickycta__close {
		position: absolute;
		inset-block-start: -10px;
		inset-inline-end: -10px;
		width: 28px;
		height: 28px;
		display: grid;
		place-items: center;
		border-radius: 50%;
		border: 1px solid var(--ogt-border);
		background: var(--ogt-surface);
		color: var(--ogt-text-subtle);
		cursor: pointer;
		box-shadow: var(--ogt-shadow-sm);
	}

	.ogt-stickycta__close:hover { color: var(--ogt-ink); }

	@media (max-width: 640px) {
		.ogt-stickycta {
			inset-inline: 0.75rem;
			inset-block-end: 0.75rem;
			max-width: none;
			padding: 0.85rem;
			gap: 0.75rem;
		}

		/* The supporting sentence goes; the offer and the button stay. */
		.ogt-stickycta__text span { display: none; }
	}

	@media (prefers-reduced-motion: reduce) {
		.ogt-stickycta { transition: opacity var(--ogt-dur) linear, visibility 0s; transform: none; }
		.ogt-stickycta.is-in { transform: none; }
	}

	/* ============================================ TRADE NAVY & SAFFRON
	 *
	 * The treatments that came in with the chosen design direction. Colours,
	 * fonts and metrics live in Site Settings as normal tokens; only the things
	 * a token cannot express are here.
	 * ================================================================== */

	/*
	 * Serif headings need different metrics from the geometric sans they
	 * replaced. Libre Baskerville has a large x-height and a heavier texture, so
	 * display sizes go slightly UP (a serif carries less presence at the same
	 * size) while line-height tightens.
	 */
	.ogt-hero__title {
		font-size: clamp(2.4rem, 5.2vw, 4rem);
		line-height: 1.12;
		letter-spacing: -0.005em;
	}

	h2,
	.ogt-section__title { line-height: 1.18; }

	/*
	 * The highlighted phrase is italic as well as gold.
	 *
	 * In a serif, italic is the native way to stress a phrase — it is what the
	 * face was cut to do. The same treatment in a geometric sans reads as a
	 * rendering fault, which is why this arrived with the serif and not before.
	 */
	.ogt-hl {
		font-style: italic;
		color: var(--ogt-accent);
	}

	/* On navy, gold goes brighter rather than darker. */
	.ogt-hero--home .ogt-hl,
	.ogt-section--dark .ogt-hl { color: var(--ogt-accent-bright); }

	/*
	 * Eyebrows, buttons, inputs and tables stay in the sans.
	 *
	 * A serif button reads as a link rather than a control, and serif figures in
	 * a specification table do not align — IBM Plex Sans has tabular numerals,
	 * which is most of why it was chosen.
	 */
	.ogt-eyebrow {
		font-family: var(--ogt-font-body);
		font-size: 0.76rem;
		font-weight: 600;
		letter-spacing: 0.16em;
	}

	.ogt-btn,
	.ogt-filter,
	input,
	select,
	textarea {
		font-family: var(--ogt-font-body);
		letter-spacing: 0;
	}

	.ogt-spec,
	.ogt-pcard__specs {
		font-family: var(--ogt-font-body);
		font-variant-numeric: tabular-nums;
	}

	/*
	 * Hairline borders in place of resting shadow.
	 *
	 * Shadow returns on hover, where it reads as lift rather than as decoration.
	 */
	.ogt-card,
	.ogt-pcard {
		box-shadow: none;
		border: 1px solid var(--ogt-border);
	}

	.ogt-card--interactive:hover,
	.ogt-pcard:hover {
		box-shadow: 0 10px 28px rgb(22 38 92 / 10%);
		border-color: rgb(22 38 92 / 18%);
	}

	/*
	 * The product colour becomes a spine down the left edge.
	 *
	 * With no resting shadow the card needs an anchor, and a vertical spine
	 * reads as a filing system — which is the register this direction is in.
	 */
	.ogt-pcard__swatch {
		inset-block: 0;
		inset-inline: 0 auto;
		width: 3px;
		height: auto;
	}

	/* Category filter chips carry their category's colour. */
	.ogt-filter[data-ogt-cat]::before {
		content: "";
		display: inline-block;
		width: 8px;
		height: 8px;
		margin-inline-end: 0.5rem;
		border-radius: 50%;
		background: var(--chip, transparent);
		vertical-align: baseline;
	}

	.ogt-filter[data-ogt-cat=""]::before { display: none; }

	/* Interior page hero. */
	.ogt-hero--compact {
		background: var(--ogt-muted);
		border-bottom: 1px solid var(--ogt-border);
		padding-block: clamp(2rem, 4vw, 3.5rem);
	}

	.ogt-hero--compact .ogt-hero__title { max-width: 26ch; }

	.ogt-hero__meta {
		color: var(--ogt-text-subtle);
		font-size: var(--ogt-fs-sm);
	}

	/* --------------------------------------------------- 3. FEATURE CARD */

	.ogt-feature__title { font-size: 1rem; }

	.ogt-feature__text {
		font-size: var(--ogt-fs-sm);
		color: var(--card-fg);
	}

	.ogt-feature--row {
		flex-direction: row;
		align-items: flex-start;
		gap: var(--ogt-gap-sm);
	}

	.ogt-feature--row .ogt-feature__body {
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
	}

	/* --------------------------------------------------- 4. SERVICE CARD */

	.ogt-service__title { font-size: 1.0625rem; }

	/*
	 * Cards in a row are all as tall as the longest copy, and the "Learn more"
	 * is pinned to the bottom — so a short description left a visible hole in
	 * the middle of three of the four offering cards. Centring the copy in its
	 * slack splits that space above and below instead of pooling it in one gap.
	 */
	.ogt-service__text {
		font-size: var(--ogt-fs-sm);
		color: var(--card-fg);
		flex: 1;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 0.6em;
	}

	.ogt-service__more,
	.ogt-more {
		display: inline-flex;
		align-items: center;
		gap: 0.35rem;
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-sm);
		font-weight: 600;
		color: var(--card-accent, var(--ogt-primary));
		text-decoration: none;
		margin-block-start: auto;
	}

	.ogt-card--interactive:hover .ogt-more .ogt-icon,
	.ogt-card--interactive:hover .ogt-service__more .ogt-icon {
		transform: translateX(3px);
	}

	.ogt-more .ogt-icon,
	.ogt-service__more .ogt-icon {
		transition: transform var(--ogt-dur) var(--ogt-ease-out);
	}

	/* Numbered variant — the 01/02/03 offering cards. */
	.ogt-service__top {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--ogt-gap-sm);
	}

	.ogt-service__n {
		font-family: var(--ogt-font-head);
		font-size: 2rem;
		font-weight: 700;
		line-height: 1;
		color: rgb(var(--ogt-ink-rgb) / 9%);
		font-variant-numeric: tabular-nums;
		transition: color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-card--interactive:hover .ogt-service__n {
		color: rgb(var(--ogt-primary-rgb) / 20%);
	}

	/* ------------------------------------------------- 5. STATISTIC BAND */

	/*
	 * These figures are the page's proof, so they read as one claim on one
	 * panel rather than four disconnected boxes. The panel carries the world
	 * map as a dotted backdrop — the numbers are about global reach, so the
	 * texture is the subject, not decoration.
	 */
	.ogt-statband__panel {
		position: relative;
		overflow: hidden;
		border-radius: calc(var(--ogt-radius-card) * 1.5);
		background:
			linear-gradient(160deg,
				rgb(var(--ogt-accent-rgb) / 7%) 0%,
				rgb(var(--ogt-primary-rgb) / 4%) 100%),
			var(--ogt-surface);
		padding: clamp(2rem, 5vw, 3.5rem) clamp(1.25rem, 4vw, 3rem);
	}

	/*
	 * The dots are the panel's own background; the map PNG is only an alpha
	 * mask over them. So the dot grid appears over land and nowhere else, and
	 * the colour follows --ogt-primary from settings instead of being baked
	 * into the file. Same masking technique as .ogt-worldmap__plate.
	 */
	.ogt-statband__map {
		position: absolute;
		inset: 0;
		pointer-events: none;
		opacity: 0.5;
		background-image: radial-gradient(
			circle,
			rgb(var(--ogt-primary-rgb) / 55%) 1px,
			transparent 1.2px
		);
		background-size: 11px 11px;
	}

	@supports (mask-image: url("")) or (-webkit-mask-image: url("")) {
		.ogt-statband__map {
			-webkit-mask-image: url("../img/worldmap.png");
			mask-image: url("../img/worldmap.png");
			-webkit-mask-size: cover;
			mask-size: cover;
			-webkit-mask-repeat: no-repeat;
			mask-repeat: no-repeat;
			-webkit-mask-position: center;
			mask-position: center;
		}
	}

	/* Without mask support the bare dot grid would tile the whole panel edge to
	   edge, which reads as noise rather than a map. Drop it instead. */
	@supports not ((mask-image: url("")) or (-webkit-mask-image: url(""))) {
		.ogt-statband__map { display: none; }
	}

	.ogt-statband__body {
		position: relative; /* lifts the content above the map */
	}

	.ogt-statband__grid {
		/* Named, because the divider below has to sit exactly halfway into it. */
		--sb-gap: clamp(1.75rem, 4vw, 2.5rem);

		display: grid;
		gap: var(--sb-gap);
		grid-template-columns: 1fr;
	}

	/*
	 * The divider is a pseudo-element floating in the gap, NOT a border with
	 * padding on the item.
	 *
	 * A left border plus padding-inline-start shrinks the content box of every
	 * item except the one starting the row, so three equal grid tracks render
	 * three unequal columns of text — measured at 341 / 301 / 301 before this
	 * change. Putting the rule in the gap leaves all content boxes identical and
	 * centres the line between columns rather than hard against one.
	 */
	@media (min-width: 640px) {
		.ogt-statband__grid > * { position: relative; }

		/* Shared appearance; which items get one is decided per breakpoint. */
		.ogt-statband__grid > *::before {
			position: absolute;
			inset-block: 0;
			inset-inline-start: calc(var(--sb-gap) / -2);
			width: 1px;
			background: rgb(var(--ogt-ink-rgb) / 9%);
		}
	}

	/*
	 * The two-column range is bounded at the top rather than left open.
	 *
	 * Left open, its `:nth-child(2n + 1)::before { content: none }` — specificity
	 * (0,2,1) — outranks the three-column reset at (0,1,1) and keeps winning
	 * above 1024px no matter how the source is ordered. The third column then
	 * silently loses its divider. Bounding the range is the fix; raising the
	 * other rule's specificity would only postpone the same collision.
	 */
	@media (min-width: 640px) and (max-width: 1023.98px) {
		.ogt-statband__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
		.ogt-statband__grid > *::before { content: ""; }
		.ogt-statband__grid > :nth-child(2n + 1)::before { content: none; }
	}

	/*
	 * Column count follows the number of stats, rather than being fixed at four.
	 *
	 * It WAS fixed at four. With three stats that left an empty fourth track, so
	 * the row sat left-weighted and each figure got three-quarters of the width
	 * it should have had. Worse, the divider rule adds padding-inline-start to
	 * every item except the row-start, so items 2 and 3 had narrower content
	 * boxes than item 1 — three columns that were genuinely unequal, not just
	 * apparently.
	 *
	 * The template writes data-cols, so any count from two to six lays out
	 * correctly and the dividers still land between columns rather than at the
	 * start of a row.
	 */
	@media (min-width: 1024px) {
		.ogt-statband__grid {
			grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
		}

		/* Every item gets a line; the row-start rule below takes it back. */
		.ogt-statband__grid > *::before { content: ""; }

		.ogt-statband__grid[data-cols="2"] > :nth-child(2n + 1)::before,
		.ogt-statband__grid[data-cols="3"] > :nth-child(3n + 1)::before,
		.ogt-statband__grid[data-cols="4"] > :nth-child(4n + 1)::before,
		.ogt-statband__grid[data-cols="5"] > :nth-child(5n + 1)::before,
		.ogt-statband__grid[data-cols="6"] > :nth-child(6n + 1)::before {
			content: none;
		}
	}

	.ogt-stat {
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		gap: 0.4rem;
	}

	.ogt-stat--center { align-items: center; text-align: center; }

	.ogt-stat__v {
		font-family: var(--ogt-font-head);
		font-size: clamp(2.25rem, 4.5vw, 3.25rem);
		font-weight: 700;
		line-height: 1;
		/* --ogt-secondary, not --ogt-accent: the reference sets these numbers in
		   teal, which computes to ~2.2:1 on a panel this pale and fails even
		   the 3:1 large-text floor. Secondary keeps the lift at 4.5:1+. */
		color: var(--card-accent, var(--ogt-secondary));
		font-variant-numeric: tabular-nums;
	}

	.ogt-stat__l {
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-lg);
		font-weight: 600;
		line-height: 1.3;
		color: var(--card-fg, var(--ogt-ink));
	}

	.ogt-stat__t {
		margin: 0;
		font-size: var(--ogt-fs-sm);
		line-height: 1.55;
		color: var(--ogt-text);
		max-width: 32ch;
	}

	.ogt-card--dark .ogt-stat__v { color: var(--ogt-accent); }

	/*
	 * Badge card — a stat laid out as a row beside an icon, rather than stacked.
	 * Distinct enough from .ogt-stat to earn its own element names; it was being
	 * approximated with three inline styles before.
	 */
	.ogt-badge-card { gap: var(--ogt-gap-sm); }

	.ogt-badge-card__body {
		display: flex;
		flex-direction: column;
	}

	.ogt-badge-card__v {
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-h4);
		font-weight: 700;
		line-height: 1.2;
		color: var(--card-head);
		font-variant-numeric: tabular-nums;
	}

	.ogt-badge-card__l {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-subtle);
	}

	/* ------------------------------------------------------ 6. TIMELINE */

	.ogt-timeline {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap-lg);
		position: relative;
	}

	/* The spine. */
	.ogt-timeline::before {
		content: "";
		position: absolute;
		inset-block: 0.75rem 0.75rem;
		inset-inline-start: 11px;
		width: 2px;
		background: linear-gradient(
			to bottom,
			var(--ogt-primary) 0%,
			rgb(var(--ogt-primary-rgb) / 15%) 100%
		);
		border-radius: 2px;
	}

	.ogt-timeline__item {
		position: relative;
		padding-inline-start: 3rem;
		display: flex;
		flex-direction: column;
		gap: 0.35rem;
	}

	.ogt-timeline__marker {
		position: absolute;
		inset-inline-start: 0;
		top: 0.25rem;
		width: 24px;
		height: 24px;
		border-radius: 50%;
		background: var(--ogt-surface);
		border: 2px solid var(--ogt-primary);
		display: grid;
		place-items: center;
		color: var(--ogt-primary);
	}

	.ogt-timeline__marker::after {
		content: "";
		width: 8px;
		height: 8px;
		border-radius: 50%;
		background: var(--ogt-primary);
	}

	.ogt-timeline__year {
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-sm);
		font-weight: 700;
		color: var(--ogt-primary);
		font-variant-numeric: tabular-nums;
	}

	.ogt-timeline__title { font-size: 1.0625rem; }

	.ogt-timeline__text {
		font-size: var(--ogt-fs-sm);
		color: var(--ogt-text-muted);
	}

	.ogt-section--dark .ogt-timeline__marker {
		background: var(--ogt-deep);
		border-color: var(--ogt-accent);
	}

	.ogt-section--dark .ogt-timeline__marker::after { background: var(--ogt-accent); }
	.ogt-section--dark .ogt-timeline__year { color: var(--ogt-accent); }
	.ogt-section--dark .ogt-timeline__text { color: var(--ogt-on-dark-muted); }

	/* ------------------------------------------------- 7. IMAGE + TEXT */

	.ogt-mediatext__media {
		margin: 0;
		border-radius: var(--ogt-radius-img);
		overflow: hidden;
		position: relative;
	}

	.ogt-mediatext__img {
		width: 100%;
		aspect-ratio: 4 / 3;
		object-fit: cover;
	}

	/* ------------------------------------------------------ WHO WE ARE */

	/* The values row is a second band under the lead, not a continuation of the
	   right-hand column — it needs the gap that says so. */
	.ogt-about__values { margin-block-start: var(--ogt-gap-lg); }

	/* In the lead band the heading sits beside the image, so top-align the two
	   rather than centring: a short heading floating against a tall image is the
	   imbalance this section had in the first place. */
	.ogt-about__lead { align-items: start; }

	.ogt-about__media { align-self: stretch; }

	.ogt-about__media .ogt-mediatext__img {
		height: 100%;
		aspect-ratio: 5 / 4;
	}

	@media (max-width: 900px) {
		.ogt-about__media .ogt-mediatext__img {
			height: auto;
			aspect-ratio: 4 / 3;
		}
	}

	.ogt-mediatext__content {
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap-sm);
		align-items: flex-start;
	}

	.ogt-mediatext__text { color: var(--ogt-text-muted); }

	/* ------------------------------------------- 8. CERTIFICATION CARD */

	.ogt-cert {
		flex-direction: row;
		align-items: center;
		justify-content: flex-start;
		gap: 0.85rem;
		padding: 0.95rem 1.15rem;
		position: relative;
		overflow: hidden;
		transition:
			transform var(--ogt-dur) var(--ogt-ease-out),
			box-shadow var(--ogt-dur) var(--ogt-ease-out),
			border-color var(--ogt-dur) var(--ogt-ease);
	}

	/* A brand wash that wipes in from the seal side on hover. */
	.ogt-cert::before {
		content: "";
		position: absolute;
		inset: 0;
		pointer-events: none;
		opacity: 0;
		background: linear-gradient(100deg, rgb(var(--ogt-primary-rgb) / 9%) 0%, transparent 62%);
		transition: opacity var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-cert:hover {
		transform: translateY(-3px);
		box-shadow: var(--ogt-shadow-lg);
		border-color: rgb(var(--ogt-primary-rgb) / 32%);
	}

	.ogt-cert:hover::before { opacity: 1; }

	.ogt-cert__seal {
		flex: 0 0 auto;
		display: grid;
		place-items: center;
		color: var(--ogt-success);
		transition: color var(--ogt-dur) var(--ogt-ease), transform var(--ogt-dur-slow) var(--ogt-ease-spring);
	}

	.ogt-cert:hover .ogt-cert__seal {
		color: var(--ogt-primary);
		transform: rotate(-8deg) scale(1.06);
	}

	.ogt-seal { display: block; }

	.ogt-card--dark .ogt-cert__seal { color: var(--ogt-accent); }

	.ogt-cert__body {
		display: flex;
		flex-direction: column;
		min-width: 0;
		position: relative; /* above the ::before wash */
	}

	.ogt-cert__name {
		font-family: var(--ogt-font-head);
		font-weight: 700;
		font-size: 0.9375rem;
		color: var(--card-head);
		letter-spacing: -0.01em;
	}

	.ogt-cert__note {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-subtle);
	}

	.ogt-card--dark .ogt-cert__note { color: var(--ogt-on-dark-subtle); }

	/*
	 * Section grounds are left to the plain --muted / --ground alternation.
	 *
	 * A dot grid plus two brand washes was tried here and on the offerings, why
	 * and stat sections. Stacked across four consecutive sections it read as
	 * noise, and it competed with the cards it was meant to support. The
	 * separation those blocks needed came from card contrast, elevation and
	 * hover — not from patterning the page behind them.
	 */
	.ogt-excellence {
		position: relative;
		background-color: var(--ogt-muted);
	}

	/* ---------------------------------------------------- 9. INDUSTRY CARD */

	.ogt-industry__title { font-size: 1rem; }

	.ogt-industry__text {
		font-size: var(--ogt-fs-sm);
		color: var(--card-fg);
	}

	/* ------------------------------------------------ 10. TESTIMONIAL CARD */

	/*
	 * The testimonial block used to read as dead: white cards on a near-white
	 * ground, hairline details, and — because every card is forced to the height
	 * of the longest quote — a void between a short quote and its author. The
	 * cure is contrast and scale, not more elements: lift the card off the
	 * ground, set the quote at reading size, and let an oversized quote glyph
	 * occupy the space the text does not.
	 */
	.ogt-quote {
		gap: var(--ogt-gap-sm);
		position: relative;
		overflow: hidden;
		box-shadow: var(--ogt-shadow-md);
		transition:
			transform var(--ogt-dur) var(--ogt-ease-out),
			box-shadow var(--ogt-dur) var(--ogt-ease-out),
			border-color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-quote:hover {
		transform: translateY(var(--ogt-lift));
		box-shadow: var(--ogt-shadow-lg);
		border-color: rgb(var(--ogt-primary-rgb) / 28%);
	}

	/* Watermark: fills the dead space a short quote leaves behind. */
	.ogt-quote::after {
		content: "";
		position: absolute;
		right: -0.35em;
		bottom: -0.55em;
		width: 8rem;
		height: 8rem;
		pointer-events: none;
		background: radial-gradient(circle at 70% 70%, rgb(var(--ogt-primary-rgb) / 7%) 0%, transparent 68%);
	}

	.ogt-quote__mark {
		color: rgb(var(--ogt-primary-rgb) / 20%);
		align-self: flex-start;
	}

	.ogt-quote__mark .ogt-icon {
		width: 34px;
		height: 34px;
	}

	.ogt-card--dark .ogt-quote__mark { color: rgb(var(--ogt-accent-rgb) / 35%); }

	.ogt-stars {
		display: inline-flex;
		gap: 0.15rem;
		color: var(--ogt-star);
		font-size: 1.0625rem;
		line-height: 1;
	}

	/*
	 * Every card is as tall as the longest quote in the row, so a short quote
	 * has slack. Centring the text in that slack splits it above and below
	 * instead of dumping it all between the quote and the author, which is what
	 * made the shorter cards look half-empty.
	 */
	.ogt-quote__text {
		margin: 0;
		font-size: 1.0625rem;
		line-height: 1.68;
		color: var(--ogt-ink);
		flex: 1;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 0.7em;
	}

	.ogt-card--dark .ogt-quote__text { color: var(--card-fg); }

	/* A single feature quote — the leadership message, not a grid item. */
	.ogt-quote--lead .ogt-quote__text { font-size: var(--ogt-fs-lg); }

	.ogt-quote__text p { margin: 0; }

	.ogt-quote__by {
		display: flex;
		align-items: center;
		gap: 0.75rem;
		border-top: 1px solid var(--card-bd);
		padding-block-start: var(--ogt-gap-sm);
	}

	/* Initial-avatar — a named face without a stock photo of a stranger.
	   A solid gradient chip rather than a 10% tint: at 10% it disappeared into
	   the card and the author line had no anchor. */
	.ogt-quote__avatar {
		flex: 0 0 auto;
		width: 48px;
		height: 48px;
		border-radius: 50%;
		display: grid;
		place-items: center;
		background: linear-gradient(135deg, var(--ogt-primary) 0%, var(--ogt-secondary) 100%);
		color: var(--ogt-on-primary);
		font-family: var(--ogt-font-head);
		font-weight: 700;
		font-size: 1.125rem;
		line-height: 1;
		box-shadow: 0 2px 8px rgb(var(--ogt-primary-rgb) / 30%);
	}

	.ogt-card--dark .ogt-quote__avatar {
		background: rgb(255 255 255 / 12%);
		color: var(--ogt-accent);
	}

	.ogt-quote__id {
		display: flex;
		flex-direction: column;
		gap: 0.1rem;
		min-width: 0;
	}

	.ogt-quote__name {
		font-family: var(--ogt-font-head);
		font-weight: 700;
		font-size: 0.9375rem;
		color: var(--card-head);
	}

	.ogt-quote__meta {
		font-size: var(--ogt-fs-sm);
		color: var(--ogt-text-muted);
	}

	.ogt-card--dark .ogt-quote__meta { color: var(--ogt-on-dark-subtle); }

	/* Testimonial card as a slide: full height so a row lines up, mark up top. */
	.ogt-quote--card {
		height: 100%;
		--card-pad: clamp(1.6rem, 3vw, 2.1rem);
	}

	/*
	 * Section ground. Flat --muted behind white cards gave almost no separation,
	 * which is most of why the block read as inert. A soft brand wash plus the
	 * cards' own shadow gives the white something to sit on.
	 */
	.ogt-quotesec {
		background:
			radial-gradient(70% 60% at 15% 0%, rgb(var(--ogt-primary-rgb) / 7%) 0%, transparent 60%),
			radial-gradient(60% 60% at 90% 100%, rgb(var(--ogt-accent-rgb) / 8%) 0%, transparent 62%),
			var(--ogt-muted);
	}

	/* ------------------------------------------------------ SLIDER (carousel)
	 * A scroll-snap track: usable as a plain horizontal scroller before ui.js
	 * runs and without JS at all. ui.js adds arrows, dots and autoplay.
	 * ------------------------------------------------------------------------ */
	.ogt-slider { margin-block-start: var(--ogt-gap-lg); }

	.ogt-slider__track {
		list-style: none;
		margin: 0;
		padding: 0.25rem 0.25rem 0.75rem;
		display: flex;
		gap: var(--ogt-gap);
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		scroll-behavior: smooth;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	.ogt-slider__track::-webkit-scrollbar { display: none; }

	.ogt-slider__track:focus-visible {
		outline: 2px solid var(--ogt-primary);
		outline-offset: 3px;
		border-radius: var(--ogt-radius-sm);
	}

	.ogt-slider__slide {
		flex: 0 0 100%;
		scroll-snap-align: start;
	}

	@media (min-width: 640px) {
		.ogt-slider__slide { flex-basis: calc(50% - var(--ogt-gap) / 2); }
	}

	@media (min-width: 1000px) {
		.ogt-slider__slide { flex-basis: calc((100% - var(--ogt-gap) * 2) / 3); }
	}

	/* A single quote renders centred, not stretched across a 3-up track. */
	.ogt-slider--static .ogt-slider__track { justify-content: center; }
	.ogt-slider--static .ogt-slider__slide { flex-basis: min(100%, 520px); }

	/*
	 * Card variant — used by the blog section.
	 *
	 * The slide is a flex item, so its card child sits at content height and a
	 * row of posts with different title lengths ends at ragged heights. Making
	 * the slide a flex container and stretching the card fixes it. The
	 * responsive 1/2/3-up sizing above is inherited unchanged, which is the
	 * whole reason this reuses the slider rather than adding another one.
	 */
	.ogt-slider--cards .ogt-slider__slide {
		display: flex;
		align-items: stretch;
	}

	.ogt-slider--cards .ogt-slider__slide > * {
		width: 100%;
	}

	.ogt-slider__nav {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 1.25rem;
		margin-block-start: var(--ogt-gap);
	}

	.ogt-slider__arrow {
		flex: 0 0 auto;
		width: 48px;
		height: 48px;
		display: grid;
		place-items: center;
		border-radius: var(--ogt-radius-pill);
		border: 1px solid var(--ogt-border);
		background: var(--ogt-surface);
		color: var(--ogt-primary);
		cursor: pointer;
		box-shadow: var(--ogt-shadow-sm);
		transition:
			background-color var(--ogt-dur) var(--ogt-ease),
			border-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease),
			box-shadow var(--ogt-dur) var(--ogt-ease),
			transform var(--ogt-dur) var(--ogt-ease-out),
			opacity var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-slider__arrow:hover {
		background: var(--ogt-primary);
		border-color: var(--ogt-primary);
		color: var(--ogt-on-primary);
		box-shadow: var(--ogt-shadow-md);
		transform: translateY(-2px);
	}

	.ogt-slider__arrow:disabled {
		opacity: 0.35;
		cursor: default;
		background: var(--ogt-surface);
		border-color: var(--ogt-border);
		color: var(--ogt-primary);
	}

	/* One shared arrow glyph; the previous button points the other way. */
	[data-ogt-slider-prev] .ogt-icon { transform: rotate(180deg); }

	.ogt-slider__dots {
		display: flex;
		align-items: center;

		/*
		 * No gap. The dots now carry their own padding to reach a 24px hit
		 * area, and an additional gap on top of that would push them apart
		 * visually as well — the spacing between the visible dots comes from
		 * the transparent padding instead.
		 */
		gap: 0;
	}

	/*
	 * A 9px dot inside a 24px target.
	 *
	 * The dot itself was the button, which made it a 9x9 tap target. WCAG 2.2
	 * SC 2.5.8 sets the minimum at 24x24 CSS pixels at Level AA, and 9px is
	 * the kind of control that gets missed twice and then abandoned on a
	 * phone.
	 *
	 * The size is carried by padding and a transparent background, with the
	 * visible dot drawn by ::after. That keeps the design exactly as it was
	 * while tripling the area a thumb can land on. background-clip is what
	 * stops the :hover and .is-active colours painting the whole 24px box.
	 */
	.ogt-slider__dot {
		width: 24px;
		height: 24px;
		padding: 0;
		border: 0;
		background: none;
		display: grid;
		place-items: center;
		cursor: pointer;
	}

	.ogt-slider__dot::after {
		content: "";
		width: 9px;
		height: 9px;
		border-radius: var(--ogt-radius-pill);
		background: rgb(var(--ogt-deep-rgb) / 18%);
		transition: background-color var(--ogt-dur) var(--ogt-ease), width var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-slider__dot:hover::after { background: rgb(var(--ogt-primary-rgb) / 45%); }

	.ogt-slider__dot.is-active::after {
		width: 28px;
		background: var(--ogt-primary);
	}

	/* The active pill is wider than the 24px box, so let it out. */
	.ogt-slider__dot.is-active { width: 40px; }

	/* ------------------------------------------------------ 11. CTA BANNER */

	/*
	 * Same vivid ramp as the hero.
	 *
	 * This panel was left on the old --ogt-deep → --ogt-deep-2 gradient, which
	 * starts at #0F172A — almost black. That is precisely what made the hero
	 * read heavy and lifeless before it was changed, and the brand blue never
	 * actually appeared. The closing call to action is the last thing a visitor
	 * sees; it should not be the dullest block on the page.
	 */
	.ogt-cta__panel {
		position: relative;
		border-radius: var(--ogt-radius-card);
		padding: clamp(2.75rem, 6vw, 4.25rem) clamp(1.5rem, 4vw, 3rem);
		background:
			radial-gradient(60% 100% at 88% 90%, rgb(var(--ogt-accent-rgb) / 32%) 0%, transparent 62%),
			radial-gradient(55% 90% at 10% 0%, rgb(255 255 255 / 14%) 0%, transparent 58%),
			linear-gradient(125deg, var(--ogt-primary-hover) 0%, var(--ogt-primary) 45%, var(--ogt-secondary) 100%);
		color: var(--ogt-on-dark-muted);
		display: flex;
		flex-direction: column;
		align-items: center;
		text-align: center;
		gap: var(--ogt-gap-sm);
		overflow: hidden;
	}

	/* This button is the whole purpose of the section — size it that way. */
	.ogt-cta__actions .ogt-btn {
		padding: 1rem 1.9rem;
		font-size: 1rem;
	}

	.ogt-cta__title {
		color: var(--ogt-on-dark);
		max-width: 24ch;
	}

	.ogt-cta__text {
		max-width: 58ch;
		color: var(--ogt-on-dark-muted);
		font-size: 0.9375rem;
	}

	.ogt-cta__actions {
		display: flex;
		flex-wrap: wrap;
		gap: var(--ogt-gap-sm);
		justify-content: center;
		margin-block-start: 0.75rem;
	}

	/* Split variant — copy left, actions right. */
	.ogt-cta__panel--split {
		flex-direction: row;
		align-items: center;
		text-align: start;
		justify-content: space-between;
		gap: var(--ogt-gap);
	}

	@media (max-width: 820px) {
		.ogt-cta__panel--split {
			flex-direction: column;
			text-align: center;
		}
	}

	/* --------------------------------------------------- 12. FAQ ACCORDION */

	.ogt-faqs {
		display: flex;
		flex-direction: column;
		gap: 0.6rem;
	}

	.ogt-faq {
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-sm);
		background: var(--ogt-surface);
		overflow: hidden;
		transition: border-color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-faq[open] {
		border-color: rgb(var(--ogt-primary-rgb) / 30%);
		box-shadow: var(--ogt-shadow-sm);
	}

	.ogt-faq__q {
		cursor: pointer;
		padding: 1.1rem 1.25rem;
		font-family: var(--ogt-font-head);
		font-weight: 600;
		font-size: 0.9375rem;
		color: var(--ogt-ink);
		list-style: none;
		display: flex;
		justify-content: space-between;
		align-items: center;
		gap: 1rem;
	}

	.ogt-faq__q::-webkit-details-marker { display: none; }

	.ogt-faq__q::after {
		content: "+";
		font-size: 1.35rem;
		font-weight: 400;
		line-height: 1;
		color: var(--ogt-primary);
		flex: 0 0 auto;
		transition: transform var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-faq[open] .ogt-faq__q::after { transform: rotate(45deg); }
	.ogt-faq__q:hover { background: var(--ogt-muted); }

	.ogt-faq__a {
		padding: 0 1.25rem 1.25rem;
		color: var(--ogt-text-muted);
		font-size: 0.9375rem;
	}

	/*
	 * Smooth open as progressive enhancement. Browsers without
	 * ::details-content get an instant, fully working accordion — no JS,
	 * no height measuring.
	 */
	@supports (interpolate-size: allow-keywords) {

		:root { interpolate-size: allow-keywords; }

		.ogt-faq::details-content {
			height: 0;
			overflow: hidden;
			transition:
				height var(--ogt-dur-slow) var(--ogt-ease),
				content-visibility var(--ogt-dur-slow) allow-discrete;
		}

		.ogt-faq[open]::details-content { height: auto; }
	}

	/* --------------------------------------------------- 17. ICON LIST */

	.ogt-iconlist {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: column;
		gap: 0.6rem;
		width: 100%;
	}

	.ogt-iconlist__item {
		display: flex;
		align-items: flex-start;
		gap: 0.6rem;
		font-size: 0.9375rem;
	}

	.ogt-iconlist__icon {
		color: var(--ogt-success);
		margin-block-start: 0.15rem;
	}

	.ogt-section--dark .ogt-iconlist__icon { color: var(--ogt-accent); }

	.ogt-iconlist--two {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 0.6rem 1.5rem;
	}

	@media (max-width: 560px) {
		.ogt-iconlist--two { grid-template-columns: 1fr; }
	}

	.ogt-iconlist--boxed {
		gap: 0.6rem;
	}

	.ogt-iconlist--boxed .ogt-iconlist__item {
		background: var(--ogt-muted);
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-sm);
		padding: 0.85rem 1rem;
		align-items: center;
	}

	.ogt-iconlist--inline {
		flex-direction: row;
		flex-wrap: wrap;
		gap: 0.5rem 1.5rem;
	}

	/* ---------------------------------------------------- 15. COUNTRY CARD */

	.ogt-country {
		flex-direction: row;
		align-items: center;
		gap: 0.75rem;
		padding: 0.85rem 1rem;
	}

	.ogt-country__name {
		font-family: var(--ogt-font-head);
		font-weight: 600;
		font-size: 0.9rem;
		color: var(--card-head);
	}

	.ogt-country__note {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-subtle);
	}

	/* Chip variant — the compact region list. */
	.ogt-chips {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-wrap: wrap;
		gap: 0.5rem;
	}

	.ogt-chip {
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-pill);
		padding: 0.4rem 0.9rem;
		font-size: var(--ogt-fs-sm);
		font-weight: 500;
		color: var(--ogt-text);
		background: var(--ogt-surface);
	}

	.ogt-section--dark .ogt-chip {
		background: rgb(255 255 255 / 7%);
		border-color: var(--ogt-on-dark-border);
		color: var(--ogt-on-dark-muted);
	}

	.ogt-chip__flag {
		display: inline-flex;
		align-items: center;
		flex: 0 0 auto;
		line-height: 1;
	}

	/* Drawn flags get a hairline + rounding so a white field (e.g. Japan) still
	   reads as a flag rather than a gap. */
	.ogt-flag {
		display: block;
		border-radius: 2px;
		box-shadow: 0 0 0 1px rgb(var(--ogt-deep-rgb) / 12%);
	}

	/* Region chips that link to the map are interactive. */
	.ogt-chip[data-region] {
		cursor: default;
		transition:
			border-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease),
			background-color var(--ogt-dur) var(--ogt-ease),
			transform var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-chip[data-region]:hover,
	.ogt-chip[data-region]:focus-visible,
	.ogt-chip[data-region].is-active {
		border-color: var(--ogt-primary);
		color: var(--ogt-primary);
		transform: translateY(-1px);
		outline: none;
	}

	/* ------------------------------------------------ GLOBAL PRESENCE MAP */

	.ogt-presence__map {
		display: flex;
		align-items: center;
	}

	.ogt-worldmap {
		position: relative;
		width: 100%;
		aspect-ratio: 1475 / 806;
	}

	/*
	 * The map artwork is an ALPHA MASK, not a picture: the dots are opacity and
	 * the paint is --ogt-primary. That is what lets a raster map follow the brand
	 * colour from settings instead of being locked to the source file's teal.
	 *
	 * Wrapped in @supports so a browser without mask-image gets nothing rather
	 * than a solid primary-coloured slab where the map should be.
	 */
	.ogt-worldmap__plate {
		position: absolute;
		inset: 0;
	}

	@supports (mask-image: url("")) or (-webkit-mask-image: url("")) {
		.ogt-worldmap__plate {
			background-color: var(--ogt-primary);
			opacity: 0.55;
			-webkit-mask-image: url("../img/worldmap.png");
			mask-image: url("../img/worldmap.png");
			-webkit-mask-size: contain;
			mask-size: contain;
			-webkit-mask-repeat: no-repeat;
			mask-repeat: no-repeat;
			-webkit-mask-position: center;
			mask-position: center;
		}
	}

	/*
	 * Served-region pins. The source art has no per-country geometry, so a
	 * region is marked rather than tinted. Positions are per-continent classes
	 * — the continent set is fixed, so they belong in CSS, and that keeps the
	 * template free of inline styles.
	 */
	.ogt-worldmap__pin {
		position: absolute;
		width: 14px;
		height: 14px;
		transform: translate(-50%, -50%);
	}

	.ogt-worldmap__pin--north-america { left: 19%; top: 37%; }
	.ogt-worldmap__pin--south-america { left: 30%; top: 71%; }
	.ogt-worldmap__pin--europe        { left: 54%; top: 26%; }
	.ogt-worldmap__pin--africa        { left: 52%; top: 68%; }
	.ogt-worldmap__pin--asia          { left: 76%; top: 40%; }
	.ogt-worldmap__pin--oceania       { left: 83%; top: 86%; }

	.ogt-worldmap__dot,
	.ogt-worldmap__pulse {
		position: absolute;
		inset: 0;
		border-radius: 50%;
	}

	.ogt-worldmap__dot {
		background: var(--ogt-primary);
		box-shadow: 0 0 0 3px rgb(var(--ogt-surface-rgb) / 85%);
		transition: background-color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-worldmap__pulse {
		background: rgb(var(--ogt-primary-rgb) / 45%);
		animation: ogt-ping 2.4s var(--ogt-ease-out) infinite;
	}

	.ogt-worldmap__pin.is-active .ogt-worldmap__pulse {
		background: rgb(var(--ogt-accent-rgb) / 55%);
		animation-duration: 1.1s;
	}

	.ogt-worldmap__pin.is-active .ogt-worldmap__dot { background: var(--ogt-accent); }

	@keyframes ogt-ping {
		0%   { transform: scale(0.6); opacity: 0.9; }
		70%  { transform: scale(2.8); opacity: 0; }
		100% { transform: scale(2.8); opacity: 0; }
	}

	@media (prefers-reduced-motion: reduce) {
		.ogt-worldmap__pulse { animation: none; opacity: 0.5; }
	}

	@media (max-width: 640px) {
		.ogt-presence__map { margin-block-start: var(--ogt-gap); }
	}

	/* -------------------------------------------------- 16. DOWNLOAD CARD */

	.ogt-download {
		flex-direction: row;
		align-items: center;
		gap: var(--ogt-gap-sm);
	}

	.ogt-download__body {
		display: flex;
		flex-direction: column;
		flex: 1;
		min-width: 0;
	}

	.ogt-download__title {
		font-family: var(--ogt-font-head);
		font-weight: 600;
		font-size: 0.9375rem;
		color: var(--card-head);
	}

	.ogt-download__meta {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-subtle);
		text-transform: uppercase;
		letter-spacing: 0.06em;
	}

	.ogt-download__action {
		color: var(--ogt-primary);
		flex: 0 0 auto;
	}

	/* ---------------------------------------------------- 18. LOGO SLIDER */

	.ogt-logos {
		--logo-h: 40px;
		position: relative;
		overflow: hidden;
		/* Fade the edges so the loop has no hard cut. */
		mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	}

	.ogt-logos__track {
		display: flex;
		align-items: center;
		gap: clamp(2rem, 5vw, 4rem);
		width: max-content;
		animation: ogt-marquee 40s linear infinite;
	}

	.ogt-logos:hover .ogt-logos__track { animation-play-state: paused; }

	@media (prefers-reduced-motion: reduce) {
		.ogt-logos__track { animation: none; }
		.ogt-logos { mask-image: none; }
	}

	.ogt-logos--static .ogt-logos__track {
		animation: none;
		width: auto;
		flex-wrap: wrap;
		justify-content: center;
	}

	.ogt-logos__item {
		flex: 0 0 auto;
		height: var(--logo-h);
		display: grid;
		place-items: center;
	}

	.ogt-logos__img {
		max-height: var(--logo-h);
		width: auto;
		/* Desaturate so mismatched client logos read as one row. */
		filter: grayscale(1);
		opacity: 0.55;
		transition: filter var(--ogt-dur) var(--ogt-ease), opacity var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-logos__item:hover .ogt-logos__img {
		filter: grayscale(0);
		opacity: 1;
	}

	@keyframes ogt-marquee {
		from { transform: translateX(0); }
		to { transform: translateX(-50%); }
	}

	/* ------------------------------------------------------ 19. BREADCRUMB */

	/*
	 * Breadcrumbs. A chevron separator reads as direction where a slash reads as
	 * punctuation, and the trail gets a little more presence: links respond, and
	 * the current page is the one item in full ink so you can see where you are
	 * at a glance.
	 */
	.ogt-crumbs ol {
		list-style: none;
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 0.4rem;
		margin: 0 0 0.9rem;
		padding: 0;
		font-size: var(--ogt-fs-sm);
		color: var(--ogt-text-subtle);
	}

	/*
	 * Tables scroll inside their own box, never the page.
	 *
	 * Paired with ogt_wrap_content_tables() in inc/setup.php. Both selectors are
	 * listed because the block editor already wraps its own tables in a
	 * <figure>, and whichever element is outermost needs to be the scroller.
	 */
	.ogt-tablewrap,
	.ogt-prose figure.wp-block-table {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		max-width: 100%;
	}

	/* Without this the table shrinks its columns to fit instead of scrolling,
	   which is what produced unreadable two-character-wide cells. */
	.ogt-tablewrap > table {
		min-width: max-content;
	}

	/* Breadcrumb links measured 27px tall on a phone. A generous tap box via
	   padding, with a matching negative margin so the trail's visual height and
	   alignment are unchanged. */
	@media (max-width: 860px) {
		.ogt-crumbs a {
			display: inline-flex;
			align-items: center;
			min-height: 44px;
			padding-inline: 0.25rem;
			margin-inline: -0.25rem;
		}
	}

	.ogt-crumbs li {
		display: flex;
		align-items: center;
		gap: 0.4rem;
	}

	.ogt-crumbs li + li::before {
		content: "";
		width: 5px;
		height: 5px;
		border-inline-end: 1.5px solid currentColor;
		border-block-start: 1.5px solid currentColor;
		transform: rotate(45deg);
		opacity: 0.45;
	}

	.ogt-crumbs a {
		color: var(--ogt-text-muted);
		border-radius: var(--ogt-radius-sm);
		padding: 0.1rem 0.25rem;
		transition: color var(--ogt-dur) var(--ogt-ease), background-color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-crumbs a:hover {
		color: var(--ogt-primary);
		background: rgb(var(--ogt-primary-rgb) / 8%);
	}

	/* The current page — last item, not a link. */
	.ogt-crumbs li:last-child {
		color: var(--ogt-ink);
		font-weight: 600;
	}

	.ogt-crumbs a {
		color: inherit;
		text-decoration: none;
	}

	/* Colour shift only — a breadcrumb's position already says it is a link. */
	.ogt-crumbs a:hover {
		color: var(--ogt-primary);
	}

	.ogt-crumbs [aria-current="page"] { color: var(--ogt-text); }

	/* ----------------------------------------------------- 20. EMPTY STATE */

	.ogt-empty {
		display: flex;
		flex-direction: column;
		align-items: center;
		text-align: center;
		gap: 0.6rem;
		padding: clamp(2rem, 5vw, 3.5rem) 1.5rem;
		color: var(--ogt-text-subtle);
	}

	.ogt-empty__icon {
		color: rgb(var(--ogt-ink-rgb) / 20%);
		margin-block-end: 0.25rem;
	}

	.ogt-empty__title {
		font-size: 1rem;
		color: var(--ogt-ink);
	}

	.ogt-empty__text { font-size: var(--ogt-fs-sm); max-width: 46ch; }

	/* Admin-only variant: dashed, so it reads as scaffolding not content. */
	.ogt-empty--admin {
		border: 1px dashed var(--ogt-border);
		border-radius: var(--ogt-radius-card);
		background: var(--ogt-muted);
	}

	.ogt-empty__hint {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-subtle);
	}

	/* ------------------------------------------------ 21. PLACEHOLDER IMAGE
	 * Not decorative — 0 of 80 products have a photo, so this IS the catalogue's
	 * default state. Pure CSS: zero image requests, zero bytes.
	 * Reserves the same aspect ratio as the ogt-card image size (640x420), so
	 * dropping in real photography later causes no layout shift.
	 */

	.ogt-ph {
		position: relative;
		display: grid;
		place-items: center;
		gap: 0.5rem;
		aspect-ratio: 640 / 420;
		background: var(--ogt-muted);
		border-radius: var(--ogt-radius-img);
		overflow: hidden;
		color: rgb(var(--ogt-primary-rgb) / 35%);
	}

	/* Hairline diagonals — texture without an image. */
	.ogt-ph::before {
		content: "";
		position: absolute;
		inset: 0;
		background-image: repeating-linear-gradient(
			45deg,
			rgb(var(--ogt-primary-rgb) / 3%) 0 2px,
			transparent 2px 10px
		);
	}

	.ogt-ph__inner {
		position: relative;
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 0.5rem;
	}

	.ogt-ph__label {
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-xs);
		font-weight: 600;
		letter-spacing: 0.14em;
		text-transform: uppercase;
		/* Was ink/28% — 1.84:1, unreadable. A placeholder that looks broken is
		   worse than no placeholder, and this label is what tells the client
		   which image is missing. */
		color: var(--ogt-text-subtle);
	}

	.ogt-ph--square { aspect-ratio: 1; }
	.ogt-ph--wide { aspect-ratio: 16 / 9; }
	.ogt-ph--portrait { aspect-ratio: 3 / 4; }

	.ogt-ph--dark {
		background: rgb(255 255 255 / 5%);
		color: rgb(var(--ogt-accent-rgb) / 50%);
	}

	.ogt-ph--dark .ogt-ph__label { color: var(--ogt-on-dark-subtle); }

	/*
	 * Tinted placeholder — the product's own colour instead of neutral grey.
	 *
	 * Must sit AFTER .ogt-ph: both are a single class, so at equal specificity
	 * source order decides, and a modifier declared earlier in the file silently
	 * loses to its own base. (It did, first time round.)
	 *
	 * color-mix keeps one source of truth: the same hex the buyer sees in the
	 * spec table, lightened against white here so the title and the icon over it
	 * still clear contrast. The alternative — storing a second, hand-picked
	 * "light" value per product — is 37 more values to keep in step.
	 */
	.ogt-ph--tinted {
		background:
			radial-gradient(
				120% 120% at 30% 15%,
				color-mix(in srgb, var(--ph-tint) 26%, #fff) 0%,
				color-mix(in srgb, var(--ph-tint) 12%, #fff) 60%,
				color-mix(in srgb, var(--ph-tint) 20%, #fff) 100%
			);
	}

	.ogt-ph--tinted .ogt-ph__inner { color: color-mix(in srgb, var(--ph-tint) 78%, #000); }
	.ogt-ph--tinted .ogt-ph__label { color: color-mix(in srgb, var(--ph-tint) 66%, #000); }

	/* ------------------------------------------------------ MAP (click-to-load)
	 * An embedded map iframe sets third-party cookies and sends the visitor's IP
	 * to Google before any consent, and costs ~800KB on a page whose job is a
	 * phone number. This is a link that looks like a map.
	 */

	.ogt-maplink {
		position: relative;
		display: block;
		border-radius: var(--ogt-radius-img);
		overflow: hidden;
		text-decoration: none;
		border: 1px solid var(--ogt-border);
		transition: border-color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-maplink:hover { border-color: rgb(var(--ogt-primary-rgb) / 40%); }

	/*
	 * Cap the map affordance's height.
	 *
	 * It inherits the 16:9 placeholder, which at container width renders about
	 * 1278x719 — 80% of the viewport for a grey panel holding a pin, an address
	 * and a button. A band is enough to read as "map, click me"; the height was
	 * pushing the rest of the contact page off the screen.
	 */
	.ogt-maplink .ogt-ph {
		aspect-ratio: auto;
		height: clamp(240px, 26vw, 360px);
	}

	.ogt-maplink__cta {
		position: absolute;
		inset-inline-end: 1rem;
		inset-block-end: 1rem;
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		padding: 0.6rem 1rem;
		border-radius: var(--ogt-radius-pill);
		background: var(--ogt-surface);
		color: var(--ogt-primary);
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-sm);
		font-weight: 600;
		box-shadow: var(--ogt-shadow-md);
	}

	/* ------------------------------------------- 13. PRODUCT & POST CARDS */
	/*
	 * Moved here from catalogue.css, which only loads on catalogue templates
	 * (ogt_is_catalogue() in inc/assets.php). Two places render these cards
	 * outside that condition and so got no styling at all:
	 *
	 *   - inc/patterns.php renders card/product in a block pattern, which can
	 *     be placed on any page — the "Selected APIs" grid;
	 *   - index.php renders card/post for the blog, and that card reuses
	 *     .ogt-pcard__media / .ogt-pcard__body.
	 *
	 * Unstyled, the spec <dl> fell back to the UA default — each value on its
	 * own line under a 40px indent — the body had no padding, and because the
	 * body never grew, the auto top margin on .ogt-more had nothing to push
	 * against, so CTAs sat wherever the copy happened to end.
	 *
	 * This is the shared component; catalogue.css keeps only the filter bar and
	 * other archive-specific furniture.
	 */

	.ogt-pcard {
		--card-pad: 0;
		overflow: hidden;
	}

	.ogt-pcard__media {
		position: relative;
		border-radius: 0;
	}

	/*
	 * 16:9, matching the source artwork.
	 *
	 * This box was 640/420 while the images are 16:9, and `object-fit: cover`
	 * resolves that mismatch by cropping the sides — which is fine for a
	 * photograph and destructive for the blog banners, where the headline is set
	 * inside the image. Article titles were being cut off mid-word.
	 *
	 * The ratio and the requested image size have to agree. If either changes,
	 * change both, or cover starts trimming again.
	 */
	.ogt-pcard__media img,
	.ogt-pcard__media .ogt-ph {
		width: 100%;
		aspect-ratio: 16 / 9;
		object-fit: cover;
		border-radius: 0;
	}

	/*
	 * Text-only card, when Catalogue → "Image on product cards" suppresses the
	 * media block.
	 *
	 * A thin brand rule along the top replaces the visual weight the image was
	 * carrying. Without it a grid of text cards reads as a table that has lost
	 * its lines — there is nothing to mark where one product ends and the next
	 * begins beyond a hairline border.
	 */
	.ogt-pcard--flat {
		border-block-start: 1px solid var(--ogt-primary);
	}

	/* The body's top padding was tuned to sit under an image; with the media
	   gone the title crowds the rule above it. */
	.ogt-pcard--flat .ogt-pcard__body {
		padding-block-start: 1.4rem;
	}

	.ogt-pcard__body {
		padding: 1.15rem 1.25rem 1.35rem;
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
		flex: 1;
		min-width: 0;
	}

	.ogt-pcard__title {
		font-size: 1rem;
		line-height: 1.3;
	}

	/* CAS is what B2B buyers scan for — give it monospace-like alignment. */
	.ogt-pcard__specs {
		margin: 0;
		display: flex;
		flex-direction: column;
		gap: 0.15rem;
		font-size: var(--ogt-fs-xs);
	}

	.ogt-pcard__spec {
		display: flex;
		gap: 0.4rem;
	}

	.ogt-pcard__spec dt {
		color: var(--ogt-text-subtle);
		font-weight: 600;
		flex: 0 0 auto;
		min-width: 2.4rem;
	}

	/* margin:0 kills the UA's default <dd> indent. */
	.ogt-pcard__spec dd {
		margin: 0;
		font-variant-numeric: tabular-nums;
		color: var(--ogt-text);
		overflow-wrap: anywhere;
	}

	.ogt-pcard__foot {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.5rem;
		margin-block-start: auto;
		padding-block-start: 0.6rem;
		border-top: 1px solid var(--ogt-border);
	}

	/* .ogt-more carries margin-block-start:auto for cards where it is the only
	   footer child; in this flex row that would push it off the baseline. */
	.ogt-pcard__foot .ogt-more { margin-block-start: 0; }

	/* Right-hand group: WhatsApp then Details, with the badge still pushed left
	   by the foot's space-between. */
	.ogt-pcard__actions {
		display: inline-flex;
		align-items: center;
		gap: 0.6rem;
	}

	/*
	 * WhatsApp enquiry on the card.
	 *
	 * position/z-index are load-bearing: the card title carries .ogt-card__link,
	 * whose ::after stretches over the whole card to make it one big click
	 * target. Without lifting above that overlay this link would be unclickable
	 * — the card would swallow every tap.
	 *
	 * 32px is below the 44px pointer-target guideline, which is a deliberate
	 * trade in a dense 4-across grid: it sits beside the always-available
	 * full-size button on the product page, and the whole card remains tappable
	 * for the primary action.
	 */
	.ogt-pcard__wa {
		position: relative;
		z-index: 1;
		display: grid;
		place-items: center;
		width: 32px;
		height: 32px;
		flex: none;
		border-radius: 50%;
		background: rgb(37 211 102 / 14%);
		color: #0b6b35;   /* 5.96:1 on the tint over a white card */
		transition:
			background-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease);
	}

	/*
	 * Extends the tap target to 44px without growing the circle.
	 *
	 * The visible button is 32px and carries a border-radius of 50%, so simply
	 * setting width/height to 44 would have enlarged the green disc and
	 * crowded the card footer. An invisible inset pseudo-element gives the
	 * finger 44px to land on while the design stays as drawn — on a control
	 * that repeats once per product across a 313-item catalogue.
	 */
	.ogt-pcard__wa::before {
		content: "";
		position: absolute;
		inset: -6px;
	}

	.ogt-pcard__wa:hover,
	.ogt-pcard__wa:focus-visible {
		background: #25d366;
		color: #04310f;   /* 7.30:1 — same pair as the other WhatsApp controls */
	}

	/* Availability badge. */
	.ogt-badge {
		display: inline-flex;
		align-items: center;
		gap: 0.3rem;
		/* 12px floor, from the token scale. This was a hardcoded 11px and it
		   carries the Available / On request status on every product card. */
		font-size: var(--ogt-fs-xs);
		font-weight: 700;
		letter-spacing: 0.05em;
		text-transform: uppercase;
		padding: 0.25rem 0.6rem;
		border-radius: var(--ogt-radius-pill);
	}

	/*
	 * Brand green on its own 10% tint measures 3.13:1 — under AA, and this text
	 * is 11px, so the large-text allowance does not apply. Mixing toward ink
	 * lands ~4.9:1 and still reads as the same green.
	 */
	.ogt-badge.is-yes {
		background: rgb(var(--ogt-success-rgb) / 10%);
		color: color-mix(in srgb, var(--ogt-success) 72%, var(--ogt-ink));
	}

	.ogt-badge.is-no {
		background: rgb(var(--ogt-ink-rgb) / 7%);
		color: var(--ogt-text-subtle);
	}

	/* The post card reuses .ogt-service__text, whose vertical centring is meant
	   for short service blurbs and leaves a blog excerpt floating. */
	.ogt-card--bleed .ogt-service__text { justify-content: flex-start; }

	/* --------------------------------------------------------- BLOG SECTION */

	/*
	 * The grid itself is .ogt-u-grid, which auto-fits from three columns down to
	 * one — no breakpoints here on purpose.
	 *
	 * .ogt-u-stack aligns its children to flex-start. The CTA reads as a closing
	 * action so it centres under the grid, done on the element itself rather
	 * than with .ogt-u-stack--center, which would also force text-align:center
	 * onto every card in the row.
	 */
	.ogt-blogsec__more {
		align-self: center;
		margin: 0;
	}

	/* ------------------------------------------------------------- PERSON */

	.ogt-person { overflow: hidden; }

	.ogt-person .ogt-pcard__media img,
	.ogt-person .ogt-pcard__media .ogt-ph {
		aspect-ratio: 1;
		object-fit: cover;
		border-radius: 0;
	}

	/* ------------------------------------------------------------- PROCESS */

	/* Connect the steps on wide screens — reads as a flow, not four cards. */
	@media (min-width: 901px) {

		.ogt-process .ogt-u-grid { position: relative; }

		.ogt-process__step::after {
			content: "";
			position: absolute;
			top: calc(var(--ogt-card-pad) + 24px);
			inset-inline-start: 100%;
			width: var(--ogt-gap);
			height: 2px;
			background: repeating-linear-gradient(
				90deg,
				rgb(var(--ogt-primary-rgb) / 30%) 0 4px,
				transparent 4px 8px
			);
		}

		.ogt-process__step:last-child::after { display: none; }
	}
}

/* ============================================================================
   CHROME — topbar, header, footer
   ========================================================================== */

@layer components {

	/* ---------------------------------------------------------------- TOPBAR */

	/*
	 * Shimmer — a slow highlight sweeping across the deep bar.
	 *
	 * Done as a moving BACKGROUND rather than a sliding ::after element, and
	 * that choice is load-bearing: the element version needed `overflow: hidden`
	 * to stop the sheen spilling out of the bar, and that clipped the language
	 * switcher's dropdown — a 320px menu inside a 45px bar, so it opened and was
	 * entirely invisible. A background layer is confined to the box for free, so
	 * the bar can stay `overflow: visible` and the dropdown can escape it.
	 *
	 * Low alpha, behind the text, and off entirely under reduced motion.
	 */
	.ogt-topbar {
		background-color: var(--ogt-deep);
		background-image: linear-gradient(
			100deg,
			transparent 40%,
			rgb(255 255 255 / 7%) 48%,
			rgb(var(--ogt-accent-rgb) / 10%) 53%,
			transparent 61%
		);
		background-repeat: no-repeat;
		background-size: 260% 100%;
		color: var(--ogt-on-dark-muted);
		font-size: var(--ogt-fs-xs);
		position: relative;
		animation: ogt-sheen 7s var(--ogt-ease) infinite;
	}

	@keyframes ogt-sheen {
		0%   { background-position: 130% 0; }
		55%  { background-position: -30% 0; }
		100% { background-position: -30% 0; }
	}

	@media (prefers-reduced-motion: reduce) {
		.ogt-topbar { animation: none; background-image: none; }
	}

	.ogt-topbar__inner {
		display: flex;
		flex-wrap: wrap;
		gap: 0.3rem 1.5rem;
		align-items: center;
		justify-content: space-between;
		padding-block: 0.6rem;
	}

	.ogt-topbar__msg {
		display: inline-flex;
		align-items: center;
		gap: 0.5rem;
	}

	.ogt-topbar__contact {
		display: flex;
		flex-wrap: wrap;
		gap: 1.25rem;

		/*
		 * Centre, don't stretch.
		 *
		 * The default (align-items: normal, i.e. stretch) made every child
		 * full height, so items of different heights each centred their own
		 * content at a different point. It looked fine only while the email,
		 * phone and language control happened to be the same 44px tall — the
		 * moment the language pill shrank to 25px it sat 9.4px above the text
		 * beside it. Centring makes the row hold regardless of what any one
		 * item measures.
		 */
		align-items: center;
	}

	.ogt-topbar a {
		color: inherit;
		text-decoration: none;
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		transition: color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-topbar a:hover { color: var(--ogt-on-dark); }
	.ogt-topbar .ogt-icon { opacity: 0.55; }

	@media (max-width: 860px) {
		.ogt-topbar__msg { display: none; }
		.ogt-topbar__inner { justify-content: center; }

		/*
		 * Touch targets. Measured at 390px these links were 20px tall — the
		 * email and phone number in the top bar, which on a phone are the two
		 * things a buyer is most likely to reach for.
		 *
		 * Padding rather than a fixed height: the row keeps its visual size
		 * because the bar's own padding absorbs it, while the tappable box
		 * grows to 44px. Negative inline margin stops the wider hit areas from
		 * pushing the two links apart.
		 */
		/*
		 * Tap targets WITHOUT negative margins.
		 *
		 * The previous -0.4rem inline margin was there to keep the visual
		 * spacing while padding grew the hit area, but it cancelled the flex
		 * gap: measured, the email and phone hit areas overlapped by 2px while
		 * the switcher sat 4px away — uneven, and two overlapping targets mean
		 * a tap near the boundary hits the wrong one. Padding only, and the
		 * gap below does the spacing.
		 */
		.ogt-topbar__contact a {
			min-height: 44px;
			padding-inline: 0;
			margin-inline: 0;
		}

		/*
		 * Keep the bar to ONE line.
		 *
		 * Measured at 390px: the row is 343px wide but email (169) + phone
		 * (133) + language switcher (124) plus gaps needs 450, so the switcher
		 * wrapped to a second line and the bar took double the height at the
		 * very top of every page.
		 *
		 * Dropping the two decorative icons and the switcher's globe and
		 * "English" label recovers almost exactly the 110px shortfall. The
		 * values themselves — the address and the number — are untouched,
		 * because they are the reason the bar exists.
		 */
		.ogt-topbar__contact {
			gap: 0.7rem;
			flex-wrap: nowrap;
			min-width: 0;
		}

		/* Decorative only; the address and the number identify themselves. */
		.ogt-topbar__contact > a .ogt-icon { display: none; }

		/*
		 * Never let a link shrink below its text.
		 *
		 * With min-width:0 the email box was squeezed to 123px at 360px while
		 * the address needs 134 — with nowrap, the text simply spilled over
		 * the phone number beside it and the two read as one string. Better to
		 * drop the address entirely at that width (below) than to render it
		 * broken.
		 */
		.ogt-topbar__contact > a {
			flex: 0 0 auto;
			white-space: nowrap;
		}
	}

	/*
	 * Below ~380px the address, the number and the switcher genuinely do not
	 * fit — they need 338px of a 312px row. The email becomes its icon rather
	 * than disappearing: the channel stays one tap away, the address is still
	 * in the footer and on the contact page, and the phone number keeps its
	 * text because tapping to call is the more likely action on a phone.
	 */
	@media (max-width: 380px) {
		.ogt-topbar__contact > a[href^="mailto"] .ogt-icon { display: inline-flex; }

		.ogt-topbar__contact > a[href^="mailto"] span {
			position: absolute;
			width: 1px;
			height: 1px;
			margin: -1px;
			padding: 0;
			overflow: hidden;
			clip-path: inset(50%);
			white-space: nowrap;
			border: 0;
		}
	}

	/* ---------------------------------------------------------------- HEADER */

	.ogt-sentinel {
		position: absolute;
		height: 1px;
		width: 1px;
		pointer-events: none;
	}

	.ogt-header {
		background: var(--ogt-surface);
		border-bottom: 1px solid var(--ogt-border);
		z-index: var(--ogt-z-sticky);
		transition:
			box-shadow var(--ogt-dur) var(--ogt-ease),
			background-color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-header--sticky {
		position: sticky;
		top: 0;
	}

	.ogt-header.is-stuck {
		box-shadow: var(--ogt-shadow-md);
		background: rgb(var(--ogt-surface-rgb) / 92%);
	}

	/*
	 * The frosted blur lives on a pseudo-element, NOT on .ogt-header itself.
	 *
	 * backdrop-filter makes an element a containing block for its
	 * position:fixed descendants, and the mobile drawer is a descendant of the
	 * header. With the blur on .ogt-header, the moment the header gained
	 * .is-stuck — i.e. as soon as you scrolled — the drawer stopped resolving
	 * against the viewport and started resolving against the header's own box.
	 * Two visible failures came from that one line:
	 *
	 *   1. Opening the menu after scrolling collapsed it into a ~76px strip
	 *      across the top showing a single item.
	 *   2. Once closed, the drawer's translateX(100%) was no longer a fixed
	 *      element pinned to the viewport, so it extended the page by its own
	 *      width and the whole site scrolled sideways.
	 *
	 * A pseudo-element is not an ancestor of the nav, so it can carry the blur
	 * without establishing a containing block for it.
	 */
	.ogt-header.is-stuck::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		pointer-events: none;
		-webkit-backdrop-filter: blur(10px);
		backdrop-filter: blur(10px);
	}

	.ogt-header__inner {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--ogt-gap);
		min-height: var(--ogt-header-h);
		transition: min-height var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-header.is-stuck .ogt-header__inner { min-height: var(--ogt-header-h-stuck); }

	/* ------------------------------------------------ FLOATING HEADER STYLE
	 * Optional layout (Site Settings → Header → Header style): the bar becomes a
	 * dark rounded pill inset from the page edges rather than a full-width band.
	 * The chrome moves onto .ogt-header__inner; .ogt-header itself goes
	 * transparent so the pill appears to float over the page.
	 * -------------------------------------------------------------------- */

	.ogt-header--floating {
		background: transparent;
		border-bottom: 0;
		padding-block: 0.85rem;
	}

	.ogt-header--floating .ogt-header__inner {
		background: var(--ogt-deep);
		border-radius: var(--ogt-radius-pill);
		padding-inline: 1.25rem;
		min-height: 64px;
		box-shadow: var(--ogt-shadow-lg);
		transition:
			min-height var(--ogt-dur) var(--ogt-ease),
			background-color var(--ogt-dur) var(--ogt-ease);
	}

	/* Stuck: the pill tightens and frosts; the bar behind it stays transparent
	   so the full-width white sheet never reappears. */
	.ogt-header--floating.is-stuck {
		background: transparent;
		box-shadow: none;
	}

	/* The bar behind the pill must not blur — and must not carry the pseudo
	   element the standard header uses either. */
	.ogt-header--floating.is-stuck::before { content: none; }

	.ogt-header--floating.is-stuck .ogt-header__inner {
		background: rgb(var(--ogt-deep-rgb) / 90%);
		min-height: 56px;
		/* Anchors the blur pseudo-element below. position:relative is safe
		   here — unlike transform or filter, it does NOT make this a
		   containing block for the drawer's fixed positioning. */
		position: relative;
	}

	/*
	 * Same reasoning as the standard header: .ogt-header__inner is also an
	 * ancestor of the drawer, so the blur cannot live on it without turning it
	 * into a containing block for the drawer's fixed positioning.
	 */
	.ogt-header--floating.is-stuck .ogt-header__inner::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		pointer-events: none;
		border-radius: inherit;
		-webkit-backdrop-filter: blur(12px);
		backdrop-filter: blur(12px);
	}

	.ogt-header--floating .ogt-logo { color: var(--ogt-on-dark); }

	/*
	 * Colour inversion is DESKTOP ONLY, and that scope is load-bearing.
	 *
	 * Below 1025px the nav is a fixed drawer with a white (--ogt-surface)
	 * background. Inverting these links globally would paint white text on that
	 * white drawer and make the whole menu invisible — the same failure mode as
	 * the newsletter button. Above 1025px the links sit on the dark pill, which
	 * is the only place the inversion is correct.
	 */
	@media (min-width: 1025px) {

		/* No drawer above this width, so nothing to close. Kept in the
		   components layer alongside the mobile rule — as a utilities-layer
		   rule it outranked the mobile styles and hid the button everywhere. */
		.ogt-nav__close { display: none; }

		.ogt-header--floating .ogt-menu a { color: var(--ogt-on-dark-muted); }

		.ogt-header--floating .ogt-menu > li > a:hover,
		.ogt-header--floating .ogt-menu > .current-menu-item > a,
		.ogt-header--floating .ogt-menu > .current-menu-ancestor > a { color: var(--ogt-on-dark); }

		.ogt-header--floating .ogt-menu > li > a::after { background: var(--ogt-accent); }

		/* The dropdown is still a light card, so its links keep ink. */
		.ogt-header--floating .ogt-menu .sub-menu a { color: var(--ogt-ink); }
		.ogt-header--floating .ogt-menu .sub-menu a:hover { color: var(--ogt-primary); }

		.ogt-header--floating .ogt-nav__quote { color: var(--ogt-on-dark-muted); }
		.ogt-header--floating .ogt-nav__quote:hover { color: var(--ogt-on-dark); }
	}

	/* The burger sits on the pill at every width, so it inverts unconditionally.
	   Its bars are all `background: currentColor`, so setting the colour on the
	   button covers the span and both pseudo-elements in one rule. */
	.ogt-header--floating .ogt-burger { color: var(--ogt-on-dark); }

	.ogt-logo {
		display: inline-flex;
		align-items: center;
		text-decoration: none;
		color: var(--ogt-ink);
		flex: 0 0 auto;
	}

	.ogt-logo__img {
		max-height: 44px;
		width: auto;
		transition: max-height var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-header.is-stuck .ogt-logo__img { max-height: 36px; }

	.ogt-logo__text {
		font-family: var(--ogt-font-head);
		font-weight: 700;
		font-size: 1.15rem;
		letter-spacing: -0.02em;
	}

	/* --- primary nav --- */

	.ogt-nav {
		display: flex;
		align-items: center;
		gap: var(--ogt-gap-lg);
	}

	.ogt-menu {
		display: flex;
		align-items: center;
		gap: 1.75rem;
		list-style: none;
		margin: 0;
		padding: 0;
	}

	.ogt-menu > li { position: relative; }

	.ogt-menu a {
		color: var(--ogt-ink);
		text-decoration: none;
		font-family: var(--ogt-font-head);
		font-weight: 500;
		font-size: 0.9375rem;
		padding-block: 0.5rem;
		display: inline-flex;
		align-items: center;
		gap: 0.3rem;
		position: relative;
	}

	/* Underline grows from the centre on hover / when current. */
	.ogt-menu > li > a::after {
		content: "";
		position: absolute;
		left: 0;
		right: 0;
		bottom: 0;
		height: 2px;
		background: var(--ogt-primary);
		border-radius: 2px;
		transform: scaleX(0);
		transition: transform var(--ogt-dur) var(--ogt-ease-out);
	}

	.ogt-menu > li > a:hover::after,
	.ogt-menu > .current-menu-item > a::after,
	.ogt-menu > .current-menu-ancestor > a::after,
	.ogt-menu > .current-menu-parent > a::after {
		transform: scaleX(1);
	}

	.ogt-menu > li > a:hover,
	.ogt-menu > .current-menu-item > a { color: var(--ogt-primary); }

	/* --- mega menu --- */

	.ogt-menu .sub-menu {
		position: absolute;
		top: calc(100% + 0.5rem);
		left: 0;
		min-width: 260px;
		background: var(--ogt-surface);
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-sm);
		box-shadow: var(--ogt-shadow-lg);
		list-style: none;
		margin: 0;
		padding: 0.5rem;
		display: flex;
		flex-direction: column;
		z-index: var(--ogt-z-dropdown);

		/* Fade + lift. visibility keeps it out of the tab order when closed. */
		opacity: 0;
		visibility: hidden;
		transform: translateY(-6px);
		transition:
			opacity var(--ogt-dur) var(--ogt-ease),
			transform var(--ogt-dur) var(--ogt-ease-out),
			visibility var(--ogt-dur);
	}

	.ogt-menu li:hover > .sub-menu,
	.ogt-menu li:focus-within > .sub-menu,
	.ogt-menu li.is-open > .sub-menu {
		opacity: 1;
		visibility: visible;
		transform: translateY(0);
	}

	.ogt-menu .sub-menu a {
		padding: 0.6rem 0.8rem;
		border-radius: var(--ogt-radius-sm);
		display: block;
		font-weight: 500;
		width: 100%;
	}

	.ogt-menu .sub-menu a::after { display: none; }

	.ogt-menu .sub-menu a:hover {
		background: var(--ogt-muted);
		color: var(--ogt-primary);
	}

	.ogt-nav__actions {
		display: flex;
		align-items: center;
		gap: var(--ogt-gap-sm);
		flex: 0 0 auto;
	}

	.ogt-nav__quote {
		background: none;
		border: 0;
		font-family: var(--ogt-font-head);
		font-weight: 500;
		font-size: 0.9375rem;
		color: var(--ogt-ink);
		cursor: pointer;
		padding: 0.5rem 0;
		white-space: nowrap;
	}

	.ogt-nav__quote:hover { color: var(--ogt-primary); }

	.ogt-nav__empty a { font-size: var(--ogt-fs-sm); }

	/* --- burger --- */

	.ogt-burger {
		display: none;
		background: none;
		border: 0;
		padding: 0.6rem;
		cursor: pointer;
		color: var(--ogt-ink);
		order: 3;

		/*
		 * Measured 43x21 on a phone: the bars are a few pixels tall and the
		 * padding did not make up the difference, so the single most important
		 * control on mobile had less than half the recommended touch height.
		 * The square is set here; the media query below switches display to
		 * grid so place-items can centre the icon inside it.
		 */
		place-items: center;
		min-width: 44px;
		min-height: 44px;
	}

	.ogt-burger__box,
	.ogt-burger__box span,
	.ogt-burger__box::before,
	.ogt-burger__box::after {
		display: block;
		width: 24px;
		height: 2px;
		background: currentColor;
		border-radius: 2px;
		transition: transform var(--ogt-dur) var(--ogt-ease), opacity var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-burger__box {
		position: relative;
		background: transparent;
	}

	.ogt-burger__box::before,
	.ogt-burger__box::after {
		content: "";
		position: absolute;
		left: 0;
	}

	.ogt-burger__box::before { top: -7px; }
	.ogt-burger__box::after { top: 7px; }

	.ogt-burger[aria-expanded="true"] .ogt-burger__box span { opacity: 0; }
	.ogt-burger[aria-expanded="true"] .ogt-burger__box::before { transform: translateY(7px) rotate(45deg); }
	.ogt-burger[aria-expanded="true"] .ogt-burger__box::after { transform: translateY(-7px) rotate(-45deg); }

	@media (max-width: 1024px) {

		/* grid, not block — it is what lets place-items centre the icon inside
		   the 44px square set on .ogt-burger above. */
		.ogt-burger { display: grid; }

		/*
		 * Let the logo yield so the burger always fits.
		 *
		 * Measured at 360px: the header row is 328px wide, the logo took 294 of
		 * it, and with the 24px gap the 44px burger had nowhere to go — it was
		 * pushed 18px past the viewport and clipped, leaving the menu button
		 * half visible on the narrowest common phone.
		 *
		 * A flex item will not shrink below its content's intrinsic width
		 * unless min-width is cleared, which is why the logo simply won out.
		 * The burger is flex: none so it keeps its full 44px target and the
		 * logo scales down instead.
		 */
		.ogt-header__inner { flex-wrap: nowrap; }

		.ogt-logo {
			min-width: 0;
			flex: 0 1 auto;
			overflow: hidden;
		}

		.ogt-logo__img {
			max-width: 100%;
			height: auto;
		}

		.ogt-burger { flex: none; }

		.ogt-nav {
			position: fixed;
			inset: 0 0 0 26%;
			background: var(--ogt-surface);
			flex-direction: column;
			align-items: stretch;
			justify-content: flex-start;
			gap: 0;
			padding: 5.5rem 1.5rem 2rem;
			transform: translateX(100%);
			overflow-y: auto;
			box-shadow: -20px 0 40px -20px rgb(var(--ogt-deep-rgb) / 30%);
			z-index: var(--ogt-z-overlay);

			/*
			 * visibility: hidden is load-bearing, not a nicety.
			 *
			 * translateX(100%) parks the drawer past the right edge, but it is
			 * still laid out — so it extends the page's scrollable width and
			 * the whole site scrolls sideways into a strip of empty menu. That
			 * was previously masked by `overflow-x: hidden` on <body>; when
			 * that became `overflow-x: clip` (needed to make position:sticky
			 * work at all) the drawer stopped being clipped and the sideways
			 * scroll appeared.
			 *
			 * Hiding it also fixes a real accessibility bug that was there the
			 * whole time: an off-screen drawer is still focusable, so tabbing
			 * through the page walked into an invisible menu.
			 *
			 * The 0s delay on visibility keeps it visible for the length of the
			 * slide-out, then hides it. Without the delay it vanishes instantly
			 * and the closing animation is never seen.
			 */
			visibility: hidden;
			transition:
				transform var(--ogt-dur-slow) var(--ogt-ease-out),
				visibility 0s linear var(--ogt-dur-slow);
		}

		.ogt-nav.is-open {
			transform: translateX(0);
			visibility: visible;
			transition:
				transform var(--ogt-dur-slow) var(--ogt-ease-out),
				visibility 0s;
		}

		.ogt-menu {
			flex-direction: column;
			align-items: stretch;
			gap: 0;
		}

		.ogt-menu > li { border-bottom: 1px solid var(--ogt-border); }

		.ogt-menu a {
			padding: 0.9rem 0;
			font-size: 1rem;
		}

		.ogt-menu > li > a::after { display: none; }

		.ogt-menu .sub-menu {
			position: static;
			opacity: 1;
			visibility: visible;
			transform: none;
			border: 0;
			box-shadow: none;
			padding: 0 0 0.5rem 0.85rem;
			min-width: 0;
		}

		.ogt-nav__actions {
			flex-direction: column;
			align-items: stretch;
			margin-block-start: var(--ogt-gap);
			gap: var(--ogt-gap-sm);
		}

		.ogt-nav__actions .ogt-btn { width: 100%; }

		/*
		 * Close button, pinned into the gap the drawer's top padding already
		 * reserved. 48px square: comfortably over the 44px minimum tap target,
		 * because a control a user cannot hit is the same as no control.
		 */
		/*
		 * The desktop counterpart to this rule is a min-width query further
		 * down, NOT a bare `.ogt-nav__close { display: none }` in the utilities
		 * layer. That is where it started, and it hid the button at every width
		 * — utilities ranks above components in the layer order, so it beat
		 * this rule regardless of the media query. The button rendered 0x0.
		 */
		.ogt-nav__close {
			/*
			 * fixed, not absolute. The drawer scrolls (overflow-y: auto), and
			 * an absolutely positioned child scrolls away with the menu — which
			 * is the very failure being fixed.
			 *
			 * The coordinates resolve against the drawer rather than the
			 * viewport, because .ogt-nav carries a transform and a transform
			 * makes an element the containing block for fixed descendants.
			 * That is deliberate and it lands in the same place either way:
			 * the drawer's top and right edges ARE the viewport's.
			 */
			position: fixed;
			top: 1rem;
			right: 1.25rem;
			display: inline-flex;
			align-items: center;
			justify-content: center;
			width: 48px;
			height: 48px;
			padding: 0;
			border: 1px solid var(--ogt-border);
			border-radius: 50%;
			background: var(--ogt-surface);
			color: var(--ogt-ink);
			cursor: pointer;
			transition:
				background-color var(--ogt-dur) var(--ogt-ease),
				border-color var(--ogt-dur) var(--ogt-ease);
		}

		.ogt-nav__close:hover,
		.ogt-nav__close:focus-visible {
			background: var(--ogt-muted);
			border-color: rgb(var(--ogt-primary-rgb) / 40%);
		}

		body.ogt-nav-open { overflow: hidden; }

		/*
		 * Get the floating buttons out from over the open menu.
		 *
		 * They are not simply behind it: .ogt-header is position:sticky with a
		 * z-index, which makes it a stacking context, so the drawer's z-index
		 * of 900 only ranks it INSIDE the header. Against the WhatsApp button —
		 * a sibling of the header, same z-index, later in the document — the
		 * drawer loses, and the button paints on top of the menu items.
		 *
		 * Raising numbers would be a fight with the stacking context. Hiding
		 * it while the menu is open is both simpler and better behaviour: it
		 * does not belong over a navigation list.
		 */
		body.ogt-nav-open .ogt-whatsapp {
			display: none;
		}

		/*
		 * Drop the header's blur while the drawer is open. This is the fix for
		 * "the menu breaks if you open it after scrolling".
		 *
		 * backdrop-filter makes an element a containing block for its
		 * position:fixed descendants. The drawer lives inside .ogt-header, so
		 * the moment the header gains .is-stuck — which only happens once you
		 * scroll — the drawer stopped being fixed to the VIEWPORT and became
		 * fixed to the HEADER's box, roughly 76px tall. It collapsed into a
		 * strip across the top showing one menu item.
		 *
		 * That is why it worked at the top of a page and failed everywhere
		 * else. Removing the blur restores the viewport as the containing
		 * block, and a full-screen drawer covers the header anyway, so there is
		 * nothing left for it to blur.
		 */
		body.ogt-nav-open .ogt-header,
		body.ogt-nav-open .ogt-header__inner {
			-webkit-backdrop-filter: none;
			backdrop-filter: none;
		}
	}

	@media (max-width: 560px) {
		.ogt-nav { inset: 0; }
	}

	/* ---------------------------------------------------------------- FOOTER */

	.ogt-footer {
		background: var(--ogt-footer);
		color: var(--ogt-on-footer-muted);
		margin-block-start: auto;
	}

	/*
	 * Five columns: brand, categories, company, legal, contact.
	 *
	 * The ratios are not decorative. At a 1200px container with 40px gutters
	 * there are 1040px to share, and each column needs a different amount:
	 * "Dehydrated Vegetables & Herbs" needs more than "Insights", and the
	 * legal column's labels are deliberately short so it can have the least.
	 */
	.ogt-footer__grid {
		display: grid;
		grid-template-columns: 1.4fr 1.15fr 0.85fr 0.9fr 1.15fr;
		gap: var(--ogt-gap-lg);
		padding-block: clamp(3rem, 6vw, 4.5rem);
	}

	/*
	 * Below ~1100px five columns start breaking every label onto two lines, so
	 * the link columns drop to a 2x2 block and the brand takes the full width
	 * back — its intro is capped at 42ch and reads badly in a 180px column.
	 */
	@media (max-width: 1100px) {
		.ogt-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
		.ogt-footer__brand { grid-column: 1 / -1; }
	}

	@media (max-width: 560px) {
		.ogt-footer__grid { grid-template-columns: minmax(0, 1fr); gap: var(--ogt-gap); }
	}

	.ogt-footer .ogt-logo,
	.ogt-footer .ogt-logo__text { color: var(--ogt-on-footer); }

	.ogt-footer__brand {
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap-sm);
		align-items: flex-start;
	}

	.ogt-footer__intro {
		font-size: var(--ogt-fs-sm);
		max-width: 42ch;
		color: var(--ogt-on-footer-subtle);
	}

	.ogt-footer__title {
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-xs);
		font-weight: 600;
		letter-spacing: 0.12em;
		text-transform: uppercase;
		color: var(--ogt-on-footer);
		margin-block-end: 1rem;
	}

	.ogt-footer__menu,
	.ogt-contact-list,
	/* Icon-only round buttons. These were always sized for an icon — the markup
	   was emitting the network's NAME as text, which then had to fit a 36px
	   circle. The brand marks come from Core\Brands; the name stays as
	   screen-reader text. */
	.ogt-social {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: row;
		flex-wrap: wrap;
		gap: 0.5rem;
	}

	/*
	 * Both lists were picking up `flex-direction: row` from the rule above,
	 * which is written for the social icons — a wrapping row of round buttons is
	 * exactly what that wants. Neither of these is that.
	 *
	 * The contact list put phone and email side by side on one line with the
	 * address wrapping underneath, so the column read as three fragments rather
	 * than a list. The nav menus wrapped into ragged two-item rows under a
	 * single-column heading.
	 *
	 * Worth noting why this went unnoticed: wrapping only shows up when the
	 * labels are short enough for two to share a line, so the same rule produced
	 * a tidy column for "Ground Spices & Powders" and a ragged one for
	 * "Blog"/"Contact". It looked correct exactly where the content happened to
	 * be long. Both stack now, at every width.
	 */
	.ogt-footer__menu,
	.ogt-contact-list {
		flex-direction: column;
		gap: 0.65rem;
	}

	.ogt-social a {
		display: grid;
		place-items: center;
		width: 38px;
		height: 38px;
		border-radius: 50%;
		background: var(--ogt-on-footer-surface);
		border: 1px solid var(--ogt-on-footer-border);
		color: var(--ogt-on-footer-muted);
		transition:
			background-color var(--ogt-dur) var(--ogt-ease),
			border-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease),
			transform var(--ogt-dur) var(--ogt-ease-out);
	}

	.ogt-social a:hover {
		background: var(--ogt-primary);
		border-color: var(--ogt-primary);
		color: var(--ogt-on-primary);
		transform: translateY(-2px);
	}

	.ogt-footer a {
		color: inherit;
		text-decoration: none;
		font-size: var(--ogt-fs-sm);
		transition: color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-footer a:hover { color: var(--ogt-on-footer); }

	.ogt-footer__menu a:hover { padding-inline-start: 0.25rem; }

	.ogt-footer__menu a {
		transition: color var(--ogt-dur) var(--ogt-ease), padding var(--ogt-dur) var(--ogt-ease);
	}

	/*
	 * Touch targets in the footer, phone only.
	 *
	 * Measured at 390px the footer links were 17px tall and the contact lines
	 * 24px — the smallest targets on the site, in the one region a visitor
	 * reaches by scrolling to the very bottom, usually one-handed. Padding
	 * gives each link a 44px box; the columns already have room for it, so
	 * nothing shifts visually except the spacing between links.
	 */
	@media (max-width: 860px) {
		.ogt-footer__menu a,
		.ogt-contact-list a {
			display: flex;
			align-items: center;
			min-height: 44px;
		}

		/*
		 * Now that the menu stacks, each link's own 44px box supplies the
		 * separation — keeping the 0.65rem gap as well would add ~50px of dead
		 * height per column for no benefit.
		 */
		.ogt-footer__menu {
			gap: 0;
		}

		/*
		 * ...which throws the icons out by 11.6px, measured.
		 *
		 * The rule above turns each contact link into a 44px box with its text
		 * centred at 22px, but the row still aligns to `flex-start`, so the 16px
		 * icon sits at the TOP of that box while its label sits in the middle.
		 * The icon reads as belonging to the line above it.
		 *
		 * Only the link rows are centred. The address is a multi-line span with
		 * no 44px box, and its pin belongs beside the FIRST line, which is what
		 * the inherited `flex-start` already does — hence `:has(a)` rather than
		 * centring the whole list. Where `:has()` is unsupported this simply
		 * does not apply and the alignment is what it is today, so there is no
		 * worse-than-before case.
		 */
		.ogt-contact-list li:has(a) {
			align-items: center;
		}

		.ogt-contact-list li:has(a) .ogt-icon {
			margin-block-start: 0;
		}

		/* Matches the base rule's selector (.ogt-social a). Written as
		   .ogt-social__link first, which matches nothing — the class is on the
		   element but the stylesheet targets it by descendant. */
		.ogt-social a {
			width: 44px;
			height: 44px;
		}

		/* The legal links live in the footer bar, not the footer menu, so the
		   rule above does not reach them. 17px tall as measured. */
		.ogt-footer__bar-inner a {
			display: inline-flex;
			align-items: center;
			min-height: 44px;
		}

		/* Sidebar topic links on a blog post: 41px. */
		.ogt-sidecard__link {
			min-height: 44px;
		}

		/* Category and therapeutic-area links on a product page — 17 and 20px.
		   These are how a buyer moves from one spice to the rest of its
		   class, so they are worth hitting first time. */
		.ogt-hero__meta a,
		.ogt-termlinks a {
			display: inline-flex;
			align-items: center;
			min-height: 44px;
		}

		/*
		 * Pagination. The number stays its drawn size; the box around it grows.
		 * These sit at the end of a long archive, so a mis-tap means scrolling
		 * back through everything just visited.
		 */
		.page-numbers {
			min-width: 44px;
			min-height: 44px;
			display: inline-flex;
			align-items: center;
			justify-content: center;
		}

		/*
		 * Slider dots used to extend their hit area here with an invisible
		 * absolutely-positioned ::after at inset: -18px. That worked, but it
		 * was mobile-only, and ::after is now the VISIBLE dot — see
		 * .ogt-slider__dot in the components section, where the button is a
		 * 24px box with the 9px dot drawn inside it.
		 *
		 * Leaving both in place would have been worse than either alone: this
		 * rule comes later in the same layer, so on a phone it would have
		 * replaced the visible dot with an empty overlay and the slider would
		 * have appeared to have no indicators at all.
		 *
		 * The base rule already meets the target size at every viewport, so
		 * nothing is needed here.
		 */
	}

	.ogt-contact-list li {
		display: flex;
		gap: 0.6rem;
		align-items: flex-start;
		font-size: var(--ogt-fs-sm);
	}

	.ogt-contact-list .ogt-icon {
		margin-block-start: 0.15rem;
		opacity: 0.45;
	}

	/* Opening hours: supporting detail, not a contact method, so it sits below
	   the list rather than as another row with an icon. */
	.ogt-footer__hours {
		margin-block-start: 0.9rem;
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-on-footer-subtle);
	}

	.ogt-footer__bar { border-top: 1px solid var(--ogt-on-footer-border); }

	.ogt-footer__bar-inner {
		display: flex;
		flex-wrap: wrap;
		gap: 0.75rem 1.5rem;
		align-items: center;
		justify-content: space-between;
		padding-block: 1.25rem;
		font-size: var(--ogt-fs-xs);
	}

	/*
	 * Kept for anything added back to the bottom bar later. The legal links
	 * themselves moved into the footer grid — six long document titles never
	 * fitted on one line, and flex-wrap turned that into a second, ragged row
	 * that read as a mistake.
	 */
	.ogt-legal__menu {
		list-style: none;
		display: flex;
		flex-wrap: wrap;
		gap: 1.5rem;
		margin: 0;
		padding: 0;
	}

	/* Certification strip in the footer. */
	.ogt-footer__certs {
		display: flex;
		flex-wrap: wrap;
		gap: 0.5rem;
		margin-block-start: 0.5rem;
	}

	.ogt-footer__cert {
		font-size: 0.6875rem;
		font-weight: 600;
		letter-spacing: 0.06em;
		text-transform: uppercase;
		padding: 0.25rem 0.6rem;
		border-radius: var(--ogt-radius-pill);
		background: var(--ogt-on-footer-surface);
		border: 1px solid var(--ogt-on-footer-border);
		color: var(--ogt-on-footer-muted);
	}

	/* -------------------------------------------------------------- WHATSAPP */
	/* WhatsApp green is fixed by WhatsApp's brand guidelines, not our palette. */

	.ogt-whatsapp {
		position: fixed;
		right: 20px;
		bottom: 20px;
		z-index: var(--ogt-z-sticky);
		display: inline-flex;
		align-items: center;
		gap: 0.5rem;
		background: #25d366;
		color: #04310f;
		border-radius: var(--ogt-radius-pill);
		padding: 0.75rem 1.15rem;
		font-family: var(--ogt-font-head);
		font-weight: 700;
		font-size: var(--ogt-fs-sm);
		text-decoration: none;
		box-shadow: var(--ogt-shadow-lg);
		transition: transform var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-whatsapp:hover {
		color: #04310f;
		transform: translateY(-2px);
	}

	/*
	 * On mobile the sticky CTA bar takes the bottom, so the FAB moves up — and
	 * it becomes an icon, not a labelled pill.
	 *
	 * As a ~150px wide pill it sat permanently over the content: on the
	 * homepage it covered the three trust chips in
	 * the hero, and further down it landed in the middle of a paragraph,
	 * hiding words. Being position:fixed it never counts as overflow and no
	 * measurement flags it — it is only visible by looking at the page.
	 *
	 * A circle covers roughly a fifth of the area and is the conventional
	 * mobile shape for this control.
	 */
	@media (max-width: 720px) {
		.ogt-whatsapp {
			right: 20px;
			bottom: 20px;
			width: 52px;
			height: 52px;
			padding: 0;
			gap: 0;
			border-radius: 50%;
			justify-content: center;
		}

		/*
		 * Visually hidden, NOT display:none. The icon inside is aria-hidden,
		 * so removing the label outright would leave the link with no
		 * accessible name — a button announced only as "link".
		 */
		.ogt-whatsapp__label {
			position: absolute;
			width: 1px;
			height: 1px;
			margin: -1px;
			padding: 0;
			overflow: hidden;
			clip-path: inset(50%);
			white-space: nowrap;
			border: 0;
		}
	}

	/*
	 * The sticky mobile CTA bar that used to be styled here was never rendered —
	 * no template in the theme outputs `.ogt-stickycta`. Its styles were dead, but
	 * `body { padding-block-end: 68px }` was not: it reserved a 68px strip of
	 * empty space at the foot of every page under 720px for a bar that does not
	 * exist, and the WhatsApp button was lifted to `bottom: 76px` to clear it —
	 * which is why the button floated up into the body copy instead of sitting in
	 * the corner. Removed together; the button now sits at 20px like it should.
	 *
	 * If a sticky CTA is wanted later it needs a template part first, and then
	 * the WhatsApp offset below has to move with it.
	 */
}

/* ============================================================================
   PLUGIN-OWNED MARKUP — modal + forms
   The theme styles HTML it does not emit. See the frozen list at the top.
   ========================================================================== */

@layer components {

	.ogt-modal {
		position: fixed;
		inset: 0;
		z-index: var(--ogt-z-modal);
		display: grid;
		place-items: center;
		padding: 1.25rem;
	}

	.ogt-modal[hidden] { display: none; }

	.ogt-modal__backdrop {
		position: absolute;
		inset: 0;
		background: var(--ogt-overlay);
		backdrop-filter: blur(3px);
		opacity: 0;
		transition: opacity var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-modal.is-open .ogt-modal__backdrop { opacity: 1; }

	.ogt-modal__panel {
		position: relative;
		width: min(100%, 640px);
		max-height: 90vh;
		overflow-y: auto;
		background: var(--ogt-surface);
		border-radius: var(--ogt-radius-card);
		padding: clamp(1.5rem, 4vw, 2.5rem);
		box-shadow: var(--ogt-shadow-xl);
		transform: translateY(12px) scale(0.99);
		opacity: 0;
		transition:
			transform var(--ogt-dur-slow) var(--ogt-ease-out),
			opacity var(--ogt-dur-slow) var(--ogt-ease);
	}

	.ogt-modal.is-open .ogt-modal__panel {
		transform: none;
		opacity: 1;
	}

	/*
	 * Form panels are banded instead of being one scrolling box: fixed head,
	 * scrolling body, fixed foot. The old single-scroll panel pushed the heading
	 * out of view and left the submit button below the fold, so the visitor had
	 * to scroll a dialog to find out how to finish it.
	 *
	 * Padding moves to the bands so the scrollbar tracks the panel edge rather
	 * than floating inside a padded box.
	 */
	.ogt-modal__panel--form {
		display: flex;
		flex-direction: column;
		padding: 0;
		overflow: hidden;
		width: min(100%, 620px);
	}

	.ogt-modal__head {
		flex: none;
		padding: clamp(1.35rem, 3.5vw, 1.85rem) clamp(1.35rem, 4vw, 2.15rem) clamp(1rem, 2.5vw, 1.25rem);
		padding-inline-end: 3.75rem;   /* clears the close button */
		background: linear-gradient(180deg, rgb(var(--ogt-primary-rgb) / 6%), transparent);
		border-block-end: 1px solid var(--ogt-border-soft);
	}

	.ogt-modal__close {
		position: absolute;
		top: 0.85rem;
		right: 0.85rem;
		z-index: 1;
		display: grid;
		place-items: center;
		/* 40px is a comfortable pointer target; the bare glyph before this was
		   roughly 24px and easy to miss. */
		width: 40px;
		height: 40px;
		background: var(--ogt-surface);
		border: 1px solid var(--ogt-border);
		border-radius: 50%;
		cursor: pointer;
		color: var(--ogt-text-muted);
		padding: 0;
		transition:
			background-color var(--ogt-dur) var(--ogt-ease),
			border-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-modal__close:hover {
		color: var(--ogt-ink);
		background: var(--ogt-muted);
		border-color: var(--ogt-border-strong);
	}

	.ogt-modal__title {
		font-size: var(--ogt-fs-h3);
		padding-inline-end: 2rem;
	}

	.ogt-modal__panel--form .ogt-modal__title {
		padding-inline-end: 0;
		margin: 0;
	}

	.ogt-modal__intro {
		margin-block-start: 0.5rem;
		color: var(--ogt-text-muted);
		font-size: 0.9375rem;
	}

	.ogt-modal__panel--form .ogt-modal__intro {
		margin-block-start: 0.4rem;
		margin-block-end: 0;
		max-width: 46ch;
	}

	body.ogt-modal-open { overflow: hidden; }

	/* ------------------------------------------------------ 14. INQUIRY FORM */

	.ogt-form {
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap-sm);
		margin-block-start: var(--ogt-gap);
	}

	/* ---- banded layout, used by the inquiry modal ---- */

	/* min-height:0 is what actually lets the body scroll: without it the flex
	   item refuses to shrink below its content and the panel grows instead. */
	.ogt-modal__panel--form .ogt-form {
		flex: 1;
		min-height: 0;
		margin-block-start: 0;
		gap: 0;
	}

	.ogt-form__body {
		flex: 1;
		min-height: 0;
		overflow-y: auto;
		overscroll-behavior: contain;   /* stops the page scrolling once the body bottoms out */
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap-sm);
		padding: clamp(1.1rem, 3vw, 1.5rem) clamp(1.35rem, 4vw, 2.15rem);
	}

	.ogt-form__foot {
		flex: none;
		display: flex;
		flex-direction: column;
		gap: 0.75rem;
		padding: clamp(1rem, 2.5vw, 1.25rem) clamp(1.35rem, 4vw, 2.15rem);
		background: var(--ogt-ground);
		border-block-start: 1px solid var(--ogt-border-soft);
	}

	.ogt-form__grid {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: var(--ogt-gap-sm);
	}

	@media (max-width: 520px) {
		.ogt-form__grid { grid-template-columns: 1fr; }
	}

	.ogt-form__row {
		display: flex;
		flex-direction: column;
		gap: 0.35rem;
	}

	.ogt-form label {
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-sm);
		font-weight: 600;
		color: var(--ogt-ink);
	}

	/*
	 * 1rem, not 0.9375rem, and the difference is not cosmetic.
	 *
	 * iOS Safari zooms the whole page when a form field with a font-size below
	 * 16px receives focus, and it does not zoom back out when the field is
	 * blurred. The visitor is left on a zoomed, horizontally scrolling page
	 * halfway through filling in an enquiry.
	 *
	 * 15px looked marginally tidier on a desktop and broke the form on every
	 * iPhone. Applies to all three input groups on the site: this one, the
	 * newsletter field and the catalogue search.
	 */
	.ogt-form input,
	.ogt-form select,
	.ogt-form textarea {
		width: 100%;
		padding: 0.75rem 0.9rem;
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-input);
		font-size: 1rem;
		background: var(--ogt-surface);
		color: var(--ogt-ink);
		transition: border-color var(--ogt-dur) var(--ogt-ease), box-shadow var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-form input::placeholder,
	.ogt-form textarea::placeholder { color: var(--ogt-text-subtle); }

	.ogt-form input:focus,
	.ogt-form select:focus,
	.ogt-form textarea:focus {
		border-color: var(--ogt-primary);
		box-shadow: var(--ogt-shadow-focus);
		outline: none;
	}

	/* Applied by inquiry.js on a 422. */
	.ogt-form .has-error {
		border-color: var(--ogt-danger);
		background: rgb(var(--ogt-danger-rgb) / 4%);
	}

	.ogt-form textarea {
		resize: vertical;
		min-height: 104px;
	}

	.ogt-form__consent {
		display: flex;
		gap: 0.6rem;
		align-items: flex-start;
	}

	.ogt-form__consent input {
		width: auto;
		margin-block-start: 0.3rem;
		flex: 0 0 auto;
	}

	.ogt-form__consent label {
		font-weight: 400;
		font-size: var(--ogt-fs-sm);
		line-height: 1.5;
		color: var(--ogt-text-muted);
	}

	/*
	 * The privacy notice. Quieter than the marketing opt-in above it, because
	 * it is information rather than a decision — but the policy link inside it
	 * keeps the normal link treatment, since a notice whose link nobody can
	 * find is not notice.
	 */
	.ogt-form__notice {
		margin: 0;
		font-size: var(--ogt-fs-xs);
		line-height: 1.55;
		color: var(--ogt-text-subtle);
	}

	.ogt-form__notice a {
		color: inherit;
		text-decoration: underline;
		text-underline-offset: 0.2em;
	}

	.ogt-form__notice a:hover,
	.ogt-form__notice a:focus-visible { color: var(--ogt-primary); }

	/* Native selects: draw our own chevron so Country/Interest match the inputs
	   rather than showing the OS default arrow. */
	.ogt-form select {
		appearance: none;
		-webkit-appearance: none;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23475569' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
		background-repeat: no-repeat;
		background-position: right 0.85rem center;
		padding-inline-end: 2.4rem;
	}

	.ogt-form__submit {
		margin-block-start: 0.6rem;
		width: 100%;
		padding-block: 0.95rem;
		font-size: 1rem;
	}

	/* In the banded foot the button is already spaced by the flex gap. */
	.ogt-form__foot .ogt-form__submit { margin-block-start: 0; }

	/* The status line only occupies space once inquiry.js writes into it —
	   otherwise an empty <p> would leave a permanent gap above the button. */
	.ogt-form__foot .ogt-form__status:empty {
		display: none;
	}

	.ogt-form__foot .ogt-form__trust {
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		gap: 0.4rem 1.1rem;
		margin: 0;
		padding: 0;
		list-style: none;
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-muted);
	}

	.ogt-form__foot .ogt-form__trust li {
		display: inline-flex;
		align-items: center;
		gap: 0.35rem;
	}

	.ogt-form__foot .ogt-form__trust .ogt-icon { color: var(--ogt-success); }

	/* Required marker — the bare red asterisk read as an error character. */
	.ogt-form label span[aria-hidden="true"] {
		color: var(--ogt-danger);
		font-weight: 700;
	}

	/*
	 * Mobile: a bottom sheet rather than a floating card. A centred dialog on a
	 * short viewport leaves slivers of page above and below and puts the submit
	 * button mid-screen; anchoring to the bottom keeps it under the thumb.
	 */
	@media (max-width: 560px) {
		/* Scoped with :has() so this only reshapes the form dialog — a future
		   confirm/alert modal keeps the centred card. */
		.ogt-modal:has(.ogt-modal__panel--form) {
			padding: 0;
			place-items: end stretch;
		}

		.ogt-modal__panel--form {
			width: 100%;
			max-height: 92vh;
			border-radius: var(--ogt-radius-card) var(--ogt-radius-card) 0 0;
			transform: translateY(24px);
		}
	}

	.ogt-form__status {
		margin: 0;
		font-size: var(--ogt-fs-sm);
		min-height: 1.2em;
		color: var(--ogt-text-muted);
	}

	/* Trust row under the submit — quiet reassurance, not a second CTA. */
	.ogt-form__trust {
		list-style: none;
		margin: 0.35rem 0 0;
		padding: 0;
		display: flex;
		flex-wrap: wrap;
		gap: 0.4rem 1.25rem;
	}

	.ogt-form__trust li {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-muted);
	}

	.ogt-form__trust .ogt-icon { color: var(--ogt-success); flex: 0 0 auto; }

	.ogt-form__status.is-error { color: var(--ogt-danger); }

	.ogt-form__done {
		text-align: center;
		padding: 2rem 0.5rem;
		font-size: 1rem;
		color: var(--ogt-success);
		font-weight: 600;
	}

	/*
	 * Honeypot. Visually hidden but NOT display:none — some bots read that as a
	 * tell, and it must stay in the DOM for the server-side check.
	 */
	.ogt-hp {
		position: absolute !important;
		left: -9999px;
		top: -9999px;
		height: 0;
		overflow: hidden;
	}

	/* ---------------------------------------------------------- NEWSLETTER */

	/*
	 * Mirrored version of the CTA ramp. This sat on deep-2 → deep, ending at
	 * near-black, and it renders directly beneath the CTA panel — so the page
	 * closed on two dark slabs in a row, both dull. Running the gradient the
	 * other way keeps the pair related without making them identical.
	 */
	.ogt-newsletter__panel {
		border-radius: var(--ogt-radius-card);
		padding: clamp(2rem, 5vw, 3rem);
		background:
			radial-gradient(70% 120% at 100% 0%, rgb(var(--ogt-accent-rgb) / 26%) 0%, transparent 60%),
			linear-gradient(125deg, var(--ogt-secondary) 0%, var(--ogt-primary) 55%, var(--ogt-primary-hover) 100%);
		color: var(--ogt-on-dark-muted);
		display: grid;
		grid-template-columns: 1.1fr 0.9fr;
		gap: clamp(1.5rem, 4vw, 2.5rem);
		align-items: center;
	}

	@media (max-width: 860px) {
		.ogt-newsletter__panel { grid-template-columns: 1fr; }
	}

	.ogt-newsletter__copy {
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}

	.ogt-newsletter__title {
		color: var(--ogt-on-dark);
		max-width: 20ch;
		font-size: var(--ogt-fs-h3);
	}

	.ogt-newsletter__text {
		color: var(--ogt-on-dark-muted);
		font-size: var(--ogt-fs-sm);
		max-width: 48ch;
	}

	.ogt-newsletter__form {
		display: flex;
		flex-wrap: wrap;
		gap: 0.6rem;
		align-items: center;
	}

	.ogt-newsletter__input {
		flex: 1 1 200px;
		min-width: 0;
		padding: 0.85rem 1.1rem;
		border-radius: var(--ogt-radius-pill);
		border: 1px solid rgb(255 255 255 / 20%);
		background: rgb(255 255 255 / 10%);
		color: #fff;

		/* 16px minimum, or iOS zooms the page on focus. See .ogt-form input. */
		font-size: 1rem;
	}

	.ogt-newsletter__input::placeholder { color: rgb(255 255 255 / 45%); }

	.ogt-newsletter__input:focus {
		outline: none;
		border-color: var(--ogt-accent);
		background: rgb(255 255 255 / 16%);
		box-shadow: 0 0 0 3px rgb(var(--ogt-accent-rgb) / 25%);
	}

	/* Colour comes from .ogt-btn--on-dark (white pill → primary fill on hover),
	   which carries the unlayered text guard so the label can't vanish. This
	   used to force .ogt-btn--primary here, whose unlayered white-text guard
	   painted white on the white surface below — an invisible button. Layout
	   only now. */
	.ogt-newsletter__btn {
		flex: 0 0 auto;
	}

	.ogt-newsletter__status {
		flex: 1 0 100%;
		margin: 0;
		font-size: var(--ogt-fs-xs);
		min-height: 1.1em;
		color: var(--ogt-on-dark-subtle);
	}

	.ogt-newsletter__status.is-error { color: #ffb4ab; }
	.ogt-newsletter__status.is-ok { color: var(--ogt-accent); }
}

/* ============================================================================
   PAGINATION + SEARCH
   ========================================================================== */

@layer components {

	.navigation.pagination {
		margin-block-start: var(--ogt-gap-lg);
		display: flex;
		justify-content: center;
	}

	.nav-links {
		display: flex;
		gap: 0.4rem;
		flex-wrap: wrap;
	}

	.page-numbers {
		display: inline-grid;
		place-items: center;
		min-width: 40px;
		height: 40px;
		padding-inline: 0.65rem;
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-sm);
		background: var(--ogt-surface);
		text-decoration: none;
		color: var(--ogt-ink);
		font-size: var(--ogt-fs-sm);
		font-variant-numeric: tabular-nums;
		transition: background-color var(--ogt-dur) var(--ogt-ease), color var(--ogt-dur) var(--ogt-ease);
	}

	.page-numbers.current,
	.page-numbers:hover {
		background: var(--ogt-primary);
		border-color: var(--ogt-primary);
		color: var(--ogt-on-primary);
	}

	.page-numbers.dots {
		border-color: transparent;
		background: none;
	}

	.ogt-search {
		display: flex;
		gap: 0.5rem;
		max-width: 520px;
		width: 100%;
	}

	.ogt-search__input {
		flex: 1;
		padding: 0.8rem 1rem;
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-input);
		background: var(--ogt-surface);
		color: var(--ogt-ink);

		/* 16px minimum, or iOS zooms the page on focus. See .ogt-form input. */
		font-size: 1rem;
	}

	.ogt-search__input:focus {
		outline: none;
		border-color: var(--ogt-primary);
		box-shadow: var(--ogt-shadow-focus);
	}

	.ogt-404__links {
		list-style: none;
		display: flex;
		flex-wrap: wrap;
		gap: 1.25rem;
		padding: 0;
		margin: var(--ogt-gap) 0 0;
	}

	/* ------------------------------------------- ARTICLE + STICKY SIDEBAR */

	/*
	 * A fixed sidebar track rather than a fraction. .ogt-u-split would give
	 * 0.85fr here — about 520px on a 1280 container — which is a second
	 * column, not a sidebar. A fixed 320px also keeps the reading column at a
	 * sane measure as the viewport grows instead of widening with it.
	 *
	 * minmax(0, 1fr) not 1fr: a grid item defaults to min-width:auto, so one
	 * wide table or <pre> in a post would push the column past its track and
	 * blow out the page. This is long-form content — that will happen.
	 */
	.ogt-article {
		display: grid;
		/* 72ch, not 1fr: on a 1280 container a fraction gave the reading column
		   904px — past 90 characters a line, which is punishing over an article
		   this long. Capping the track holds the measure and lets the pair
		   centre in whatever space is left. */
		grid-template-columns: minmax(0, 72ch) 320px;
		justify-content: center;
		gap: clamp(2rem, 4vw, 3.5rem);
		align-items: start; /* required, or the sticky item stretches and never sticks */
	}

	@media (max-width: 960px) {
		.ogt-article { grid-template-columns: minmax(0, 1fr); }
	}

	/*
	 * The blog index reuses the article layout, but not its reading measure.
	 *
	 * 72ch is right for one column of running text and wrong for a card grid —
	 * it would cap the grid at about 640px and leave three cards at 190px each.
	 * The index takes the rest of the container instead, and the sidebar keeps
	 * its 320px.
	 */
	.ogt-article--index {
		grid-template-columns: minmax(0, 1fr) 320px;
	}

	/*
	 * Three across, and honest about it.
	 *
	 * auto-fit/minmax was fine when the grid owned the full container, but
	 * beside a 320px sidebar it silently dropped to two columns at common
	 * laptop widths — the brief asks for three. An explicit count holds it, and
	 * the breakpoints below step down deliberately rather than by accident.
	 *
	 * minmax(0, 1fr) rather than 1fr: a long unbroken botanical name in a card
	 * title sets a min-content floor that 1fr will not go below, which pushes
	 * the last column into the sidebar.
	 */
	.ogt-u-grid--posts {
		display: grid;
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: var(--ogt-gap);
	}

	@media (max-width: 1180px) {
		.ogt-u-grid--posts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	}

	/* Below the sidebar breakpoint the grid has the full width back, so three
	   fit again until the cards genuinely run out of room. */
	@media (max-width: 960px) {
		.ogt-u-grid--posts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	}

	@media (max-width: 820px) {
		.ogt-u-grid--posts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	}

	@media (max-width: 560px) {
		.ogt-u-grid--posts { grid-template-columns: minmax(0, 1fr); }
	}

	/* Prose centres itself and caps at 68ch when it is the page container. As
	   one column of two it should fill its track and stay left. */
	.ogt-article__body .ogt-prose {
		max-width: none;
		margin-inline: 0;
	}

	.ogt-article__comments {
		margin-block-start: var(--ogt-gap-lg);
		padding-block-start: var(--ogt-gap-lg);
		border-block-start: 1px solid var(--ogt-border-soft);
	}

	/* ------------------------------------------------- ARTICLE TYPOGRAPHY */
	/*
	 * The reading experience for long-form posts.
	 *
	 * These articles run to several thousand words with tables, checklists and
	 * a dozen headings, and the prose defaults — tuned for the short intros on
	 * marketing pages — gave all of it the same weight. Scanning for "what
	 * document does a buyer receive" meant reading everything.
	 *
	 * Opt-in, via .ogt-article__body (the blog) or .ogt-doc (the legal
	 * documents template) — not applied to .ogt-prose at large. Page copy,
	 * product descriptions and card prose stay on the marketing defaults they
	 * were signed off with; only content that is genuinely long-form and
	 * scanned rather than read asks for this.
	 *
	 * .ogt-doc is a wrapper class, so the layout rules just above — which are
	 * about being one column beside a sidebar — stay article-only.
	 */

	/*
	 * Headings carry the structure, so they need to differ from body text by
	 * more than a few pixels. h2 gets a hairline above it — at this length the
	 * eye needs a section break it can find while scrolling, not just larger
	 * text.
	 */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose > h2 {
		font-size: clamp(1.4rem, 2.4vw, 1.85rem);
		line-height: 1.25;
		letter-spacing: -0.015em;
		margin-block-start: 2.25em;
		padding-block-start: 1.25rem;
		border-block-start: 1px solid var(--ogt-border-soft);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose > h2:first-child {
		margin-block-start: 0;
		padding-block-start: 0;
		border-block-start: 0;
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose > h3 {
		font-size: clamp(1.15rem, 1.7vw, 1.3rem);
		line-height: 1.3;
		margin-block-start: 1.75em;
		color: var(--ogt-ink);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose > h4 {
		font-size: 1.05rem;
		margin-block-start: 1.5em;
		color: var(--ogt-ink);
	}

	/*
	 * A slightly larger opening paragraph. It gives the article somewhere to
	 * start rather than dropping the reader straight into body text, and costs
	 * no markup — the editor writes a normal first paragraph.
	 */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose > p:first-of-type {
		font-size: 1.075rem;
		line-height: 1.7;
		color: var(--ogt-text);
	}

	/* Lists were inheriting the flow gap, which put paragraph-sized space
	   between items and made a six-point checklist look like six sections. */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose ul,
	:is(.ogt-article__body, .ogt-doc) .ogt-prose ol {
		gap: 0.4rem;
		padding-inline-start: 1.15rem;
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose li::marker {
		color: var(--ogt-primary);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose li > ul,
	:is(.ogt-article__body, .ogt-doc) .ogt-prose li > ol {
		margin-block-start: 0.4rem;
	}

	/*
	 * Tables had scroll handling and no styling at all — no header contrast, no
	 * row separation, no cell padding. A specification table is often the most
	 * useful thing on the page and it was the hardest thing to read.
	 */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose table {
		width: 100%;
		border-collapse: collapse;
		font-size: var(--ogt-fs-sm);
		line-height: 1.5;
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose th,
	:is(.ogt-article__body, .ogt-doc) .ogt-prose td {
		padding: 0.7rem 0.9rem;
		text-align: start;
		vertical-align: top;
		border-block-end: 1px solid var(--ogt-border-soft);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose thead th {
		background: var(--ogt-muted);
		color: var(--ogt-ink);
		font-weight: 600;
		border-block-end: 2px solid var(--ogt-border-strong);
		white-space: nowrap;
	}

	/* Zebra striping does the work that borders would otherwise have to, and
	   keeps the eye on one row across a wide table. */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose tbody tr:nth-child(even) {
		background: rgb(var(--ogt-ink-rgb) / 2.5%);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose tbody tr:last-child td {
		border-block-end: 0;
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose figure.wp-block-table {
		border: 1px solid var(--ogt-border-strong);
		border-radius: var(--ogt-radius-sm);
		overflow: hidden auto;
	}

	/*
	 * Quotes and callouts. The editor has no callout block, so a blockquote is
	 * what gets reached for when something needs to stand out.
	 */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose blockquote {
		margin: 0;
		padding: 1rem 1.25rem;
		border-inline-start: 3px solid var(--ogt-accent);
		background: rgb(var(--ogt-primary-rgb) / 4%);
		border-radius: 0 var(--ogt-radius-sm) var(--ogt-radius-sm) 0;
		color: var(--ogt-ink);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose blockquote p:last-child { margin-block-end: 0; }

	/* Inline emphasis inside a wall of grey text needs to actually register. */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose strong {
		font-weight: 650;
		color: var(--ogt-ink);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose hr {
		border: 0;
		border-block-start: 1px solid var(--ogt-border-soft);
		margin-block: var(--ogt-gap);
	}

	:is(.ogt-article__body, .ogt-doc) .ogt-prose code {
		font-size: 0.9em;
		padding: 0.1em 0.35em;
		border-radius: 4px;
		background: var(--ogt-muted);
	}

	/* Figures get room to breathe and a caption that reads as a caption. */
	:is(.ogt-article__body, .ogt-doc) .ogt-prose figure { margin-inline: 0; }

	:is(.ogt-article__body, .ogt-doc) .ogt-prose figcaption {
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-text-subtle);
		margin-block-start: 0.5rem;
	}

	.ogt-article__aside {
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap);
		min-width: 0;
	}

	.ogt-sidecard {
		background: var(--ogt-surface);
		border: 1px solid var(--ogt-border-strong);
		border-radius: var(--ogt-radius-card);
		padding: var(--ogt-card-pad);
	}

	.ogt-sidecard__title {
		font-family: var(--ogt-font-head);
		font-size: var(--ogt-fs-lg);
		font-weight: 600;
		line-height: 1.3;
		color: var(--ogt-ink);
		margin: 0 0 var(--ogt-gap-sm);
	}

	.ogt-sidecard__text {
		margin: 0 0 var(--ogt-gap);
		font-size: var(--ogt-fs-sm);
		line-height: 1.6;
		color: var(--ogt-text-muted);
	}

	.ogt-sidecard__list {
		display: flex;
		flex-direction: column;
		gap: 0.15rem;
	}

	.ogt-sidecard__link {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.75rem;
		padding: 0.55rem 0.7rem;
		border-radius: var(--ogt-radius-sm);
		font-size: var(--ogt-fs-sm);
		color: var(--ogt-text);
		transition:
			background-color var(--ogt-dur) var(--ogt-ease),
			color var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-sidecard__link:hover,
	.ogt-sidecard__link:focus-visible {
		background: rgb(var(--ogt-primary-rgb) / 7%);
		color: var(--ogt-primary);
	}

	/*
	 * The current topic is marked with weight and a tinted ground as well as
	 * colour. Colour alone would fail WCAG 1.4.1 — it is the only thing
	 * separating this row from its neighbours.
	 */
	.ogt-sidecard__link.is-current {
		background: rgb(var(--ogt-primary-rgb) / 9%);
		color: var(--ogt-primary);
		font-weight: 600;
	}

	.ogt-sidecard__count {
		flex: 0 0 auto;
		font-size: var(--ogt-fs-xs);
		font-variant-numeric: tabular-nums;
		color: var(--ogt-text-subtle);
	}

	.ogt-sidecard__link.is-current .ogt-sidecard__count { color: inherit; }

	.ogt-sidecard--contact {
		background: var(--ogt-muted);
		border-color: transparent;
	}

	.ogt-sidecard__actions {
		display: flex;
		flex-direction: column;
		gap: var(--ogt-gap-sm);
		margin-block-start: var(--ogt-gap);
	}

	/* Full-width buttons: in a 320px column two side-by-side pills wrap into a
	   ragged pair, and the CTA reads as an afterthought. */
	.ogt-sidecard__actions .ogt-btn {
		width: 100%;
		justify-content: center;
	}
}

/* ---------------------------------------------------------- dark-ground text
 * These were in the layout layer, where they were dead: they set the same
 * properties as the component rules above, and a later layer wins over an
 * earlier one no matter how specific the earlier selector is. Nothing used
 * --dark until the Process section did, so the breakage never surfaced —
 * section intros rendered at 2.14:1 on the deep ground.
 *
 * Kept last in components so ordinary component colours are what they
 * override, and so this stays out of the unlayered tail, which is reserved
 * for beating WordPress core.
 */
@layer components {
	/* :not(.ogt-card ...) is load-bearing. A dark section still contains white
	   cards, and those keep their own light surface — an unscoped
	   `.ogt-section--dark h3` painted card titles white on white. The card
	   chassis already themes its own text through --card-fg, so headings
	   inside a card must be left to it. */
	.ogt-section--dark h2:not(.ogt-card h2),
	.ogt-section--dark h3:not(.ogt-card h3),
	.ogt-section--dark h4:not(.ogt-card h4),
	.ogt-section--dark .ogt-secthead__title { color: var(--ogt-on-dark); }

	.ogt-section--dark .ogt-secthead__intro,
	.ogt-section--dark > .ogt-wrap > p { color: var(--ogt-on-dark-muted); }

	.ogt-section--dark .ogt-eyebrow {
		color: var(--ogt-accent);
		background: rgb(255 255 255 / 10%);
	}
}

/* ------------------------------------------------------------ LEGAL DOCUMENTS
 * page-legal.php: privacy, cookies, trading terms, claims, shipping.
 *
 * These are read the way a contract is read — someone arrives from a link in
 * an email looking for one clause. So the page is a two-column document
 * reader: a sticky contents list that shows where you are, and a body that
 * borrows the blog's long-form typography (see :is(.ogt-article__body,
 * .ogt-doc) above) so the h2s are findable while scrolling.
 */
@layer components {
	.ogt-doc__layout {
		display: grid;
		grid-template-columns: 250px minmax(0, 1fr);
		gap: clamp(2rem, 5vw, 4.5rem);
		align-items: start;
	}

	/*
	 * The contents list is first in the DOM as well as first on screen, so
	 * keyboard and screen-reader users reach the jump links before the
	 * document rather than after it.
	 */
	.ogt-doc__toc {
		position: sticky;

		/* Clear the sticky header, plus a little breathing room. */
		top: calc(var(--ogt-header-h-stuck) + 1.5rem);
		padding-inline-start: 1rem;
		border-inline-start: 2px solid var(--ogt-border);
	}

	.ogt-doc__toc-title {
		font-family: var(--ogt-font-body);
		font-size: var(--ogt-fs-xs);
		font-weight: 600;
		letter-spacing: 0.14em;
		text-transform: uppercase;
		color: var(--ogt-text-subtle);
		margin: 0 0 0.9rem;
	}

	.ogt-doc__toc-list {
		list-style: none;
		margin: 0;
		padding: 0;
		display: grid;
		gap: 0.55rem;
	}

	.ogt-doc__toc-list a {
		display: grid;
		grid-template-columns: 1.85rem minmax(0, 1fr);
		align-items: baseline;
		font-size: var(--ogt-fs-sm);
		line-height: 1.4;
		color: var(--ogt-text-muted);
		text-decoration: none;
		border: 0;
	}

	.ogt-doc__toc-list a:hover,
	.ogt-doc__toc-list a:focus-visible { color: var(--ogt-primary); }

	.ogt-doc__toc-n {
		font-variant-numeric: tabular-nums;
		font-size: var(--ogt-fs-xs);
		color: var(--ogt-accent);
	}

	/*
	 * Effective date. Small, but the single most important line on the page for
	 * anyone checking which version of the terms their contract was formed
	 * under.
	 */
	.ogt-doc__meta {
		margin: 0 0 2rem;
		padding-block-end: 1.25rem;
		border-block-end: 1px solid var(--ogt-border-soft);
		font-size: var(--ogt-fs-sm);
		color: var(--ogt-text-subtle);
	}

	/*
	 * Keep the base reading measure, drop the auto-centring. The body track is
	 * ~880px at container width, and prose set to fill it runs to well over 100
	 * characters a line — legible in a screenshot, exhausting in a document
	 * someone is reading for a clause.
	 */
	.ogt-doc .ogt-prose,
	.ogt-doc .ogt-doc__meta { margin-inline: 0; }

	.ogt-doc__meta { max-width: 68ch; }

	/*
	 * A jump link must not drop its heading under the sticky header. Applied to
	 * the whole document rather than to :target so it also covers the browser's
	 * own "find and scroll" behaviour on reload.
	 */
	.ogt-doc :is(h2, h3)[id] { scroll-margin-block-start: calc(var(--ogt-header-h-stuck) + 1.5rem); }

	/*
	 * [orion_field] with nothing to show, rendered only for users who can fix
	 * it. Loud on purpose — it is a pre-launch checklist item sitting in the
	 * page it belongs to, not a design element.
	 */
	.ogt-todo {
		background: #fff3cd;
		color: #6b4b1c;
		padding: 0.1em 0.45em;
		border: 1px dashed #b45309;
		border-radius: var(--ogt-radius-sm);
		font-size: 0.9em;
		font-family: var(--ogt-font-body);
	}

	.ogt-doc-also__title {
		font-family: var(--ogt-font-body);
		font-size: var(--ogt-fs-xs);
		font-weight: 600;
		letter-spacing: 0.14em;
		text-transform: uppercase;
		color: var(--ogt-text-subtle);
		margin: 0 0 1.25rem;
	}

	.ogt-doc-also__list {
		display: flex;
		flex-wrap: wrap;
		gap: 0.75rem;
	}

	.ogt-doc-also__link {
		display: inline-flex;
		align-items: center;
		gap: 0.5rem;
		padding: 0.6rem 1.1rem;
		border: 1px solid var(--ogt-border);
		border-radius: var(--ogt-radius-pill);
		background: var(--ogt-surface);
		font-size: var(--ogt-fs-sm);
		color: var(--ogt-primary);
		text-decoration: none;
		transition: border-color var(--ogt-dur) var(--ogt-ease), background var(--ogt-dur) var(--ogt-ease);
	}

	.ogt-doc-also__link:hover,
	.ogt-doc-also__link:focus-visible {
		border-color: var(--ogt-accent);
		background: var(--ogt-muted);
	}

	/*
	 * Below the two-column threshold the contents list becomes a panel above
	 * the document. Un-sticking it is deliberate: stuck to the top of a narrow
	 * viewport it would eat most of the screen.
	 */
	@media (max-width: 1000px) {
		.ogt-doc__layout { grid-template-columns: minmax(0, 1fr); }

		.ogt-doc__toc {
			position: static;
			padding: 1.25rem 1.5rem;
			border-inline-start: 0;
			border: 1px solid var(--ogt-border-soft);
			border-radius: var(--ogt-radius-card);
			background: var(--ogt-muted);
		}
	}
}

/* ============================================================================
   UNLAYERED TAIL
   ----------------------------------------------------------------------------
   MUST stay outside @layer. Unlayered CSS beats layered CSS regardless of
   specificity or source order, and WordPress emits its global styles unlayered.
   Nothing in the @layer blocks above can override them — these rules are the
   only ones that can.

   Because both sides are unlayered here, ordinary specificity decides. Core
   wraps its selectors in :where(), which contributes ZERO specificity, so
   `body a` (0,0,2) beats `a:where(:not(.wp-element-button))` (0,0,1).

   Keep this section short and say why each rule is here.
   ========================================================================== */

/*
 * Links.
 *
 * WordPress core emits, unlayered, from its own theme.json:
 *
 *     a:where(:not(.wp-element-button)) { text-decoration: underline; }
 *
 * That underlines every link on the site — the primary navigation, footer
 * columns, breadcrumbs, card links and any button rendered as an <a>. It cannot
 * be turned off from inside a layer.
 *
 * (The matching `color: var(--ogt-primary)` came from THIS theme's own
 * theme.json elements.link and has been removed there — it was painting
 * "Explore Products" primary-blue on a primary-blue button, i.e. invisible,
 * while "Watch Overview" was fine because it is a <button>, not an <a>.)
 *
 * No link on the site carries a resting underline.
 */
body a {
	text-decoration: none;
}

/*
 * Figures.
 *
 * WordPress core emits, unlayered, from its own theme.json:
 *
 *     :where(figure) { margin: 0 0 1em; }
 *
 * That put a stray 16px under every <figure> on the site. Two visible effects:
 * the hero's media frame sat 16px short of its wrapper, so the two floating
 * cards — which anchor to the wrapper — landed at 8px and 24px insets instead
 * of matching; and every table in an article carried an extra gap, since the
 * table markup wraps them in <figure class="wp-block-table">.
 *
 * `figure` is (0,0,1) and core's `:where(figure)` is (0,0,0), so this wins on
 * specificity now that both sides are unlayered. It cannot be fixed from inside
 * a layer at any specificity.
 */
figure {
	margin: 0;
}

/*
 * Prose links: no underline, but still identifiable.
 *
 * A link inside a paragraph cannot be marked by colour alone here — measured,
 * --ogt-primary (#123B8F) against --ogt-text (#475569) is 1.35:1, far below the
 * 3:1 WCAG 1.4.1 requires for colour-only identification (technique G183). A
 * reader who cannot separate those hues would have no way to tell it is a link.
 *
 * Weight is the non-colour cue: it is visible without colour vision, and it
 * costs no underline. The underline returns on hover and focus as confirmation,
 * which is the pattern G183 itself describes.
 *
 * Chrome links (nav, footer, cards, buttons, breadcrumbs) need none of this —
 * their position and styling already identify them.
 */
body .ogt-prose a,
body .ogt-faq__a a,
body .ogt-quote__text a {
	font-weight: 600;
	text-decoration: none;
}

body .ogt-prose a:hover,
body .ogt-prose a:focus-visible,
body .ogt-faq__a a:hover,
body .ogt-faq__a a:focus-visible,
body .ogt-quote__text a:hover,
body .ogt-quote__text a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 0.18em;
}

/*
 * And make the button colour un-overridable, so a future core change that
 * re-introduces an unlayered link colour cannot make button labels vanish
 * again. Class beats :where().
 */
body .ogt-btn--primary { color: var(--ogt-on-primary); }
body .ogt-btn--secondary { color: var(--ogt-on-secondary); }
body .ogt-btn--on-dark { color: var(--ogt-deep); }
body .ogt-btn--glass { color: #fff; }
body .ogt-btn--ghost { color: var(--ogt-primary); }

/* on-dark is the one button whose text colour CHANGES on hover (deep → white as
   it fills primary). Its hover colour must be unlayered too, or the guard above
   pins the label to --ogt-deep and it goes dark-on-primary — the same
   invisible-label failure the guard exists to prevent. :hover adds specificity,
   so this wins over the base guard. */
body .ogt-btn--on-dark:hover { color: var(--ogt-on-primary); }

/* The floating header renders its CTA as --on-dark so it reads as a light pill
   on the dark bar. Below 1025px that same button moves into the mobile drawer,
   whose ground is white — a white button on white, with a readable label but no
   visible edge. Restore the primary fill for the drawer only. This has to be
   unlayered: a layered `color` here would lose to the --on-dark guard above and
   pin the label to --ogt-deep over the primary fill. */
@media (max-width: 1024px) {
	body .ogt-header--floating .ogt-nav__actions .ogt-btn--on-dark {
		background: var(--ogt-primary);
		color: var(--ogt-on-primary);
	}

	body .ogt-header--floating .ogt-nav__actions .ogt-btn--on-dark:hover {
		background: var(--ogt-primary-hover);
		color: var(--ogt-on-primary);
	}
}

/* ---------------------------------------------------------- block editor */

/*
 * Core ships container blocks with their own bottom margin (columns get
 * 1.75em). Inside .ogt-prose the vertical rhythm comes from the flow gap plus
 * the heading margins, so a core margin does not replace that spacing — it
 * stacks on top of it. Measured on About: the gap before "Who We Are" was 93px
 * (28 core + 47 heading + 18 flow) where every other h2 on the page sat at 65.
 *
 * Unlayered on purpose. Core block CSS is itself unlayered, so a layered rule
 * here would lose no matter how specific it is. Scoped to direct children of
 * .ogt-prose so block spacing elsewhere is untouched, and deliberately NOT
 * written as [class*="wp-block-"] — that would also zero out
 * h2.wp-block-heading's top margin and collapse every heading into its
 * preceding paragraph.
 */
.ogt-prose > .wp-block-columns,
.ogt-prose > .wp-block-group,
.ogt-prose > .wp-block-buttons {
	margin-block: 0;
}

.wp-block-button__link {
	border-radius: var(--ogt-radius-pill);
	padding: 0.85rem 1.5rem;
	font-family: var(--ogt-font-head);
	font-weight: 600;
	font-size: 0.9375rem;
	background-color: var(--ogt-primary);
	color: var(--ogt-on-primary);
	transition: background-color var(--ogt-dur) var(--ogt-ease);
}

.wp-block-button__link:hover {
	background-color: var(--ogt-primary-hover);
	color: var(--ogt-on-primary);
}

.wp-block-button.is-style-outline .wp-block-button__link {
	background-color: transparent;
	border-color: var(--ogt-border);
	color: var(--ogt-primary);
}

.wp-block-image img,
.wp-block-post-featured-image img {
	border-radius: var(--ogt-radius-img);
}

.wp-block-columns { gap: var(--ogt-gap); }

/*
 * Panel — the surface the block patterns sit on.
 *
 * The patterns used to hand-write `border-radius:8px;padding:2rem` into the
 * block markup. That pinned them to 8px next to the theme's 20px cards, and put
 * the value out of reach of the radius setting: a client moving the slider
 * changed every card on the site except the ones inside a pattern.
 *
 * Carrying it as a className instead means the pattern declares WHAT the box is
 * and the stylesheet decides how it looks, which is the same deal every other
 * component in this file gets.
 */
.ogt-panel {
	border-radius: var(--ogt-radius-card);
	padding: var(--ogt-card-pad);
}

/*
 * The inline padding has to be restated at higher specificity, because core
 * adds .has-global-padding to a group block and that rule sets
 *   padding-right/left: var(--wp--style--root--padding-right/left)
 * Those custom properties are EMPTY here — theme.json declares no root
 * padding — and a var() with no fallback that resolves to nothing is invalid
 * at computed-value time, so the property falls back to its initial value, 0.
 * It only clobbers the inline axis, which is why the panel kept its 32px top
 * and bottom while the text sat flush against the left edge.
 *
 * Restating the shorthand would not help: .has-global-padding (0,1,0) ships in
 * the global-styles block after this file, so it wins on source order at equal
 * specificity. .ogt-panel.has-global-padding is (0,2,0) and wins outright.
 */
.ogt-panel.has-global-padding,
.ogt-panel {
	padding-inline: var(--ogt-card-pad);
}

/*
 * A panel nested inside a column, rather than being the column itself, sits at
 * content height while the column stretches to the row — Mission measured
 * 206px against Vision's 178px, so the two boxes ended at different points.
 * Both markup shapes exist: the shipped pattern puts .ogt-panel on the column,
 * the stored About content nests a group inside it.
 */
.wp-block-column > .ogt-panel {
	height: 100%;
}

.ogt-panel--cta {
	padding-block: var(--ogt-space-12);
}

.has-muted-background-color { background-color: var(--ogt-muted) !important; }
.has-surface-background-color { background-color: var(--ogt-surface) !important; }
.has-primary-color { color: var(--ogt-primary) !important; }
