/* Brand color tokens — docs/spec/brand-system.md §Color tokens.
 *
 * Single definition block: every color in primary UI resolves to one of
 * these thirteen custom properties (REQ-BRAND-DEFAULT-001). Components consume
 * tokens, never raw hex — scripts/brand-tokens.test.ts fails on any raw
 * color literal outside the tokens:begin/tokens:end block below, and
 * scripts/contrast-check.ts mechanically re-verifies every text/surface
 * pairing in use against WCAG 2.1 AA (REQ-A11Y-DEFAULT-002).
 *
 * Candidate tokens from the spec table were mechanically verified
 * 2026-08-17 (scripts/contrast-check.ts). One adjustment was needed:
 * gold-700 spec candidate #A86200 measured 4.491:1 against paper-tint —
 * below the 4.5:1 AA body threshold — and was tone-adjusted to #9C5B00.
 * The adjustment preserves the hue exactly (g/r = 98/168 with b = 0, the
 * same 35° hue angle) per the spec's "hue kept, tone may adapt" rule;
 * it measures 5.37:1 vs paper and 5.07:1 vs paper-tint. All other
 * candidates passed as specced.
 *
 * Usage rules (enforced by Chunk 6 brand-conformance checks):
 *   - gold-* colors key numbers only — never links/borders/surfaces
 *     (REQ-BRAND-DEFAULT-002);
 *   - blue-500 / cyan-400 / gold-500 are dark-surface roles only;
 *   - blue-700 / gold-700 are their light-surface counterparts.
 */

/* tokens:begin */
:root {
  /* frame surfaces; ink (body text) on light — vs paper 16.28:1 */
  --navy-900: #0d2137;
  /* secondary text on light — vs paper 7.74:1, vs tint 7.30:1 */
  --ink-muted: #3a5570;
  /* primary content surface */
  --paper: #ffffff;
  /* alternating content surface (blue-tinted near-white) */
  --paper-tint: #f4f9fd;
  /* accents/links on dark only — vs navy 5.48:1 */
  --blue-500: #2e9bf0;
  /* links/interactive text on light — vs paper 5.12:1, vs tint 4.83:1 */
  --blue-700: #1170c0;
  /* accents/highlights on dark only — vs navy 10.34:1 */
  --cyan-400: #4fe0f7;
  /* key numbers on dark only — vs navy 8.03:1 */
  --gold-500: #f5a623;
  /* key numbers on light — tone-adjusted, vs paper 5.37:1, vs tint 5.07:1 */
  --gold-700: #9c5b00;
  /* body text on frame surfaces — vs navy 16.28:1 */
  --on-dark: #ffffff;
  /* Derived tokens (2026-08-18, workstream refresh, REQ-BRAND-REFRESH-004
   * — hex computed in docs/spec/design-refresh.md §Derived tokens; table
   * of record docs/spec/brand-system.md). navy-800 and glass-navy are
   * worst-case composite CAPS, not paint colors: actual composites must
   * stay at or darker than them (glow alpha <= 8%, glass alpha >= 60%). */
  /* gradient floor of frame-surface depth — on-dark 18.41:1 */
  --navy-950: #081522;
  /* composite cap: cyan-400 @ 8% over navy-900 — blue-500 4.60:1 */
  --navy-800: #123046;
  /* worst-case glass composite: navy-900 @ 60% over navy-800 */
  --glass-navy: #0f273d;
}
/* tokens:end */

/* Fluid type + spacing scales — docs/spec/design-refresh.md §Fluid type
 * scale, §Layout (REQ-BRAND-REFRESH-002/-008; workstream refresh Chunk 3).
 *
 * The only sizing vocabulary: every font-size resolves to a --step-* token
 * and every section-level (block-axis) padding/margin resolves to a
 * --space-* token — enforced by scripts/design-scale.test.ts, which pins
 * each value verbatim to the spec's clamp() table. Ratio ~1.2 at 360px
 * growing to ~1.333 at 1200px (linear interpolation over 22.5rem–75rem,
 * rounded to two decimals); --step-6 is the requirement's hero ceiling,
 * pinned verbatim. --step--1 shrinks as the viewport grows — normal for
 * negative steps in a fluid scale (min < max still holds).
 */

/* scale:begin */
:root {
  /* Type scale (--step--1 … --step-6). */
  --step--1: clamp(0.75rem, 0.87rem - 0.16vw, 0.83rem);
  --step-0: 1rem; /* body; line-height 1.6 */
  --step-1: clamp(1.2rem, 1.14rem + 0.25vw, 1.33rem);
  --step-2: clamp(1.44rem, 1.3rem + 0.64vw, 1.78rem);
  --step-3: clamp(1.73rem, 1.45rem + 1.22vw, 2.37rem);
  --step-4: clamp(2.07rem, 1.61rem + 2.06vw, 3.16rem);
  --step-5: clamp(2.49rem, 1.75rem + 3.28vw, 4.21rem);
  --step-6: clamp(2.75rem, 7.5vw, 5rem); /* hero display */

  /* Spacing scale (--space-1 … --space-10): 0.25…8 rem. Desktop frame
   * bands draw --space-9/--space-10 when Chunk 6 authors the push. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;
  --space-10: 8rem;
}
/* scale:end */
/* Global typography, surfaces & layout — docs/spec/brand-system.md
 * §Typography and §Frame vs. content region enumeration.
 *
 * The two shipped families arrive through the Astro Fonts API css
 * variables (--font-space-grotesk, --font-inter), each of which expands to
 * the family, its generated metric-adjusted fallback face, and then the
 * exact fallback stack from the spec table verbatim (configured in
 * astro.config.mjs `fallbacks`). The mono stack is system-only — no mono
 * font ships, zero bytes (REQ-BRAND-DEFAULT-007).
 */

:root {
  /* Mono accents (l>> contexts, code-flavored elements) — spec stack verbatim. */
  --font-mono:
    ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas,
    "Liberation Mono", monospace;
}

body {
  /* Body / UI: Inter (variable, wght 400-700, latin). */
  font-family: var(--font-inter);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  /* Headings / display: Space Grotesk (variable, wght 300-700, latin). */
  font-family: var(--font-space-grotesk);
}

code,
pre,
kbd,
samp {
  font-family: var(--font-mono);
}

/* --- Surfaces (Chunk 4) — docs/spec/brand-system.md §Enumeration.
 * Every region resolves to its enumerated surface token via exactly one of
 * these greppable classes on the region element (header, <section>, footer),
 * keeping the Chunk 6.2 region-conformance check mechanical:
 *   surface-frame        -> navy-900 (header, #hero, #contact, footer)
 *   surface-content      -> paper    (#method, #team, 404 message)
 *   surface-content-alt  -> paper-tint (#product)
 * Text colors stay inside the AA-verified pairing matrix
 * (scripts/contrast-check.ts PAIRINGS_IN_USE). */

.surface-frame {
  background: var(--navy-900);
  color: var(--on-dark);
}

.surface-content {
  background: var(--paper);
  color: var(--navy-900);
}

.surface-content-alt {
  background: var(--paper-tint);
  color: var(--navy-900);
}

/* --- Base layout ------------------------------------------------------- */

body {
  margin: 0;
  background: var(--paper);
  color: var(--navy-900);
  /* Body type sits on the fluid scale's base step (design-refresh.md
   * §Fluid type scale — --step-0 = 1rem, line-height 1.6). */
  font-size: var(--step-0);
  line-height: 1.6;
}

.container {
  max-width: 68rem;
  margin-inline: auto;
  padding-inline: 1.5rem;
}

/* Section rhythm draws from the --space-* scale (design-refresh.md
 * §Layout): the shipped 4.5rem snaps to --space-8 (4rem) — the scale is
 * the only spacing vocabulary; Chunk 6 pushes frame bands to
 * --space-9/--space-10. */
.section {
  padding-block: var(--space-8);
}

.section-body {
  max-width: 46rem;
}

/* Heading sizes resolve to step tokens (REQ-BRAND-REFRESH-002): the
 * shipped h1 clamp (3.4rem cap) becomes --step-6, the hero-display step
 * whose table note records exactly that mapping; section h2s take
 * --step-3. Display tracking at --step-4 and up sits in the spec's
 * −0.02…−0.035em window (design-refresh.md §Typography, Chunk 6). */
h1 {
  margin: 0.75rem 0 1rem;
  font-size: var(--step-6);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

h2 {
  margin: 0 0 1rem;
  font-size: var(--step-3);
  font-weight: 600;
  line-height: 1.25;
}

/* Links: blue-700 on light surfaces; blue-500 on the navy frame, with
 * cyan-400 reserved for on-dark emphasis (all dark-only roles by spec).
 * The color transition is the motion contract's sole media-guard
 * exemption (color-only — design-refresh.md §Motion contract point 4). */
a {
  color: var(--blue-700);
  transition: color 0.15s ease;
}

.surface-frame a {
  color: var(--blue-500);
}

.surface-frame a:hover,
.surface-frame a:focus-visible {
  color: var(--cyan-400);
}

/* --- Header / footer frame ---------------------------------------------- */

/* Sticky header (navigation.md, REQ-SITE-NAV-001). z-index invariant:
 * this is the ONLY z-index declared anywhere in shipped CSS, so any
 * positive value unconditionally wins over the later-in-DOM positioned
 * and stacking-context layers (.motif-field, .glow-orb, .ghost-mark,
 * .hero-glass, transform-bearing hover/reveal states). Do not add a
 * second z-index anywhere without revisiting this contract. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Anchor offset (navigation.md §Anchor-offset contract, REQ-SITE-NAV-002):
 * every same-page anchor target clears the sticky bar. Exactly ONE
 * var(--space-N) token — 4rem clears the ≤4rem header by the ~1px margin
 * the header_height_within_4rem guard pins mechanically (the two are a
 * mandatory pair). No scroll-behavior ships this cycle (kickoff decision:
 * it would perturb the anchor smoke's 1.2s settle window). */
section[id] {
  scroll-margin-top: var(--space-8);
}

.site-header nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: var(--space-4);
}

.brand {
  display: inline-flex;
  align-items: center;
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-header .nav-links a {
  color: var(--on-dark);
  text-decoration: none;
}

.site-header .nav-links a:hover,
.site-header .nav-links a:focus-visible {
  color: var(--cyan-400);
  text-decoration: underline;
}

.site-header .nav-links a.nav-product {
  color: var(--cyan-400);
}

/* --- Mobile nav disclosure (navigation.md §Disclosure contract,
 * REQ-SITE-NAV-004/-005, REQ-A11Y-NAV-001/-002; workstream nav Chunk 1).
 * Zero JS: toggling is native <details>/<summary>; the ☰→✕ swap is plain
 * details[open] attribute-selector CSS — an instant display change, ZERO
 * new @keyframes/transition/animation declarations (motion-lint-inert by
 * design). Native keyboard operability and focus-visible styling are left
 * native — nothing re-implemented, nothing removed. ----------------------- */

/* Marker removal — TWO separate rules, verbatim (never merged into one
 * selector list: the unrecognized -webkit- selector would invalidate the
 * standard rule in non-WebKit engines — RS-nav-001 RQ2). */
summary {
  list-style: none;
}

summary::-webkit-details-marker {
  display: none;
}

.nav-disclosure summary {
  cursor: pointer;
  /* Inline-axis padding only: a comfortable pointer target that never
   * grows the bar — the ≤4rem header-height guard binds the block axis. */
  padding-inline: var(--space-2);
}

/* ☰→✕ via details[open] — display/content change only, instant toggle. */
.nav-glyph-close {
  display: none;
}

details[open] .nav-glyph-open {
  display: none;
}

details[open] .nav-glyph-close {
  display: inline;
}

/* Open panel — Q-IMPL-NAV-102 decision of record: IN-FLOW expansion. The
 * open <details> grows the header and pushes content down (user-input-
 * driven, post-load — outside the CLS measurement window). No positioning,
 * so no new z-index anywhere (the REQ-SITE-NAV-001 invariant) and no paint-
 * order dependence; the panel surface is the header's own opaque navy-900
 * .surface-frame — no new text/surface token pairing. Compact vertical
 * list, never full-screen; block-axis spacing on --space-* tokens. */
.nav-disclosure .nav-links {
  flex-direction: column;
  align-items: flex-end;
  gap: 0.75rem;
  padding-block: var(--space-3) var(--space-4);
}

/* Exactly ONE media query swaps the active rendering, at the recorded
 * 45rem breakpoint (Q-IMPL-NAV-101: the seven-item row fits at ≥709px —
 * measured 2026-08-19 — so the 45rem/720px default stands). display:none
 * is the exact mechanism: it also removes the inactive rendering from the
 * accessibility tree and the tab order (REQ-A11Y-NAV-002). The 60rem grid
 * breakpoint below is independent and untouched. */
.nav-disclosure {
  display: none; /* desktop default: inline row on, disclosure off */
}

@media (width < 45rem) {
  .nav-inline {
    display: none; /* mobile: row off, */
  }

  .nav-disclosure {
    display: block; /* disclosure on   */
  }
}

.site-footer .container {
  /* Shipped 2.5rem snaps down to --space-6 (2rem) on the scale. */
  padding-block: var(--space-6);
}

.site-footer p {
  margin: 0.35rem 0;
}

.site-footer .copyright {
  color: var(--on-dark);
}

/* --- Hero ----------------------------------------------------------------- */

#hero.section {
  /* Shipped 6rem/5rem snaps to --space-9 (6rem) / --space-8 (4rem). */
  padding-block: var(--space-9) var(--space-8);
}

.hero-mark {
  width: 84px;
  height: auto;
}

/* --- CTAs (bordered links; text tokens stay in the verified pairings) --- */

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: var(--space-6);
}

.cta {
  display: inline-block;
  padding: 0.65rem 1.4rem;
  border: 2px solid var(--blue-500);
  border-radius: 4px;
  font-weight: 600;
  text-decoration: none;
  /* Color-only transition — exempt from the media guard; the
   * transform-bearing hover lift lives inside the guarded motion layer
   * below (design-refresh.md §Motion contract point 4). */
  transition:
    color 0.15s ease,
    border-color 0.15s ease;
}

.surface-frame .cta {
  color: var(--on-dark);
}

.surface-frame .cta:hover,
.surface-frame .cta:focus-visible {
  border-color: var(--cyan-400);
  color: var(--cyan-400);
}

.cta-light {
  border-color: var(--blue-700);
  color: var(--blue-700);
}

/* --- Featured Atlassian practice (accent card, domain list) -------------
 * Workstream refresh Chunk 5: the hub #atlassian featured block and the
 * homepage flagship card render as a COMPONENT-LEVEL dark accent card —
 * navy-family treatment on a light content section, never a full-bleed
 * frame band, and never spanning the full reading width (max-width below
 * the 46rem reading measure; site-structure.md §/services hub,
 * services-page.md §`#atlassian`; the region check pins the constraint).
 * Text/link tokens stay inside the AA-verified pairing matrix: on-dark /
 * blue-500 / cyan-400 on navy-900. */

.accent-card {
  max-width: 36rem;
  padding: var(--space-6);
  border-radius: 6px;
  background: var(--navy-900);
  color: var(--on-dark);
}

.accent-card a {
  color: var(--blue-500);
}

.accent-card a:hover,
.accent-card a:focus-visible {
  color: var(--cyan-400);
}

.accent-card :first-child {
  margin-top: 0;
}

.accent-card :last-child {
  margin-bottom: 0;
}

/* Compact four-domain list on the homepage overview (one-line mentions
 * max — REQ-SVC-SERVICES-006). */
.domain-list {
  margin-block: var(--space-5);
  padding-inline-start: 1.25rem;
}

.flagship-card {
  margin-block: var(--space-6);
}

/* --- Contact band --------------------------------------------------------- */

.contact-address a {
  font-family: var(--font-space-grotesk);
  /* Shipped 1.35rem sits on --step-1 (1.2 → 1.33rem). */
  font-size: var(--step-1);
  color: var(--cyan-400);
}

/* ==========================================================================
 * Deep-Field Editorial — workstream refresh Chunk 6
 * (docs/spec/design-refresh.md; guardrails from Chunk 3 bind every rule:
 * scripts/design-lint.ts, design-scale.test.ts, brand-conformance.test.ts).
 *
 * Greppable conventions honored (design-refresh.md Q-IMPL-REFRESH-301):
 *   glow zones     -> selectors containing `glow` (.glow-zone, .glow-orb)
 *   ghost motif    -> class containing `ghost` (.ghost-mark)
 *   eyebrows/index -> class containing `eyebrow` (.section-eyebrow)
 *   wght-300 marker-> the `font-weight: 300` declaration (.display-moment)
 *   section spacing-> padding-block / margin-block on --space-* tokens
 *
 * Joint ≤8% composite bound (stacked treatments under text — authoring
 * computation of record for the brand-system manual pass):
 *   hero:    grain 2% (asset-embedded opacity) + glow tail — the orb's
 *            8%-alpha core sits in the top-right quadrant, off the text
 *            columns (grid columns 2–8), and its radial falls to
 *            transparent at 72% radius, so under any text region the glow
 *            contributes ≤ ~4% effective alpha; 2% + 4% ≤ 8%.  ✓
 *   contact: ghost at 6% is the ONLY alpha treatment on the band (flat
 *            navy-900 — no glow, no grain — which is also what keeps the
 *            gradient-text heading on a PLAIN navy-900 surface, rule (c));
 *            6% ≤ 8% everywhere by construction.  ✓
 *   footer:  grain 2% only; 2% ≤ 8%.  ✓
 *   No ghost+glow overlap exists anywhere (they live on different bands).
 * ========================================================================== */

/* --- Depth: gradient + grain frame bands --------------------------------- */

/* Navy-family depth on frame bands (REQ-BRAND-REFRESH-004): vertical
 * navy-900 → navy-950 gradient with the colorless 2%-opacity feTurbulence
 * grain tiled above it. Applied to #hero and the footer; the header and
 * the contact band stay plain navy-900 (the contact band is the sanctioned
 * plain-navy-900 home of the gradient-text heading). The frame-surface
 * base rule is untouched — the region check pins it to pure navy tokens. */
.band-deep {
  background:
    url("data:image/svg+xml,%3c!--%20Grain%20texture%20—%20docs/spec/design-refresh.md%20§Depth%20and%20texture%20(REQ-BRAND-REFRESH-004,%20workstream%20refresh%20Chunk%206):%20colorless%20luminance-only%20feTurbulence%20(saturate%200),%20opacity%20embedded%20at%200.02%20(cap%200.05),%20no%20color%20literal%20anywhere%20—%20referenced%20from%20src/styles/global.css%20via%20url(),%20never%20a%20hex-bearing%20data-URI.%20The%202%25%20opacity%20is%20a%20joint-composite-bound%20input:%20under%20text%20it%20stacks%20with%20the%20glow%20tail%20and%20must%20keep%20the%20sum%20within%20the%208%25%20cyan-over-navy-900%20equivalent%20(see%20global.css%20depth%20notes).%20--%3e%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='280'%20height='280'%3e%3cfilter%20id='g'%3e%3cfeTurbulence%20type='fractalNoise'%20baseFrequency='0.8'%20numOctaves='2'%20stitchTiles='stitch'/%3e%3cfeColorMatrix%20type='saturate'%20values='0'/%3e%3c/filter%3e%3crect%20width='280'%20height='280'%20filter='url(%23g)'%20opacity='0.02'/%3e%3c/svg%3e") repeat,
    linear-gradient(180deg, var(--navy-900), var(--navy-950));
}

/* Positioning context for the decorative motif layers (glow orb, ghost
 * glyph); clips their overflow so no band ever scrolls horizontally. */
.motif-field {
  position: relative;
  overflow: clip;
}

/* Decorative radial glow (REQ-BRAND-REFRESH-004/-007 branch (b)): cyan
 * at the 8% alpha cap via color-mix — never relative-color syntax. The
 * orb is an aria-hidden child of the hero band, core held off the text
 * columns (see the joint-bound computation above). */
.glow-orb {
  position: absolute;
  top: -22%;
  right: -12%;
  width: min(36rem, 85vw);
  aspect-ratio: 1;
  background: radial-gradient(
    closest-side,
    color-mix(in srgb, var(--cyan-400) 8%, transparent),
    transparent 72%
  );
  pointer-events: none;
}

/* Glow-zone text discipline (closed-matrix guard): text tokens inside the
 * glow zone resolve only to on-dark / cyan-400 / blue-500 — stated on the
 * zone itself so the restriction is real in shipped CSS. */
.glow-zone {
  color: var(--on-dark);
}

/* --- Glass (≤2 elements/page, frame only, @supports fallback) ------------ */

/* The hero lede panel is the page's single glass element. Base state is
 * the mandated solid-navy fallback; the composite (navy-900 at 62% ≥ the
 * 60% floor) stays darker than the glass-navy worst-case token, and every
 * text token on it is on-dark. Glass is the first named Lighthouse cut. */
.hero-glass {
  padding: var(--space-5);
  border-radius: 6px;
  background: var(--navy-900);
}

@supports (backdrop-filter: blur(1px)) {
  .surface-frame .hero-glass {
    background: color-mix(in srgb, var(--navy-900) 62%, transparent);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
  }
}

/* --- Ghost `>>` motif (accepted 2026-08-18) ------------------------------ */

/* Frame surfaces only, aria-hidden in markup, token-derived color-mix at
 * 6% — inside both the ≤8% per-motif cap and the joint bound (the contact
 * band carries no other alpha treatment). Droppable: delete this rule and
 * the span; nothing else references either. */
.ghost-mark {
  position: absolute;
  right: -0.06em;
  bottom: -0.3em;
  font-family: var(--font-space-grotesk);
  font-size: calc(var(--step-6) * 3.4);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.12em;
  color: color-mix(in srgb, var(--cyan-400) 6%, transparent);
  pointer-events: none;
  user-select: none;
}

/* --- Gradient hairlines (gradient rule (b): 1px, on dark) ----------------- */

.hairline {
  height: 1px;
  background: linear-gradient(
    90deg,
    var(--blue-500),
    var(--cyan-400) 55%,
    transparent
  );
}

/* --- Mono voice: eyebrows / section indices (REQ-BRAND-REFRESH-003) ------ */

/* Indices encode the page's real section sequence (01, 02, … in document
 * order; hero and page intros are unnumbered). NEVER gold: ink-muted on
 * light, blue-500 on dark — the brand-conformance eyebrow/index arm pins
 * this. aria-hidden in markup: decorative voice, outside the outline. */
.section-eyebrow {
  margin: 0 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 400;
  letter-spacing: 0.16em;
  color: var(--ink-muted);
}

.surface-frame .section-eyebrow {
  color: var(--blue-500);
}

/* The method chain set in the zero-byte system mono stack — the factory's
 * own vernacular as design material (design-refresh.md §Mono voice). */
.method-chain {
  margin-block: var(--space-6) 0;
  font-family: var(--font-mono);
  font-size: var(--step-0);
  letter-spacing: 0.02em;
  color: var(--ink-muted);
}

/* --- Display typography: the wght-300 moment + gradient text -------------- */

/* The ONE accepted oversized wght-300 display moment (≤1 element/page —
 * counted over dist HTML by the wght-300 lint; the declaration below IS
 * the greppable marker). Droppable without cascade: deleting this single
 * rule reverts every h1 to the base 600 weight; no other rule, token, or
 * check references the class. */
.display-moment {
  font-weight: 300;
  letter-spacing: -0.02em;
}

/* Gradient display text — placement rule (c): plain navy-900 frame
 * surfaces only, display size only (the contact-band heading at --step-4).
 * Banned on light surfaces and inside glow zones; worst stop blue-500 vs
 * navy-900 = 5.48:1 (spec-computed). */
.gradient-text {
  background: linear-gradient(90deg, var(--blue-500), var(--cyan-400));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.contact-title {
  font-size: var(--step-4);
  font-weight: 600;
  letter-spacing: -0.02em;
}

/* --- Asymmetric 12-column grid + frame-band spacing push ------------------ */

/* Content sections adopt off-center alternating placement (columns 2–8,
 * alternating to 5–11 via .place-alt) on desktop; frame bands run
 * full-bleed with the same inner grid and push to --space-9/--space-10.
 * Mobile/tablet keep the single-column flow — no layout breakage at
 * 360/768. Semantic order is untouched (CSS placement only). */
@media (min-width: 60rem) {
  .section > .container {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--space-4);
  }

  .section > .container > * {
    grid-column: 2 / 9;
  }

  .section.place-alt > .container > * {
    grid-column: 5 / 12;
  }

  /* Frame-band spacing push (desktop): the contact band draws --space-9;
   * #hero (below, higher specificity) draws --space-10. Targeted via the
   * bands' own .motif-field class — never via a compound selector ending
   * in the frame-surface class, which the region check's shipped-CSS grep
   * would read as the base surface rule. */
  .section.motif-field {
    padding-block: var(--space-9);
  }

  #hero.section {
    padding-block: var(--space-10) var(--space-9);
  }
}

/* --- Motion (double-guarded; base state is final state everywhere) -------- */

/* Everything below is an enhancement layered on the finished static page:
 * reduced-motion users and unsupporting browsers get the identical
 * content with zero running animations (REQ-PERF-REFRESH-001…004). The
 * hero h1 and its whole ancestor chain carry NO entry animation — only
 * sibling elements marked .hero-entry move at load. */
@media (prefers-reduced-motion: no-preference) {
  /* Micro-interaction: CTA hover/focus lift (transform ≤ 4px). */
  .cta {
    transition:
      color 0.15s ease,
      border-color 0.15s ease,
      transform 0.15s ease;
  }

  .cta:hover,
  .cta:focus-visible {
    transform: translateY(-2px);
  }

  /* First-paint entry on hero SECONDARY elements only (@starting-style +
   * transition): the mark, the lede panel, and the CTA row rise around a
   * statically rendered LCP h1. */
  .hero-entry {
    transition:
      opacity 0.7s cubic-bezier(0.22, 0.7, 0.3, 1),
      transform 0.7s cubic-bezier(0.22, 0.7, 0.3, 1);
  }

  .cta-row.hero-entry {
    transition-delay: 0.12s;
  }

  @starting-style {
    .hero-entry {
      opacity: 0;
      transform: translateY(0.75rem);
    }
  }

  /* Ambient glow drift — at most one element per page (the hero orb);
   * transform-only, time-based (not scroll-driven). */
  .glow-orb {
    animation: glow-drift 26s ease-in-out infinite alternate;
  }

  @keyframes glow-drift {
    from {
      transform: translate3d(0, 0, 0);
    }
    to {
      transform: translate3d(-3rem, 2rem, 0);
    }
  }

  /* Scroll-driven entrance reveals (enhancement tier): card-level,
   * sub-viewport-height elements ONLY — never an in-page anchor-target
   * section — TRANSFORM-ONLY, entry 0% → 40%, from a visible base
   * (no start-hidden authoring). Opacity is deliberately excluded: a
   * view()-timeline element that straddles the fold at load rests frozen
   * mid-range, and a partial-opacity rest state fails color contrast
   * (Q-IMPL-REFRESH-606); a frozen partial translate is visually benign. */
  @supports (animation-timeline: view()) {
    .rise-reveal {
      animation: rise-reveal 1ms linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 40%;
    }

    @keyframes rise-reveal {
      from {
        transform: translateY(1rem);
      }
      to {
        transform: none;
      }
    }
  }
}
