/* ==========================================================================
   GENZAX — Syed Abdul Rafay portfolio
   Design system + all component styling. Motion (hero glow, pipeline dot,
   service card tilt, scroll reveal) lives in script.js.

   Style direction: smooth, cinematic, premium-tech base (generous
   whitespace, restrained color, soft cubic-bezier easing) — Apple/Linear
   register — with more playful/reactive micro-interactions reserved for
   buttons, cards and the future Automation Playground.
   ========================================================================== */

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }

/* height:auto is not cosmetic here. <img width> and <img height> become CSS
   presentational hints, so an image carrying height="808" renders 808px tall
   however narrow its column is — a 1600x808 case-study screenshot came out
   327x800 on a phone instead of 327x165. Setting it once in the reset fixes
   every current and future image; any component that genuinely wants a fixed
   height still overrides it with its own rule. */
img, svg, canvas { display: block; max-width: 100%; height: auto; }

a { color: inherit; text-decoration: none; }

ul, dl, dd { list-style: none; }

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }

/* ==========================================================================
   DESIGN TOKENS
   ========================================================================== */
:root {
  /* ---- Color ---- */
  --bg:          #0A0B08;
  --surface:     #111310;
  --surface-2:   #191C17;
  --border:      #232720;
  --text:        #F2F4EF;
  --text-dim:    #ABB1A3; /* raised from #949A8C (6.82:1 vs bg) to 8.97:1 for comfortable long-form reading; still clearly below --text (17.82:1) for hierarchy */
  --text-faint:  #7A8074;
  --lime:        #C8FF3D;
  --lime-dim:    rgba(200, 255, 61, 0.12);
  /* 2.0 additions. Not new hues -- extra steps between the existing ones, so
     surfaces and rules can express depth without introducing another colour.
     --border-strong is the hairline used where a rule must actually read as a
     boundary (section rails, active states); --border stays the quiet default.
     --sheen is the top-lit gradient that keeps large panels from looking like
     flat blocks of one colour. */
  --surface-3:    #1F231C;
  --border-strong: #2F3529;
  --lime-faint:   rgba(200, 255, 61, 0.06);
  --sheen: linear-gradient(180deg, rgba(255, 255, 255, 0.028), rgba(255, 255, 255, 0) 60%);

  /* ---- Type families ---- */
  --font-display: 'Clash Display', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;

  /* ---- Type scale: size ----
     display > h1 > h2 > h3 > h4 > body-lg > body > small > caption */
  --fs-display: clamp(40px, 6.2vw, 80px);
  --fs-h1:      clamp(33px, 4.4vw, 54px);
  --fs-h2:      clamp(27px, 3.2vw, 40px);
  --fs-h3:      clamp(19px, 1.55vw, 22px);
  --fs-h4:      clamp(16.5px, 1.35vw, 19px);
  --fs-body-lg: clamp(16.5px, 1.2vw, 18px);
  --fs-body:    clamp(15px, 1vw, 16px);
  --fs-small:   clamp(13.5px, 0.9vw, 14.5px);
  --fs-caption: clamp(11.5px, 0.85vw, 12px);
  --fs-label:   clamp(11px, 0.85vw, 12px);

  /* ---- Type scale: line-height / letter-spacing (paired per level) ---- */
  --lh-display: 1.06; --ls-display: -0.025em;
  --lh-h1:      1.08; --ls-h1:      -0.028em;
  --lh-h2:      1.14; --ls-h2:      -0.022em;
  --lh-h3:      1.3;  --ls-h3:      -0.01em;
  --lh-h4:      1.35; --ls-h4:      0em;
  --lh-body:    1.6;
  --lh-small:   1.5;
  --lh-caption: 1.4;  --ls-caption: 0.05em;
  --lh-label:   1.4;  --ls-label:   0.08em;

  /* ---- Type scale: word-spacing (display face only) ----
     Clash Display's own space glyph is 0.154em wide; most text faces sit near
     0.25em. Negative display tracking is applied after every character, the
     space included, so it narrows the word gap a second time. Measured at
     80px before this existed: a 9.5px gap against a 45.6px average glyph --
     21%, where Inter body copy reads at 54%. These values bring headings back
     to roughly 36-40% of a glyph: still tighter than body copy, which is
     correct for display, but no longer running the words together.
     Inter and JetBrains Mono are untouched; neither has the problem. */
  --ws-display: 0.085em;
  --ws-h1:      0.08em;
  --ws-h2:      0.075em;
  --ws-h3:      0.05em;
  --ws-h4:      0.04em;

  /* ---- Vertical rhythm: space directly under a heading-level element,
     before the text/label that follows it. One token, used everywhere
     a heading is immediately followed by body copy (was a 20px literal
     repeated in two places by coincidence — formalized here). ---- */
  --space-heading: 20px;

  /* ---- Type scale: weight (only two weights used anywhere on the site) ---- */
  --fw-medium:   500;
  --fw-semibold: 600;

  /* ---- Spacing scale (4px base) ---- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;
  --space-10: 140px;

  /* ---- Layout (semantic tokens, built from the spacing scale) ---- */
  --content-width: 1200px;
  --side-pad: var(--space-6);     /* 32px */
  /* Fluid rather than a fixed 140px: the old value was generous on a laptop
     and cramped at 1920, so the page rhythm changed meaning with the window.
     Floor is the old mobile value, ceiling is roomier than the old desktop. */
  --section-pad: clamp(80px, 9vw, 168px);
  /* Radius set. Pill is now reserved for genuine chips/labels (.tag, the
     availability marker); everything structural -- buttons, cards, panels,
     inputs -- uses the flat-ish steps below, which is the single biggest
     reason the old build read "template" rather than "product". */
  --radius-sm:  8px;
  --radius: 12px;
  --radius-lg:  16px;
  --radius-pill: 100px;
  /* Header height, referenced by every page's top padding and by the mobile
     panel. Overridden on .nav.is-stuck so the condensed header and the space
     reserved for it can never disagree. */
  --nav-h: 76px;

  /* ---- Motion: durations ----
     fast   = hover / micro-interactions
     base   = default UI transitions
     slow   = section-level movement
     slower = scroll-reveal entrances */
  --duration-fast:   0.15s;
  --duration-base:   0.25s;
  --duration-slow:   0.5s;
  --duration-slower: 0.6s;

  /* ---- Motion: easing curves ----
     standard = default smooth cinematic curve (most transitions)
     out      = confident deceleration, for things entering/settling
     in-out   = symmetric, for looping/ambient motion
     bounce   = soft overshoot, reserved for playful micro-interactions */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out:      cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out:   cubic-bezier(0.65, 0, 0.35, 1);
  --ease-bounce:   cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Back-compat shorthand — every existing "transition: X var(--ease)"
     call site keeps working unchanged, now riding the cinematic curve. */
  --ease: var(--duration-base) var(--ease-standard);
}

@media (max-width: 860px) {
  :root {
    --side-pad: var(--space-5); /* 24px */
    --nav-h: 64px;
  }
}

/* ---------- Base ---------- */
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.mono { font-family: var(--font-mono); }

/* Generic heading defaults — the type scale applied to bare semantic tags,
   so any new heading (blog copy, future pages) looks right with zero extra
   classes. Existing components below override these with their own
   (same-token-driven) rules where they need a different context. */
h1 {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h1);
  line-height: var(--lh-h1);
  letter-spacing: var(--ls-h1);
  word-spacing: var(--ws-h1);
}
h2 {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--ls-h2);
  word-spacing: var(--ws-h2);
}
h3 {
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--ls-h3);
  word-spacing: var(--ws-h3);
}
h4 {
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-h4);
  line-height: var(--lh-h4);
  letter-spacing: var(--ls-h4);
  word-spacing: var(--ws-h4);
}

.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  background: var(--lime);
  color: var(--bg);
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  z-index: 999;
  transition: top var(--ease);
}
.skip-link:focus { top: 16px; }

:focus-visible {
  outline: 2px solid var(--lime);
  outline-offset: 3px;
  border-radius: 4px;
}

.section__inner {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: var(--section-pad) var(--side-pad);
}

/* Stacked homepage sections each carry a full top+bottom --section-pad,
   which doubles the gap at every internal boundary (top of the next
   section + bottom of the current one). Only the last section needs its
   own bottom padding, since that's the only boundary without a sibling
   section supplying the top half. */
main > section:not(:last-child) > .section__inner {
  padding-bottom: 0;
}

/* ---------- Section heading pattern (eyebrow + title[+ subhead]) ----------
   .section__intro is the reusable wrapper for this pattern — used across
   About/Services/Process/Work so every section header reads consistently.
   No layout CSS of its own; spacing comes from the children's own rules. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--lime);
  margin-bottom: var(--space-heading);
}

.section__title {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--ls-h2);
  word-spacing: var(--ws-h2);
  max-width: 720px;
}

.section__subhead {
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  max-width: 560px;
  margin-top: var(--space-heading);
}

.pulse-dot, .status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--lime);
  box-shadow: 0 0 0 0 rgba(200, 255, 61, 0.6);
  animation: pulse 2.4s var(--ease-in-out) infinite;
}

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(200, 255, 61, 0.5); }
  70%  { box-shadow: 0 0 0 8px rgba(200, 255, 61, 0); }
  100% { box-shadow: 0 0 0 0 rgba(200, 255, 61, 0); }
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-body);
  padding: 14px 26px;
  border-radius: var(--radius-pill);
  transition: transform var(--ease), box-shadow var(--ease), background var(--ease), border-color var(--ease);
  white-space: nowrap;
}

.btn--primary {
  background: var(--lime);
  color: var(--bg);
}
.btn--primary:hover {
  transform: translateY(-2px) scale(1.02);
  box-shadow: 0 0 24px rgba(200, 255, 61, 0.35);
}

.btn--secondary {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}
.btn--secondary:hover {
  transform: translateY(-2px);
  border-color: var(--text-dim);
}

/* ---------- Card (generic, reusable — Work / Blog / future grids) ----------
   Standalone bordered surface, distinct from the "connected hairline grid"
   treatment Services uses (that one relies on its parent grid, not a
   per-card border, so it stays a bespoke pattern for now). This is the
   default card for anything built after this point. */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-6);
  transition: border-color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard),
              transform var(--duration-base) var(--ease-standard);
}
.card:hover {
  border-color: var(--text-faint);
  background: var(--surface-2);
  transform: translateY(-4px);
}

/* ---------- Navigation ---------- */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--nav-h);
  background: rgba(10, 11, 8, 0.75);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border);
  transition: backdrop-filter var(--ease);
}

.nav__inner {
  max-width: var(--content-width);
  margin: 0 auto;
  height: 100%;
  padding: 0 var(--side-pad);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.nav__logo {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h4);
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.nav__logo .nav__mark {
  /* Cap-height of "E" measured via canvas at this exact font/weight
     (600 Clash Display) = 12px at 17px font-size — coincidentally
     identical to the previous font (Space Grotesk), also 12px, so
     the values below are unchanged, but re-verify this after any
     future font swap since it's re-derived from the font's own
     metrics, not a fixed constant.
     Sizing is scaled from the target reference lockup, where the ring
     overshoots the letter cap-height by ~13% above and ~20% below
     (ring height = 1.324x cap-height) rather than matching it 1:1 —
     a pure cap-height match reads as a thin, disconnected badge since
     the ring's stroke is much lighter than the bold wordmark; the
     extra size gives it comparable visual weight. Both ratios
     measured directly from the reference image's pixel bounding boxes.
     margin-right is intentionally kept tighter (0.02em) than the
     wordmark's own letter-spacing (0.03em) — the icon should read as
     fused to the "E" like part of the word, while the letters within
     "ENZAX" itself need more room to stay legible at this weight/size. */
  height: 0.9344em;
  width: auto;
  vertical-align: -0.1387em;
  margin-right: 0.02em;
  display: inline-block;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}
.nav__links a {
  font-size: var(--fs-small);
  color: var(--text-dim);
  transition: color var(--ease);
}
.nav__links a:hover { color: var(--text); }

.nav__panel {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

.nav__toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 32px;
  height: 32px;
}
.nav__toggle span {
  display: block;
  width: 100%;
  height: 1.5px;
  background: var(--text);
  transition: transform var(--ease), opacity var(--ease);
}
.nav__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

@media (max-width: 860px) {
  .nav__toggle { display: flex; }

  .nav__panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-5);
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    padding: 28px var(--side-pad) var(--space-6);
    transform: translateY(-12px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: transform var(--ease), opacity var(--ease), visibility var(--ease);
  }
  .nav__panel.is-open {
    transform: translateY(0);
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  .nav__links {
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
  }
  .nav__links a { font-size: var(--fs-body); }
}

/* ---------- Nav (expanded, 7-item multi-page nav) ----------
   Same mobile-panel pattern as the base nav above, but switches to the
   hamburger sooner (1100px vs 860px) since "Automation Playground" plus
   six other links won't fit a horizontal row much below that. Scoped to
   .nav--expanded so the homepage's original 5-item nav is untouched. */
@media (max-width: 1100px) {
  .nav--expanded .nav__toggle { display: flex; }

  .nav--expanded .nav__panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-5);
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    padding: 28px var(--side-pad) var(--space-6);
    transform: translateY(-12px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: transform var(--ease), opacity var(--ease), visibility var(--ease);
  }
  .nav--expanded .nav__panel.is-open {
    transform: translateY(0);
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  .nav--expanded .nav__links {
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
  }
  .nav--expanded .nav__links a { font-size: var(--fs-body); }
}

/* ---------- 404 error page (/404.html) ----------
   Same section rhythm as the case-study pages: the extra top padding clears
   the fixed 72px nav, which would otherwise overlap the heading. Deliberately
   uses no .reveal classes and no reveal guard, so the message is readable
   even if script.js never loads. */
.error-page .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad));
  min-height: 70vh;
}
.error-page__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-7);
}
.error-page__note {
  margin-top: var(--space-7);
  max-width: 560px;
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}
.error-page__note a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color var(--duration-base) var(--ease-standard);
}
.error-page__note a:hover { color: var(--lime); }

/* ---------- Skeleton placeholder pages (multi-page scaffold) ----------
   Minimal shared treatment for the new section stub pages until each
   gets real content/design. */
.placeholder-section {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: calc(var(--nav-h) + var(--section-pad)) var(--side-pad) var(--section-pad);
  min-height: 60vh;
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  /* Contain the decorative .hero__glow horizontally. The glow is deliberately
     oversized and offset past the right edge (right: -30% at mobile widths),
     which widened the initial containing block to 416px at a 320px viewport.
     body{overflow-x:hidden} hid the resulting scrollbar, but the fixed .nav
     still sized itself against that wider box, pushing the hamburger button
     to x=392 — completely off-screen and unreachable on 320px devices.
     overflow-x:clip is used rather than hidden because clip does not create a
     scroll container, so it leaves the glow's intentional vertical bleed below
     the hero intact. Visually identical: the clipped-off area was already
     invisible behind body's horizontal clip. */
  overflow-x: clip;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: calc(var(--nav-h) + var(--space-8));
  isolation: isolate;
}

@media (max-width: 860px) {
  .hero { padding-top: calc(var(--nav-h) + var(--space-6)); }
}

.hero__glow {
  position: absolute;
  top: 50%;
  right: -18%;
  z-index: -1;
  width: 65%;
  height: 85%;
  transform: translateY(-50%);
  background: radial-gradient(ellipse at center, rgba(200, 255, 61, 0.32), rgba(200, 255, 61, 0.1) 45%, transparent 72%);
  filter: blur(90px);
  animation: heroGlowDrift 26s var(--ease-in-out) infinite alternate;
  pointer-events: none;
}

@keyframes heroGlowDrift {
  0%   { transform: translateY(-50%) translateX(0) scale(1); }
  50%  { transform: translateY(-53%) translateX(-3%) scale(1.1); }
  100% { transform: translateY(-47%) translateX(2%) scale(1); }
}

.hero__inner {
  position: relative;
  max-width: var(--content-width);
  margin: 0 auto;
  width: 100%;
  padding: 0 var(--side-pad);
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  align-items: center;
  gap: var(--space-7);
}

.hero__text {
  max-width: 680px;
}

.hero__title {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  word-spacing: var(--ws-display);
  margin-bottom: var(--space-5);
}
.hero__title .accent { color: var(--lime); }

.hero__subhead {
  color: var(--text);
  font-weight: var(--fw-medium);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  max-width: 480px;
  margin-bottom: var(--space-6);
}

.hero__actions {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.hero.is-offscreen .hero__glow {
  animation-play-state: paused;
}

/* ---------- Hero visual: orbiting nodes around a central hub ----------
   Pure SVG + CSS transform/opacity animation — decorative (aria-hidden),
   echoes the Ecosystem section's hub-and-spoke language so the hero
   reads as "GENZAX" from the very first screen, not just an unrelated
   graphic. Paused off-screen the same way the ambient glow is. */
.hero__visual {
  position: relative;
  width: 100%;
  max-width: 440px;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
}

.hero__orbits {
  width: 100%;
  height: 100%;
  overflow: visible;
}

.hero__ring {
  fill: none;
  stroke: var(--border);
  stroke-width: 1;
}

.hero__spoke {
  stroke: var(--border);
  stroke-width: 1;
}

.hero__orbit-group {
  transform-origin: 240px 240px;
  animation: heroOrbitSpin linear infinite;
}
.hero__orbit-group--a { animation-duration: 50s; }
.hero__orbit-group--b { animation-duration: 70s; animation-direction: reverse; }

@keyframes heroOrbitSpin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.hero__node {
  fill: var(--lime);
  filter: drop-shadow(0 0 6px rgba(200, 255, 61, 0.6));
  animation: heroNodePulse 4s var(--ease-in-out) infinite;
}
.hero__node--sm {
  animation-duration: 5s;
  animation-delay: 1s;
}

@keyframes heroNodePulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}

.hero__hub {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--lime);
  box-shadow: 0 0 32px rgba(200, 255, 61, 0.3);
}
.hero__hub img { width: 28px; height: auto; }

.hero.is-offscreen .hero__orbit-group,
.hero.is-offscreen .hero__node {
  animation-play-state: paused;
}

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

@media (prefers-reduced-motion: reduce) {
  .hero__orbit-group { animation: none; }
  .hero__node { animation: none; opacity: 0.75; }
}

.hero__stats {
  max-width: var(--content-width);
  margin: 100px auto 0;
  width: 100%;
  padding: 0 var(--side-pad);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.stat-card {
  background: var(--surface);
  padding: 28px 32px;
}
.stat-card__number {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-semibold);
  color: var(--lime);
  margin-bottom: var(--space-2);
}
.stat-card__label {
  font-size: var(--fs-small);
  color: var(--text-dim);
}

@media (max-width: 860px) {
  .hero__stats { grid-template-columns: 1fr; margin-top: 60px; }
  .hero__glow { width: 90%; right: -30%; height: 60%; }
}

/* ---------- Introduction / Approach ---------- */
.intro__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  max-width: 720px;
  margin-top: var(--space-6);
}
.intro__body p:first-child { color: var(--text); }

/* ---------- Services ecosystem ---------- */
.ecosystem__diagram {
  position: relative;
  margin-top: var(--space-8);
}

.ecosystem__hub {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  width: fit-content;
  margin: 0 auto var(--space-9);
}
.ecosystem__hub-mark {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--lime);
  box-shadow: 0 0 24px rgba(200, 255, 61, 0.25);
}
.ecosystem__hub-label {
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}

.ecosystem__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  overflow: visible;
  pointer-events: none;
}
.ecosystem__lines .ecosystem__line {
  stroke: var(--border);
  stroke-width: 1.5;
  fill: none;
  animation: ecosystemPulse 4s var(--ease-in-out) infinite;
}
@keyframes ecosystemPulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}

.ecosystem__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
}

.ecosystem__node {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.ecosystem__node-icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--lime-dim);
  color: var(--lime);
  display: flex;
  align-items: center;
  justify-content: center;
}
.ecosystem__node-icon svg { width: 20px; height: 20px; }

.ecosystem__node-title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
}
.ecosystem__node-desc {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

@media (max-width: 860px) {
  .ecosystem__grid { grid-template-columns: 1fr; gap: var(--space-6); }
  .ecosystem__hub { margin-bottom: var(--space-7); }
}

/* ---------- Why GENZAX (storytelling, no card grid) ---------- */
.why__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  max-width: 720px;
  margin-top: var(--space-6);
}
.why__body p:first-child { color: var(--text); }

.why__lede {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  line-height: var(--lh-h3);
  color: var(--text);
  padding-left: var(--space-5);
  border-left: 2px solid var(--lime);
}

/* ---------- Methodology (5-step process) ---------- */
.methodology__steps {
  margin-top: var(--space-7);
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.methodology__step {
  background: var(--surface);
  padding: var(--space-6) var(--space-5);
  transition: background var(--ease);
}
.methodology__step:hover { background: var(--surface-2); }

.methodology__step-number {
  display: block;
  font-size: var(--fs-small);
  color: var(--lime);
  margin-bottom: var(--space-4);
}
.methodology__step-title {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  margin-bottom: var(--space-2);
}
.methodology__step-desc {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

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

/* ---------- Founders (formerly single-founder About) ---------- */
.founders {
  margin-top: var(--space-8);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: start;
}

.founder-card .photo-frame {
  max-width: 320px;
  margin: 0 auto;
}

.founder-card__body {
  margin-top: var(--space-6);
}

.photo-frame {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  background: var(--surface);
  overflow: hidden;
}

.photo-frame img {
  width: 100%;
  aspect-ratio: 4 / 5;
  height: auto;
  object-fit: cover;
  border-radius: 8px;
  filter: grayscale(0.4) contrast(1.05);
  transform: scale(1.45);
  transform-origin: 50% 28%;
}

.photo-frame::before {
  content: '';
  position: absolute;
  top: 12px;
  left: 12px;
  right: 12px;
  bottom: 44px;
  border-radius: 8px;
  background: linear-gradient(160deg, var(--lime-dim), transparent 55%);
  mix-blend-mode: soft-light;
  pointer-events: none;
}

.photo-frame::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 40%;
  height: 2px;
  background: var(--lime);
}

.photo-caption {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caption);
  color: var(--text-faint);
  margin-top: var(--space-3);
  padding-left: 2px;
}

.about__narrative {
  display: flex;
  flex-direction: column;
  gap: 18px;
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  margin: 24px 0 40px;
  max-width: 640px;
}
.about__narrative p:first-child { color: var(--text); }

.fact-list {
  border-top: 1px solid var(--border);
}
.fact-list__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-5);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-small);
}
.fact-list__row dt { font-family: var(--font-mono); color: var(--text-faint); flex-shrink: 0; }
.fact-list__row dd { color: var(--text); text-align: right; font-weight: var(--fw-medium); flex: 1; min-width: 0; }

@media (max-width: 860px) {
  .founders { grid-template-columns: 1fr; gap: var(--space-7); }
  .founder-card .photo-frame { max-width: 360px; margin: 0 auto; }
}

.tag {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 6px var(--space-3);
}

/* ---------- Testimonials (inactive — see commented-out section in index.html) ---------- */
.testimonials__grid {
  margin-top: var(--space-7);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
}
.testimonial-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.testimonial-card__quote {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  color: var(--text);
}
.testimonial-card__author {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.testimonial-card__name {
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-small);
}
.testimonial-card__role {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--text-faint);
}

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

/* ---------- Process / Pipeline ---------- */
.pipeline-panel {
  margin-top: var(--space-7);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--space-7);
}

.pipeline-panel__status {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caption);
  color: var(--text-faint);
  padding-bottom: var(--space-5);
  margin-bottom: var(--space-7);
  border-bottom: 1px solid var(--border);
}
.pipeline-panel__status-live {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--lime);
}

.pipeline { position: relative; }

.pipeline__svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  overflow: visible;
}
.pipeline__line {
  stroke: var(--border);
  stroke-width: 2;
}
.pipeline__dot {
  fill: var(--lime);
  filter: drop-shadow(0 0 6px rgba(200, 255, 61, 0.8));
}

.pipeline__nodes {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

.pipeline-node {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
}
.pipeline-node__circle {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color var(--ease), color var(--ease), box-shadow var(--ease);
}
.pipeline-node__circle svg { width: 24px; height: 24px; }

.pipeline-node.is-active .pipeline-node__circle {
  border-color: var(--lime);
  color: var(--lime);
  box-shadow: 0 0 20px rgba(200, 255, 61, 0.25);
}

.pipeline-node__label {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
}
.pipeline-node__sub {
  font-size: var(--fs-caption);
  color: var(--text-faint);
}

@media (max-width: 860px) {
  .pipeline-panel { padding: 28px; }
  .pipeline__nodes {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }
}

/* ---------- Work ---------- */

/* Featured project cards — used for entries with a real visual and enough
   detail (problem / approach / solution) to warrant full case-study-style
   treatment.
   .work-features stacks them (each stays full-width so the two-column
   media+body split inside each card has room to breathe — a side-by-side
   card grid would squeeze that content too tight to read comfortably). */
.work-features {
  margin-top: var(--space-7);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* Composes .card (border/radius/hover) with padding:0 so the media can
   bleed to the edges; body padding lives on .work-feature__body instead. */
.work-feature {
  display: grid;
  grid-template-columns: 1fr 1fr;
  padding: 0;
  overflow: hidden;
}

.work-feature__media {
  position: relative;
  background: var(--bg);
  border-right: 1px solid var(--border);
}
.work-feature__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.work-feature__placeholder {
  display: none;
  width: 100%;
  height: 100%;
  min-height: 260px;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--surface-2);
  color: var(--text-faint);
  font-size: var(--fs-caption);
  line-height: var(--lh-body);
  padding: var(--space-5);
}

.work-feature__body {
  padding: var(--space-7);
}

.work-feature__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.work-feature__status {
  font-size: var(--fs-caption);
  color: var(--lime);
  white-space: nowrap;
}

.work-feature__title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  margin-bottom: var(--space-2);
}
.work-feature__disclaimer {
  color: var(--text-faint);
  font-size: var(--fs-small);
  margin-bottom: var(--space-6);
}

.work-feature__breakdown {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.work-feature__row dt {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--lime);
  margin-bottom: var(--space-2);
}
.work-feature__row dd {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

.work-feature__demo-label {
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
  margin-bottom: var(--space-3);
}
.work-feature__demo-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.work-feature__demo-list li {
  position: relative;
  padding-left: var(--space-4);
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}
.work-feature__demo-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--lime);
}

.work-feature__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-6);
  font-family: var(--font-body);
  font-weight: var(--fw-medium);
  font-size: var(--fs-small);
  color: var(--text);
  transition: color var(--ease), gap var(--ease);
}
.work-feature__link:hover {
  color: var(--lime);
  gap: var(--space-3);
}

@media (max-width: 860px) {
  .work-feature { grid-template-columns: 1fr; }
  .work-feature__media { border-right: none; border-bottom: 1px solid var(--border); }
  .work-feature__placeholder { min-height: 200px; }
  .work-feature__body { padding: var(--space-6) var(--space-5); }
}

/* ---------- Case study detail pages (/work/[slug]/) ---------- */
.case-study .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad));
}

.case-study__meta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
}
.case-study__status {
  font-size: var(--fs-caption);
  color: var(--lime);
}

.case-study__title {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h1);
  line-height: var(--lh-h1);
  letter-spacing: var(--ls-h1);
  max-width: 800px;
  margin-bottom: var(--space-4);
}
.case-study__disclaimer {
  color: var(--text-faint);
  font-size: var(--fs-small);
  margin-bottom: var(--space-8);
}

.case-study__media {
  margin-bottom: var(--space-8);
}
.case-study__media .work-feature__placeholder {
  min-height: 360px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.case-study__media img {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.case-study__sections {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
  max-width: 720px;
  margin-bottom: var(--space-9);
}
.case-study__section .eyebrow { margin-bottom: var(--space-3); }
.case-study__text {
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
}

.case-study__cta {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
  padding-bottom: var(--section-pad);
}

@media (max-width: 860px) {
  .case-study__media .work-feature__placeholder { min-height: 200px; }
}

/* ---------- Blog listing (/blog/) ---------- */
.blog-page .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad));
}

.blog__grid {
  margin-top: var(--space-7);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 380px));
  gap: var(--space-6);
}

.blog-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.blog-card__date {
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caption);
  color: var(--text-faint);
}
.blog-card__title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  color: var(--text);
}
.blog-card__excerpt {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  flex-grow: 1;
}
.blog-card__link {
  font-family: var(--font-body);
  font-weight: var(--fw-medium);
  font-size: var(--fs-small);
  color: var(--lime);
  margin-top: var(--space-2);
}

/* ---------- Article detail pages (/blog/[slug]/) ---------- */
.article .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad));
}

.article__header .eyebrow { margin-bottom: var(--space-3); }

.article__title {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h1);
  line-height: var(--lh-h1);
  letter-spacing: var(--ls-h1);
  max-width: 800px;
  margin-bottom: var(--space-4);
}

.article__meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-small);
  color: var(--text-faint);
  margin-bottom: var(--space-9);
}

.article__body {
  max-width: 720px;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
}
.article__body h2 {
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--ls-h3);
  color: var(--text);
  margin-top: var(--space-4);
}

/* Bulleted lists inside long-form/legal copy. The global reset clears
   list markers, so without this a <ul> in an article renders as
   undifferentiated stacked lines. Reuses the same lime-dot marker as
   .work-feature__demo-list so list styling stays consistent sitewide;
   sized to article body copy rather than the smaller card text. */
.article__body ul {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.article__body li {
  position: relative;
  padding-left: var(--space-5);
}
.article__body li::before {
  content: '';
  position: absolute;
  left: var(--space-2);
  top: 0.62em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--lime);
}

/* In-copy links. The global reset sets a { color: inherit; text-decoration:
   none }, so without this an inline link inside article/legal copy is
   completely indistinguishable from the text around it — same colour, no
   underline. That is a WCAG 1.4.1 failure (colour alone can't convey the
   link, and here there wasn't even a colour difference) as well as making
   contextual links useless. Brightened to --text plus an underline so the
   link reads as a link in body copy; nav, buttons and cards are unaffected
   because this is scoped to .article__body. */
.article__body a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--duration-base) var(--ease-standard);
}
.article__body a:hover { color: var(--lime); }

/* End-of-article block: hand-picked related reading + a single CTA. */
.article-end {
  margin-top: var(--space-9);
  padding-top: var(--space-7);
  border-top: 1px solid var(--border);
}
.article-end__label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
  margin-bottom: var(--space-4);
}
.article-end__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-7);
}
.article-end__links a {
  color: var(--text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--duration-base) var(--ease-standard);
}
.article-end__links a:hover { color: var(--lime); }
.article-end__links span {
  display: block;
  color: var(--text-faint);
  font-size: var(--fs-small);
  text-decoration: none;
}
.article-end__cta {
  color: var(--text-dim);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  max-width: 560px;
  margin-bottom: var(--space-5);
}
.article-end__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.article__footer {
  margin-top: var(--space-7);
  padding-bottom: var(--section-pad);
}

@media (max-width: 860px) {
  .article__meta { margin-bottom: var(--space-7); }
}

/* ---------- FAQ ---------- */
.faq__list {
  margin-top: var(--space-7);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 800px;
}

.faq-item {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  transition: border-color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard);
}
.faq-item:hover { border-color: var(--text-faint); }
.faq-item.is-open { background: var(--surface-2); }

.faq-item__question {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  text-align: left;
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  color: var(--text);
}

.faq-item__icon {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  font-size: var(--fs-body);
  line-height: 1; /* glyph-centering inside a fixed 26px circle, not paragraph copy — intentionally not tied to a text line-height token */
  transition: transform var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard);
}
.faq-item.is-open .faq-item__icon {
  transform: rotate(45deg);
  border-color: var(--lime);
  color: var(--lime);
}

.faq-item__answer {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--duration-base) var(--ease-standard);
}
.faq-item__answer p {
  padding: 0 var(--space-6) var(--space-5);
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  max-width: 640px;
}

/* Links inside FAQ answers. Same reasoning as .article__body a: the global
   reset makes an inline link inherit the surrounding colour with no
   underline, which inside a paragraph is indistinguishable from plain text
   and fails WCAG 1.4.1. Scoped to FAQ answers so buttons, cards and nav are
   unaffected. */
.faq-item__answer a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--duration-base) var(--ease-standard);
}
.faq-item__answer a:hover { color: var(--lime); }

@media (max-width: 860px) {
  .faq-item__question {
    padding: var(--space-4) var(--space-5);
    font-size: var(--fs-body);
  }
  .faq-item__answer p {
    padding: 0 var(--space-5) var(--space-4);
  }
}

/* ---------- Contact ---------- */
.contact-page .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad));
}
.contact-page .contact-panel,
.contact .contact-panel {
  margin-top: var(--space-3);
  grid-template-columns: 1fr;
  gap: var(--space-5);
  padding: var(--space-7);
}
.contact-page .contact-panel__right,
.contact .contact-panel__right {
  align-items: flex-start;
}

/* ---------- Contact: "what happens next" mini-steps ---------- */
.contact-steps {
  margin-top: var(--space-7);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.contact-step {
  background: var(--surface);
  padding: var(--space-5);
}
.contact-step__number {
  display: block;
  font-size: var(--fs-small);
  color: var(--lime);
  margin-bottom: var(--space-3);
}
.contact-step__title {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  margin-bottom: var(--space-2);
}
.contact-step__desc {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

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

/* ---------- Contact: form ---------- */
.contact-form-panel {
  margin-top: var(--space-8);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--space-8);
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}
.form-label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}
.form-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  transition: border-color var(--duration-base) var(--ease-standard);
}
.form-input:focus { outline: none; border-color: var(--lime); }
.form-textarea { resize: vertical; min-height: 130px; }

.contact-form__status {
  margin-top: var(--space-5);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  color: var(--text-dim);
  min-height: 1.4em;
}
.contact-form__status.is-success { color: var(--lime); }
.contact-form__status.is-error { color: var(--text); }

.contact-form__alt-label {
  margin-top: var(--space-9);
  margin-bottom: var(--space-3);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}

@media (max-width: 860px) {
  .contact-form-panel { padding: var(--space-6) var(--space-5); }
}

.contact-panel {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 72px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
}

.contact-panel__left p {
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  margin-top: var(--space-4);
  max-width: 440px;
}

.contact-panel__right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: var(--space-4);
}
.contact-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 14px 26px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: var(--fw-medium);
  font-size: var(--fs-body-lg);
  color: var(--text);
  transition: transform var(--ease), border-color var(--ease), color var(--ease);
}
.contact-link:hover {
  transform: translateY(-2px);
  border-color: var(--lime);
  color: var(--lime);
}
.contact-link__icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--lime);
}

@media (max-width: 860px) {
  .contact-panel {
    grid-template-columns: 1fr;
    padding: 40px 28px;
  }
  .contact-panel__right { align-items: flex-start; }
}

/* ---------- Footer ---------- */
.footer {
  border-top: 1px solid var(--border);
}
.footer__inner {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: var(--space-8) var(--side-pad) 28px;
}

.footer__top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--space-7);
  padding-bottom: var(--space-7);
  border-bottom: 1px solid var(--border);
}

.footer__tagline {
  margin-top: var(--space-4);
  max-width: 320px;
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

.footer__col-title {
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
  margin-bottom: var(--space-4);
}

.footer__nav,
.footer__contact {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}

.footer__contact-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  color: var(--text-dim);
  transition: color var(--duration-base) var(--ease-standard);
}
.footer__contact-link:hover { color: var(--lime); }
.footer__contact-link svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--lime);
}

.footer__bottom {
  padding-top: 28px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: var(--fs-caption);
  color: var(--text-faint);
  letter-spacing: var(--ls-caption);
  flex-wrap: wrap;
  gap: var(--space-2);
}

.footer__meta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.footer__link {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caption);
  color: var(--text-faint);
  transition: color var(--duration-base) var(--ease-standard);
}
.footer__link:hover { color: var(--text); }

@media (max-width: 860px) {
  .footer__top {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }
  .footer__bottom {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ---------- Automation Playground (/automation-playground/) ---------- */
.playground .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad));
}

.playground-note {
  max-width: 760px;
  margin-top: var(--space-6);
  padding: var(--space-5) var(--space-6);
  border: 1px solid var(--border);
  border-left: 2px solid var(--lime);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}
.playground-note strong { color: var(--text); }
.playground-note a { color: var(--lime); text-decoration: underline; text-underline-offset: 3px; }
.playground-note a:hover { color: var(--text); }

.playground-layout {
  margin-top: var(--space-8);
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: var(--space-7);
  align-items: start;
}

.playground-console {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  position: sticky;
  top: 96px;
}

.playground-console__label {
  display: block;
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
  margin-bottom: var(--space-3);
}

.playground-presets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.playground-preset {
  font-family: var(--font-body);
  font-size: var(--fs-small);
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  color: var(--text-dim);
  transition: border-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard);
}
.playground-preset:hover { border-color: var(--text-dim); color: var(--text); }
.playground-preset.is-active {
  border-color: var(--lime);
  background: var(--lime-dim);
  color: var(--lime);
}

.playground-textarea {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  resize: vertical;
  min-height: 110px;
  transition: border-color var(--duration-base) var(--ease-standard);
}
.playground-textarea:focus { outline: none; border-color: var(--lime); }

.playground-console__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.playground-console__hint {
  font-size: var(--fs-caption);
  color: var(--lime);
  min-height: 1.4em;
}

.playground-stage {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.playground-panel { margin-top: 0; }

.playground-steps { display: flex; flex-direction: column; }

.playground-step {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: var(--space-5);
  padding-bottom: var(--space-6);
}
.playground-step:last-child { padding-bottom: 0; }

.playground-step__marker { position: relative; display: flex; justify-content: center; }
.playground-step__marker::after {
  content: '';
  position: absolute;
  top: 40px;
  bottom: calc(-1 * var(--space-6));
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: var(--border);
  transition: background var(--duration-slow) var(--ease-standard);
}
.playground-step:last-child .playground-step__marker::after { display: none; }

.playground-step__circle {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  transition: border-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard),
              transform var(--duration-base) var(--ease-bounce),
              background var(--duration-base) var(--ease-standard);
}
.playground-step__circle svg { width: 18px; height: 18px; }

.playground-step.is-active .playground-step__circle {
  border-color: var(--lime);
  color: var(--lime);
  box-shadow: 0 0 20px rgba(200, 255, 61, 0.3);
  transform: scale(1.15);
}
.playground-step.is-done .playground-step__circle {
  border-color: var(--lime);
  color: var(--lime);
  background: var(--lime-dim);
  transform: scale(1);
}
.playground-step.is-done .playground-step__marker::after,
.playground-step.is-active .playground-step__marker::after {
  background: var(--lime);
}

.playground-step__title {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  padding-top: 8px;
}
.playground-step__sub {
  font-size: var(--fs-caption);
  color: var(--text-faint);
  margin-top: 2px;
  transition: color var(--duration-base) var(--ease-standard);
}
.playground-step__sub.is-thinking {
  color: var(--lime);
  animation: playgroundThinking 1.1s var(--ease-in-out) infinite;
}
@keyframes playgroundThinking {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

.playground-step__badge {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  color: var(--text-faint);
  margin-left: var(--space-3);
  vertical-align: middle;
  transition: border-color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard);
}
.playground-step__badge.is-yes {
  background: var(--lime-dim);
  border-color: var(--lime);
  color: var(--lime);
}
.playground-step__badge.is-no {
  color: var(--text-dim);
  border-color: var(--border);
}

.playground-result {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.playground-result__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
}
.playground-result__field { display: flex; flex-direction: column; gap: var(--space-2); }
.playground-result__label {
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}
.playground-result__value {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  color: var(--text);
  transition: color var(--duration-base) var(--ease-standard);
}
.playground-result__value.is-positive { color: var(--lime); }

.playground-result__reply {
  border-top: 1px solid var(--border);
  padding-top: var(--space-5);
}
.playground-result__reply-text {
  margin-top: var(--space-3);
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  min-height: 3.2em;
}

@media (max-width: 960px) {
  .playground-layout { grid-template-columns: 1fr; }
  .playground-console { position: static; }
}
@media (max-width: 640px) {
  .playground-result__grid { grid-template-columns: 1fr; gap: var(--space-4); }
}

/* ---------- Automation Playground teaser (homepage) ---------- */
.playground-teaser__panel {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: var(--space-8);
  align-items: center;
  padding: var(--space-8);
}

.playground-teaser__body .section__title { margin-bottom: var(--space-4); }
.playground-teaser__body .section__subhead { margin: 0 0 var(--space-6); max-width: 480px; }

.playground-teaser__preview {
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--border);
  padding-left: var(--space-6);
}
.playground-teaser__step {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 0;
}
.playground-teaser__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--lime);
  flex-shrink: 0;
  animation: playgroundTeaserPulse 3.2s var(--ease-in-out) infinite;
}
.playground-teaser__step:nth-child(1) .playground-teaser__dot { animation-delay: 0s; }
.playground-teaser__step:nth-child(2) .playground-teaser__dot { animation-delay: 0.6s; }
.playground-teaser__step:nth-child(3) .playground-teaser__dot { animation-delay: 1.2s; }
.playground-teaser__step:nth-child(4) .playground-teaser__dot { animation-delay: 1.8s; }

.playground-teaser__step-label {
  font-size: var(--fs-caption);
  color: var(--text-dim);
}

@keyframes playgroundTeaserPulse {
  0%, 100% { opacity: 0.35; box-shadow: 0 0 0 0 rgba(200, 255, 61, 0); }
  50%      { opacity: 1; box-shadow: 0 0 10px 2px rgba(200, 255, 61, 0.5); }
}

@media (prefers-reduced-motion: reduce) {
  .playground-teaser__dot { animation: none; opacity: 0.8; }
}

@media (max-width: 860px) {
  .playground-teaser__panel {
    grid-template-columns: 1fr;
    padding: var(--space-6);
    gap: var(--space-6);
  }
  .playground-teaser__preview {
    border-left: none;
    border-top: 1px solid var(--border);
    padding-left: 0;
    padding-top: var(--space-5);
  }
}

/* ---------- Scroll reveal ----------
   Base motion utility: fade + confident rise as elements enter the
   viewport (IntersectionObserver-driven, see script.js). Both opacity
   and transform ride --ease-out (confident deceleration, no overshoot)
   so the entrance reads as a deliberate settle rather than a bounce.
   .section__intro gets its own lighter/quicker variant below so
   headings lead the sequence rather than matching the body/card
   reveal beat by beat.

   PROGRESSIVE ENHANCEMENT — read this before changing anything here.
   .reveal deliberately has NO styles of its own: content marked
   .reveal is fully visible by default. The hidden-then-animate state
   applies only inside .js-reveal, a class set on <html> by a tiny
   inline script in each page's <head> (which also removes it again if
   script.js never confirms it booted). The practical effect:

     - JS disabled, blocked, failed, or still loading  -> content visible
     - IntersectionObserver unsupported                -> content visible
     - prefers-reduced-motion: reduce                  -> content visible
     - everything working                              -> content animates

   Meaningful content (legal pages, article bodies, case studies) must
   never depend on scroll position or an observer firing to be readable,
   so the animation is layered on top of a working page rather than the
   page depending on the animation. */
@media (prefers-reduced-motion: no-preference) {
  .js-reveal .reveal {
    opacity: 0;
    transform: translateY(28px) scale(0.96);
    transition: opacity var(--duration-slower) var(--ease-out),
                transform var(--duration-slower) var(--ease-out);
  }
  .js-reveal .reveal.is-visible {
    opacity: 1;
    transform: translateY(0) scale(1);
  }

  /* Section headings settle a beat faster and with less travel than
     body/card content beneath them — a small, deliberate variation so
     the whole page doesn't read as one identical canned animation. */
  .js-reveal .section__intro.reveal {
    transform: translateY(18px);
    transition-duration: var(--duration-slow);
  }
}

/* ==========================================================================
   GENZAX 2.0 — VISUAL SYSTEM
   Replaces the earlier, much thinner "hierarchy and rhythm refinements"
   block. Brand foundations are unchanged: same near-black ground, same lime,
   same Clash / Inter / JetBrains Mono. What changes is how they are used.

   The four moves everything below comes from:

   1. HIERARCHY BY SIZE, NOT BY COLOUR. The old type scale put display, h1
      and h2 within 8px of each other, so nothing led. Headings now step
      80 / 54 / 40 / 22, and colour is freed up to mean something.
   2. STRUCTURE BY HAIRLINE, NOT BY BOX. Rounded bordered cards were doing
      every job on the page, which is what made it read as a template. Rules,
      rails and connected grids now carry structure; boxes are reserved for
      things that genuinely are objects (work, forms, panels).
   3. ONE COMPOSITION PER SECTION. Problem is a two-column editorial split,
      capabilities are a connected system panel, process is a timeline rail,
      proof is a framed feature, FAQ is a hairline list. No two adjacent
      sections share a shape.
   4. LESS GLOW. The hero's radial was 32% lime over 65% of the viewport.
      Restraint reads as confidence; a wash of neon reads as a template.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. SECTION RHYTHM
   The old model gave every section a full --section-pad on top and zeroed
   the bottom, so a section's box was lopsided. That was invisible while
   every section shared one background — but it makes a tinted band
   impossible to balance. Each section now carries half the rhythm on each
   side, so two neighbours still sum to the same gap as before while each
   owns a symmetric box that can take a surface of its own.
   -------------------------------------------------------------------------- */
main > section > .section__inner {
  padding-top: calc(var(--section-pad) / 2);
  padding-bottom: calc(var(--section-pad) / 2);
}
main > section:not(:last-child) > .section__inner {
  padding-bottom: calc(var(--section-pad) / 2);
}
main > section:last-child > .section__inner { padding-bottom: var(--section-pad); }

/* Any page whose first block is a plain <section> still has to clear the
   fixed header. The homepage hero is the only first section that doesn't use
   .section__inner (it has .hero__inner and its own nav-aware padding), so it
   is deliberately untouched by this. */
main > section:first-child > .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad) * 0.62);
}

/* Interior pages built on <article> rather than <section> — same clearance,
   reached through their own page class. */
.case-study .section__inner,
.article .section__inner,
.blog-page .section__inner,
.contact-page .section__inner,
.playground .section__inner,
.about .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad) * 0.62);
}

/* Restrained background alternation. Applied to three homepage sections
   only — enough to break the page into movements, few enough that it never
   reads as stripes. */
.section--tint {
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

/* --------------------------------------------------------------------------
   2. SECTION HEADERS
   Every section opens on the same hairline, so the eye learns where a new
   idea starts without needing a coloured band or an oversized gap.
   -------------------------------------------------------------------------- */
.section__intro {
  border-top: 1px solid var(--border-strong);
  padding-top: var(--space-6);
  margin-bottom: var(--space-8);
}
.hero .section__intro { border-top: none; padding-top: 0; }

.eyebrow {
  letter-spacing: 0.12em;
  margin-bottom: var(--space-5);
}

.section__title {
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--ls-h2);
  max-width: 18ch;
  text-wrap: balance;
}
/* Titles that are a full sentence rather than a phrase need room to breathe
   past the 18ch lockup used for short section names. */
.why .section__title,
.contact .section__title,
.playground-teaser .section__title { max-width: 24ch; }

.section__intro .section__title + .section__subhead {
  margin-top: var(--space-5);
  max-width: 62ch;
  color: var(--text-dim);
}

/* --------------------------------------------------------------------------
   3. BUTTONS
   Off the pill and onto an 8px radius. The pill is what made the old CTAs
   read as a marketing template; it survives only where it is genuinely a
   chip (.tag, the hero availability marker). Glow is replaced by a single
   soft drop and a 1px inner highlight, so the button reads as a physical
   surface rather than a light source.
   -------------------------------------------------------------------------- */
.btn {
  border-radius: var(--radius-sm);
  padding: 13px 22px;
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.005em;
  line-height: 1.2;
  transition: transform var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard);
}
.btn [aria-hidden="true"] {
  display: inline-block;
  transition: transform var(--duration-base) var(--ease-out);
}
.btn:hover [aria-hidden="true"] { transform: translateX(3px); }

.btn--primary {
  background: var(--lime);
  color: var(--bg);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.38),
              0 4px 16px rgba(200, 255, 61, 0.13);
}
.btn--primary:hover {
  /* A lighter tint of the same lime, not a second brand colour. */
  background: #D5FF63;
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5),
              0 8px 24px rgba(200, 255, 61, 0.2);
}
.btn--primary:active { transform: translateY(0); }

.btn--secondary {
  border: 1px solid var(--border-strong);
  background: rgba(255, 255, 255, 0.016);
  color: var(--text);
}
.btn--secondary:hover {
  transform: translateY(-1px);
  border-color: var(--text-faint);
  background: rgba(255, 255, 255, 0.045);
}
.btn--secondary:active { transform: translateY(0); }

/* Hero-scale CTAs only. */
.btn--lg { padding: 16px 28px; font-size: var(--fs-body); }

/* --------------------------------------------------------------------------
   4. CARDS AND SURFACES
   A flat fill at this size reads as a block of colour; the top-lit --sheen
   gradient gives large surfaces a light direction, which is most of what
   separates a designed panel from a div with a background.
   -------------------------------------------------------------------------- */
.card {
  background-color: var(--surface);
  background-image: var(--sheen);
  border-color: var(--border);
  border-radius: var(--radius);
}
.card:hover {
  border-color: var(--border-strong);
  background-color: var(--surface-2);
  transform: none;
}
/* Only cards that are actually links get the lift — a lift on a static card
   is motion with nothing behind it. */
a.card:hover { transform: translateY(-3px); }

.tag {
  border-color: var(--border-strong);
  color: var(--text-dim);
  padding: 5px var(--space-3);
}

/* --------------------------------------------------------------------------
   5. NAVIGATION
   Three states rather than one. The default (below) is the safe state: if
   script.js never boots, the header stays a legible translucent bar exactly
   as before. .is-flush (top of page) and .is-stuck (scrolled) are layered on
   top by JS and are pure enhancement.
   -------------------------------------------------------------------------- */
.nav {
  background: rgba(10, 11, 8, 0.72);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid var(--border);
  transition: height var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
.nav.is-flush {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom-color: transparent;
}
.nav.is-stuck {
  --nav-h: 62px;
  background: rgba(10, 11, 8, 0.86);
  border-bottom-color: var(--border);
  box-shadow: 0 1px 24px rgba(0, 0, 0, 0.5);
}

/* The logo is the Home link on every page, but it had no hover or focus
   response at all, so nothing signalled that it was interactive. A soft lift
   on the mark is enough. Deliberately no movement and no rotation: the mark
   stands in for the G of the wordmark, so anything that shifts it breaks the
   word itself. */
.nav__logo .nav__mark {
  transition: filter var(--duration-base) var(--ease-standard);
}
.nav__logo:hover .nav__mark,
.nav__logo:focus-visible .nav__mark {
  filter: brightness(1.2) drop-shadow(0 0 9px rgba(200, 255, 61, 0.5));
}
.nav__logo {
  font-size: clamp(17px, 1.4vw, 19px);
  letter-spacing: 0.02em;
  transition: opacity var(--duration-base) var(--ease-standard);
}
.nav__logo:hover { opacity: 0.86; }

.nav__links { gap: var(--space-5); }
.nav__links a {
  position: relative;
  display: inline-block;
  padding: 7px 0;
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--text-dim);
}
.nav__links a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--lime);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duration-base) var(--ease-out);
}
.nav__links a:hover::after,
.nav__links a[aria-current="page"]::after { transform: scaleX(1); }
.nav__links a[aria-current="page"] { color: var(--text); }

/* The header CTA is a wayfinding element, not the page's main call to
   action, so it is deliberately smaller than an in-page button. */
.nav__cta {
  padding: 9px 17px;
  font-size: var(--fs-small);
  box-shadow: none;
}
.nav__cta:hover { box-shadow: 0 6px 18px rgba(200, 255, 61, 0.18); }

/* --------------------------------------------------------------------------
   6. HERO
   -------------------------------------------------------------------------- */
.hero {
  padding-bottom: var(--space-9);
  min-height: min(100vh, 880px);
}

/* Fine technical grid, masked to a soft ellipse so it never reads as a
   tiled background. Sits under the glow: ::before is the first child, so
   the glow element paints over it. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(to right, rgba(242, 244, 239, 0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(242, 244, 239, 0.04) 1px, transparent 1px);
  background-size: 76px 76px;
  -webkit-mask-image: radial-gradient(ellipse 85% 65% at 50% 32%, #000, transparent 76%);
  mask-image: radial-gradient(ellipse 85% 65% at 50% 32%, #000, transparent 76%);
  pointer-events: none;
}

/* Was 32% lime across 65% of the viewport — a wash rather than a light
   source. Tighter, dimmer, and pulled behind the orbit so it reads as the
   hub glowing rather than the page being tinted green. */
.hero__glow {
  width: 44%;
  height: 58%;
  right: -6%;
  filter: blur(100px);
  background: radial-gradient(ellipse at center,
              rgba(200, 255, 61, 0.19),
              rgba(200, 255, 61, 0.055) 46%,
              transparent 70%);
}

/* The old hero split the row into text | visual, which capped the headline
   column at ~640px. At the new display size a single line of the headline
   measures ~930px, so a two-column hero could only ever render it four lines
   deep, broken mid-phrase. The text now runs the full measure and the orbit
   moves behind it, bleeding off the right edge — the headline gets the
   two-line lockup it was written for, and the visual reads as atmosphere
   rather than as a graphic sitting in a box next to some words. */
.hero__inner {
  display: block;
  position: relative;
}
.hero__text {
  position: relative;
  z-index: 1;
  max-width: min(100%, 1000px);
}
.hero__visual {
  position: absolute;
  top: 50%;
  right: -40px;
  transform: translateY(-50%);
  z-index: 0;
  width: min(44vw, 540px);
  max-width: none;
  margin: 0;
  opacity: 0.9;
  pointer-events: none;
}
@media (max-width: 1240px) {
  .hero__visual { width: min(38vw, 420px); right: -20px; opacity: 0.6; }
}

/* Availability marker as a real chip: bordered, on its own surface, sized
   like a UI object rather than floating mono text. */
.hero .eyebrow {
  display: inline-flex;
  width: fit-content;
  align-items: center;
  gap: var(--space-2);
  padding: 7px 15px 7px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: rgba(200, 255, 61, 0.035);
  color: var(--text-dim);
  letter-spacing: 0.1em;
  margin-bottom: var(--space-6);
}

.hero__title { margin-bottom: var(--space-6); }
/* Force the two-line lockup rather than leaving the break to the wrap
   point, so the accent line always starts flush left. */
.hero__title .accent { display: block; }

.hero__subhead {
  color: var(--text-dim);
  font-weight: 400;
  font-size: var(--fs-body-lg);
  line-height: 1.62;
  max-width: 54ch;
  margin-bottom: var(--space-7);
}

.hero__actions { gap: var(--space-4); }

/* Orbit: finer strokes, dimmer nodes, a heavier hub. Reads as an instrument
   rather than a decoration. */
.hero__ring { stroke: rgba(242, 244, 239, 0.075); }
.hero__spoke { stroke: rgba(242, 244, 239, 0.06); }
.hero__node { filter: drop-shadow(0 0 5px rgba(200, 255, 61, 0.45)); }
.hero__hub {
  width: 84px;
  height: 84px;
  background: var(--surface);
  border-color: var(--border-strong);
  box-shadow: 0 0 0 8px rgba(200, 255, 61, 0.025),
              0 0 44px rgba(200, 255, 61, 0.16);
}
.hero__hub img { width: 32px; }

/* Stats: out of the bordered box and onto a rail. Three lime ticks on a
   single hairline give the hero a base without adding another card. */
.hero__stats {
  margin-top: var(--space-9);
  gap: 0;
  background: none;
  border: none;
  border-top: 1px solid var(--border);
  border-radius: 0;
  overflow: visible;
}
.stat-card {
  position: relative;
  background: none;
  padding: var(--space-6) var(--space-6) 0 var(--space-6);
  border-left: 1px solid var(--border);
}
.stat-card:first-child { border-left: none; padding-left: 0; }
.stat-card::before {
  content: '';
  position: absolute;
  top: -1px;
  left: -1px;
  width: 28px;
  height: 1px;
  background: var(--lime);
}
.stat-card:first-child::before { left: 0; }
.stat-card__number {
  color: var(--text);
  font-size: var(--fs-h4);
  letter-spacing: -0.01em;
  margin-bottom: var(--space-3);
}
.stat-card__label { color: var(--text-dim); max-width: 34ch; }

/* --------------------------------------------------------------------------
   7. PROBLEM — two-column editorial split
   The one section on the page that asks the reader to agree before anything
   is offered, so it gets the only asymmetric text layout on the site: the
   claim holds the left rail while the argument scrolls past it.
   -------------------------------------------------------------------------- */
.why .why__body {
  border-left: none;
  padding-left: 0;
  margin-top: 0;
  max-width: 60ch;
  font-size: var(--fs-body-lg);
}
.why .why__lede {
  margin: var(--space-2) 0;
  padding: var(--space-2) 0 var(--space-2) var(--space-6);
  border-left: 2px solid var(--lime);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
}

@media (min-width: 981px) {
  .why .section__inner {
    display: grid;
    grid-template-columns: 0.95fr 1.05fr;
    column-gap: var(--space-9);
    align-items: start;
  }
  .why .section__intro {
    margin-bottom: 0;
    position: sticky;
    top: calc(var(--nav-h) + var(--space-7));
  }
  /* Both columns start on the same hairline, so the split reads as one spread
     rather than two blocks that happen to sit next to each other. */
  .why .why__body {
    border-top: 1px solid var(--border-strong);
    padding-top: var(--space-6);
  }
  .why .section__title { max-width: 14ch; }
}

/* --------------------------------------------------------------------------
   8. CAPABILITIES — connected system panel
   Six separate floating cards said "six services". One panel divided by
   hairlines says "six parts of one system", which is the actual claim the
   section is making.
   -------------------------------------------------------------------------- */
.ecosystem__hub-mark {
  width: 56px;
  height: 56px;
  border-color: var(--border-strong);
  box-shadow: 0 0 0 7px rgba(200, 255, 61, 0.028),
              0 0 34px rgba(200, 255, 61, 0.14);
}
.ecosystem__hub-mark img { width: 24px; height: auto; }
/* Enough clearance for the connector curves to actually fan out between
   the hub and the top edge of the panel; at --space-8 they read as one line. */
.ecosystem__hub { margin-bottom: var(--space-9); }

.ecosystem__grid {
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.ecosystem__grid .card {
  position: relative;
  border: none;
  border-radius: 0;
  background-color: var(--surface);
  background-image: none;
  padding: var(--space-7) var(--space-6);
  gap: var(--space-4);
}
.ecosystem__grid .card:hover,
.ecosystem__grid a.card:hover {
  transform: none;
  background-color: var(--surface-2);
}
/* Lime edge grows from the left on hover — the only motion in the panel, so
   it reads as "this one" rather than as ambient animation. */
.ecosystem__node::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--lime);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duration-base) var(--ease-out);
}
.ecosystem__node:hover::after,
.ecosystem__node:focus-visible::after { transform: scaleX(1); }

.ecosystem__node-icon {
  width: 42px;
  height: 42px;
  border-radius: var(--radius-sm);
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-dim);
  transition: color var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard);
}
.ecosystem__node:hover .ecosystem__node-icon {
  color: var(--lime);
  border-color: var(--lime);
  background: var(--lime-faint);
}
.ecosystem__node-title { font-size: var(--fs-h3); letter-spacing: var(--ls-h3); }
.ecosystem__node-desc { font-size: var(--fs-small); max-width: 44ch; }

/* --------------------------------------------------------------------------
   9. PROOF — work feature
   The screenshot used to be cropped to fill half the card, which read as
   texture. Matted inside a frame with a real shadow, it reads as a product
   shot of something that exists. The breakdown becomes a spec table: mono
   label column, hairline rows.
   -------------------------------------------------------------------------- */
.work-features { gap: var(--space-7); }

.work-feature.card {
  border-top: 2px solid var(--lime);
  border-radius: var(--radius-lg);
  background-image: none;
  grid-template-columns: 1.02fr 1fr;
}
.work-feature.card:hover { transform: none; background-color: var(--surface); }

/* clip rather than hidden: both crop the card to its radius, but clip does
   not create a scroll container, which is what lets the screenshot below
   stick while the breakdown beside it scrolls past. */
.work-feature.card { overflow: clip; }

.work-feature__media {
  border-right: 1px solid var(--border);
  background: radial-gradient(ellipse 120% 90% at 50% 0%, var(--surface-2), var(--bg) 78%);
  padding: var(--space-7);
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
.work-feature__media img {
  /* The breakdown column runs several times taller than a 2:1 screenshot, so
     centring it left most of this panel empty. Sticky keeps the thing being
     described on screen for as long as the description lasts. */
  position: sticky;
  top: calc(var(--nav-h) + var(--space-6));
  width: 100%;
  height: auto;
  object-fit: contain;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  box-shadow: 0 20px 44px rgba(0, 0, 0, 0.5);
}

.work-feature__body { padding: var(--space-7); }

.work-feature__meta {
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--border);
}
.work-feature__title { font-size: var(--fs-h3); margin-bottom: var(--space-3); }
.work-feature__disclaimer {
  border-left: 2px solid var(--border-strong);
  padding-left: var(--space-3);
  margin-bottom: var(--space-6);
  color: var(--text-faint);
  max-width: 52ch;
}

.work-feature__breakdown {
  gap: 0;
  margin-bottom: var(--space-6);
  border-top: 1px solid var(--border);
}
.work-feature__row {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--space-5);
  align-items: start;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}
.work-feature__row dt {
  margin-bottom: 0;
  color: var(--text-faint);
  padding-top: 2px;
}

.work-feature__demo {
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.014);
}
.work-feature__demo-label { margin-bottom: var(--space-3); }

/* Was a bare text link at the end of a long card, which is where a reader is
   least likely to notice one. Now a real target. */
.work-feature__link {
  padding: 11px 18px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.016);
  font-weight: var(--fw-semibold);
  transition: color var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard);
}
.work-feature__link:hover {
  gap: var(--space-2);
  color: var(--lime);
  border-color: var(--lime);
  background: var(--lime-faint);
}
.work-feature__link [aria-hidden="true"] {
  display: inline-block;
  transition: transform var(--duration-base) var(--ease-out);
}
.work-feature__link:hover [aria-hidden="true"] { transform: translateX(3px); }

/* --------------------------------------------------------------------------
   10. PROCESS — timeline rail
   Five equal boxes in a bordered grid said nothing about sequence. Five
   segments of one rule, each with a node on it, says "these happen in this
   order" without a single extra word.
   -------------------------------------------------------------------------- */
.methodology__steps {
  margin-top: var(--space-8);
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-5);
  background: none;
  border: none;
  border-radius: 0;
  overflow: visible;
}
.methodology__step {
  position: relative;
  background: none;
  padding: var(--space-6) var(--space-4) 0 0;
  border-top: 1px solid var(--border-strong);
}
.methodology__step:hover { background: none; }
.methodology__step::before {
  content: '';
  position: absolute;
  top: -4px;
  left: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--lime);
  opacity: 0.55;
  transition: opacity var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
.methodology__step:hover::before {
  opacity: 1;
  box-shadow: 0 0 0 4px rgba(200, 255, 61, 0.12);
}
.methodology__step-number {
  color: var(--text-faint);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caption);
  margin-bottom: var(--space-3);
  transition: color var(--duration-base) var(--ease-standard);
}
.methodology__step:hover .methodology__step-number { color: var(--lime); }
.methodology__step-title { font-size: var(--fs-h4); margin-bottom: var(--space-3); }

/* --------------------------------------------------------------------------
   11. PIPELINE PANEL
   -------------------------------------------------------------------------- */
.pipeline-panel {
  margin-top: var(--space-8);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  background-image: var(--sheen);
  padding: var(--space-8);
}
.pipeline-panel__status {
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-caption);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-8);
}
.pipeline-node__circle {
  width: 68px;
  height: 68px;
  background: var(--bg);
  border-color: var(--border-strong);
}
.pipeline-node__label { font-size: var(--fs-h4); }
.pipeline__line { stroke: var(--border-strong); }

/* --------------------------------------------------------------------------
   12. PLAYGROUND — teaser and page
   The simulated labelling is content and is left exactly as it is; only the
   surfaces around it change.
   -------------------------------------------------------------------------- */
.playground-teaser__panel {
  border-radius: var(--radius-lg);
  background-image: var(--sheen);
  padding: var(--space-8);
  gap: var(--space-8);
}
.playground-teaser__panel:hover { background-color: var(--surface); border-color: var(--border); }
.playground-teaser__body .section__title { max-width: 20ch; margin-bottom: var(--space-4); }
.playground-teaser__preview {
  border-left: 1px solid var(--border);
  padding-left: var(--space-7);
}
.playground-teaser__step {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}
.playground-teaser__step:last-child { border-bottom: none; }
.playground-teaser__step-label { font-size: var(--fs-small); color: var(--text-dim); }

.playground-note { border-radius: var(--radius); border-left-width: 2px; }
.playground-console,
.playground-result { border-radius: var(--radius-lg); background-image: var(--sheen); }
.playground-console { top: calc(var(--nav-h) + var(--space-5)); }
.playground-preset { border-radius: var(--radius-sm); border-color: var(--border-strong); }
.playground-textarea {
  border-radius: var(--radius-sm);
  border-color: var(--border-strong);
  background: rgba(0, 0, 0, 0.28);
  font-size: var(--fs-body);
  transition: border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
.playground-textarea:focus { box-shadow: 0 0 0 3px rgba(200, 255, 61, 0.1); }
.playground-panel { border-radius: var(--radius-lg); }
.playground-step__circle { background: var(--bg); border-color: var(--border-strong); }
.playground-step__badge { border-color: var(--border-strong); }

/* --------------------------------------------------------------------------
   13. FAQ — hairline list
   Fourteen separate rounded boxes was fourteen objects to parse. One ruled
   list is a single object, which is what a set of questions actually is.
   -------------------------------------------------------------------------- */
.faq__list {
  margin-top: var(--space-8);
  gap: 0;
  max-width: 900px;
  border-top: 1px solid var(--border);
}
.faq-item {
  border: none;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  background: none;
}
.faq-item:hover { border-color: var(--border); }
.faq-item.is-open { background: none; }

.faq-item__question {
  padding: var(--space-5) var(--space-3) var(--space-5) 0;
  font-size: var(--fs-h4);
  gap: var(--space-6);
  transition: color var(--duration-base) var(--ease-standard);
}
.faq-item__question:hover { color: var(--lime); }
.faq-item.is-open .faq-item__question { color: var(--lime); }

.faq-item__icon {
  width: 22px;
  height: 22px;
  border: none;
  color: var(--text-faint);
  font-size: 20px;
}
.faq-item.is-open .faq-item__icon { border: none; color: var(--lime); }

.faq-item__answer p {
  padding: 0 var(--space-8) var(--space-6) 0;
  font-size: var(--fs-body);
  max-width: 68ch;
}

/* --------------------------------------------------------------------------
   14. CONTACT
   -------------------------------------------------------------------------- */
.contact-form-panel {
  margin-top: var(--space-7);
  border-radius: var(--radius-lg);
  background-image: var(--sheen);
  padding: var(--space-8);
  max-width: 760px;
}
.form-label { color: var(--text-dim); letter-spacing: 0.1em; }
.form-input {
  border-radius: var(--radius-sm);
  border-color: var(--border-strong);
  background: rgba(0, 0, 0, 0.28);
  padding: 13px var(--space-4);
  font-size: var(--fs-body);
  transition: border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
.form-input:focus {
  border-color: var(--lime);
  box-shadow: 0 0 0 3px rgba(200, 255, 61, 0.1);
}

/* Name and email are short and related, so they pair on one row; the two
   long fields stay full width. Pure layout — field order, names, labels,
   required flags and the Formspree action are untouched. */
@media (min-width: 720px) {
  .contact-form {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-5);
  }
  /* Keyed off an explicit class, not :nth-of-type — position-based selectors
     silently mis-key the moment a field is added or reordered, which is exactly
     what happened when Company and Service were introduced. */
  .contact-form .form-field--full { grid-column: 1 / -1; }
  .contact-form button { grid-column: 1 / -1; justify-self: start; }
}

.contact-panel {
  border-radius: var(--radius-lg);
  background-image: var(--sheen);
  padding: var(--space-7);
}
.contact-link {
  border-radius: var(--radius-sm);
  border-color: var(--border-strong);
  background: rgba(255, 255, 255, 0.016);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  padding: 13px 20px;
}
.contact-link:hover { background: var(--lime-faint); }
.contact-form__alt-label { margin-top: var(--space-8); }

/* "What happens next" reuses the process rail, so the two step sequences on
   the site read as the same device rather than two unrelated card grids. */
.contact-steps {
  margin-top: var(--space-7);
  gap: var(--space-5);
  background: none;
  border: none;
  border-radius: 0;
  overflow: visible;
}
.contact-step {
  position: relative;
  background: none;
  padding: var(--space-6) var(--space-4) 0 0;
  border-top: 1px solid var(--border-strong);
}
.contact-step::before {
  content: '';
  position: absolute;
  top: -4px;
  left: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--lime);
  opacity: 0.55;
}
.contact-step__number { color: var(--text-faint); font-size: var(--fs-caption); letter-spacing: var(--ls-caption); }
.contact-step__title { font-size: var(--fs-h4); }

/* --------------------------------------------------------------------------
   15. ABOUT
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   FOUNDERS — composition
   Two founders side by side gave each one a ~330px column, which meant the
   portrait ran almost the full width of its column (an image sitting on top
   of some text, hard against the left edge) and the bio underneath read at a
   38-character measure — too narrow to be comfortable.
   Now one founder per row: the portrait holds a 340px rail at roughly a
   third of the grid, the copy gets a proper 640px measure beside it, and a
   hairline separates the two rows in the same language the rest of the site
   uses. The portrait is present without dominating, and the row reads as a
   composed spread rather than a photo placed next to a paragraph.
   -------------------------------------------------------------------------- */
.founders {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin-top: var(--space-7);
}
.founder-card {
  display: grid;
  grid-template-columns: minmax(0, 340px) minmax(0, 640px);
  gap: var(--space-9);
  align-items: start;
  padding: var(--space-8) 0;
  border-top: 1px solid var(--border);
}
.founder-card .photo-frame { max-width: none; margin: 0; }
.founder-card__body { margin-top: 0; }
.founder-card .about__narrative { margin: 0 0 var(--space-6); max-width: none; }

/* Below the point where a 640px measure stops fitting beside a 340px
   portrait, the row stacks — but the portrait keeps a deliberate width
   instead of stretching to fill the column, which is what made it feel
   full-bleed in the first place. */
@media (max-width: 900px) {
  .founder-card {
    grid-template-columns: 1fr;
    gap: var(--space-6);
    padding: var(--space-7) 0;
  }
  /* Full column width once stacked, rather than 300px inside a 327px column
     — a 27px trailing gap reads as a mistake, where edge-to-edge reads as a
     decision. Capped so it cannot balloon on a large tablet. */
  .founder-card .photo-frame { max-width: min(100%, 420px); }
}
.photo-frame {
  border-radius: var(--radius-lg);
  border-color: var(--border-strong);
  padding: 10px;
}
.photo-frame img { border-radius: var(--radius); }
.photo-frame::after { width: 34%; height: 2px; }
.founder-card__body { margin-top: var(--space-5); }
.about__narrative { margin: var(--space-5) 0 var(--space-6); }
.fact-list { border-top-color: var(--border); }
.fact-list__row { padding: var(--space-4) 0; }



/* --------------------------------------------------------------------------
   16. BLOG, ARTICLES, CASE STUDIES
   -------------------------------------------------------------------------- */
.blog__grid {
  margin-top: var(--space-8);
  /* min() floor, not a bare 300px: below ~350px viewport a fixed track floor
     is wider than the column it sits in and pushes the page sideways. */
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: var(--space-5);
}
.blog-card { padding: var(--space-6); border-radius: var(--radius); }
.blog-card__title {
  font-size: var(--fs-h3);
  letter-spacing: var(--ls-h3);
  transition: color var(--duration-base) var(--ease-standard);
}
a.blog-card:hover .blog-card__title { color: var(--lime); }
.blog-card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  color: var(--text);
  transition: color var(--duration-base) var(--ease-standard);
}
.blog-card__link [aria-hidden="true"] {
  display: inline-block;
  transition: transform var(--duration-base) var(--ease-out);
}
a.blog-card:hover .blog-card__link { color: var(--lime); }
a.blog-card:hover .blog-card__link [aria-hidden="true"] { transform: translateX(3px); }

.article__title, .case-study__title { max-width: 20ch; text-wrap: balance; }
.article__body { font-size: var(--fs-body-lg); }
.article__body h2 { font-size: var(--fs-h3); margin-top: var(--space-6); }

/* Long-form blocks embedded in a non-article page (Services, Work, Contact)
   reuse .article__body, so they used to start immediately under a panel or a
   grid with nothing marking the change of register — they read as an appendix
   someone forgot to design. A rule and a measure turn each one into a second
   movement of the page rather than leftover text. */
.ecosystem .article__body,
.work .article__body,
.contact-page .article__body {
  max-width: 760px;
  margin-top: var(--space-8);
  padding-top: var(--space-7);
  border-top: 1px solid var(--border-strong);
}
.ecosystem .article__body h2,
.work .article__body h2,
.contact-page .article__body h2 { margin-top: 0; }
.ecosystem .article__body h2 ~ h2,
.work .article__body h2 ~ h2,
.contact-page .article__body h2 ~ h2 {
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid var(--border);
}

.case-study__media img {
  border-radius: var(--radius-lg);
  border-color: var(--border-strong);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.case-study__sections { gap: 0; max-width: 760px; margin-bottom: 0; }
.case-study__section {
  border-top: 1px solid var(--border);
  padding: var(--space-6) 0;
}

/* Closing CTA. On Services this sat directly under the last paragraph with no
   separation at all, so the two buttons read as part of that sentence. The
   rule and the space give it the same closing treatment .article-end gives
   Work, About and Insights — and because it now supplies the bottom rule of
   the case-study stack, .case-study__section no longer needs one of its own. */
.case-study__cta {
  gap: var(--space-4);
  /* Same measure as the copy above it, so the closing rule lines up with the
     text it closes rather than running the full grid width. */
  max-width: 760px;
  margin-top: var(--space-8);
  padding-top: var(--space-7);
  border-top: 1px solid var(--border-strong);
}

@media (max-width: 860px) {
  .case-study__cta { margin-top: var(--space-7); padding-top: var(--space-6); }
}

.article-end { border-top-color: var(--border-strong); }

/* --------------------------------------------------------------------------
   17. FOOTER
   Lifted onto its own surface so the page has a floor, with the wordmark set
   large and very low-contrast behind it. One deliberate flourish, in the one
   place where a flourish costs nothing.
   -------------------------------------------------------------------------- */
.footer {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border-top: 1px solid var(--border);
}
.footer::after {
  content: 'GENZAX';
  position: absolute;
  left: 50%;
  bottom: -0.3em;
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: clamp(88px, 18vw, 240px);
  line-height: 1;
  letter-spacing: -0.045em;
  color: rgba(242, 244, 239, 0.022);
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
}
.footer__inner {
  position: relative;
  z-index: 1;
  padding-top: var(--space-9);
  padding-bottom: var(--space-6);
}
.footer__top {
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: var(--space-8);
  padding-bottom: var(--space-7);
  border-bottom: 1px solid var(--border);
}
.footer__brand .nav__logo { font-size: clamp(20px, 2vw, 26px); }
.footer__tagline { margin-top: var(--space-5); max-width: 36ch; }
.footer__col-title {
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
  letter-spacing: 0.12em;
}
.footer__nav, .footer__contact { gap: var(--space-3); }
.footer__nav .footer__link {
  font-family: var(--font-body);
  font-size: var(--fs-small);
  letter-spacing: 0;
  color: var(--text-dim);
}
.footer__nav .footer__link:hover { color: var(--lime); }
.footer__bottom { padding-top: var(--space-5); }

/* --------------------------------------------------------------------------
   18. MOTION
   The reveal used to scale as well as translate, which softened text
   mid-transition. Translate only: the same entrance, without the blur.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .js-reveal .reveal {
    transform: translateY(20px);
    transition-duration: var(--duration-slow);
  }
  .js-reveal .reveal.is-visible { transform: translateY(0); }
  .js-reveal .section__intro.reveal { transform: translateY(12px); }
}

/* --------------------------------------------------------------------------
   19. RESPONSIVE
   -------------------------------------------------------------------------- */
@media (max-width: 1180px) {
  .methodology__steps { grid-template-columns: repeat(3, 1fr); row-gap: var(--space-7); }
}

@media (max-width: 1024px) {
  .ecosystem__grid { grid-template-columns: repeat(2, 1fr); }
  .hero__inner { grid-template-columns: 1.2fr 0.8fr; gap: var(--space-7); }
}

@media (max-width: 980px) {
  .work-feature.card { grid-template-columns: 1fr; }
  .work-feature__media { border-right: none; border-bottom: 1px solid var(--border); padding: var(--space-6); }
}

@media (max-width: 860px) {
  .hero { min-height: auto; padding-bottom: var(--space-8); }
  .hero::before { background-size: 56px 56px; }
  .hero__glow { width: 92%; right: -26%; height: 52%; }
  .hero__stats {
    grid-template-columns: 1fr;
    margin-top: var(--space-8);
    border-top: none;
  }
  .stat-card {
    border-left: none;
    border-top: 1px solid var(--border);
    padding: var(--space-5) 0 0;
  }
  .stat-card::before { left: 0; }

  .ecosystem__grid .card { padding: var(--space-6) var(--space-5); }

  .methodology__steps { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
  .methodology__step { padding: var(--space-5) var(--space-4) 0 0; }

  .contact-steps { grid-template-columns: 1fr; gap: var(--space-6); }
  .contact-step { padding: var(--space-5) 0 0; }

  .work-feature__body { padding: var(--space-6) var(--space-5); }
  .work-feature__row { grid-template-columns: 1fr; gap: var(--space-2); }
  .work-feature__row dt { padding-top: 0; }

  .pipeline-panel { padding: var(--space-6) var(--space-5); }
  .playground-teaser__panel { padding: var(--space-6) var(--space-5); }
  .playground-teaser__preview { border-left: none; border-top: 1px solid var(--border); padding-left: 0; padding-top: var(--space-4); }

  .faq-item__question { font-size: var(--fs-body); gap: var(--space-4); padding: var(--space-4) 0; }
  .faq-item__answer p { padding: 0 var(--space-5) var(--space-5) 0; }

  .contact-form-panel { padding: var(--space-6) var(--space-5); }
  .contact-panel { padding: var(--space-6) var(--space-5); }

  .footer__inner { padding-top: var(--space-8); }
  .footer__top { grid-template-columns: 1fr; gap: var(--space-7); }
  .footer::after { font-size: 24vw; bottom: -0.24em; }

  /* Touch targets. Stacked on a phone these were 22px tall with 12px between
     them — seven links in a column, all easy to mis-tap. Padding rather than
     font-size, so nothing about the footer's look changes. */
  .footer__nav, .footer__contact { gap: var(--space-1); }
  .footer__nav .footer__link,
  .footer__contact-link { padding: 9px 0; }

}

/* The menu button is the only way to navigate on a phone and was a 32px
   square. Padded to 44px on a border-box, so the three bars keep their exact
   32px width and the target grows around them.
   The 12px sits on the inner (right) side because the button is the first
   item in the header: that keeps the bars flush with the page's left rail at
   var(--side-pad), lined up with the logo, hero eyebrow and headline below. */
@media (max-width: 1100px) {
  .nav__toggle {
    width: 44px;
    height: 44px;
    /* Asymmetric so the bars sit flush against the right page rail while the
       tap target stays a full 44px. Mirrors the logo's flush left edge. */
    padding: 6px 0 6px 12px;
  }
}

/* The logo is the Home link on every page; vertical padding on an inline
   element extends the hit area without touching the baseline lockup between
   the mark and the wordmark. */
.nav__logo { padding: 11px 0; }

/* Insights listing. The three-column grid is right on desktop and stays, but
   once it collapses to one column the card chrome stops doing any work: ten
   identical bordered boxes stacked in a row, each already separated by 24px of
   space. Below the collapse they become a ruled list — same cards, same order,
   same links, noticeably less to scroll past. */
@media (max-width: 680px) {
  .blog__grid {
    gap: 0;
    border-top: 1px solid var(--border);
  }
  .blog-card,
  a.blog-card:hover {
    padding: var(--space-6) 0;
    border: none;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
    background: none;
    transform: none;
  }
  .blog-card__link { margin-top: var(--space-3); }
}

/* Tablet keeps a grid: at 768 a single column turned six short capability
   cards and five process steps into a very long scroll for no gain. */
@media (max-width: 680px) {
  .ecosystem__grid { grid-template-columns: 1fr; }
  .methodology__steps { grid-template-columns: 1fr; }
  .methodology__step { padding: var(--space-5) 0 0; }
}

/* The self-initiated-build tag is 33 characters. On a phone it wraps to two
   lines, and a two-line pill reads as a stretched capsule rather than a label,
   so below this width it becomes a square-cornered block instead. */
@media (max-width: 620px) {
  .work-feature__meta .tag,
  .case-study__meta .tag {
    border-radius: var(--radius-sm);
    padding: 7px var(--space-3);
    /* Tracking tuned down just enough to save a wrapped line at 375px. */
    letter-spacing: 0.04em;
  }
}

@media (max-width: 480px) {
  .hero__actions { width: 100%; }
  .hero__actions .btn { flex: 1 1 auto; justify-content: center; }
  .contact-panel__right .contact-link { width: 100%; justify-content: center; }
}

/* --------------------------------------------------------------------------
   20. MOBILE NAVIGATION
   Full-bleed sheet, one link per ruled row, display type, 48px+ targets and
   the CTA pinned full width at the foot of the sheet.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .nav .nav__panel,
  .nav--expanded .nav__panel {
    top: 100%;
    gap: 0;
    padding: var(--space-2) var(--side-pad) var(--space-7);
    /* Opaque, not translucent. A blurred sheet at 97% still let the hero
       headline ghost through behind the menu, which made the sheet read as
       an overlay rather than a place you had navigated to. */
    background: var(--bg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    height: calc(100dvh - var(--nav-h));
    overflow-y: auto;
  }
  .nav .nav__links,
  .nav--expanded .nav__links { width: 100%; gap: 0; }
  .nav .nav__links li,
  .nav--expanded .nav__links li { width: 100%; border-bottom: 1px solid var(--border); }
  .nav .nav__links a,
  .nav--expanded .nav__links a {
    display: block;
    width: 100%;
    padding: 15px 0;
    font-family: var(--font-display);
    font-weight: var(--fw-medium);
    font-size: var(--fs-h4);
    color: var(--text);
  }
  /* The sliding underline is a pointer affordance; on a touch sheet it would
     just flash a full-width line under whatever was last tapped. */
  .nav .nav__links a::after,
  .nav--expanded .nav__links a::after { display: none; }
  .nav .nav__links a[aria-current="page"],
  .nav--expanded .nav__links a[aria-current="page"] { color: var(--lime); }

  .nav .nav__cta,
  .nav--expanded .nav__cta {
    width: 100%;
    justify-content: center;
    /* auto, not a fixed gap: the CTA sits at the foot of the sheet where a
       thumb reaches it, whatever the device height. */
    margin-top: auto;
    padding: 15px 22px;
    font-size: var(--fs-body);
  }
}

/* --------------------------------------------------------------------------
   TYPOGRAPHY — word-spacing for display text that is not a heading tag
   The h1-h4 rules above cover most of the site, but these elements set
   --font-display on a <p>, <span>, <button> or <a>, so they inherit nothing.
   Grouped here rather than scattered so the display face is either covered
   everywhere or provably not.
   -------------------------------------------------------------------------- */
.stat-card__number,
.why__lede,
.pipeline-node__label,
.faq-item__question,
.contact-step__title,
.playground-step__title,
.playground-result__value,
.testimonial-card__name { word-spacing: var(--ws-h3); }

.nav__logo,
.footer__brand .nav__logo { word-spacing: 0.02em; }

/* The ghost wordmark is a single word — tracking, not word-spacing. */
.footer::after { word-spacing: 0; }

/* The hero's second line grew by ~55px once the words were separated, which
   put the full stop of "system." 9px inside the orbit hub behind it. Nudging
   the decorative visual clears the collision; below 1240px the smaller
   variant already sits clear. */
.hero__visual { right: -72px; }

/* --------------------------------------------------------------------------
   ARTICLE PAGES — editorial reading experience
   Reading measure was 720px at 17.28px, roughly 83 characters a line. The
   header ended in a 96px gap with nothing marking where the article started,
   and H2s were 22px against 17.28px body -- a 1.27x step, not enough to read
   as a section break. Scoped to .article so the long-form blocks embedded in
   Services, Work and Contact keep their own treatment.
   -------------------------------------------------------------------------- */
.article .article__header {
  /* Bounded so the masthead rule relates to the 660px body measure instead of
     running the full 1136px grid. Slightly wider than the body on purpose:
     the header is its own zone, not the first paragraph. */
  max-width: 760px;
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--border);
}
.article .article__meta {
  margin-bottom: 0;
  color: var(--text-dim);
  gap: var(--space-3);
}

.article .article__body {
  /* ~76 characters at the body size, instead of 83. */
  max-width: 660px;
  margin-top: var(--space-8);
  gap: var(--space-6);
}

/* Opening paragraph as a lede — a standard editorial device, and it costs
   nothing: no markup, no content change, just the first paragraph doing the
   job an opening paragraph already does. */
.article .article__body > p:first-of-type {
  font-size: clamp(18px, 1.35vw, 20px);
  line-height: 1.55;
  color: var(--text);
}

/* Section starts. A short lime rule rather than a full-width border: the site
   already marks things this way (hero stats, process nodes), and seven
   full-width rules down one article would be heavy. */
.article .article__body h2 {
  position: relative;
  font-size: clamp(22px, 1.85vw, 26px);
  line-height: 1.24;
  margin-top: var(--space-7);
  padding-top: var(--space-5);
}
.article .article__body h2::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 28px;
  height: 2px;
  background: var(--lime);
}
.article .article__body h2:first-child { margin-top: 0; }

/* Lists read as a block rather than as paragraphs that happen to have dots. */
.article .article__body ul { gap: var(--space-3); }
.article .article__body li { padding-left: var(--space-6); }
.article .article__body li::before { left: var(--space-3); top: 0.68em; }

.article .article-end { margin-top: var(--space-8); }

@media (max-width: 860px) {
  .article .article__body { margin-top: var(--space-7); gap: var(--space-5); }
  .article .article__body h2 { margin-top: var(--space-6); }
}

/* --------------------------------------------------------------------------
   BREADCRUMB — interior-page location and the way back to Home
   Sits in the slot the eyebrow used, in the same mono/uppercase/lime
   language, so nothing about the page's look changes; what changes is that
   the first item is a real link to Home. Semantic <nav>/<ol>, keyboard
   reachable, focus ring inherited from :focus-visible.
   -------------------------------------------------------------------------- */
.breadcrumb { margin-bottom: var(--space-5); }
.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  list-style: none;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  line-height: var(--lh-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
}
.breadcrumb li { display: flex; align-items: center; gap: var(--space-2); }
.breadcrumb li + li::before { content: '/'; color: var(--text-faint); }
.breadcrumb a {
  color: var(--text-dim);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  padding: 4px 0;
  transition: color var(--duration-base) var(--ease-standard);
}
.breadcrumb a:hover { color: var(--lime); }
/* The last crumb is the page you are on, and keeps the lime the eyebrow had.
   On a child page (an article, a case study) it is its parent listing, and is
   a link as well — so both crumbs go somewhere useful. */
.breadcrumb [aria-current="page"],
.breadcrumb li:last-child a { color: var(--lime); }

/* Home is the control this whole change exists to surface, so on a phone it
   has to be comfortably tappable. Vertical padding only, so the crumbs keep
   their spacing and the underline stays under the word. Placed here rather
   than in the responsive section because that block sits earlier in the file
   than the base rule above and would lose on equal specificity. */
@media (max-width: 860px) {
  .breadcrumb a { padding: 10px 0; }
}

/* --------------------------------------------------------------------------
   FOUNDER PHOTOS
   Two real people. The 40% grayscale, the contrast bump and the lime
   soft-light wash were all styling applied on top of them, which reads as
   stock photography rather than as the founders. Removed. The crop moves
   into its own clipping element so it can no longer paint over the caption
   sitting beneath it, and each portrait is framed individually — the two
   source photos have different aspect ratios (0.67 and 0.80) and different
   original framing, so one shared transform could never match them.
   Nothing about either face is altered.
   -------------------------------------------------------------------------- */
.photo-frame__crop {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  aspect-ratio: 4 / 5;
  background: var(--bg);
}
.photo-frame__crop img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
  filter: none;
  transform: none;
}
.photo-frame::before { content: none; }

/* Both sources are 4:5, the same ratio as the frame, so object-fit alone
   would show each photo exactly as shot — one a seated three-quarter shot,
   the other a tight head-and-shoulders. Framed individually so the pair
   matches: same head size, same eye line, same amount of headroom. Crop and
   position only; nothing is retouched, recoloured or altered. */
.founders .founder-card:nth-of-type(1) .photo-frame__crop img {
  transform: scale(1.72);
  transform-origin: 67% 24%;
}
.founders .founder-card:nth-of-type(2) .photo-frame__crop img {
  transform: scale(1.06);
  transform-origin: 50% 24%;
}

/* --------------------------------------------------------------------------
   PUBLIC PORTFOLIO (admin-published projects)
   Rendered by /portfolio-public.js. Every field on a project is optional, so
   these rules must look right whether a card carries an image, a title, three
   chip lists and a link — or nothing but a name. Nothing here reserves space
   for an element that may not exist: no fixed media height, no min-height on
   the body, no empty-state boxes.
   -------------------------------------------------------------------------- */
.pf { margin-top: var(--space-9); }
.pf .section__intro { margin-bottom: var(--space-7); }

.pf-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr));
  gap: var(--space-5);
}

.pf-card {
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.pf-card__media { border-bottom: 1px solid var(--border); background: var(--surface-2); }
.pf-card__media img { width: 100%; height: auto; display: block; }

.pf-card__body {
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.pf-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.pf-card__date { font-size: var(--fs-caption); color: var(--text-faint); letter-spacing: var(--ls-caption); }
.pf-card__title { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: var(--fw-medium); letter-spacing: var(--ls-h3); word-spacing: var(--ws-h3); }
.pf-card__client { font-size: var(--fs-caption); color: var(--text-faint); letter-spacing: var(--ls-caption); }
.pf-card__desc { color: var(--text-dim); font-size: var(--fs-small); line-height: var(--lh-body); }

.pf-card__list { display: flex; flex-direction: column; gap: var(--space-3); }
.pf-card__list-label {
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}
.pf-card__chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.pf-card .work-feature__link { margin-top: auto; align-self: flex-start; }

@media (max-width: 860px) {
  .pf { margin-top: var(--space-8); }
  .pf-card__body { padding: var(--space-5); }
}

/* --------------------------------------------------------------------------
   21. CASE STUDY — MULTI-MODULE SYSTEM
   The first two case studies each document one workflow, so a single canvas
   and four prose blocks carried them. A system built out of eleven separate
   workflows needs a second register: full-width bands, one per module, each
   holding its own canvas at a size you can actually read.
   Everything here is additive. The original .case-study rules are untouched,
   so /work/lead-qualification-system/ and /work/content-repurposing-system/
   render exactly as they did before this section existed.
   -------------------------------------------------------------------------- */

/* Standfirst under the title. Same measure and colour as .case-study__text,
   so it reads as the opening of the piece rather than a caption. */
.case-study__lead {
  max-width: 760px;
  margin-bottom: var(--space-8);
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
}

/* Full-width band inside a case study. .case-study__sections is capped at
   760px because it is prose; these blocks hold canvases and grids, so they
   take the whole column and mark themselves off with the same strong rule
   the closing CTA uses. */
.cs-block {
  margin-top: var(--space-8);
  padding-top: var(--space-7);
  border-top: 1px solid var(--border-strong);
}
.cs-block__title {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: var(--fw-medium);
  line-height: var(--lh-h2);
  letter-spacing: var(--ls-h2);
  word-spacing: var(--ws-h2);
  margin-bottom: var(--space-4);
}
.cs-block__intro {
  max-width: 760px;
  margin-bottom: var(--space-7);
  color: var(--text-dim);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

/* ---------- Architecture flow ----------
   Eleven stages in a fixed order. A wrapping row of chips with arrows between
   them says "sequence" without needing a diagram to maintain, and it degrades
   to a readable stack on a phone rather than a shrunken graphic. */
.cs-flow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-6);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  background-image: var(--sheen);
}
.cs-flow__step {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 9px var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.016);
  font-size: var(--fs-small);
  color: var(--text);
  white-space: nowrap;
}
.cs-flow__step-num {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  color: var(--lime);
}
.cs-flow__arrow {
  color: var(--text-faint);
  font-size: var(--fs-small);
  line-height: 1;
}

/* ---------- Module bands ---------- */
.cs-modules {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* Composes .card with padding:0 so the canvas frame can run edge to edge;
   padding lives on the head and body rows instead. No lime top border here:
   what reads as an accent once reads as noise eleven times, so the only lime
   on a module band is its number. */
.cs-module.card {
  padding: 0;
  overflow: hidden;
  border-radius: var(--radius-lg);
}
.cs-module.card:hover { transform: none; background-color: var(--surface); }

.cs-module__head {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-5);
  align-items: start;
  padding: var(--space-6);
  border-bottom: 1px solid var(--border);
}
.cs-module__num {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  line-height: 1;
  color: var(--lime);
  border: 1px solid var(--lime);
  background: var(--lime-faint);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}
.cs-module__title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  line-height: var(--lh-h3);
  letter-spacing: var(--ls-h3);
  word-spacing: var(--ws-h3);
  margin-bottom: var(--space-2);
}
.cs-module__purpose {
  max-width: 72ch;
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

.cs-module__figure { border-bottom: 1px solid var(--border); }
.cs-module__media {
  padding: var(--space-6);
  background: radial-gradient(ellipse 120% 90% at 50% 0%, var(--surface-2), var(--bg) 78%);
}
.cs-module__media img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  box-shadow: 0 20px 44px rgba(0, 0, 0, 0.5);
}
.cs-module__caption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-6);
  border-top: 1px solid var(--border);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-faint);
}
.cs-module__zoom {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  color: var(--text-dim);
  transition: color var(--duration-base) var(--ease-standard);
}
.cs-module__zoom [aria-hidden="true"] {
  display: inline-block;
  transition: transform var(--duration-base) var(--ease-out);
}
.cs-module__zoom:hover { color: var(--lime); }
.cs-module__hint {
  display: none;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  color: var(--text-faint);
}

.cs-module__zoom:hover [aria-hidden="true"] { transform: translate(2px, -2px); }

.cs-module__body {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--space-6);
  align-items: start;
  padding: var(--space-6);
}
.cs-module__text {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}
.cs-module__resp {
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.014);
}
.cs-module__resp-label {
  display: block;
  margin-bottom: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}
.cs-module__resp-text {
  color: var(--text);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

/* ---------- Key capabilities ----------
   Same idiom as .methodology__step: a strong hairline above each item does
   the separating, so ten of these read as one list rather than ten boxes. */
.cs-caps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-6);
}
.cs-caps__item {
  padding-top: var(--space-5);
  border-top: 1px solid var(--border-strong);
}
.cs-caps__title {
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  margin-bottom: var(--space-2);
}
.cs-caps__desc {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

/* ---------- Technology ----------
   Reuses the .work-feature__breakdown spec table wholesale; only the label
   column is widened, because these labels are words rather than the
   Problem / Approach / Solution triplet that 88px was cut for. */
.cs-tech { max-width: 900px; margin-bottom: 0; }
.cs-tech .work-feature__row { grid-template-columns: 150px 1fr; }

@media (max-width: 980px) {
  .cs-module__body { grid-template-columns: 1fr; gap: var(--space-5); }
  .cs-caps { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 860px) {
  .cs-block { margin-top: var(--space-7); padding-top: var(--space-6); }
  .cs-block__intro { margin-bottom: var(--space-6); }
  .cs-flow { padding: var(--space-5); gap: var(--space-2); }
  .cs-modules { gap: var(--space-5); }
  .cs-module__head { padding: var(--space-5); gap: var(--space-4); }
  .cs-module__media { padding: var(--space-4); }
  .cs-module__caption { padding: var(--space-3) var(--space-5); }
  .cs-module__body { padding: var(--space-5); }
  .cs-caps { grid-template-columns: 1fr; gap: var(--space-5); }
  .cs-tech .work-feature__row { grid-template-columns: 1fr; gap: var(--space-2); }
}

/* A 1650px-wide n8n canvas scaled into a ~300px column puts its node labels at
   roughly 3px: technically present, actually unreadable, which defeats the
   point of showing the canvas. Below this width the frame becomes a horizontal
   scroller and the image holds a legible minimum width, so a phone reader pans
   across the workflow instead of squinting at all of it. The caption sits
   outside the scroller, so the full-size link stays where it was. */
@media (max-width: 720px) {
  /* Workflow canvases stay fitted at every width. An earlier revision showed
     them at 1:1 with horizontal panning so node labels stayed legible, but a
     1648px canvas in a 390px viewport shows about a quarter of the diagram and
     reads as an arbitrary crop of a technical drawing. The whole workflow is
     the point of these images, so they scale to the frame; the "Open full
     size" link on each module is what serves close inspection. */
}

/* --------------------------------------------------------------------------
   22. SERVICE PAGES AND PROJECT CARDS
   One page per service, each listing only the case studies that belong to it.
   Almost everything here is composition rather than new design: the hero uses
   .section__intro, the capability grid reuses .cs-caps from section 21, the
   closing buttons reuse .case-study__cta, and the card CTA reuses
   .work-feature__link. What is genuinely new is a compact project card (the
   .work-feature card is a two-column spec sheet, far too heavy to list four
   of), an empty state for services with no published work yet, and a pill row
   for moving between services.
   -------------------------------------------------------------------------- */

/* .tag as a link. The pill already exists for labels; a service label that
   navigates needs nothing but a hover state, so this stays one rule rather
   than a second pill component that would drift from the first. */
a.tag {
  transition: color var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard);
}
a.tag:hover {
  color: var(--lime);
  border-color: var(--lime);
  background: var(--lime-faint);
}

/* Row of service pills. Used under the Work page intro and at the foot of a
   service page, so a reader who lands on one service can reach the rest
   without going back through the hub. */
.svc-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-5);
}
.svc-links__label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
  align-self: center;
  margin-right: var(--space-2);
}

/* The services hub and the homepage ecosystem both run a three-column
   hairline grid. Seven nodes leaves the last row short, and an empty cell in
   that grid shows as a bare strip of border colour rather than as nothing —
   so the last node takes the whole row instead. */
.ecosystem__node--wide { grid-column: 1 / -1; }

/* ---------- Service page ---------- */
.service .section__inner {
  padding-top: calc(var(--nav-h) + var(--section-pad));
}
.service__hero-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-6);
}

/* Full-width band inside a service page, same rule and rhythm the case-study
   blocks use, so the two page types read as one family. */
.svc-block {
  margin-top: var(--space-8);
  padding-top: var(--space-7);
  border-top: 1px solid var(--border-strong);
}
.svc-block__title {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: var(--fw-medium);
  line-height: var(--lh-h2);
  letter-spacing: var(--ls-h2);
  word-spacing: var(--ws-h2);
  margin-bottom: var(--space-4);
}
.svc-block__intro {
  max-width: 760px;
  margin-bottom: var(--space-7);
  color: var(--text-dim);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

/* Overview: prose on the left, the problems it addresses as a ruled list on
   the right. Two columns because the list is a scan target, not reading. */
.svc-overview {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: var(--space-8);
  align-items: start;
}
.svc-overview__text {
  color: var(--text-dim);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
}
.svc-overview__text p + p { margin-top: var(--space-5); }
.svc-overview__panel {
  padding: var(--space-6);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  background-image: var(--sheen);
}
.svc-overview__label {
  display: block;
  margin-bottom: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}

/* ---------- Project cards ---------- */
.pj-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr));
  gap: var(--space-5);
}

.pj-card {
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-lg);
}
.pj-card:hover { transform: none; }

.pj-card__media {
  border-bottom: 1px solid var(--border);
  background: radial-gradient(ellipse 120% 90% at 50% 0%, var(--surface-2), var(--bg) 78%);
}
.pj-card__media img {
  display: block;
  width: 100%;
  height: auto;
  /* Workflow canvases are ~2:1 and full of small nodes. Cropped to a card
     header they read as texture, which is all a listing card needs them to
     do; the readable copy is on the case-study page. Top-left origin keeps
     the trigger node — the part that says "this is a workflow" — in frame. */
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: top left;
}

.pj-card__body {
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
}
.pj-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.pj-card__status {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--lime);
}
.pj-card__title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  line-height: var(--lh-h3);
  letter-spacing: var(--ls-h3);
  word-spacing: var(--ws-h3);
}
.pj-card__desc {
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}
.pj-card .work-feature__link {
  margin-top: auto;
  align-self: flex-start;
}

/* ---------- Empty state ----------
   Shown where a service has no published case study yet. Dashed rather than
   solid so it reads as a placeholder rather than as a card with nothing in
   it, and it says plainly that nothing is published rather than implying
   work exists that is merely hidden. */
.svc-empty {
  padding: var(--space-8) var(--space-6);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.012);
  text-align: center;
}
.svc-empty__label {
  display: block;
  margin-bottom: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--text-faint);
}
.svc-empty__text {
  max-width: 56ch;
  margin: 0 auto;
  color: var(--text-dim);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}
/* --------------------------------------------------------------------------
   BACK CONTROL — return to wherever the visitor actually came from

   Sits directly above the breadcrumb, in the same mono/uppercase language, so
   it reads as part of the existing location strip rather than a new component.
   It is an <a> pointing at Home, not a <button>: with JavaScript off it is a
   working link, and script.js upgrades it to history.back() only when the
   previous page was on this site. 44px min target, and the label is the
   accessible name.
   -------------------------------------------------------------------------- */
.backlink {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 4px 12px 4px 0;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  line-height: var(--lh-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  /* Same colour as the breadcrumb links it sits above (8.97:1 on --bg).
     --text-faint would also pass at 4.86:1, but this is an interactive
     control sitting beside links of exactly this weight. */
  color: var(--text-dim);
  text-decoration: none;
  transition: color var(--duration-base) var(--ease-standard);
}
.backlink:hover { color: var(--lime); }
.backlink__icon {
  display: flex;
  transition: transform var(--duration-base) var(--ease-standard);
}
.backlink__icon svg { width: 13px; height: 13px; display: block; }
.backlink:hover .backlink__icon { transform: translateX(-3px); }

/* Homepage work teaser -> full portfolio. The grid above it is a teaser layer;
   this is the way through to /work/, which owns the full breakdowns. */
.work__cta {
  margin-top: var(--space-7);
  display: flex;
  justify-content: center;
}

.svc-empty__actions {
  margin-top: var(--space-6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: center;
}

@media (max-width: 980px) {
  .svc-overview { grid-template-columns: 1fr; gap: var(--space-6); }
}

@media (max-width: 860px) {
  .svc-block { margin-top: var(--space-7); padding-top: var(--space-6); }
  .svc-block__intro { margin-bottom: var(--space-6); }
  .svc-overview__panel { padding: var(--space-5); }
  .pj-card__body { padding: var(--space-5); }
  .svc-empty { padding: var(--space-7) var(--space-5); }
}

/* --------------------------------------------------------------------------
   CONTACT FORM — field separation and states
   The fields were rgba(0,0,0,.28) sitting on a #111310 panel: two near-blacks
   a few percent apart, so the inputs read as flat voids rather than as places
   to type. They are now a lighter well than the panel with a defined edge and
   a top inner shadow, which is what makes an input look inset rather than
   painted on. Restrained on purpose — no glow, no glass, no gradient.
   -------------------------------------------------------------------------- */
.contact-form-panel .form-input,
.contact-form .form-input {
  background: #191C17;
  border: 1px solid var(--border-strong);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.45);
  transition: border-color var(--duration-base) var(--ease-standard),
              background var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
.contact-form .form-input:hover:not(:focus) {
  border-color: var(--text-faint);
  background: #1D211B;
}
.contact-form .form-input:focus {
  background: #1D211B;
  border-color: var(--lime);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 0 0 3px rgba(200, 255, 61, 0.12);
}
.contact-form .form-input::placeholder { color: var(--text-faint); }

/* Error state. :user-invalid fires only after the visitor has actually
   interacted, so a pristine empty form is never shown as wrong. */
.contact-form .form-input:user-invalid {
  border-color: var(--danger, #FF6B5A);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 0 0 3px rgba(255, 107, 90, 0.1);
}

/* Select: same well as the inputs, with a caret drawn in CSS so it matches the
   type system instead of inheriting the OS widget. */
.contact-form .form-select {
  appearance: none;
  padding-right: 40px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-position: right 18px center, right 13px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}
.contact-form .form-select:invalid { color: var(--text-faint); }

.form-optional {
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--fs-caption);
  color: var(--text-faint);
  margin-left: var(--space-2);
}
.form-hint {
  margin-top: var(--space-2);
  font-size: var(--fs-caption);
  color: var(--text-faint);
  line-height: var(--lh-body);
}

/* Honeypot: removed from view and from the accessibility tree, but still a
   real focusable-by-nothing input in the DOM so a bot fills it. */
.form-honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
  padding: 0;
  margin: -1px;
}

@media (max-width: 860px) {
  .contact-form .form-input { font-size: 16px; }  /* stops iOS zooming on focus */
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}
