/* ===========================================================================
   ArkBoosted site.css
   Hand-authored. No framework, no build step, no runtime dependencies.

   Direction: ink on paper. Structure comes from alignment, hairlines and
   full-bleed surface bands, never from cards. One typeface (Archivo
   variable); the width axis carries the voice: display is set expanded,
   labels are set condensed.
   =========================================================================== */

/* --- 1. Font ------------------------------------------------------------- */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url('../assets/fonts/archivo-latin-var.woff2') format('woff2-variations'),
       url('../assets/fonts/archivo-latin-var.woff2') format('woff2');
}

/* --- 2. Tokens ----------------------------------------------------------- */

:root {
  /* Surface and ink */
  --paper:     #FCFCFB;
  --ink:       #0C0D0E;
  --ink-2:     #4A4D52;
  --ink-3:     #6E7278;   /* 4.71:1 on paper; labels are 12px normal text */
  --rule:      rgba(12, 13, 14, 0.13);
  --rule-soft: rgba(12, 13, 14, 0.07);

  --band:        #0C0D0E;
  --band-ink:    #F4F4F3;
  --band-ink-2:  #9DA1A6;
  --band-rule:   rgba(244, 244, 243, 0.16);

  --accent: #C2410C;

  /* Type */
  --font: 'Archivo', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;

  --wd-display: 112;   /* expanded: statement voice   */
  --wd-body:    100;   /* normal:   reading voice     */
  --wd-label:    84;   /* condensed: labelling voice */

  /* Geometry */
  --container: 1344px;
  --gutter: 48px;
  --spine: 176px;       /* width of the label column */
  --spine-gap: 40px;
  --rhythm: 112px;      /* vertical section rhythm   */

  /* Motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* One multiplier over the whole entrance choreography. The load sequence is
     interlocked — the mark's fold is timed to land just after the headline
     finishes streaming — so the durations and the delays have to move together
     or the gesture comes apart. Change this one number to retime the site.

     1 is the original pace (hero resolves at ~1.28s). 1.4 lands it at ~1.79s.
     Past ~1.6 the headline is still assembling after the eye has finished
     reading it, which reads as lag rather than as poise.

     Deliberately NOT applied to --t-feedback: hover and focus are responses to
     the visitor, and a slow response feels broken where a slow entrance feels
     considered. */
  --motion-scale: 1.4;

  --t-feedback: 0.15s;
  --t-entrance: calc(0.55s * var(--motion-scale));
}

@media (max-width: 1100px) {
  :root { --gutter: 32px; --spine: 144px; --spine-gap: 32px; --rhythm: 88px; }
}
@media (max-width: 720px) {
  :root { --gutter: 20px; --rhythm: 72px; }
}

/* --- 3. Reset ------------------------------------------------------------ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 96px;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  font-weight: 400;
  font-stretch: 100%;
  line-height: 1.6;
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
img { height: auto; border-radius: 0; }
button { font: inherit; color: inherit; }

a { color: inherit; }

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

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

.skip-link {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 100;
  transform: translateY(-120%);
  background: var(--ink);
  color: var(--paper);
  padding: 12px 20px;
  text-decoration: none;
}
.skip-link:focus { transform: none; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- 4. Type roles ------------------------------------------------------- */

.display {
  font-size: clamp(2.625rem, 5.4vw, 4.75rem);
  font-weight: 545;
  font-stretch: 112%;
  line-height: 0.98;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.headline {
  font-size: clamp(1.875rem, 3.2vw, 2.625rem);
  font-weight: 545;
  font-stretch: 112%;
  line-height: 1.0;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.lead {
  font-size: clamp(1.0625rem, 1.35vw, 1.375rem);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: -0.012em;
  color: var(--ink-2);
  max-width: 54ch;
  text-wrap: pretty;
}

.detail {
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-2);
}

.label {
  display: block;
  font-size: 0.75rem;
  font-weight: 560;
  font-stretch: 84%;
  line-height: 1.2;
  letter-spacing: 0.075em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* --- 5. Layout ----------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--rhythm); }
.section--rule { border-top: 1px solid var(--rule); }

/* The label spine: a narrow label column against a wide content column.
   Every section shares this skeleton; the content inside varies. */
.spine {
  display: grid;
  grid-template-columns: var(--spine) minmax(0, 1fr);
  column-gap: var(--spine-gap);
  align-items: start;
}
.spine > .label { padding-top: 0.45em; }

@media (max-width: 860px) {
  .spine { grid-template-columns: minmax(0, 1fr); row-gap: 20px; }
  .spine > .label { padding-top: 0; }
}

/* Full-bleed dark band */
.band {
  background: var(--band);
  color: var(--band-ink);
}
.band .label { color: var(--band-ink-2); }
.band .lead  { color: var(--band-ink-2); }

/* --- 6. Controls --------------------------------------------------------- */

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 22px 28px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 26px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
  font-size: 0.9375rem;
  font-weight: 560;
  letter-spacing: 0;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t-feedback) ease, color var(--t-feedback) ease;
}
.btn:hover { background: var(--accent); border-color: var(--accent); }

.btn--ghost {
  background: transparent;
  color: var(--ink);
}
.btn--ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* A text link that reads as an action, with a drawn arrow rather than a glyph */
.textlink {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  color: var(--ink);
  padding-bottom: 2px;
  border-bottom: 1px solid var(--rule);
  transition: color var(--t-feedback) ease, border-color var(--t-feedback) ease;
}

/* The underline here is a real border, so padding the link to reach a 44px
   target would push that line away from the text it belongs to. This grows the
   hit area on the vertical axis only and leaves the drawn line where it is.
   Vertical-only because the horizontal neighbour in the hero is the primary
   button, and a wider box would start swallowing taps meant for it. */
.textlink::after {
  content: '';
  position: absolute;
  inset: -9px 0;
}
.textlink:hover { color: var(--accent); border-bottom-color: var(--accent); }
.textlink .arrow { transition: transform var(--t-feedback) ease; }
.textlink:hover .arrow { transform: translateX(3px); }

.band .textlink { color: var(--band-ink); border-bottom-color: var(--band-rule); }

.arrow { width: 13px; height: 13px; flex: none; }

/* --- 7. Header ----------------------------------------------------------- */

.header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}

.header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 76px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  color: var(--ink);
}
.brand__mark { width: 25px; height: 26px; color: var(--ink); }
.brand__name {
  font-size: 1.0625rem;
  font-weight: 640;
  font-stretch: 104%;
  letter-spacing: -0.018em;
}

.nav { display: flex; align-items: center; gap: 34px; }
.nav__link {
  font-size: 0.9375rem;
  font-weight: 450;
  text-decoration: none;
  color: var(--ink-2);
  transition: color var(--t-feedback) ease;
}
.nav__link:hover { color: var(--ink); }

.header__cta { padding: 11px 20px; font-size: 0.875rem; }

.menu-btn {
  display: none;
  align-items: center;
  gap: 9px;
  /* 12px vertical takes the pill to 45px. It is the only way to reach the nav on
     a phone, so it should not be the smallest target on the page. */
  padding: 12px 14px 12px 12px;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 560;
  font-stretch: 84%;
  letter-spacing: 0.075em;
  text-transform: uppercase;
  color: var(--ink);
}
.menu-btn__bars { width: 15px; height: 9px; }
.menu-btn__bars line { stroke: var(--ink); stroke-width: 1.5; }

.mobile-nav {
  display: none;
  border-top: 1px solid var(--rule);
  padding-block: 8px 24px;
}
.mobile-nav[data-open='true'] { display: block; }
.mobile-nav__link {
  display: block;
  padding: 14px 0;
  border-bottom: 1px solid var(--rule-soft);
  font-size: 1.125rem;
  font-weight: 500;
  text-decoration: none;
  color: var(--ink);
}
.mobile-nav .btn { margin-top: 22px; }

@media (max-width: 860px) {
  .header__bar { height: 66px; }
  .nav, .header__cta { display: none; }
  .menu-btn { display: inline-flex; }
}

/* --- 8. Hero ------------------------------------------------------------- */

/* The hero is the one section that does not use the spine. It is the
   statement; the spine is the rhythm of everything under it. */

.hero { padding-block: clamp(44px, 5vw, 72px) 0; }

.hero__title {
  margin-top: clamp(18px, 2vw, 26px);
  font-size: clamp(2.625rem, 6.1vw, 5.125rem);
}
.hero__lead {
  margin-top: clamp(22px, 2.2vw, 30px);
  /* Two columns is 77ch at 1440 and 82ch at 1280, past readable, so the measure
     is capped here rather than left to the grid. The trailing rag this leaves is
     a paragraph rag, not the hole this layout kept hitting: that failure needed
     an empty neighbouring column, and column 3 holds the mark. */
  max-width: 68ch;
}
.hero__actions { margin-top: clamp(28px, 2.8vw, 38px); }

/* The fold runs the same three columns as the fact strip below it, so the two
   halves of the hero share one grid instead of each inventing its own. Copy
   takes columns 1 and 2; the mark takes column 3. The payoff is a single
   vertical line down the right of the fold: the mark's left edge, then the
   "Working together" label, then that rail. Structure a reader can feel.

   The previous 48.2% split was derived to land the mark's flat top under the
   "m" of "customers", a word the headline no longer contains, so it aligned to
   nothing at all. */
.hero__grid {
  position: relative;   /* the mark is positioned against this box */
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--spine-gap);
}

.hero__copy {
  grid-column: 1 / 3;
  /* This was .hero__facts's margin-top, held here so the copy column owns the
     space above the rule and therefore sets where that rule falls. */
  padding-bottom: clamp(44px, 5.4vw, 78px);
}

/* The mark fills the fact strip's third column exactly, so it is bounded by two
   hard edges: its left on that column's line, its right on the container edge.
   Sized smaller it stopped 146px short of the right margin while the rule and
   the rail beneath it ran all the way out, which read as the mark floating.

   Taken out of flow deliberately. At full column width the mark is taller than
   the copy block, so in flow it drove the grid row and left ~180px dead under
   the CTA. Absolute means the copy alone sets the row, the mark's base still
   lands on the fact-strip hairline, and the extra height rises into the
   headline's band instead of pushing the fold open.

   It cannot touch the headline: this column starts at the fact strip's third
   line, which is one --spine-gap clear of the h1's longest line, and the mark's
   ink narrows toward its apex, away from the type. */
.hero__mark {
  position: absolute;
  /* Inset by the system's own column gap on both axes, so the mark sits off the
     rule and off the right margin rather than wedged into the corner. */
  right: var(--spine-gap);
  bottom: var(--spine-gap);
  width: calc((100% - 2 * var(--spine-gap)) / 3);
}
.markdraw { width: 100%; height: auto; color: var(--ink); }

/* Two columns rather than three, with the mark sized to itself so it tucks into
   the lower right of the copy block. Giving it its own full-width row instead
   left a 550px band of nothing beside it at tablet. Both anchors survive. */
@media (max-width: 980px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr) auto; }
  .hero__copy { grid-column: 1; }
  /* Back into flow: there is no third column to fill here, and the headline
     wraps close enough that an out-of-flow mark would land on top of it. */
  .hero__mark {
    position: static;
    grid-column: 2;
    align-self: end;
    width: clamp(112px, 17vw, 150px);
  }
}

/* Below this a side column starves the lead, so the mark takes its own row.
   Still hard right and still on the rule: left-aligned under the CTA, which is
   where it sat before, it read as an orphan. */
@media (max-width: 640px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr); row-gap: 32px; }
  .hero__copy { grid-column: 1; padding-bottom: 0; }
  .hero__mark {
    grid-column: 1;
    justify-self: end;   /* nothing to key to horizontally once the h1 reflows */
    /* The mark has its own row here, so the lift has no headline to tuck into
       and only eats the row gap, leaving it 2px under the CTA. */
    margin-top: 0;
    width: clamp(88px, 26vw, 112px);
  }
}

/* The fact strip: real information holding the bottom of the fold. Its rule is
   also what the mark bottoms out on, so the space above it is carried by
   .hero__copy's padding rather than a margin here. */
.hero__facts {
  padding-top: 22px;
  padding-bottom: clamp(44px, 5vw, 72px);
  border-top: 1px solid var(--ink);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--spine-gap);
  row-gap: 36px;
}

.rail__items { margin-top: 14px; }
.rail__items li {
  font-size: 0.9375rem;
  line-height: 1.35;
  color: var(--ink-2);
  padding-block: 8px;
  border-bottom: 1px solid var(--rule-soft);
  overflow: hidden;      /* the mask each row rolls up through */
}
.rail__items li:last-child { border-bottom: 0; }

/* Rows roll up from behind their own rule, sweeping diagonally across the
   three columns. Triggered once, on entry. Never loops. */
[data-roll] li > span { display: block; }

html.js [data-roll] li > span {
  transform: translateY(115%);
  opacity: 0;
}
html.js [data-roll].roll--on li > span {
  animation: roll-up calc(0.62s * var(--motion-scale)) var(--ease) forwards;
  animation-delay: calc((var(--row) * 70ms + var(--col) * 55ms) * var(--motion-scale));
}

@keyframes roll-up {
  to { transform: none; opacity: 1; }
}

/* Three columns hold down to tablet; below that a single column reads better
   than an orphaned third group. */
@media (max-width: 640px) {
  .hero__facts { grid-template-columns: minmax(0, 1fr); row-gap: 28px; }
}

/* --- 9. Trust band -------------------------------------------------------- */

/* A customer speaking, on the first dark surface. The width axis carries the
   distinction: ArkBoosted's own voice is expanded, a person's voice is not. */
.quote {
  font-size: clamp(1.4375rem, 3vw, 2.5rem);
  font-weight: 400;
  font-stretch: 100%;
  line-height: 1.28;
  letter-spacing: -0.018em;
  max-width: 22em;
  text-wrap: pretty;
  --stream-step: 22ms;
}

.quote__foot {
  margin-top: clamp(34px, 4vw, 52px);
  padding-top: 20px;
  border-top: 1px solid var(--band-rule);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px 40px;
}

.quote__who {
  font-size: 0.9375rem;
  font-weight: 560;
  color: var(--band-ink);
}
.quote__source { margin-top: 5px; }

@media (max-width: 560px) {
  .quote__foot { display: block; }
  .quote__foot .textlink { margin-top: 22px; }
}

/* --- 10. Services --------------------------------------------------------- */

/* An open list with an accordion's typography: big headings, full-width rules,
   generous rows. Nothing collapses, because hiding the answer to "what do you
   do" behind a click is friction on a page whose job is to get an enquiry. */
/* Equal columns so the description fills its measure and the list sits directly
   beside it. An unequal split leaves a void between the two. */
.svc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: var(--spine-gap);
  border-top: 1px solid var(--rule);
  padding-block: clamp(30px, 3.2vw, 46px);
}
.svc:first-of-type { margin-top: clamp(36px, 3.8vw, 54px); }
.svc:last-of-type { border-bottom: 1px solid var(--rule); }

.svc__head {
  font-size: clamp(1.375rem, 2.2vw, 1.875rem);
  font-weight: 545;
  font-stretch: 112%;
  line-height: 1.08;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.svc__text {
  margin-top: 16px;
  color: var(--ink-2);
}

.svc__list { margin-top: 4px; }
.svc__intro { margin-top: 18px; }

/* Section intros stop on the first column's right edge. At the global 54ch they
   landed 20 to 98px past it, which reads as a miss rather than a measure, since
   the hairlines below them all break at that same edge. Every grid under a
   .spine is two equal columns with --spine-gap, so one expression covers all. */
.svc__intro,
.split__intro {
  max-width: calc((100% - var(--spine-gap)) / 2);
}

@media (max-width: 860px) {
  .svc { grid-template-columns: minmax(0, 1fr); row-gap: 24px; }
  .svc__head { max-width: none; }
  /* One column below here, so there is no edge to align to. */
  .svc__intro,
  .split__intro { max-width: 54ch; }
}

/* --- 11. Selected work ---------------------------------------------------- */

/* Same two-column, hairline-topped row as .svc, because these are the same
   kind of object: a claim on the left, its evidence on the right. Here the
   evidence is the client's own review, so it gets the larger type. */
.work {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: var(--spine-gap);
  border-top: 1px solid var(--rule);
  padding-block: clamp(30px, 3.2vw, 46px);
}
.work:first-of-type { margin-top: clamp(36px, 3.8vw, 54px); }
.work:last-of-type { border-bottom: 1px solid var(--rule); }

/* Below the hero quote and below .headline, so a customer speaking reads as
   substance without competing with the section's own voice. Normal width and
   weight 400: the width axis is what separates a person from the brand. */
.work__quote {
  font-size: clamp(1.125rem, 1.55vw, 1.4375rem);
  font-weight: 400;
  font-stretch: 100%;
  line-height: 1.4;
  letter-spacing: -0.014em;
  text-wrap: pretty;
}

.work__by {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--rule-soft);
}
.work__who {
  display: block;
  font-size: 0.9375rem;
  font-weight: 560;
}
.work__by .label { margin-top: 4px; }

.work__verify { margin-top: clamp(28px, 3vw, 40px); }

@media (max-width: 860px) {
  .work { grid-template-columns: minmax(0, 1fr); row-gap: 24px; }
}

/* --- 12. The honest split ------------------------------------------------- */

/* Two ruled lists on the dark surface. The structure is the argument, so there
   is nothing here but two labels and eight lines. */
.split {
  margin-top: clamp(44px, 4.6vw, 64px);
  padding-top: clamp(30px, 3.2vw, 44px);
  border-top: 1px solid var(--band-rule);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--spine-gap);
  row-gap: 36px;
}
.split__intro { margin-top: 18px; }

/* Spans both columns and introduces them, so the two lists are not the first
   thing after a rule with no explanation. Full ink at the claim weight: it is the
   thesis the two lists divide up, and at --band-ink-2 it rendered dimmer than both
   the stage claims above it and the list rows below it — the one line that frames
   the block was the faintest thing in it. */
.split__lead {
  grid-column: 1 / -1;
  margin-bottom: 8px;
  font-weight: 545;
  color: var(--band-ink);
  max-width: 62ch;
}

.split .rail__items { margin-top: 14px; }
.split .rail__items li {
  color: var(--band-ink);
  border-bottom-color: var(--band-rule);
}

@media (max-width: 640px) {
  .split { grid-template-columns: minmax(0, 1fr); row-gap: 28px; }
}

/* --- 13. Approach --------------------------------------------------------- */

/* Two by two, and lighter than .svc on purpose. Services is already a big
   three-row typographic list; a second one at the same weight would read as
   the page repeating itself rather than moving on. */
.steps {
  margin-top: clamp(36px, 3.8vw, 52px);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--spine-gap);
  row-gap: clamp(30px, 3.2vw, 44px);
}
.steps > li {
  border-top: 1px solid var(--rule);
  padding-top: 18px;
}

.steps__num { margin-bottom: 12px; }

.steps__head {
  font-size: 1.25rem;
  font-weight: 545;
  font-stretch: 112%;
  line-height: 1.15;
  letter-spacing: -0.018em;
}

/* The claim a skimmer takes away, at full ink so it separates from the evidence
   below it without needing a second type size. Its own rule rather than a weight
   override on .steps__text, because the two lines differ in job, not in emphasis. */
.steps__claim {
  margin-top: 12px;
  font-weight: 545;
  color: var(--ink);
}

.steps__text {
  margin-top: 10px;
  color: var(--ink-2);
}

/* Binds the evidence to the claim it belongs to: tighter than the gap above the
   claim, so the pair reads as one block rather than two loose paragraphs. */
.steps__claim + .steps__text { margin-top: 7px; }

/* The steps live on the dark surface. Without these the hairlines are near
   invisible and the body copy is #4A4D52 on #0C0D0E, which fails contrast. */
.band .steps > li { border-top-color: var(--band-rule); }
.band .steps__claim { color: var(--band-ink); }
.band .steps__text { color: var(--band-ink-2); }

@media (max-width: 640px) {
  .steps { grid-template-columns: minmax(0, 1fr); }
}

/* --- 14. Contact ---------------------------------------------------------- */

/* Form left, everything you might want instead of a form on the right. */
.contact {
  margin-top: clamp(36px, 3.8vw, 52px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: var(--spine-gap);
  row-gap: clamp(44px, 4.6vw, 64px);
}

.form__row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--spine-gap);
}

/* min-width: 0, or the input's intrinsic size (browsers default to size=20)
   refuses to shrink below the grid track and overflows it. */
.field { min-width: 0; margin-bottom: 26px; }
.field .label { margin-bottom: 8px; }

/* Underlined rather than boxed. A hairline under a field is the same device
   the rest of the page uses to separate things, where a bordered box would be
   the one rounded card on a site that has none. */
.field input,
.field select,
.field textarea {
  width: 100%;
  /* 10px rather than 8px: at 8px every control measured 43px, a hair under the
     44px touch minimum, on the one form that earns the site its money. */
  padding: 10px 0;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  transition: border-color var(--t-feedback) ease;
}
.field textarea { resize: vertical; min-height: 92px; }
.field select { appearance: none; cursor: pointer; }

/* The budget field is the only one with a placeholder. Without this it inherits
   the UA grey, which is the one text colour on the page nobody chose. --ink-3 is
   the documented 4.71:1 muted value, so the hint stays legible while reading as
   a hint rather than as an entered answer. */
.field input::placeholder { color: var(--ink-3); opacity: 1; }

.field input:hover,
.field select:hover,
.field textarea:hover { border-bottom-color: var(--ink-2); }

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-bottom-color: var(--ink);
}

/* The honeypot, hidden the same way production has always hidden it. An
   off-screen version at left:-9999px works too, but it trips the review
   harness's overflow check on every run, and a permanently noisy check is one
   nobody reads. */
input[name="_honey"] { display: none; }

.form__actions { margin-top: 10px; }
.form__note { color: var(--ink-3); }

.contact__block + .contact__block { margin-top: clamp(32px, 3.4vw, 44px); }
.contact__block .rail__items { margin-top: 14px; }
.contact__text { margin-top: 12px; }

.contact__side a { text-decoration: none; }
.contact__side a:hover { color: var(--accent); }

/* The email and booking links are the shortest path to a lead on this page, and
   as inline anchors their tap target was the 16px height of the text itself. The
   row already reserves the space; the link just was not claiming it. Moving the
   padding from the row onto the anchor makes the whole row tappable at 44px.

   :has() rather than a positional selector because the condition is "rows that
   contain a link" — the engagements block below has none. Where :has() is
   unsupported the row keeps its own padding and the target simply ends up
   taller, which fails safe. */
.contact__side .rail__items li:has(a) { padding-block: 0; }
.contact__side .rail__items li > a { display: block; padding-block: 12px; }

@media (max-width: 860px) {
  .contact { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .form__row { grid-template-columns: minmax(0, 1fr); }
}

/* --- 15. Footer ---------------------------------------------------------- */

.footer { border-top: 1px solid var(--rule); padding-block: 44px 52px; }
.footer__row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px 40px;
}
.footer__legal { display: flex; flex-wrap: wrap; gap: 4px 22px; margin-block: -6px; }
.footer__legal a { padding-block: 6px; }
.footer a { color: var(--ink-2); text-decoration: none; }
.footer a:hover { color: var(--ink); }

/* --- 11. Motion ---------------------------------------------------------- */

/* The statement streams in a word at a time, and the rest of the hero arrives
   under it. One orchestrated moment on load. Nothing loops.

   Progressive enhancement: the hidden state is scoped to html.js, so with
   JavaScript off the text is simply there. */
.stream .w { display: inline-block; }

html.js .stream .w {
  opacity: 0;
  transform: translateY(0.36em);
}
html.js .stream--on .w {
  animation: stream-in calc(0.6s * var(--motion-scale)) var(--ease) forwards;
  animation-delay: calc(var(--i) * var(--stream-step, 52ms) * var(--motion-scale));
}

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

/* The mark draws itself, then resolves into the solid form. The stroked paths
   are the real geometry, so the drawing and the fill register exactly.
   Default state, and the state with JavaScript off, is simply the solid mark. */
.markdraw__line { opacity: 0; }

html.js .markdraw__fill { opacity: 0; }
html.js .markdraw__line {
  opacity: 1;
  /* viewBox units, so this scales with the mark. 4 holds ~5px on screen at the
     column-3 size; 5 read heavy once the mark grew past 320px. */
  stroke-width: 4;
  stroke-linejoin: round;
  stroke-linecap: round;
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
}

/* The draw starts with the word stream rather than after it, and the arrow
   begins while the contour is still running, so the two are one gesture. The
   fold then settles after the stream finishes, and the last thing moving is a
   cross-fade rather than a stroke.

   Every value here is expressed at --motion-scale: 1 and multiplied, so the
   interlock holds at any pace. At 1 the fold lands at 1.28s against the stream's
   0.86s; at the current 1.4, 1.79s against 1.20s. */
html.js .markdraw--on .markdraw__line--body {
  animation: mark-draw calc(0.9s * var(--motion-scale)) var(--ease)
                       calc(0.08s * var(--motion-scale)) forwards,
             mark-hide calc(0.3s * var(--motion-scale)) ease
                       calc(0.98s * var(--motion-scale)) forwards;
}
html.js .markdraw--on .markdraw__line--arrow {
  animation: mark-draw calc(0.35s * var(--motion-scale)) var(--ease)
                       calc(0.68s * var(--motion-scale)) forwards,
             mark-hide calc(0.3s * var(--motion-scale)) ease
                       calc(0.98s * var(--motion-scale)) forwards;
}
html.js .markdraw--on .markdraw__fill {
  animation: mark-show calc(0.3s * var(--motion-scale)) ease
                       calc(0.98s * var(--motion-scale)) forwards;
}

@keyframes mark-draw { to { stroke-dashoffset: 0; } }
@keyframes mark-hide { to { opacity: 0; } }
@keyframes mark-show { from { opacity: 0; } to { opacity: 1; } }

.enter {
  opacity: 0;
  transform: translateY(14px);
  animation: enter var(--t-entrance) var(--ease) forwards;
}
.enter--1 { animation-delay: calc(0.02s * var(--motion-scale)); }
.enter--2 { animation-delay: calc(0.10s * var(--motion-scale)); }
.enter--3 { animation-delay: calc(0.18s * var(--motion-scale)); }
.enter--4 { animation-delay: calc(0.26s * var(--motion-scale)); }
.enter--5 { animation-delay: calc(0.34s * var(--motion-scale)); }

@keyframes enter {
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .enter { opacity: 1; transform: none; animation: none; }
  html.js .stream .w,
  html.js .stream--on .w { opacity: 1; transform: none; animation: none; }
  html.js [data-roll] li > span,
  html.js [data-roll].roll--on li > span {
    opacity: 1; transform: none; animation: none;
  }
  html.js .markdraw__fill { opacity: 1; animation: none; }
  html.js .markdraw__line { opacity: 0; animation: none; }
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}
