/* ==========================================================================
   Gemial Digital v2 — Component layer
   --------------------------------------------------------------------------
   Reset, typography, layout primitives, buttons and the generic components.
   Loads ONLY on redesigned pages (REDESIGN-PLAN.md section 5, layer three).
   Page-specific sections live in v2/pages/{page-key}.css; the header and
   footer live in v2/shell.css; every hidden animation state lives in
   v2/motion.css and nowhere else.

   SPECIFICITY — read this before adding a rule.

   Every selector here is prefixed :where(body.gd2-page), which contributes
   ZERO specificity. So each rule scores exactly what its own selector scores:
   an element reset is (0,0,1), a component is (0,1,0), a modifier is (0,1,0)
   and wins on source order. The scope is belt-and-braces — this file is never
   enqueued on a legacy page — so it should not also be a specificity weapon.

   That is the direct lesson of CLAUDE.md section 7.2. In v1, `.gd-root a` at
   (0,2,0) beat `.gd-btn--primary` at (0,1,0) and every link-styled button
   rendered green text on a green ground. The first fix, :not(), scored (0,3,1)
   because :not() inherits its argument's specificity, and broke the footer
   instead. :where() is the tool that ends that whole class of bug: components
   are excluded from element rules by definition rather than by out-scoring
   them.

   The one exception is `body.gd2-page` itself at (0,1,1), because it styles a
   single element and competes with nothing.

   No rule in this file may set opacity: 0 or otherwise hide content. That is
   motion.css's job, behind the gate described in REDESIGN-PLAN.md section 6.
   There must be no failure mode where content stays invisible.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Root
   --------------------------------------------------------------------------
   The ground is painted on <html>, not <body>, so it is there before any JS
   runs and before the first paint of the body box. On a near-black site the
   alternative is a white overscroll flash, which is very visible on iOS.

   overflow-x: clip, never hidden. CLAUDE.md section 7.6: hidden forces
   overflow-y: auto, which turns the element into a scroll container and
   silently kills position: sticky on the header. v1 needed JS to apply this;
   here it is plain CSS on a class the header template prints.
   -------------------------------------------------------------------------- */

html.gd2-page {
	background: var(--ground);
	color-scheme: dark;
	overflow-x: clip;
	scroll-padding-block-start: calc(var(--header-h) + 1.5rem);
	-webkit-text-size-adjust: 100%;
	text-size-adjust: 100%;
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

:where(body.gd2-page) *,
:where(body.gd2-page) *::before,
:where(body.gd2-page) *::after {
	box-sizing: border-box;
}

body.gd2-page {
	margin: 0;
	background: var(--ground);
	color: var(--text);
	font-family: var(--f-body);
	font-size: 17px;
	line-height: 1.65;
	overflow-x: clip;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	/* Figures line up in columns without any per-component opt-in. */
	font-variant-numeric: tabular-nums;
}

:where(body.gd2-page) h1,
:where(body.gd2-page) h2,
:where(body.gd2-page) h3,
:where(body.gd2-page) h4,
:where(body.gd2-page) h5,
:where(body.gd2-page) h6,
:where(body.gd2-page) p,
:where(body.gd2-page) figure,
:where(body.gd2-page) blockquote {
	margin: 0;
}

:where(body.gd2-page) h1,
:where(body.gd2-page) h2,
:where(body.gd2-page) h3,
:where(body.gd2-page) h4 {
	font-family: var(--f-display);
	font-weight: 700;
	line-height: 1.04;
	letter-spacing: -.035em;
	color: var(--text-hi);
	/* Long service names and blog titles have nowhere to go on a 360px
	   screen, and a display face set tight is where overflow shows up. */
	overflow-wrap: break-word;
}

:where(body.gd2-page) img,
:where(body.gd2-page) svg,
:where(body.gd2-page) video,
:where(body.gd2-page) canvas {
	display: block;
	max-width: 100%;
}

:where(body.gd2-page) img,
:where(body.gd2-page) video {
	height: auto;
}

/* Colour comes from the component. An unclassed link in prose is styled by
   .gd-prose below; this only strips the browser default so a link inside a
   card or a nav does not arrive blue and underlined. */
:where(body.gd2-page) a {
	color: inherit;
	text-decoration: none;
}

:where(body.gd2-page) button,
:where(body.gd2-page) input,
:where(body.gd2-page) select,
:where(body.gd2-page) textarea {
	font: inherit;
	color: inherit;
}

:where(body.gd2-page) button {
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
}

:where(body.gd2-page) ::selection {
	background: var(--lime);
	color: var(--on-accent);
}

/* --------------------------------------------------------------------------
   3. Typography
   --------------------------------------------------------------------------
   Archivo carries a width axis and that is the reason it was chosen, so the
   display sizes drive font-stretch as well as font-size. The stretch tokens
   live in tokens.css; if a headline ever renders at normal width, the cause is
   a missing font-stretch descriptor in fonts.css, not this file.
   -------------------------------------------------------------------------- */

:where(body.gd2-page) .gd-display {
	font-family: var(--f-display);
	font-size: clamp(2.9rem, 8.6vw, 7.2rem);
	font-stretch: var(--wide-hero);
	font-weight: 700;
	line-height: .94;
	letter-spacing: -.035em;
	color: var(--text-hi);
	max-width: 15ch;
}

:where(body.gd2-page) .gd-h2 {
	font-family: var(--f-display);
	font-size: clamp(1.9rem, 4.4vw, 3.35rem);
	font-stretch: var(--wide-h2);
	font-weight: 700;
	line-height: 1.02;
	letter-spacing: -.035em;
	color: var(--text-hi);
	max-width: 19ch;
}

:where(body.gd2-page) .gd-h3 {
	font-family: var(--f-display);
	font-size: clamp(1.125rem, 1.4vw + .8rem, 1.4rem);
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -.025em;
	color: var(--text-hi);
}

/* The monospace eyebrow is the signature of this system, carried over from v1
   deliberately. Section labels, figure captions and spec rows all use it. */
:where(body.gd2-page) .gd-eyebrow {
	display: inline-flex;
	align-items: center;
	gap: .65rem;
	font-family: var(--f-mono);
	font-size: 10px;
	font-weight: 500;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--accent-ink);
	margin: 0 0 1.5rem;
}

:where(body.gd2-page) .gd-eyebrow::before {
	content: "";
	flex: none;
	width: 20px;
	height: 1px;
	background: currentColor;
}

:where(body.gd2-page) .gd-eyebrow--plain::before {
	display: none;
}

:where(body.gd2-page) .gd-lead {
	font-size: clamp(1.0625rem, 1.05vw + .85rem, 1.25rem);
	line-height: 1.6;
	color: var(--text-mid);
	max-width: 62ch;
}

:where(body.gd2-page) .gd-mono {
	font-family: var(--f-mono);
	font-size: 10px;
	font-weight: 500;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--text-low);
	line-height: 1.6;
}

:where(body.gd2-page) .gd-label {
	display: block;
	font-family: var(--f-mono);
	font-size: 9.5px;
	font-weight: 500;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--text-low);
}

/* Long-form copy: legal pages (M9), blog posts (M8). The only place an
   unclassed link is styled, and it is scoped to this container so it can
   never reach a component. */
:where(body.gd2-page) .gd-prose {
	max-width: 68ch;
	color: var(--text);
}

:where(body.gd2-page) .gd-prose > * + * {
	margin-block-start: 1.25em;
}

:where(body.gd2-page) .gd-prose h2,
:where(body.gd2-page) .gd-prose h3 {
	margin-block-start: 2em;
}

:where(body.gd2-page) .gd-prose a {
	color: var(--accent-ink);
	text-decoration: underline;
	text-underline-offset: .18em;
	text-decoration-thickness: 1px;
	text-decoration-color: color-mix(in srgb, var(--accent-ink) 45%, transparent);
	transition: text-decoration-color var(--dur-fast) var(--ease);
}

:where(body.gd2-page) .gd-prose a:hover {
	text-decoration-color: var(--accent-ink);
}

:where(body.gd2-page) .gd-prose ul,
:where(body.gd2-page) .gd-prose ol {
	padding-inline-start: 1.25em;
}

:where(body.gd2-page) .gd-prose li + li {
	margin-block-start: .5em;
}

/* --------------------------------------------------------------------------
   4. Layout
   -------------------------------------------------------------------------- */




:where(body.gd2-page) .gd-section {
	position: relative;
	padding-block: var(--sec-y);
}

:where(body.gd2-page) .gd-section--tight {
	padding-block: calc(var(--sec-y) * .55);
}

:where(body.gd2-page) .gd-section--flush-top {
	padding-block-start: 0;
}

/* Raised band. Bordered rather than shadowed: borders do the structural work
   in this system, shadows are almost invisible. */
:where(body.gd2-page) .gd-section--raised {
	background: var(--raised);
	border-block: 1px solid var(--hairline);
}

:where(body.gd2-page) .gd-rule {
	height: 1px;
	border: 0;
	margin: 0;
	background: var(--hairline);
}

:where(body.gd2-page) .gd-grid {
	display: grid;
	gap: clamp(1rem, 2vw, 1.75rem);
}

:where(body.gd2-page) .gd-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
:where(body.gd2-page) .gd-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
:where(body.gd2-page) .gd-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 900px) {
	:where(body.gd2-page) .gd-grid--3,
	:where(body.gd2-page) .gd-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 620px) {
	:where(body.gd2-page) .gd-grid--2,
	:where(body.gd2-page) .gd-grid--3,
	:where(body.gd2-page) .gd-grid--4 { grid-template-columns: minmax(0, 1fr); }
}

/*
 * .gd-btn, .gd-container and .gd-list-plain MOVED to
 * v2/components-shared.css at M5. The shell uses all three and it renders on
 * every page, including the twenty this file never loads on. Do not define
 * them here again.
 */

/* --------------------------------------------------------------------------
   5. Buttons
   --------------------------------------------------------------------------
   Base at (0,1,0), modifiers at (0,1,0) resolved by source order. Nothing
   here can be outranked by the element reset above, which is the entire point
   of the :where() scope.
   -------------------------------------------------------------------------- */











/* Arrow nudge. Transform only, so it costs no layout, and it is a hover
   affordance rather than an entrance animation — no gate needed. */
:where(body.gd2-page) .gd-arrow {
	flex: none;
	transition: transform var(--dur) var(--ease);
}

:where(body.gd2-page) a:hover .gd-arrow,
:where(body.gd2-page) button:hover .gd-arrow {
	transform: translateX(4px);
}

/* --------------------------------------------------------------------------
   6. Surfaces
   -------------------------------------------------------------------------- */

:where(body.gd2-page) .gd-card {
	background: var(--raised);
	border: 1px solid var(--hairline);
	border-radius: var(--r-md);
	padding: clamp(1.5rem, 2.4vw, 2rem);
}

:where(body.gd2-page) .gd-card--link {
	display: flex;
	flex-direction: column;
	transition:
		border-color var(--dur) var(--ease),
		transform var(--dur) var(--ease);
}

:where(body.gd2-page) .gd-card--link:hover {
	border-color: var(--border-int);
	transform: translateY(-3px);
}

:where(body.gd2-page) .gd-chip {
	display: inline-flex;
	align-items: center;
	gap: .45rem;
	font-family: var(--f-mono);
	font-size: 9.5px;
	font-weight: 500;
	letter-spacing: .14em;
	text-transform: uppercase;
	padding: .4rem .6rem;
	border: 1px solid var(--hairline-hi);
	border-radius: var(--r-sm);
	color: var(--text-mid);
}

:where(body.gd2-page) .gd-chip--lime {
	border-color: color-mix(in srgb, var(--accent-ink) 40%, transparent);
	color: var(--accent-ink);
}

/* Mint carries data and live indicators — the job v1's cold blue did, in a
   value that survives a near-black ground. */
:where(body.gd2-page) .gd-chip--live {
	border-color: color-mix(in srgb, var(--mint) 40%, transparent);
	color: var(--mint);
}

:where(body.gd2-page) .gd-dot {
	flex: none;
	width: 6px;
	height: 6px;
	border-radius: var(--r-pill);
	background: currentColor;
}

/* --------------------------------------------------------------------------
   6.b Derived figures — the shared primitive
   --------------------------------------------------------------------------
   A number that comes out of the data layer, with a mono label under it.

   ARRANGEMENT IS NOT HERE, DELIBERATELY. This file owns what a figure LOOKS
   like -- the display face, the tabular numerals, the accent unit, the mono
   label. Each page owns how many there are and which way they run, because
   those are layout decisions that differ per page and do not belong in a
   shared sheet: the homepage runs four across under a full-width hero, and
   the services index runs three down a narrow column beside the deck.

   WHY base.css AND NOT components-shared.css. components-shared.css has been
   site-wide since M5, so anything added there lands on twenty legacy Elementor
   pages that carry no .gd-fig markup at all. CLAUDE.md §7.13 is precisely
   about rules that are safe only because nothing happens to match them --
   "a coincidence with an expiry date". base.css loads on v2 pages only, which
   is the real scope of this component, so the guarantee is structural rather
   than lucky.

   KNOWN DIVERGENCE, SCHEDULED. The homepage's .hstat in v2/pages/home.css is a
   second implementation of this same visual idea. It is NOT converged here,
   because that is a live-page refactor and it has no business riding along
   inside a hero fix. It is recorded with a milestone in REDESIGN-PLAN.md §12's
   divergence register. Do not add a third: build on .gd-fig.

   TABULAR NUMERALS MATTER HERE. A stacked column of figures with proportional
   digits has ragged left edges where a 1 follows an 8, and the eye reads that
   as misalignment rather than as type. CLAUDE.md §5 already calls for
   tabular-nums on figures.
   -------------------------------------------------------------------------- */

:where(body.gd2-page) .gd-fig__val {
	font-family: var(--f-display);
	font-size: clamp(1.75rem, 3.2vw, 2.6rem);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	letter-spacing: -.04em;
	line-height: 1;
	color: var(--text-hi);
}

/* The unit -- %, yrs -- rides small and in the accent, so the number keeps the
   weight and the unit still reads as part of it. --accent-ink, never --lime:
   this is ink, and raw lime measures 1.24:1 on a light ground. CLAUDE.md §5. */
:where(body.gd2-page) .gd-fig__val small {
	font-size: .45em;
	font-weight: 500;
	color: var(--accent-ink);
}

:where(body.gd2-page) .gd-fig__lab {
	margin: .7rem 0 0;
	font-family: var(--f-mono);
	font-size: 9.5px;
	font-weight: 500;
	letter-spacing: .16em;
	text-transform: uppercase;
	line-height: 1.6;
	color: var(--text-mid);
}

/* --------------------------------------------------------------------------
   7. Utilities
   -------------------------------------------------------------------------- */

:where(body.gd2-page) .gd-measure { max-width: 62ch; }
:where(body.gd2-page) .gd-measure-sm { max-width: 48ch; }
:where(body.gd2-page) .gd-center { text-align: center; }
:where(body.gd2-page) .gd-center-x { margin-inline: auto; }
:where(body.gd2-page) .gd-hide { display: none !important; }


/* Visually hidden, still read aloud. Not display:none, which removes it from
   the accessibility tree entirely. */
:where(body.gd2-page) .gd-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* --------------------------------------------------------------------------
   8. Focus
   --------------------------------------------------------------------------
   :focus-visible only, so a mouse click does not draw a ring but a keyboard
   tab always does. Lime measures 15:1 on this ground, so the ring is the most
   visible thing on the page — which is what a focus ring is for.
   -------------------------------------------------------------------------- */

:where(body.gd2-page) :focus-visible {
	outline: 2px solid var(--accent-ink);
	outline-offset: 3px;
	border-radius: var(--r-sm);
}

/* --------------------------------------------------------------------------
   9. Reduced motion
   --------------------------------------------------------------------------
   Belt-and-braces. Nothing in this file hides content, and every entrance
   animation lives in motion.css behind the gate, but a visitor who has asked
   for less motion should get none of the hover transitions either.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	:where(body.gd2-page) *,
	:where(body.gd2-page) *::before,
	:where(body.gd2-page) *::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
		scroll-behavior: auto !important;
	}
}

/* --------------------------------------------------------------------------
   10. Print
   -------------------------------------------------------------------------- */

@media print {
	html.gd2-page {
		background: #fff;
	}

	body.gd2-page {
		background: #fff;
		color: #000;
		font-size: 12pt;
	}

	:where(body.gd2-page) h1,
	:where(body.gd2-page) h2,
	:where(body.gd2-page) h3,
	:where(body.gd2-page) h4 {
		color: #000;
	}
}
