/* ==========================================================================
   Velocity Build AI
   Hand-authored stylesheet. No build step, no preprocessor, no dependencies.
   Every value below traces to the Velocity Build AI brand system, v1.0.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Brand tokens. Pasted verbatim from brand system section 2.2.
   -------------------------------------------------------------------------- */

:root {
  /* darks */
  --vb-navy-deep: #0A1A2B;
  --vb-navy:      #14304A;
  --vb-navy-soft: #22496B;
  /* gradient trio */
  --vb-green:     #34C06D;
  --vb-teal:      #23A7A9;
  --vb-blue:      #2B8FE3;
  --vb-blue-deep: #1E6ED0;
  --vb-link:      #1A5FB4;
  /* neutrals */
  --vb-paper:     #FFFFFF;
  --vb-mist:      #F4F7FA;
  --vb-fog:       #E4EAF1;
  --vb-steel:     #7C8DA0;
  --vb-slate:     #46586C;
  /* reserved accent */
  --vb-signal:    #F0A63A;
  /* signature gradient */
  --vb-gradient: linear-gradient(120deg, #34C06D 0%, #23A7A9 48%, #2B8FE3 100%);
}

/* --------------------------------------------------------------------------
   2. System tokens. Type stacks (3.4), type scale (3.5), spacing (4.1),
      containers (4.2), motion (4.6).
   -------------------------------------------------------------------------- */

:root {
  --vb-font-display: 'Space Grotesk', 'Eurostile', 'Bahnschrift',
    'DIN Alternate', 'Roboto Condensed', 'Segoe UI', system-ui,
    -apple-system, 'Helvetica Neue', Arial, sans-serif;

  --vb-font-body: 'Inter', system-ui, -apple-system, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;

  --vb-font-mono: ui-monospace, 'SF Mono', 'Cascadia Mono', 'Segoe UI Mono',
    'Roboto Mono', Menlo, Consolas, monospace;

  /* Type scale. The four display steps are fluid between the brand system's
     published mobile value and its published desktop value; steps 5 to 9 are
     fixed at every width, per section 3.5.
     Exception: --fs-display-l floors at 2.5rem (40px) rather than the published
     2.25rem (36px), because the h1 carries the page's one gradient word and
     section 2.3 forbids gradient text below 40px. The floor is the binding
     constraint, so it wins over the mobile step value. */
  --fs-display-xl: clamp(2.75rem, 1.55rem + 4vw, 4.25rem);
  --fs-display-l:  clamp(2.5rem, 1.05rem + 4vw, 3.25rem);
  --fs-h1:         clamp(2rem, 1.2rem + 2.7vw, 2.5rem);
  --fs-h2:         clamp(1.5rem, 0.75rem + 2.5vw, 1.875rem);
  --fs-h3:         1.375rem;
  --fs-body-l:     1.125rem;
  --fs-body:       1rem;
  --fs-small:      0.875rem;
  --fs-eyebrow:    0.75rem;

  /* Spacing scale, 8px base with one 4px half step. */
  --sp-1:  0.25rem;
  --sp-2:  0.5rem;
  --sp-3:  0.75rem;
  --sp-4:  1rem;
  --sp-5:  1.5rem;
  --sp-6:  2rem;
  --sp-7:  3rem;
  --sp-8:  4rem;
  --sp-9:  6rem;
  --sp-10: 8rem;
  --sp-11: 10rem;

  /* Containers */
  --c-prose:   720px;
  --c-content: 1200px;
  --c-wide:    1440px;

  /* Reading measure. The brand system caps body copy at 68 characters. At the
     18px body size used here that is roughly 38rem, not the 34rem quoted for a
     16px body. The character cap is what is being preserved. */
  --measure: 38rem;

  --ease: cubic-bezier(0.2, 0, 0, 1);
  --dur-state: 160ms;
  --dur-enter: 240ms;
}

/* --------------------------------------------------------------------------
   3. Fonts. Self hosted, no remote host, no @import.
      Both files are VARIABLE fonts (see assets/fonts/README.md), so each face
      declares a weight RANGE and the browser interpolates along the wght axis.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Space Grotesk';
  src: url('/assets/fonts/space-grotesk-var.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   4. Reset and base
   -------------------------------------------------------------------------- */

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

html {
  font-size: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6.5rem;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background-color: var(--vb-paper);
  color: var(--vb-navy);
  font-family: var(--vb-font-body);
  font-size: var(--fs-body-l);
  font-weight: 400;
  line-height: 1.65;
  hyphens: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, p, ul, ol, dl, dd, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }

/* Honour the hidden attribute even against a later display declaration, so
   anything the markup ships hidden stays hidden. */
[hidden] { display: none !important; }

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

::selection {
  background-color: var(--vb-navy);
  color: var(--vb-paper);
}

/* Focus states, locked by brand system 2.4. Light background default; dark
   bands override to green. */
:focus-visible {
  outline: 3px solid var(--vb-blue-deep);
  outline-offset: 2px;
  border-radius: 4px;
}

.band--navy :focus-visible,
.site-footer :focus-visible {
  outline: 3px solid var(--vb-green);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   5. Typography primitives
   -------------------------------------------------------------------------- */

h1 {
  font-family: var(--vb-font-display);
  font-weight: 700;
  font-size: var(--fs-display-l);
  line-height: 1.08;
  letter-spacing: -0.025em;
  text-wrap: pretty;
}

h2 {
  font-family: var(--vb-font-display);
  font-weight: 700;
  font-size: var(--fs-h2);
  line-height: 1.2;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h3 {
  font-family: var(--vb-font-display);
  font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.3;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

h4 {
  font-family: var(--vb-font-display);
  font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.3;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

p { text-wrap: pretty; }

.prose { max-width: var(--measure); }
.prose p + p { margin-top: var(--sp-5); }

/* All caps tracked label. Permitted only in the five cases in section 3.7.
   Colour is navy on light and mist on dark, because 12px type is too small
   for steel or slate. */
.eyebrow {
  display: block;
  font-family: var(--vb-font-display);
  font-weight: 500;
  font-size: var(--fs-eyebrow);
  line-height: 1.4;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--vb-navy);
  margin-bottom: var(--sp-4);
}

.band--navy .eyebrow,
.site-footer .eyebrow { color: var(--vb-mist); }

/* Inline links, locked by brand system 2.4. */
a {
  color: var(--vb-link);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1.5px;
  transition: color var(--dur-state) var(--ease),
              text-decoration-thickness var(--dur-state) var(--ease);
}

a:hover {
  color: var(--vb-navy);
  text-decoration-thickness: 2px;
}

.band--navy a,
.site-footer a { color: var(--vb-green); }

.band--navy a:hover,
.site-footer a:hover { color: var(--vb-paper); }

/* --------------------------------------------------------------------------
   6. Layout primitives
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--c-content);
  margin-inline: auto;
  padding-inline: 20px;
}

.section { padding-block: var(--sp-8); }

@media (min-width: 480px) {
  .container { padding-inline: 24px; }
}

@media (min-width: 768px) {
  .section { padding-block: var(--sp-9); }
}

@media (min-width: 1024px) {
  .container { padding-inline: 40px; }
  .section { padding-block: var(--sp-10); }
}

/* Background bands. Order across the page is paper, mist, paper, mist, navy,
   paper, mist, navy footer. No two navy bands touch, and every paper-to-mist
   seam carries a rule so the 4-value luminance gap reads as deliberate. */
.band--paper { background-color: var(--vb-paper); }

/* Mist carries a hairline on both seams because the 4-value luminance gap
   against paper is otherwise too small to read as deliberate. */
.band--mist {
  background-color: var(--vb-mist);
  border-top: 1px solid var(--vb-fog);
  border-bottom: 1px solid var(--vb-fog);
}

.band--navy {
  background-color: var(--vb-navy-deep);
  color: var(--vb-mist);
}

.band--navy h2,
.band--navy h3,
.band--navy h4 { color: var(--vb-paper); }

/* The one signature-gradient section divider on the page: a 3px rule across
   the top of the first mist band. */
.band--gradient-top {
  background-image: var(--vb-gradient);
  background-repeat: no-repeat;
  background-size: 100% 3px;
  background-position: left top;
  border-top: 0;
}

/* --------------------------------------------------------------------------
   7. Components
   -------------------------------------------------------------------------- */

/* 7.1 Skip link */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: var(--sp-3) var(--sp-5);
  background-color: var(--vb-navy-deep);
  color: var(--vb-paper);
  font-size: var(--fs-small);
  font-weight: 500;
  text-decoration: none;
  border-radius: 0 0 8px 0;
}

.skip-link:focus {
  left: 0;
  color: var(--vb-paper);
}

/* 7.2 Logo lockups.
   Below 480px the header falls back to the mark alone rather than shrinking
   the horizontal lockup past its 180px minimum (brand system 7.3). Both
   header variants carry the navy right stroke and therefore only ever sit on
   paper. The footer uses the knockout variant because the band is navy. */
.brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  text-decoration: none;
}

.brand__img {
  width: auto;
  height: 36px;
}

@media (min-width: 480px) {
  .brand__img { height: 44px; }
}

.footer__logo {
  width: 230px;
  height: auto;
}

/* 7.3 Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 48px, not the 44px WCAG floor. The submit control measured 44.8px in QA,
     which passes but leaves no margin on a thumb. */
  min-height: 48px;
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--vb-blue-deep);
  border-radius: 8px;
  background-color: var(--vb-blue-deep);
  color: var(--vb-paper);
  font-family: var(--vb-font-body);
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-state) var(--ease),
              border-color var(--dur-state) var(--ease),
              color var(--dur-state) var(--ease);
}

.btn:hover {
  background-color: var(--vb-navy);
  border-color: var(--vb-navy);
  color: var(--vb-paper);
  text-decoration: none;
}

.btn--quiet {
  background-color: var(--vb-mist);
  border-color: var(--vb-fog);
  color: var(--vb-navy);
}

.btn--quiet:hover {
  background-color: var(--vb-fog);
  border-color: var(--vb-steel);
  color: var(--vb-navy);
}

/* The header button. It matches the 48px tap target every other call to action
   on the page uses, and its three-word label never wraps to a second line
   inside the pill. The header has room for nowrap at every width because the
   nav does not appear until 820px. See the .nav breakpoint note below. */
.btn--small {
  min-height: 48px;
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-small);
  white-space: nowrap;
}

/* The work band is the one place a button sits on navy. The inline-link rule
   `.band--navy a` is one type selector heavier than `.btn`, so without this it
   wins the cascade and repaints the label green on the blue-deep fill. */
.band--navy .btn { color: var(--vb-paper); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin-top: var(--sp-7);
}

/* 7.4 Speed streaks, brand system 6.1. One cluster on the page.
   Lengths are drawn from the spacing scale so they never look arbitrary. */
.streaks {
  display: grid;
  gap: 8px;
  margin-bottom: var(--sp-5);
}

.streaks span {
  display: block;
  height: 3px;
  background-color: var(--vb-blue-deep);
}

.streaks span:nth-child(1) { width: 128px; }
.streaks span:nth-child(2) { width: 96px; background-color: var(--vb-blue); }
.streaks span:nth-child(3) { width: 48px; }
.streaks span:nth-child(4) { width: 24px; background-color: var(--vb-blue); }

/* 7.6 Stack chips */
.stack {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.stack li {
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--vb-fog);
  border-radius: 4px;
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1.55;
  letter-spacing: 0.005em;
  color: var(--vb-slate);
}

.band--navy .stack li {
  border-color: var(--vb-navy-soft);
  color: var(--vb-mist);
}

.stack-label {
  display: block;
  font-family: var(--vb-font-display);
  font-weight: 500;
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--vb-navy);
  margin-bottom: var(--sp-3);
}

.band--navy .stack-label { color: var(--vb-mist); }

/* 7.7 Colophon list: removed in v5. Both users (the hero spec block and the
   founder aside) were deleted, so the rules went with them rather than sitting
   here as dead weight. Recover from git if a colophon is ever wanted again. */

/* --------------------------------------------------------------------------
   8. Header
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background-color: var(--vb-paper);
  /* A hairline that is invisible at rest and appears once the page scrolls.
     Declared as a real colour rather than a keyword so nothing shifts. */
  border-bottom: 1px solid var(--vb-paper);
  transition: border-color var(--dur-state) var(--ease);
}

.site-header.is-scrolled { border-bottom-color: var(--vb-fog); }

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--sp-3);
}

.nav { display: none; }

/* The nav appears at 820px, not 768px. v3 had four nav items and fitted next
   to the logo and the header button at 768px. v4 added a fifth, Approach, and
   at exactly 768px the row no longer fits: the button either wrapped to two
   lines or, once it was set to nowrap, pushed about 2px past the viewport and
   produced a horizontal scrollbar on the whole page. Raising the breakpoint
   removes the cramped band entirely rather than shaving padding inside it.
   Nothing is lost below 820px: the page is a single scroll and the footer
   carries the full site map. */
@media (min-width: 820px) {
  .nav {
    display: flex;
    align-items: center;
    gap: var(--sp-5);
  }
}

.nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.005em;
  color: var(--vb-navy);
  text-decoration: none;
  border-bottom: 2px solid var(--vb-paper);
  transition: border-color var(--dur-state) var(--ease);
}

.nav a:hover {
  color: var(--vb-navy);
  border-bottom-color: var(--vb-blue-deep);
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

/* --------------------------------------------------------------------------
   9. Hero
   -------------------------------------------------------------------------- */

.hero { padding-block: 6rem 5rem; }

@media (min-width: 768px) {
  .hero { padding-block: 8rem 7rem; }
}

@media (min-width: 1024px) {
  .hero { padding-block: 10rem 8rem; }
}

/* Single column since the hero colophon was removed (v5). The h1 and the lead
   carry their own measure caps below, so the band still reads as a column
   rather than running the full container width. */
.hero__grid {
  display: grid;
  gap: var(--sp-7);
}

.hero h1 { max-width: 22ch; }

.hero__lead {
  margin-top: var(--sp-6);
  max-width: var(--measure);
  color: var(--vb-navy);
}

/* The one gradient word on the page. Solid navy is declared first and is what
   renders wherever background-clip is unsupported. The word inherits
   --fs-display-l, whose clamp floors at 2.5rem, so it renders at exactly the
   40px minimum the brand system sets for gradient text at the narrowest
   viewports and larger everywhere else. It never falls below the floor. */
/* The gradient phrase stays on one line. It is a two-word phrase carrying a
   background-clipped gradient, and when it broke across a line the gradient
   restarted on the second fragment and the emphasis read as an accident.
   Safe at the narrow end: the h1 is clamped to a 2rem floor, so the phrase
   measures well inside the container even at 320px. */
.grad-word {
  color: var(--vb-navy);
  white-space: nowrap;
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .grad-word {
    background-image: var(--vb-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
  }
}

/* --------------------------------------------------------------------------
   10. Approach
   -------------------------------------------------------------------------- */

/* Matches the gap every other section puts between its head and its body
   (.process__steps, .featured), so the new band joins the existing rhythm
   instead of starting its own. */
.approach__body { margin-top: var(--sp-8); }

/* --------------------------------------------------------------------------
   11. Services
   -------------------------------------------------------------------------- */

/* Shared section header block: eyebrow, h2, one intro paragraph. */
.section__head { max-width: var(--measure); }

.section__intro {
  margin-top: var(--sp-5);
  max-width: var(--measure);
  color: var(--vb-slate);
}

.band--navy .section__intro { color: var(--vb-mist); }

/* Services: a narrow left column that stays put while the list scrolls past
   it on desktop, and a rule-separated list rather than a card grid. */
.split {
  display: grid;
  gap: var(--sp-7);
}

@media (min-width: 1024px) {
  .split {
    grid-template-columns: minmax(0, 21rem) minmax(0, 1fr);
    gap: var(--sp-9);
    align-items: start;
  }

  .split > .split__aside {
    position: sticky;
    top: 8rem;
  }
}

.service {
  padding-block: var(--sp-6);
  border-top: 1px solid var(--vb-fog);
}

.service:first-child {
  padding-top: 0;
  border-top: 0;
}

/* :last-of-type, not :last-child. A .btn-row div now follows the four
   articles, so :last-child would stop matching the fourth service. */
.service:last-of-type { padding-bottom: 0; }

/* Primary reading copy, so navy per brand 2.1. --vb-slate is reserved for
   genuinely secondary material (section intros, captions, chips, metadata). */
.service p {
  margin-top: var(--sp-3);
  max-width: var(--measure);
  color: var(--vb-navy);
}

/* The one-line promise under each category heading. Carries the weight so the
   explanatory paragraph beneath it can stay at normal weight. */
.service__lede {
  font-weight: 600;
  color: var(--vb-navy);
}

/* The Examples chip row reuses the stack-chip component rather than
   introducing a second list treatment. */
.service .stack-label { margin-top: var(--sp-5); }

/* --------------------------------------------------------------------------
   12. Process
   -------------------------------------------------------------------------- */

.process__steps {
  margin-top: var(--sp-8);
  padding-left: var(--sp-5);
  background-image: var(--vb-gradient);
  background-repeat: no-repeat;
  background-size: 3px 100%;
  background-position: left top;
}

@media (min-width: 900px) {
  .process__steps { padding-left: var(--sp-6); }
}

.step { padding-bottom: var(--sp-7); }
.step:last-child { padding-bottom: 0; }

.step__num {
  display: block;
  font-family: var(--vb-font-display);
  font-weight: 700;
  font-size: var(--fs-h1);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--vb-steel);
  margin-bottom: var(--sp-3);
}

.step p {
  margin-top: var(--sp-3);
  max-width: var(--measure);
  color: var(--vb-navy);
}

@media (min-width: 900px) {
  .step {
    display: grid;
    grid-template-columns: 5rem minmax(0, 1fr);
    column-gap: var(--sp-6);
  }

  .step__num {
    grid-column: 1;
    grid-row: 1 / span 2;
    margin-bottom: 0;
  }

  .step h3 { grid-column: 2; }
  .step p  { grid-column: 2; }
}

/* --------------------------------------------------------------------------
   13. Work

   Two tiers. Tier 1 is three featured projects, each a real screenshot beside
   a short story, with the image side alternating down the page. Tier 2 is
   three compact text-only entries carried on a hairline, deliberately lighter
   so the eye reads the featured three first.
   -------------------------------------------------------------------------- */

/* 13.1 Tier 1, featured */
.featured { margin-top: var(--sp-8); }

.feature {
  display: grid;
  gap: var(--sp-5);
}

.feature + .feature { margin-top: var(--sp-8); }

@media (min-width: 900px) {
  .feature {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: var(--sp-7);
    align-items: center;
  }

  .feature + .feature { margin-top: var(--sp-9); }

  /* Alternate which side the screenshot sits on, for rhythm. The source order
     stays media-then-story in every item, so the reading order is unchanged
     and only the visual placement flips. */
  .feature:nth-child(even) .feature__media { order: 2; }
}

/* The <figure> itself carries no frame, so the caption sits outside the
   screenshot rather than inside its border. Reset the UA figure margin. */
.feature__media { margin: 0; }

/* Screenshot frame, brand system 6.3: 16px radius, a 1px hairline (navy-soft
   because this band is --vb-navy-deep), and the resting elevation from 4.6.
   No browser chrome, no tilt, no extra shadow. */
.feature__frame {
  border: 1px solid var(--vb-navy-soft);
  border-radius: 16px;
  overflow: hidden;
  background-color: var(--vb-navy);
  box-shadow: 0 1px 2px rgba(10, 26, 43, 0.06);
}

.feature__media img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
}

/* Screenshot caption, brand system 6.3: type step 8 (14px), --vb-mist because
   this band is --vb-navy-deep, sentence case, one line. */
.feature__caption {
  margin-top: var(--sp-3);
  font-size: var(--fs-small);
  line-height: 1.55;
  letter-spacing: 0.005em;
  color: var(--vb-mist);
}

.feature__body h3 { color: var(--vb-paper); }

.feature__body p {
  margin-top: var(--sp-4);
  max-width: var(--measure);
  color: var(--vb-mist);
}

.work-status {
  display: block;
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  letter-spacing: 0.005em;
  color: var(--vb-mist);
}

/* 13.2 Tier 2, compact. No frame and no image, so it reads as a footnote to
   the two above it rather than as a third, fourth, fifth, and sixth headline.

   Column counts are chosen so the first and the last brief never share a
   column and never sit side by side. Both of those describe an unnamed client
   engagement, and visual adjacency is exactly the grouping that invites a
   reader to treat two anonymous cards as one story. A 3-column grid would
   wrap the fourth card directly underneath the first, which is the one
   arrangement that has to be avoided: 4 columns puts them at opposite ends of
   a single row, and 2 columns puts them on a diagonal. */
.work-compact {
  display: grid;
  gap: var(--sp-6);
  margin-top: var(--sp-8);
  padding-top: var(--sp-7);
  border-top: 1px solid var(--vb-navy-soft);
}

@media (min-width: 768px) {
  .work-compact {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
  }
}

@media (min-width: 900px) {
  .work-compact {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--sp-5);
    align-items: start;
    margin-top: var(--sp-9);
  }
}

.work-brief h3 {
  font-size: var(--fs-body-l);
  font-weight: 500;
  color: var(--vb-mist);
}

.work-brief p {
  margin-top: var(--sp-3);
  font-size: var(--fs-body);
  color: var(--vb-mist);
}

/* A status line inside a compact card. `.work-brief p` is one type selector
   heavier than `.work-status`, so without this the line renders at body size
   and reads as a fourth sentence rather than as metadata. The mist colour
   already matches, so only the size and the gap need restating. */
.work-brief .work-status {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
}

/* --------------------------------------------------------------------------
   14. Founder
   -------------------------------------------------------------------------- */

.founder__grid {
  display: grid;
  gap: var(--sp-6);
}

@media (min-width: 900px) {
  .founder__grid {
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
    gap: var(--sp-9);
    align-items: start;
  }
}

.founder__bio p { color: var(--vb-navy); }

/* --------------------------------------------------------------------------
   15. Intake form

   The band is --vb-mist, and brand system 2.1 puts input fields at rest on
   --vb-mist too, so the form sits inside a --vb-paper panel ("card surface on
   mist", also 2.1). That keeps the fixed band order intact and still leaves
   the controls a full step of luminance away from what they sit on.
   -------------------------------------------------------------------------- */

.intake {
  margin-top: var(--sp-7);
  max-width: 46rem;
  padding: var(--sp-5);
  background-color: var(--vb-paper);
  border: 1px solid var(--vb-fog);
  border-radius: 16px;
  box-shadow: 0 1px 2px rgba(10, 26, 43, 0.06);
}

@media (min-width: 768px) {
  .intake { padding: var(--sp-6); }
}

/* 15.1 Groups. A fieldset carries no visual box of its own; the rule between
   groups does the separating, which keeps the panel from turning into a stack
   of nested boxes. */
.field-group {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.field-group + .field-group { margin-top: var(--sp-6); }

.field-group > legend {
  width: 100%;
  padding: 0;
  font-family: var(--vb-font-display);
  font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--vb-navy);
}

/* The separator rule is carried by the legend rather than by the fieldset. A
   browser always paints a legend at the fieldset's top border edge and notches
   the fieldset border around it, so a border-top on the fieldset would render
   as two stubs either side of the heading. Putting the rule and the space on
   the legend keeps it unbroken and keeps every box in normal flow, which
   matters because one of these groups contains a nested fieldset. */
.field-group + .field-group > legend {
  padding-top: var(--sp-6);
  border-top: 1px solid var(--vb-fog);
}

/* 15.2 Fields */
.field { margin-top: var(--sp-5); }

.field > label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.4;
  color: var(--vb-navy);
}

/* The required marker is decorative: the required attribute is what carries
   the fact to assistive technology, so this is aria-hidden in the markup and
   exists only so a sighted visitor can see which rows are mandatory. */
.field__req {
  margin-left: var(--sp-2);
  font-size: var(--fs-small);
  font-weight: 400;
  letter-spacing: 0.005em;
  color: var(--vb-slate);
}

.field input[type="text"],
.field input[type="email"],
.field input[type="tel"],
.field select,
.field textarea {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--vb-fog);
  border-radius: 4px;
  background-color: var(--vb-mist);
  color: var(--vb-navy);
  font-family: var(--vb-font-body);
  font-size: var(--fs-body);
  line-height: 1.5;
  transition: border-color var(--dur-state) var(--ease),
              background-color var(--dur-state) var(--ease);
}

.field textarea {
  min-height: 8rem;
  resize: vertical;
}

.field select { cursor: pointer; }

.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--vb-steel); }

.field input::placeholder,
.field textarea::placeholder {
  color: var(--vb-steel);
  opacity: 1;
}

/* Native invalid styling only after the browser has actually validated, so a
   pristine empty required field is never painted as an error. The signal is a
   border-weight change as well as a colour change, per brand system 2.4, which
   forbids leaning on colour alone. --vb-signal is deliberately not used here:
   it is capped at one instance per viewport and a form can hold several
   invalid rows at once. */
.field input:user-invalid,
.field select:user-invalid,
.field textarea:user-invalid {
  border-color: var(--vb-navy);
  border-width: 2px;
  background-color: var(--vb-paper);
}

/* Paired short fields sit two up from 768px and stack below it. */
.field-row { display: grid; }

@media (min-width: 768px) {
  .field-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--sp-5);
    align-items: start;
  }
}

/* 15.3 Radio group. Nested inside its parent group, so the inner legend steps
   down to field-label size rather than repeating the group heading treatment. */
.radio-group {
  margin: var(--sp-5) 0 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.radio-group > legend {
  width: 100%;
  padding: 0;
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.4;
  color: var(--vb-navy);
}

.field__help {
  margin-top: var(--sp-2);
  max-width: var(--measure);
  font-size: var(--fs-small);
  letter-spacing: 0.005em;
  color: var(--vb-slate);
}

.radio {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: 44px;
}

.radio:first-of-type { margin-top: var(--sp-3); }

.radio input[type="radio"] {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--vb-blue-deep);
  cursor: pointer;
}

.radio label {
  font-size: var(--fs-body);
  line-height: 1.4;
  color: var(--vb-navy);
  cursor: pointer;
}

/* 15.4 Submit and status */
.intake__submit {
  margin-top: var(--sp-6);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--vb-fog);
}

.intake__submit .btn { width: 100%; }

@media (min-width: 480px) {
  .intake__submit .btn { width: auto; }
}

#intake-status {
  margin-top: var(--sp-4);
  max-width: var(--measure);
  font-size: var(--fs-body);
  color: var(--vb-navy);
}

#intake-status:empty { margin-top: 0; }

#intake-status.is-error { color: var(--vb-navy); font-weight: 600; }
#intake-status.is-ok { color: var(--vb-slate); }

/* 15.5 Visually hidden. Used for the honeypot row, which must stay a real,
   submittable field for a bot to find and fill. Never display: none. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   16. Footer
   -------------------------------------------------------------------------- */

.site-footer {
  background-color: var(--vb-navy-deep);
  color: var(--vb-mist);
  padding-block: var(--sp-8);
}

@media (min-width: 768px) {
  .site-footer { padding-block: var(--sp-9); }
}

.footer__grid {
  display: grid;
  gap: var(--sp-7);
}

@media (min-width: 768px) {
  .footer__grid {
    grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
    gap: var(--sp-6);
  }
}

.footer__tagline {
  margin-top: var(--sp-5);
  font-size: var(--fs-small);
  letter-spacing: 0.005em;
  color: var(--vb-mist);
  max-width: 22rem;
}

.footer__col h2 {
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--vb-mist);
  margin-bottom: var(--sp-4);
}

.footer__col li + li { margin-top: var(--sp-3); }

.footer__col a,
.footer__col span {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-small);
  letter-spacing: 0.005em;
  overflow-wrap: anywhere;
}

.footer__col span { color: var(--vb-mist); }

.footer__legal {
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--vb-navy-soft);
  font-size: var(--fs-small);
  letter-spacing: 0.005em;
  color: var(--vb-mist);
}

/* --------------------------------------------------------------------------
   17. Reduced motion. Every transition drops to zero and smooth scrolling is
       switched off, per brand system 4.6.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

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