/* CREE8 — site styles.
   Layout and copy come from the original design (design/cree8-homepage.html).
   The visual system — colour, type, radius, spacing, easing — is the one
   harvested from the Webflow generation; see the token block below.
   Layout order: tokens → primitives → sections → responsive layer. */

/* ============================================================
   Tokens
   ============================================================ */

:root {
  /* Dark is the only theme. Ink is expressed as alpha over the background
     rather than as opaque greys — one ink colour at several weights, which
     keeps the greys consistent when a section changes its background. */
  --bg: #131315;
  --surface: #1b1b1e;
  --ink: #f5f5f5;
  --ink-2: rgba(245, 245, 245, 0.72);
  --ink-3: rgba(245, 245, 245, 0.60);
  --muted: rgba(245, 245, 245, 0.58);
  --muted-2: rgba(245, 245, 245, 0.48);
  --line: rgba(255, 255, 255, 0.10);
  --line-soft: rgba(255, 255, 255, 0.06);
  --line-strong: rgba(255, 255, 255, 0.20);
  --photo-bg: #1b1b1e;

  --accent: #5695ff;
  --accent-bright: #8fb2f5;

  /* Elevated surface — sits above --bg. Used by the service cards, the closing
     CTA and the footer. */
  --dark: #1e1e1f;
  --dark-line: rgba(255, 255, 255, 0.12);
  --dark-ink: #ffffff;
  --dark-ink-2: rgba(255, 255, 255, 0.72);
  --dark-muted: rgba(255, 255, 255, 0.60);
  --dark-muted-2: rgba(255, 255, 255, 0.48);
  --dark-muted-3: rgba(255, 255, 255, 0.52);
  --dark-label: rgba(255, 255, 255, 0.52);

  --card-dark: #1e1e1f;
  --card-accent: #5695ff;

  /* Glass panel. One recipe, shared by the footer panel and the service cards.
     The rim gradient is only ever seen as the 3px edge left over once the panel
     layer covers the rest — bright top and bottom, dark through the middle,
     which is what reads as a bevelled glass edge. No backdrop-filter involved. */
  --glass-rim: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.28) 0%,
    rgba(255, 255, 255, 0.06) 9%,
    rgba(255, 255, 255, 0.02) 32%,
    rgba(255, 255, 255, 0.05) 73%,
    rgba(255, 255, 255, 0.22) 100%
  );
  --glass-rim-hover: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.44) 0%,
    rgba(255, 255, 255, 0.10) 9%,
    rgba(255, 255, 255, 0.04) 32%,
    rgba(255, 255, 255, 0.09) 73%,
    rgba(255, 255, 255, 0.36) 100%
  );
  --glass-panel: linear-gradient(150deg, #26262a 0%, #1f1f23 50%, #191a1d 100%);
  --glass-lip:
    0 1px 1.5px rgba(255, 255, 255, 0.06) inset,
    0 -1px 1.5px rgba(0, 0, 0, 0.50) inset;

  /* Stacked soft shadow — many low-opacity layers rather than one big blur,
     which is what keeps the float looking diffuse instead of smudged. */
  --glass-float:
    0.29px 4.36px 2.18px rgba(0, 0, 0, 0.10),
    0.48px 7.24px 3.63px rgba(0, 0, 0, 0.10),
    0.78px 11.70px 5.86px rgba(0, 0, 0, 0.14),
    1.28px 19.15px 9.60px rgba(0, 0, 0, 0.18),
    2.20px 32.97px 16.52px rgba(0, 0, 0, 0.22),
    4px 60px 30.07px rgba(0, 0, 0, 0.34);
  --glass-float-sm:
    0.2px 2.6px 1.3px rgba(0, 0, 0, 0.10),
    0.3px 4.3px 2.2px rgba(0, 0, 0, 0.10),
    0.5px 7px 3.5px rgba(0, 0, 0, 0.14),
    0.8px 11.5px 5.8px rgba(0, 0, 0, 0.16),
    1.3px 19.8px 9.9px rgba(0, 0, 0, 0.18),
    2.4px 36px 18px rgba(0, 0, 0, 0.26);

  --header-bg: rgba(19, 19, 21, 0.72);
  --header-bg-scrolled: rgba(19, 19, 21, 0.92);
  --shadow-1: rgba(0, 0, 0, 0.3);
  --shadow-2: rgba(0, 0, 0, 0.5);

  --sans: "DM Sans", Helvetica, Arial, sans-serif;

  --shell: 1280px;
  --gutter: 48px;
  --header-h: 64px;

  /* Radius. Controls take the 6px step (matching the Peec AI reference);
     surfaces take 10px; true circles use 50% directly. */
  --radius-sm: 6px;
  --radius-btn: 6px;
  --radius: 10px;
  --radius-xl: 1rem;

  /* Spacing scale, 1rem base. */
  --s-0-25: 0.25rem;
  --s-0-5: 0.5rem;
  --s-0-75: 0.75rem;
  --s-1x: 1rem;
  --s-1-25: 1.25rem;
  --s-1-5: 1.5rem;
  --s-1-75: 1.75rem;
  --s-2x: 2rem;
  --s-3x: 3rem;
  --s-4x: 4rem;
  --s-5x: 5rem;
  --s-6x: 6rem;
  --s-7x: 7rem;
  --s-8x: 8rem;
  --s-10x: 10rem;

  --section-pad: var(--s-10x);
  --card-pad: var(--s-2x);

  /* Motion. */
  --dur-fast: 200ms;
  --dur: 300ms;
  --dur-slow: 600ms;
  --ease-out: cubic-bezier(0.165, 0.84, 0.44, 1);
  --ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  --stagger: 70ms;
}

/* Accents are authored as hex for compatibility, then upgraded to oklch where
   supported so they render at full saturation on P3 displays. */
@supports (color: oklch(0.678 0.170 260.2)) {
  :root {
    --accent: oklch(0.678 0.170 260.2);
    --accent-bright: oklch(0.764 0.104 262.9);
  }
}

/* ============================================================
   Base
   ============================================================ */

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--header-h);

  /* No visible scrollbar. Scrolling itself is untouched — wheel, trackpad,
     keyboard, touch and programmatic scrolling all still work; only the track
     is hidden. Worth knowing it also removes the drag handle and the visual
     position cue.

     On macOS with overlay scrollbars this changes nothing, since the bar was
     never taking layout space. On Windows/Linux, and on any machine set to
     always show scrollbars, it hands ~15px back to the layout. */
  scrollbar-width: none;      /* Firefox */
  -ms-overflow-style: none;   /* legacy Edge */
}

html::-webkit-scrollbar {     /* Chrome, Safari */
  width: 0;
  height: 0;
  display: none;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--ink); text-decoration: none; }

h1, h2, h3, p { margin: 0; }

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: var(--bg);
  font-size: 14px;
  font-weight: 500;
  padding: 12px 20px;
  border-radius: var(--radius-sm);
}

.skip-link:focus {
  left: 0;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* ============================================================
   Primitives
   ============================================================ */

.shell {
  max-width: var(--shell);
  margin: 0 auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

.btn {
  display: inline-block;
  font-size: 15px;
  font-weight: 500;
  padding: 14px 20px;
  border-radius: var(--radius-btn);
  border: 1px solid transparent;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}

.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); transition-duration: 60ms; }

.btn-sm { font-size: 14px; padding: 10px 14px; }

.btn-solid { background: var(--ink); color: var(--bg); }
.btn-solid:hover { background: var(--ink-2); }

.btn-outline { border-color: var(--line-strong); color: var(--ink); }
.btn-outline:hover { border-color: var(--ink); }

.btn-light {
  background: var(--dark-ink);
  color: var(--dark);
  font-size: 16px;
  padding: 18px 30px;
}
.btn-light:hover { background: var(--accent-bright); color: #fff; }

.btn-block { display: block; width: 100%; text-align: center; }

.nowrap { white-space: nowrap; }

/* ============================================================
   Header + nav
   ============================================================ */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--header-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid transparent;
}

.header-inner {
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 40px;
}

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

.brand-lockup { display: flex; flex-direction: column; align-items: flex-start; }

.brand-mark {
  display: inline-flex;
  align-items: center;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.14em;
}

.brand-divider {
  display: inline-block;
  width: 2px;
  height: 0.55em;
  margin: 0 6px;
  background: var(--line-strong);
  vertical-align: middle;
}

.site-nav { display: flex; align-items: center; gap: 4px; }

/* Ghost buttons: no chrome at rest, an outline on hover. The border is always
   present but transparent, so nothing reflows when it appears. */
.site-nav > a:not(.btn),
.nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 400;
  line-height: 1;
  color: var(--ink-3);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-btn);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}

.site-nav > a:not(.btn):hover,
.nav-trigger:hover,
.has-menu:hover .nav-trigger,
.has-menu.is-open .nav-trigger {
  color: var(--ink);
  border-color: var(--line-strong);
}

.site-nav > a.btn-solid { color: var(--bg); }
.site-nav > a.btn-solid:hover { color: var(--bg); }

.site-nav > .btn { margin-left: 12px; }

/* ---- Services dropdown ---- */

.nav-item { position: relative; }

.nav-caret {
  opacity: 0.7;
  transition: transform var(--dur-fast) var(--ease-out);
}

.has-menu:hover .nav-caret,
.has-menu.is-open .nav-caret { transform: rotate(180deg); }

.nav-menu {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  z-index: 60;
  min-width: 320px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              visibility 0s linear var(--dur-fast);
}

/* A transparent bridge over the gap, so the pointer can travel from the
   trigger into the panel without the hover dropping out. */
.nav-menu::before {
  content: "";
  position: absolute;
  top: -10px;
  left: 0;
  right: 0;
  height: 10px;
}

.has-menu:hover .nav-menu,
.has-menu.is-open .nav-menu,
.nav-trigger:focus-visible + .nav-menu,
.nav-menu:focus-within {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

.nav-menu-inner {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px;
  background: var(--dark);
  border: 1px solid var(--dark-line);
  border-radius: var(--radius);
  box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.6);
}

.nav-menu-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 12px;
  border-radius: var(--radius-btn);
  opacity: 0;
  transform: translateY(-4px);
  transition: background-color var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}

/* Items cascade in behind the panel itself. */
.has-menu:hover .nav-menu-item,
.has-menu.is-open .nav-menu-item,
.nav-trigger:focus-visible + .nav-menu .nav-menu-item,
.nav-menu:focus-within .nav-menu-item {
  opacity: 1;
  transform: none;
}

.nav-menu-item:nth-child(1) { transition-delay: 40ms, 40ms, 40ms; }
.nav-menu-item:nth-child(2) { transition-delay: 40ms, 80ms, 80ms; }
.nav-menu-item:nth-child(3) { transition-delay: 40ms, 120ms, 120ms; }
.nav-menu-item:nth-child(4) { transition-delay: 40ms, 160ms, 160ms; }

.nav-menu-item:hover { background: rgba(255, 255, 255, 0.06); }

.nav-menu-title { font-size: 14px; font-weight: 500; color: var(--dark-ink); }
.nav-menu-desc { font-size: 12.5px; line-height: 1.45; color: var(--dark-muted); }

/* header condenses once the page has scrolled past the hero's top */
.site-header {
  transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}

/* At rest the header is invisible against the page; the hairline only appears
   once content is passing underneath it. */
.site-header.is-scrolled {
  border-bottom-color: var(--line);
  background: var(--header-bg-scrolled);
}

/* Hidden until the mobile breakpoint — desktop renders exactly as designed. */
.nav-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  background: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-btn);
  padding: 9px 14px;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.nav-toggle:hover { border-color: var(--ink); }

.nav-toggle-bars {
  position: relative;
  display: block;
  width: 16px;
  height: 2px;
  background: var(--ink);
  transition: background-color var(--dur-fast) var(--ease-out);
}

.nav-toggle-bars::before,
.nav-toggle-bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 16px;
  height: 2px;
  background: var(--ink);
  transition: transform var(--dur-fast) var(--ease-out);
}

.nav-toggle-bars::before { top: -5px; }
.nav-toggle-bars::after { top: 5px; }

[data-nav-open] .nav-toggle-bars { background: transparent; }
[data-nav-open] .nav-toggle-bars::before { transform: translateY(5px) rotate(45deg); }
[data-nav-open] .nav-toggle-bars::after { transform: translateY(-5px) rotate(-45deg); }

/* Two columns: copy left, the particle canvas right — the slot the reference
   sites fill with a product screenshot. */
/* The hero fills one screen below the header and centres its content, rather
   than stacking fixed padding on top of a tall visual — which left a large dead
   band above the headline. svh keeps it honest on mobile, where browser chrome
   resizes the viewport. */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
  border-bottom: 1px solid var(--line);
}

/* Homepage only. The hero gives up just enough height for the logo banner's
   strapline to clear the fold — the logos themselves stay below it, so the
   line reads as a promise of what is coming rather than a truncated grid.

   92px covers the banner's 56px top padding plus the 16px line, landing inside
   the 36px gap that follows. The first row starts at 108px, so it stays under.
   Below 1180px the hero drops its min-height entirely, so this only governs
   desktop. */
.hero.is-peek {
  --peek: 92px;
  min-height: calc(100vh - var(--header-h) - var(--peek));
  min-height: calc(100svh - var(--header-h) - var(--peek));
}

.hero-inner {
  width: 100%;
  padding-top: 48px;
  padding-bottom: 48px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.62fr);
  gap: 24px;
  align-items: center;
}

.hero-visual {
  position: relative;
  aspect-ratio: 1 / 1;
  max-height: min(620px, 62vh);
  justify-self: end;
  /* Grows leftward into the slack beside the capped copy column and bleeds
     right to the shell edge, so it reads large without squeezing the headline
     into an extra line. */
  width: calc(100% + 200px);
  margin-left: -200px;
  margin-right: calc(var(--gutter) * -1);
}

.hero-visual canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Without JS the canvas never paints, so it must not reserve a hole. */
html:not(.js) .hero-visual { display: none; }

.hero-copy { max-width: 720px; }

.hero-title {
  font-size: 64px;
  line-height: 1.02;
  letter-spacing: -0.02em;
  font-weight: 500;
  max-width: 880px;
  text-wrap: balance;
}

/* One column now: headline, a single grey subline, then the actions —
   the three-element hero the reference set uses. */
.hero-foot { margin-top: 32px; }

/* The hero lede carries a condensed variant for phones. The full sentence runs
   to 4-6 lines on a 390px screen, which buries the CTA; the desktop length is
   right where there is room for it. Only one is ever in the flow, so screen
   readers announce a single lede. */
.lede-short { display: none; }

.hero-lede {
  font-size: 18px;
  line-height: 1.6;
  color: var(--ink-3);
  max-width: 620px;
  text-wrap: pretty;
}

.hero-actions { display: flex; gap: 12px; margin-top: 40px; }

/* Service-page heroes carry one- or two-word titles, which leaves the
   two-column title/lede split looking lopsided. They get a centred stack
   instead. The homepage hero keeps the split — it has the canvas on the right
   to balance against. */
/* Service heroes put the copy on the side opposite the chrome asset, mirrored
   per page. The is-chrome-* class already says which side the asset takes, so
   it drives the copy too rather than adding a second class that could fall out
   of sync with it.

   .hero-inner's two children (.hero-title and .hero-foot) have to stack in one
   column, so both are pinned to the same column with explicit rows — left to
   the grid they would sit side by side.

   Text stays left-aligned in both mirrors. Only the block moves: right-aligning
   a four-line lede to mirror the layout costs real readability for a symmetry
   nobody notices once the asset is beside it. */
.hero.is-chrome-right .hero-inner,
.hero.is-chrome-left .hero-inner {
  position: relative;
  z-index: 1;
}

.hero.is-chrome-right .hero-inner { grid-template-columns: minmax(0, 560px) 1fr; }
.hero.is-chrome-right .hero-title { grid-column: 1; grid-row: 1; }
.hero.is-chrome-right .hero-foot { grid-column: 1; grid-row: 2; }

.hero.is-chrome-left .hero-inner { grid-template-columns: 1fr minmax(0, 560px); }
.hero.is-chrome-left .hero-title { grid-column: 2; grid-row: 1; }
.hero.is-chrome-left .hero-foot { grid-column: 2; grid-row: 2; }

.hero.is-chrome-right .hero-foot,
.hero.is-chrome-left .hero-foot { margin-top: 28px; }

/* The chrome blob sits behind the centred copy and bleeds past the shell edge
   so the form reads as cropped rather than as an object in a box. Which side
   it takes alternates across the four service pages. */
.hero-chrome {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: min(700px, 58vw);
  aspect-ratio: 1 / 1;
  max-width: 100%;
  pointer-events: none;
  z-index: 0;
}

/* The copy is centred, so the blob's inner edge runs under it. Rather than
   shrinking the form until it clears the text — which loses the cropped,
   bleeding-off-the-edge look — it dissolves as it approaches the copy. */
.hero.is-chrome-right .hero-chrome {
  right: calc(var(--gutter) * -1.5);
  -webkit-mask-image: linear-gradient(to left, #000 46%, transparent 96%);
  mask-image: linear-gradient(to left, #000 46%, transparent 96%);
}

.hero.is-chrome-left .hero-chrome {
  left: calc(var(--gutter) * -1.5);
  -webkit-mask-image: linear-gradient(to right, #000 46%, transparent 96%);
  mask-image: linear-gradient(to right, #000 46%, transparent 96%);
}

/* The blob deliberately hangs past the shell, which on the right-hand pages
   extends the scroll width and gives the document a horizontal scrollbar.
   Clipping at the hero is also the crop the design wants. Scoped to the
   chrome heroes so the homepage hero is left alone. */
.hero.is-chrome-right,
.hero.is-chrome-left { overflow: hidden; }

/* Without JS the canvas never paints, so it must not reserve a hole. */
html:not(.js) .hero-chrome { display: none; }


/* The animated wave canvas behind the homepage hero is always dark, so its
   text needs the theme-invariant "dark-*" tokens (used elsewhere for the
   always-dark CTA section) instead of the ink tokens that flip to a dark
   color in light mode and disappear against the canvas. */

/* The accent is tuned for contrast against a light page, so on the dark hero it
   swaps to the inverse accent — the same one the dark service cards use. */

/* ============================================================
   Value strip
   ============================================================ */

.strip {
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.strip-inner {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

.strip-cell {
  padding: 36px 32px;
  border-right: 1px solid var(--line-soft);
}

.strip-cell:first-child { padding-left: 0; }
.strip-cell:last-child { padding-right: 0; border-right: 0; }

.strip-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--muted);
  margin-bottom: 10px;
  transition: color var(--dur-fast) var(--ease-out);
}

.strip-text { font-size: 15px; line-height: 1.5; color: var(--ink-2); }

/* ============================================================
   Logo banner
   ============================================================ */

.logos {
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  padding: 56px 0 60px;
}

.logos-title {
  text-align: center;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 36px;
}

/* Three across, as in the reference — nine marks become three rows of three.
   Wide cells give each logo room rather than squeezing nine into one band. */
.logo-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

/* Crop-mark corners instead of a full lattice, matching the reference. Eight
   gradients: a horizontal and a vertical tick at each of the four corners. */
.logo-cell {
  --tick: 10px;
  --tick-color: rgba(255, 255, 255, 0.16);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-width: 0;
  min-height: 116px;
  padding: 16px;
  background-image:
    linear-gradient(var(--tick-color), var(--tick-color)),
    linear-gradient(var(--tick-color), var(--tick-color)),
    linear-gradient(var(--tick-color), var(--tick-color)),
    linear-gradient(var(--tick-color), var(--tick-color)),
    linear-gradient(var(--tick-color), var(--tick-color)),
    linear-gradient(var(--tick-color), var(--tick-color)),
    linear-gradient(var(--tick-color), var(--tick-color)),
    linear-gradient(var(--tick-color), var(--tick-color));
  background-size:
    var(--tick) 1px, 1px var(--tick),
    var(--tick) 1px, 1px var(--tick),
    var(--tick) 1px, 1px var(--tick),
    var(--tick) 1px, 1px var(--tick);
  background-position:
    left top, left top,
    right top, right top,
    left bottom, left bottom,
    right bottom, right bottom;
  background-repeat: no-repeat;
  transition: background-image var(--dur-fast) var(--ease-out);
}

.logo-cell:hover { --tick-color: rgba(255, 255, 255, 0.34); }

/* The marks stay put once they have revealed in — no pointer tracking. Only
   the tone shifts on hover. */
.logo-mark,
.logo-name {
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

/* The real marks are painted as a mask over the theme ink rather than shown as
   an <img>. An external SVG in an <img> is isolated and cannot be recoloured
   from the page, so brand colours inside the file would survive; a mask throws
   the file's own colours away and paints the silhouette white whatever the
   supplied file happens to contain. Each brand sets --logo. */
.logo-mark {
  display: block;
  flex: none;
  width: 34px;
  height: 34px;
  background-color: var(--ink-2);
  -webkit-mask: var(--logo) center / contain no-repeat;
  mask: var(--logo) center / contain no-repeat;
}

.logo-cell:hover .logo-mark { background-color: var(--ink); }

/* Placeholder state: the name stands in for the mark until real art lands.
   Removing .is-placeholder and setting --logo is the whole swap. */
.is-placeholder .logo-mark { display: none; }

.logo-name {
  min-width: 0;
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink-3);
  white-space: nowrap;
}

.logo-cell:hover .logo-name { color: var(--ink); }

/* Per-brand art. Simple Icons ships single-path 24x24 glyphs, so each mark is
   a square painted through a mask — the name beside it completes the lockup. */
.logo-openai      { --logo: url("../assets/logos/openai.svg"); }
.logo-gemini      { --logo: url("../assets/logos/gemini.svg"); }
.logo-claude      { --logo: url("../assets/logos/claude.svg"); }
.logo-python      { --logo: url("../assets/logos/python.svg"); }
.logo-postgresql  { --logo: url("../assets/logos/postgresql.svg"); }
.logo-mcp         { --logo: url("../assets/logos/mcp.svg"); }
.logo-aws         { --logo: url("../assets/logos/aws.svg"); }
.logo-googlecloud { --logo: url("../assets/logos/googlecloud.svg"); }
.logo-azure       { --logo: url("../assets/logos/azure.svg"); }

/* ============================================================
   Section scaffolding
   ============================================================ */

.section {
  padding-top: var(--section-pad);
  padding-bottom: var(--section-pad);
}
.section-faq { padding-bottom: 120px; }

/* Tonal bands.
   Sections are also the width-constraining .shell, so a background set on them
   would stop at 1280px. box-shadow spreads past that to fill the viewport
   without adding layout width (which would cause horizontal overflow), and
   clip-path trims the spread vertically so it doesn't bleed into neighbours. */
.section.is-secondary,
.section.is-inverse {
  padding-bottom: var(--section-pad);
  box-shadow: 0 0 0 100vmax var(--band, var(--surface));
  clip-path: inset(0 -100vmax);
}

.section.is-secondary { background: var(--surface); }

.section.is-inverse {
  --band: var(--dark);
  background: var(--dark);
  color: var(--dark-ink);
}

.section.is-inverse .section-title,
.section.is-inverse .card-title { color: var(--dark-ink); }
.section.is-inverse .section-aside,
.section.is-inverse .card-body { color: var(--dark-ink-2); }
.section.is-inverse 
/* A banded section immediately followed by another band would read as one
   block; the hairline keeps the seam legible. */
.section.is-secondary + .section.is-secondary { border-top: 1px solid var(--line); }

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 60px;
  margin-bottom: 64px;
}

.section-title {
  font-size: 46px;
  line-height: 1.06;
  letter-spacing: -0.025em;
  font-weight: 500;
  max-width: 620px;
  text-wrap: balance;
}

.section-aside {
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-3);
  max-width: 360px;
}

.section-aside-wide { max-width: 400px; text-wrap: pretty; }

/* ============================================================
   Services
   ============================================================ */

.card-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px;
}

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

.card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 28px;
}

.card-title {
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.015em;
}

.card-index {
  font-size: 11px;
  color: var(--muted-2);
}

.card-body {
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* ============================================================
   Services — icon cards
   ============================================================ */

/* Bento layout rather than an even 2x2 — a wide/narrow pair, mirrored on the
   second row, so the block has some rhythm instead of sitting as four equal
   boxes. Three columns is what makes the 2:1 and 1:2 splits possible. */
.service-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 1fr;
  gap: 20px;
}

.service-card:nth-child(1) { grid-column: span 2; }
.service-card:nth-child(2) { grid-column: span 1; }
.service-card:nth-child(3) { grid-column: span 1; }
.service-card:nth-child(4) { grid-column: span 2; }

/* Same glass as the footer panel, but from a single element: a transparent 3px
   border painted by the rim gradient clipped to the border box, over the panel
   gradient clipped to the padding box. Saves wrapping every card in an inner
   div. The inner corner radius follows automatically at 22px - 3px. */
.service-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 300px;
  border: 3px solid transparent;
  border-radius: 22px;
  background: var(--glass-panel) padding-box, var(--glass-rim) border-box;
  box-shadow: var(--glass-lip), var(--glass-float-sm);
  padding: 32px;
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

/* The rim brightens rather than the border colour changing — there is no
   border colour to animate now that the border is the gradient's canvas. */
.service-card:hover {
  background: var(--glass-panel) padding-box, var(--glass-rim-hover) border-box;
  transform: translateY(-2px);
}

.service-card:focus-visible {
  outline: 2px solid var(--card-accent);
  outline-offset: 3px;
  background: var(--glass-panel) padding-box, var(--glass-rim-hover) border-box;
}

.service-card-label {
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--dark-ink);
}

.service-card-caption {
  /* Bottom-anchored: the heading sits at the top of the card and the empty
     space falls between them rather than above the whole block. */
  margin-top: auto;
  font-size: 15px;
  line-height: 1.55;
  color: var(--dark-ink-2);
  text-wrap: pretty;
  /* The wide bento cards would otherwise run to ~100 characters a line. Capping
     the measure also leaves intentional space to the right of the text. */
  max-width: 46ch;
}

.service-card-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: flex-start;
  font-size: 15px;
  padding-top: 20px;
  color: var(--dark-ink-2);
  transition: color var(--dur-fast) var(--ease-out);
}

.service-card:hover .service-card-cta,
.service-card:focus-visible .service-card-cta { color: var(--card-accent); }

.service-card-cta-arrow { transition: transform var(--dur-fast) var(--ease-out); }
.service-card:hover .service-card-cta-arrow { transform: translateX(4px); }

/* ============================================================
   Process
   ============================================================ */

.stages { border-top: 1px solid var(--line); }

.stage {
  display: grid;
  grid-template-columns: 56px 320px 1fr;
  gap: 40px;
  padding: 34px 16px;
  margin: 0 -16px;
  border-bottom: 1px solid var(--line);
  align-items: start;
  border-radius: var(--radius);
  transition: background-color var(--dur-fast) var(--ease-out);
}

.stage:hover { background: var(--line-soft); }

.stage-index {
  font-size: 12px;
  color: var(--muted);
  padding-top: 4px;
  transition: transform var(--dur) var(--ease-out);
}

.stage:hover .stage-index { transform: translateX(6px); }

.stage-title {
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.015em;
}

.stage-body {
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 700px;
}

/* ---- Diagonal process path -------------------------------------------
   The four steps descend left to right, joined by dashed arcs drawn by
   js/process-path.js. The whole layout is gated on html.js: the connector is
   generated at runtime, so without JS the steps fall back to the plain
   vertical ladder above rather than floating unconnected. */

.stage-dot { display: none; }

.process-figure { position: relative; }

.process-path {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 0;
}

.process-track,
.process-live {
  fill: none;
  stroke-width: 1;
  stroke-dasharray: 7 7;
  stroke-linecap: round;
}

.process-track { stroke: var(--line); }

/* The dashes flow along the path continuously, as in the reference. The offset
   travels exactly one dash period (7 + 7), which is what makes the loop
   seamless — any other value lands the pattern off itself and visibly jumps on
   repeat. */
@keyframes process-flow {
  to { stroke-dashoffset: -14px; }
}

.process-live {
  stroke: var(--ink-3);
  animation: process-flow 560ms linear infinite;
}

/* Wiped left to right, which works because the path is monotonic in x.
   fill-box keeps the origin on the rect rather than the whole SVG canvas. */
.process-wipe {
  transform: scaleX(0);
  transform-box: fill-box;
  transform-origin: left center;
  transition: transform 1600ms var(--ease-out);
}

.stages.is-visible + .process-path .process-wipe { transform: scaleX(1); }

@media (min-width: 941px) {
  /* Headroom for the arcs. Each link humps ~0.24 of its horizontal run above
     the dot it leaves, so without this the first arc is drawn at negative y
     and (the SVG being overflow:visible) spills into the section heading. */
  html.js .process-figure { padding-top: 86px; }

  html.js .stages.is-path {
    --path-col: 270px;
    --path-drop: 108px;
    position: relative;
    z-index: 1;
    border-top: 0;
    /* Three drops plus room for the tallest block, since the children are
       taken out of flow and no longer give the container height. */
    height: calc(var(--path-drop) * 3 + 210px);
  }

  html.js .stages.is-path .stage {
    position: absolute;
    top: calc(var(--i) * var(--path-drop));
    left: calc(var(--i) * (100% - var(--path-col)) / 3);
    display: block;
    width: var(--path-col);
    padding: 0;
    margin: 0;
    border-bottom: 0;
    border-radius: 0;
  }

  html.js .stages.is-path .stage:nth-child(1) { --i: 0; }
  html.js .stages.is-path .stage:nth-child(2) { --i: 1; }
  html.js .stages.is-path .stage:nth-child(3) { --i: 2; }
  html.js .stages.is-path .stage:nth-child(4) { --i: 3; }

  html.js .stages.is-path .stage:hover { background: none; }

  html.js .stages.is-path .stage-dot {
    display: block;
    width: 9px;
    height: 9px;
    margin-bottom: 30px;
    border-radius: 50%;
    background: var(--ink);
  }

  html.js .stages.is-path .stage-index {
    display: inline;
    padding-top: 0;
    margin-right: 8px;
  }

  html.js .stages.is-path .stage:hover .stage-index { transform: none; }

  html.js .stages.is-path .stage-title {
    display: inline;
    font-size: 20px;
  }

  html.js .stages.is-path .stage-body {
    margin-top: 14px;
    font-size: 15px;
    max-width: none;
  }
}

/* ============================================================
   Service pages — "what this looks like for you" + tooling
   ============================================================ */

.practice-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--line);
}

.practice-cell {
  padding: 28px 32px 0;
  border-right: 1px solid var(--line-soft);
}

.practice-cell:first-child { padding-left: 0; }
.practice-cell:last-child { padding-right: 0; border-right: 0; }

.practice-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--muted);
  margin-bottom: 10px;
  transition: color var(--dur-fast) var(--ease-out);
}

.practice-text { font-size: 15px; line-height: 1.55; color: var(--ink-2); }

.tech-block { margin-top: 56px; }

.tech-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 16px;
}

.tech-tag {
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-btn);
  padding: 8px 16px;
}

/* ============================================================
   Founders
   ============================================================ */

.founder {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  display: grid;
  grid-template-columns: 200px 1fr;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.founder:hover { border-color: var(--ink); }

.founder-photo {
  background-color: var(--photo-bg);
  border-right: 1px solid var(--line);
  display: flex;
  align-items: flex-end;
  padding: 16px;
}

.founder-photo > span {
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--muted);
}

/* Real headshots drop in as <img class="founder-photo"> and fill the same cell. */
img.founder-photo {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}

.founder:hover img.founder-photo { transform: scale(1.04); }

.founder-copy { padding: 36px 36px 38px; }

.founder-name {
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.015em;
}

.founder-role {
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--muted);
  margin: 10px 0 20px;
}

.founder-bio {
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* ============================================================
   FAQ
   ============================================================ */

.faq-layout {
  display: grid;
  grid-template-columns: 380px 1fr;
  gap: 80px;
}

.faq-aside {
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-3);
  margin-top: 24px;
}

.faq-list { border-top: 1px solid var(--line); }

.faq-list details { border-bottom: 1px solid var(--line); }

.faq-list summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  padding: 28px 0;
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.01em;
  list-style: none;
  cursor: pointer;
}

.faq-list summary::-webkit-details-marker { display: none; }

.faq-list .plus {
  flex: none;
  font-size: 20px;
  font-weight: 400;
  color: var(--muted);
  line-height: 1;
}

.faq-list details[open] .plus { transform: rotate(45deg); }

.faq-list .faq-body p {
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink-2);
  margin-bottom: 30px;
  max-width: 760px;
  text-wrap: pretty;
}

/* ============================================================
   Contact CTA
   ============================================================ */

.cta { background: var(--dark); color: var(--dark-ink); border-top: 1px solid var(--dark-line); }

.cta-inner {
  padding-top: 104px;
  padding-bottom: 96px;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 80px;
  align-items: end;
}

.cta-title {
  font-size: 56px;
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: 500;
  max-width: 640px;
  text-wrap: balance;
}

.cta-lede {
  font-size: 18px;
  line-height: 1.6;
  color: var(--dark-muted);
  margin-top: 26px;
  max-width: 560px;
}

.cta-actions {
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: flex-start;
  width: 100%;
}

/* A pill stretched across a whole column reads as a banner, not a button. */
.cta-actions .btn-block {
  display: inline-block;
  width: auto;
}

.cta-email {
  font-size: 13px;
  color: var(--dark-muted);
  padding-left: 4px;
  transition: color var(--dur-fast) var(--ease-out);
}

.cta-email:hover { color: var(--dark-ink); }

/* ============================================================
   Footer
   ============================================================ */

.site-footer {
  /* Was an opaque --dark band with a top hairline. The footer is now a panel
     floating on the page background, so the band and its border come off. */
  background: transparent;
  color: var(--dark-ink);
  padding-top: 40px;
  padding-bottom: 48px;
}

/* The footer builds the glass from two real elements. The service cards build
   the same look from one element via background-clip — see .service-card. */
.footer-glass {
  padding: 3px;
  border-radius: 40px;
  background: var(--glass-rim);
  box-shadow: var(--glass-float);
}

/* Radius is the rim's 40px minus the 3px it inherits as padding. */
.footer-glass-inner {
  border-radius: 37px;
  padding: 40px 44px 30px;
  background: var(--glass-panel);
  box-shadow: var(--glass-lip);
}

.footer-grid {
  padding-top: 0;
  padding-bottom: 36px;
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: 48px;
}

.footer-mark {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.16em;
  margin-bottom: 6px;
}

.footer-blurb {
  font-size: 14px;
  line-height: 1.6;
  color: var(--dark-muted-2);
  max-width: 280px;
}

.footer-cta { margin-top: 20px; }

.footer-label {
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--dark-muted-3);
  margin-bottom: 16px;
}

.footer-links {
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 14px;
}

.footer-links a { color: var(--dark-ink-2); transition: color var(--dur-fast) var(--ease-out); }
.footer-links a:hover { color: #fff; }

.footer-coords {
  font-size: 13px;
  line-height: 1.7;
  color: var(--dark-ink-2);
}

.footer-coords span { color: var(--dark-muted-3); }

.footer-base {
  padding-top: 24px;
  padding-bottom: 0;
  border-top: 1px solid var(--dark-line);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 32px;
  font-size: 11px;
  color: var(--dark-muted-3);
}

/* ============================================================
   Responsive layer
   Desktop above 1180px is the design, untouched.
   ============================================================ */

@media (max-width: 1180px) {
  :root { --gutter: 32px; }

  /* The backdrop is deliberately wider than the viewport so the cloud bleeds
     past both edges; without a clip that widens the document and gives the
     page a horizontal scroll. The service heroes already clip for the same
     reason — this brings the homepage into line. */
  .hero { min-height: 0; display: block; overflow: hidden; }

  .hero-inner {
    padding-top: 128px;
    padding-bottom: 104px;
    grid-template-columns: 1fr;
    gap: 0;
  }

  /* One column here, so the left-asset mirror's `grid-column: 2` would invent
     a second implicit column and shove the copy off to the side. */
  .hero.is-chrome-right .hero-inner,
  .hero.is-chrome-left .hero-inner { grid-template-columns: 1fr; }

  .hero.is-chrome-left .hero-title,
  .hero.is-chrome-left .hero-foot { grid-column: 1; }

  /* Was hidden from 1180px down, so an iPad in portrait got no hero visual at
     all. It becomes a backdrop behind the copy instead of a grid cell, so it
     never pushes the CTA down. */
  .hero-visual {
    display: block;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(620px, 96vw);
    margin: 0;
    max-height: none;
    opacity: 0.5;
    pointer-events: none;
    z-index: 0;
  }

  .hero-copy { position: relative; z-index: 1; }
  .hero-title { font-size: clamp(44px, 5.6vw, 64px); }

  .faq-layout { grid-template-columns: 320px 1fr; gap: 56px; }
  .stage { grid-template-columns: 48px 260px 1fr; gap: 28px; }
  .cta-inner { gap: 56px; }
}

@media (max-width: 940px) {
  /* Stays three across; the cells just get shorter. */
  .logo-cell { min-height: 96px; }
  .logo-name { font-size: 20px; }
  .logo-mark { width: 28px; height: 28px; }

  /* The diagonal is wrong on a narrow screen, but losing the connector
     altogether is what made the process read as plain text. js/process-path.js
     builds a vertical rail here instead, down the left of the ladder. */
  .process-figure { padding-left: 26px; }

  .stages .stage-dot {
    display: block;
    position: absolute;
    left: -26px;
    top: 30px;
    width: 9px;
    height: 9px;
    margin: 0;
    border-radius: 50%;
    background: var(--ink);
  }

  .stages .stage { position: relative; }

  /* The rail is vertical here, so the reveal has to run down it rather than
     across — a scaleX wipe on a 1px-wide path reveals the whole thing at once. */
  .process-wipe { transform: scaleY(0); transform-origin: center top; }
  .stages.is-visible + .process-path .process-wipe { transform: scaleY(1); }

  .section { padding-top: 88px; padding-bottom: 88px; }
  .section.is-secondary,
  .section.is-inverse { padding-bottom: 88px; }
  .section-faq { padding-bottom: 88px; }

  .section-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 24px;
    margin-bottom: 36px;
  }

  .section-title { font-size: clamp(32px, 4.6vw, 46px); }
  .section-aside, .section-aside-wide { max-width: 560px; }

  /* Services: two 40px-padded cards get cramped below ~900px, so one column.
     Full-width cards would run to ~95 characters a line, so cap the measure. */
  .card-grid { grid-template-columns: 1fr; }

  /* Below this width the bento has no room to read as one, so it flattens to a
     single column — the spans have to be cleared or every card claims two. */
  .service-grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .service-card:nth-child(n) { grid-column: auto; }
  .service-card { min-height: 0; }
  .card-body, .founder-bio { max-width: 68ch; }

  /* Process: number and title share a row, body spans the full width beneath. */
  .stage {
    grid-template-columns: 40px 1fr;
    gap: 8px 20px;
    padding: 28px 0;
  }

  .stage-body { grid-column: 2; max-width: none; }

  .faq-layout { grid-template-columns: 1fr; gap: 36px; }
  .faq-list summary { font-size: 18px; gap: 24px; }

  .cta-inner {
    grid-template-columns: 1fr;
    gap: 40px;
    padding-top: 80px;
    padding-bottom: 80px;
  }

  .cta-title { font-size: clamp(34px, 5.2vw, 56px); }
  .cta-actions { max-width: 380px; }

  .footer-grid { grid-template-columns: repeat(2, 1fr); gap: 40px; }
  .footer-glass-inner { padding: 36px 32px 26px; }

  /* Too narrow to sit beside the copy, so it becomes a backdrop behind it and
     drops back far enough not to fight the text for contrast. */
  .hero.is-chrome-right .hero-chrome,
  .hero.is-chrome-left .hero-chrome {
    left: 50%;
    right: auto;
    width: min(560px, 92vw);
    transform: translate(-50%, -50%);
    opacity: 0.42;
  }

  /* --- service pages: the 4-up practice grid drops to 2-up. At four columns
     the cells are ~215px, which wraps the body copy to a ~24-character
     measure — too narrow to read comfortably. --- */
  .practice-grid { grid-template-columns: repeat(2, 1fr); }
  .practice-cell { padding: 28px 32px; }
  .practice-cell:nth-child(odd) { padding-left: 0; }
  .practice-cell:nth-child(even) { padding-right: 0; border-right: 0; }
  .practice-cell:nth-child(-n+2) { border-bottom: 1px solid var(--line-soft); }

  /* --- value strip drops to 2-up for the same reason. The homepage strip has
     short enough copy to survive four columns here, but the service-page
     strips carry a full sentence per cell. --- */
  .strip-inner { grid-template-columns: repeat(2, 1fr); }
  .strip-cell:nth-child(odd) { padding-left: 0; }
  .strip-cell:nth-child(even) { padding-right: 0; border-right: 0; }
  .strip-cell:nth-child(-n+2) { border-bottom: 1px solid var(--line-soft); }
}

@media (max-width: 640px) {
  .logos { padding: 40px 0 44px; }
  .logo-grid { gap: 10px; }
  .logo-cell {
    flex-direction: column;
    gap: 7px;
    min-height: 92px;
    padding: 10px 6px;
    --tick: 7px;
  }
  .logo-name { font-size: 13px; white-space: normal; text-align: center; }
  .logo-mark { width: 20px; height: 20px; }

  :root { --gutter: 20px; --header-h: 56px; }

  /* --- mobile nav --- */
  .header-inner { gap: 16px; }
  .nav-toggle { display: inline-flex; }

  /* --- tap targets: every control clears the 44px minimum on touch. The
     extra height is padding only, so nothing shifts visually. --- */
  .nav-toggle { padding: 12px 14px; }
  .brand { padding: 11px 0; }
  .footer-links { gap: 0; }
  .footer-links a { display: block; padding: 13px 0; }
  .cta-email { display: inline-block; padding: 14px 4px; }
  /* Full-screen panel rather than a strip hanging off the header. It starts
     below the header so the brand and the close control stay visible, and
     scrolls internally if the accordion opens past the viewport. */
  .site-nav {
    display: none;
    /* Absolute, not fixed. .site-header carries a backdrop-filter, which makes
       it the containing block for fixed-position descendants — so `fixed` here
       sized against the 56px header instead of the viewport, collapsing the
       panel to 36px and squashing every tap target. Positioning against the
       header and giving it an explicit viewport-height is what actually fills
       the screen. */
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: calc(100vh - var(--header-h));
    height: calc(100svh - var(--header-h));
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Near-solid by default so the panel is never see-through if the blur is
       unsupported or dropped under load; the blur is layered on top as polish
       where it is available. */
    background: rgba(19, 19, 21, 0.985);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    border-bottom: 0;
    padding: 12px var(--gutter) calc(24px + env(safe-area-inset-bottom));
  }

  [data-nav-open] .site-nav { display: flex; }
  [data-nav-open] body { overflow: hidden; }

  /* Items rise in on the same stagger the scroll reveals use, so the panel
     resolves rather than snapping. */
  [data-nav-open] .site-nav > * {
    animation: nav-in var(--dur) var(--ease-out) backwards;
  }
  .site-nav > *:nth-child(1) { animation-delay: 30ms; }
  .site-nav > *:nth-child(2) { animation-delay: calc(30ms + var(--stagger)); }
  .site-nav > *:nth-child(3) { animation-delay: calc(30ms + var(--stagger) * 2); }
  .site-nav > *:nth-child(4) { animation-delay: calc(30ms + var(--stagger) * 3); }
  .site-nav > *:nth-child(5) { animation-delay: calc(30ms + var(--stagger) * 4); }

  @keyframes nav-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }

  /* :not(.btn) matches the desktop rule's specificity — a plain `.site-nav > a`
     loses to it and the links kept their 8px desktop padding, landing at a
     32px tap target. */
  .site-nav > a:not(.btn) {
    display: flex;
    align-items: center;
    font-size: 20px;
    letter-spacing: -0.015em;
    color: var(--ink);
    padding: 17px 0;
    border: 0;
    border-bottom: 1px solid var(--line-soft);
    border-radius: 0;
  }

  .site-nav > a.btn-solid {
    margin-top: 24px;
    padding: 17px 18px;
    font-size: 16px;
    text-align: center;
    border-bottom: 0;
  }

  /* --- Services accordion ---------------------------------------------
     These rules were lost to an earlier bulk edit, which is why Services
     behaved like a desktop dropdown inside the panel: `.site-nav > a` only
     matches direct <a> children, and Services is a <div> wrapping a <button>,
     so it matched nothing and kept its absolute positioning. */
  .site-nav > .nav-item { display: block; }

  .nav-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 17px 0;
    font-family: var(--sans);
    font-size: 20px;
    letter-spacing: -0.015em;
    font-weight: 400;
    color: var(--ink);
    background: none;
    border: 0;
    border-bottom: 1px solid var(--line-soft);
    cursor: pointer;
  }

  .nav-caret { width: 12px; height: 12px; }

  /* static, not absolute: it now sits in the flow and pushes the rest down.
     0fr -> 1fr animates a collapse to auto height with no JS measurement. */
  .nav-menu {
    position: static;
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    visibility: hidden;
    transform: none;
    box-shadow: none;
    background: none;
    border: 0;
    padding: 0;
    min-width: 0;
    transition: grid-template-rows var(--dur) var(--ease-out),
                opacity var(--dur-fast) var(--ease-out),
                visibility 0s linear var(--dur);
  }

  .nav-item.is-open .nav-menu {
    grid-template-rows: 1fr;
    opacity: 1;
    visibility: visible;
    transition: grid-template-rows var(--dur) var(--ease-out),
                opacity var(--dur-fast) var(--ease-out),
                visibility 0s;
  }

  /* Strip the desktop dropdown's card chrome — in the panel it reads as a
     floating box inside a menu rather than part of it. */
  .nav-menu-inner {
    overflow: hidden;
    display: block;
    gap: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }

  /* Keep the desktop's column flex: overriding it to `block` put the title and
     its description on one line. Only the spacing changes here. */
  .nav-menu-item {
    gap: 2px;
    padding: 15px 0 15px 18px;
    border-radius: 0;
    border-bottom: 1px solid var(--line-soft);
    border-left: 1px solid var(--line);
    /* the desktop entrance staggers these from opacity 0; in an accordion the
       container already animates, so they sit visible */
    opacity: 1;
    transform: none;
    transition-delay: 0s;
  }

  .nav-menu-item:last-child { border-bottom: 0; }
  .nav-menu-title { font-size: 17px; color: var(--ink); }
  .nav-menu-desc { font-size: 13px; color: var(--muted-2); }

  /* --- hero --- */
  /* The asset stays on phones as ground behind the copy, same relationship it
     has on desktop. Opacity comes down so the headline keeps its contrast;
     the renderers cut their geometry budget separately. */
  .hero.is-chrome-right .hero-chrome,
  .hero.is-chrome-left .hero-chrome {
    display: block;
    width: min(420px, 108vw);
    opacity: 0.30;
  }

  .hero-visual { width: min(460px, 112vw); opacity: 0.38; }

  /* Centred hero on phones, across every page. The capped title and lede need
     margin-inline:auto as well as text-align — text-align centres the lines
     inside the box, but the box itself is narrower than the column and would
     otherwise stay pinned left. */
  .hero-inner { padding-top: 88px; padding-bottom: 72px; text-align: center; }
  .hero-title,
  .hero-lede { margin-inline: auto; }
  .hero-title { font-size: clamp(34px, 8.4vw, 44px); }
  .hero-lede { font-size: 17px; }
  .lede-full { display: none; }
  .lede-short { display: inline; }
  .hero-actions { flex-direction: column; align-items: stretch; gap: 10px; }
  .hero-actions .btn { text-align: center; }

  /* --- value strip: rules become horizontal dividers --- */
  .strip-inner { grid-template-columns: 1fr; }

  .strip-cell {
    padding: 22px 0;
    border-right: 0;
    border-bottom: 1px solid var(--line-soft);
  }

  .strip-cell:last-child { border-bottom: 0; }

  /* --- service pages: practice grid collapses like the value strip --- */
  .practice-grid { grid-template-columns: 1fr; }

  .practice-cell {
    padding: 22px 0;
    border-right: 0;
    border-bottom: 1px solid var(--line-soft);
  }

  .practice-cell:last-child { border-bottom: 0; }

  /* --- services --- */
  .section { padding-top: 64px; padding-bottom: 64px; }
  .section.is-secondary,
  .section.is-inverse { padding-bottom: 64px; }
  .section-faq { padding-bottom: 64px; }
  .card { padding: 28px; }
  .card-head { margin-bottom: 20px; }
  .card-title { font-size: 20px; }
  .card-body { font-size: 15px; }
  .service-card { padding: 24px 22px 22px; gap: 16px; }
  .service-card-label { font-size: 20px; }
  .service-card-caption { font-size: 14px; }

  /* --- process --- */
  .stage { grid-template-columns: 32px 1fr; gap: 6px 16px; padding: 24px 12px; margin: 0 -12px; }
  .stage-title { font-size: 19px; }
  .stage-body { font-size: 15px; }

  /* --- founders: photo becomes a band above the bio --- */
  .founder { grid-template-columns: 1fr; }

  .founder-photo {
    aspect-ratio: 16 / 7;
    min-height: 132px;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }

  img.founder-photo { height: auto; aspect-ratio: 16 / 9; }

  .founder-copy { padding: 26px 24px 28px; }
  .founder-name { font-size: 20px; }
  .founder-bio { font-size: 15px; }

  /* --- faq --- */
  .faq-list summary { font-size: 17px; gap: 18px; padding: 22px 0; }
  .faq-list .faq-body p { font-size: 15px; margin-bottom: 24px; }

  /* --- cta + footer --- */
  .cta-inner { padding-top: 64px; padding-bottom: 64px; }
  .cta-lede { font-size: 17px; }
  .cta-actions { max-width: none; }

  /* A 40px rim radius is right on a 1280px panel and far too round on a
     phone, so the panel tightens with the viewport. */
  .site-footer { padding-top: 24px; padding-bottom: 32px; }
  .footer-glass { border-radius: 28px; }
  .footer-glass-inner { border-radius: 25px; padding: 32px 22px 24px; }

  .footer-grid { grid-template-columns: 1fr; gap: 32px; padding-top: 0; padding-bottom: 28px; }
  .footer-blurb { max-width: none; }

  .footer-base {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    padding-bottom: 0;
  }
}

/* ============================================================
   Motion — scroll reveals + hero entrance
   Enhancement only: without JS, html.js is never set and every
   element renders in its final state. reduced-motion opts out.
   ============================================================ */

.js [data-reveal] {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* children of a [data-reveal-group] cascade in on a short stagger */
.js [data-reveal-group] > * {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

.js [data-reveal-group].is-visible > * {
  opacity: 1;
  transform: none;
}

/* main.js stamps --reveal-i on each child so the cascade works for groups of
   any length (the FAQ has seven items, the footer four, the process five).
   The delay is capped so a long group never crawls; --reveal-i falls back to 0
   if the script hasn't run, which just means everything arrives together. */
.js [data-reveal-group].is-visible > * {
  transition-delay: min(var(--reveal-i, 0) * var(--stagger), 560ms);
}

/* hero paints in on load, no observer needed above the fold */
.js .hero [data-hero] {
  opacity: 0;
  transform: translateY(16px);
  animation: hero-in 640ms var(--ease-out) forwards;
}

.js .hero [data-hero="1"] { animation-delay: 60ms; }
.js .hero [data-hero="2"] { animation-delay: 150ms; }
.js .hero [data-hero="3"] { animation-delay: 280ms; }
.js .hero [data-hero="4"] { animation-delay: 360ms; }

@keyframes hero-in {
  to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------
   Hover choreography
   Every interactive surface moves on more than one property, on the
   system's duration and curve. ------------------------------------------------------------ */

.card {
  transition: border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}

.card:hover {
  border-color: var(--ink);
  transform: translateY(-2px);
}

.faq-list .plus { transition: transform var(--dur) var(--ease-out); }

/* The FAQ answer animates open and closed. main.js drives it via the Web
   Animations API — the [open] attribute is held on through the closing
   animation, which is why a wrapper element is needed to measure against. */
.faq-body { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-reveal].is-visible,
  .js [data-reveal-group] > *,
  .js [data-reveal-group].is-visible > *,
  .js .hero [data-hero] {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }

  .card:hover { transform: none; }

  /* The path is shown fully drawn, and the dashes hold still. */
  .process-wipe { transform: scaleX(1); transition: none; }
  .process-live { animation: none; }
}
