/* ═══════════════════════════════════════════════════════════════════
   Contact — the page the whole site points at.

   Written in the same language as the rest: nothing is boxed, nothing is
   carded, the only edges on screen belong to the dock in the header.
   The type does the structuring.

   Inherits every token from style.css. The footer is css/footer.css
   unchanged — this file only adds what sits above it.
   ═══════════════════════════════════════════════════════════════════ */

/* ── the header this page uses ────────────────────────────────────── */
/* Taller than the site default because it is carrying the dock. At the
   normal clamp(68px,8vh,92px) a 60px panel would have four pixels of air
   above and below it and read as jammed in rather than set in. */
.nav--dock{
  height:clamp(86px,11vh,108px);
}

/* The wordmark is the first thing to go when the three lockups start
   competing — the mark alone still identifies the page, and the burger
   and the dock both have to stay whole.

   A range, not a ceiling. This was written as max-width:1180px, which
   was right about where the crowding starts and wrong about where it
   stops: below 900px css/dock.css takes the dock out of the bar
   altogether and pins it to the foot of the screen. From there down
   this header holds a mark and a burger, exactly like every other page
   on the site — and every other page shows the name. So the contact
   page was the one place the phone had no wordmark, for a reason that
   had stopped applying two breakpoints earlier. */
@media (min-width:901px) and (max-width:1180px){
  .nav--dock .brand__rule,
  .nav--dock .brand__name{ display:none }
}

/* ── the page ─────────────────────────────────────────────────────── */
.ct{
  padding-inline:var(--gut);
  /* clears the fixed header, which on this page is the tall one */
  padding-top:calc(clamp(86px,11vh,108px) + clamp(3rem,10vh,7rem));
  padding-bottom:clamp(4rem,12vh,8rem);
}

/* ── opening ──────────────────────────────────────────────────────── */
/* The page head is set to the site's page-head step, not to one of its
   own. There are four standalone pages and three of them — about,
   services and this one — open on the same three things in the same
   order: a small letterspaced eyebrow, a display heading, a lede. The
   sizes in css/about-page.css and css/services.css are identical to each
   other to the digit, which is what makes them a step rather than two
   coincidences, and this file is now the third copy of it.

   It used to be its own: the heading was clamp(2rem,5.6vw,4.4rem) and
   the lede clamp(1rem,1.35vw,1.18rem), both a step above the other two.
   That is small on paper — ten per cent on the heading, eleven on the
   lede — and quite plain on screen once you have walked between the
   pages, because the eye is not comparing sizes, it is comparing where
   the writing starts and how big the page's voice is, and this one was
   simply louder than the pages either side of it.

   Only the sizes moved. The heading keeps .display, so it stays upper
   case at line-height .86 and stays the two masked lines the home page
   opens on — that is a voice, deliberately chosen in contact.html, and
   it is not what was out of step. */
.ct__title{
  font-size:var(--fs-display);
  margin-bottom:clamp(1.25rem,3vh,2rem);
}

/* The measure comes with the size; the two are one decision, which is
   why about and services set them together. 34ch was the right column
   for type at 1.18rem and is a narrow one at 1.06 — the same words would
   have gone from four lines to five, so fixing the size alone would have
   left the block looking less like its neighbours than before, not more.
   46ch is what the other two ledes are set to, and it lands this one on
   three lines, as theirs are. */
.ct__lede{
  max-width:46ch;
  font-size:var(--fs-body);
  line-height:1.65;
  color:var(--bone-dim);
  margin-bottom:clamp(2rem,5vh,3.25rem);
}

/* The eyebrow is the shared .eyebrow from css/style.css rather than a
   class of this page's own, because this one carries the rule the home
   page's does and the other two do not. That leaves it at .66rem where
   .ab__eyebrow and .svc__eyebrow are .7 — four hundredths of a rem, and
   the only reason to bother is that it is the first line of the three
   and it sets what the reader expects the other two to be.

   Scoped to .ct so the shared class goes on doing what it does
   everywhere else. The letterspacing is already .32em in style.css, the
   same as the other two, so the size is the whole of the difference. */
.ct .eyebrow{ font-size:var(--fs-micro) }

/* Two buttons, and only one of them solid. The page has a single
   primary action — start the conversation — and a second solid pill
   beside it would make the reader choose between two equals. */
.ct__actions{
  display:flex; flex-wrap:wrap;
  gap:.75rem;
  /* This used to separate the buttons from the routes below them. With
     that block gone it is what holds the page open where the form will
     land — the section would otherwise close straight onto the footer. */
  margin-bottom:clamp(4rem,14vh,9rem);
}

/* ── kept for the enquiry form ────────────────────────────────────── */
/* The routes list and the two founder blocks that used to sit between
   the buttons and the footer were removed; a form is going in their
   place. These three are what that form will want, so they stay:
   the label voice, the painted-underline link, and the touch padding.
   Everything else that styled the removed markup went with it. */

.ct__lbl{
  font-size:var(--fs-micro);
  letter-spacing:.28em;
  text-transform:uppercase;
  color:var(--bronze-lt);
  margin-bottom:.85rem;
}

/* Same painted underline the footer uses — one hover language across
   both, so a reader who has been to the bottom of the home page already
   knows what these do. */
.ct__link{
  display:inline-block;
  padding-block:.35rem;
  color:inherit;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;
  background-origin:content-box;
  background-position:0 100%;
  background-size:0% 1px;
  transition:background-size .3s var(--ease-out), color .3s var(--ease-out);
}
@media (hover:hover) and (pointer:fine){
  .ct__link:hover{ color:var(--bronze-lt); background-size:100% 1px }
}
.ct__link:active{ color:var(--bronze-lt) }
/* 44×44 where a finger is aiming; see the same note in footer.css */
@media (pointer:coarse){
  .ct__link{ padding-block:.7rem }
}

/* ── enquiry form ─────────────────────────────────────────────────── */
/* ═══ the enquiry panel ═══════════════════════════════════════════ */
/* One raised surface holding two columns: who you are writing to on the
   left, the fields on the right. The panel is --ink on a --black page —
   the same half-step the site uses to lift a section off the paper — so
   it reads as a card without a heavy border doing the reading.

   The fields inside it stay hairline-only. Every rule on this site is
   1px of --hair and nothing here earns an exception: a set of bordered
   inputs would be the only closed shapes on the page. */

.ct__panel{
  margin-top:clamp(3.5rem,10vh,6rem);
  max-width:72rem;
  display:grid;
  /* The rail is the narrower column. It carries three short blocks and
     nothing that wraps badly; the fields need the room. */
  grid-template-columns:minmax(0,.78fr) minmax(0,1.22fr);
  gap:clamp(2rem,4.5vw,5rem);
  padding:clamp(1.75rem,3.6vw,3.5rem);
  background:var(--ink);
  border:1px solid var(--hair);
  border-radius:.5rem;
  /* Wide and shallow, thrown well down: paper resting on paper, not a
     card floating over it. One shadow token, so nothing invents its
     own. */
  box-shadow:0 26px 70px -44px var(--lift-deep),
             0 2px 10px -6px var(--lift);
}

/* ── the rail ─────────────────────────────────────────────────────── */
.ct__aside{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
}
.ct__aside-eye{
  display:flex; align-items:center; gap:.7rem;
  margin:0 0 1.1rem;
  font-family:var(--sans);
  font-size:var(--fs-micro); letter-spacing:.22em; text-transform:uppercase;
  color:var(--bronze-lt);
}
.ct__aside-rule{
  display:block; width:clamp(1.5rem,3vw,2.5rem); height:1px;
  background:currentColor; opacity:.6;
}
.ct__form-t{
  font-family:var(--display); font-weight:400;
  font-size:var(--fs-title);
  line-height:1.12;
  margin:0;
  color:var(--bone);
}
.ct__aside-p{
  margin:1rem 0 0;
  max-width:34ch;
  font-size:var(--fs-body); line-height:1.65;
  color:var(--bone-dim);
}

/* Three facts, set as a definition list because that is what they are.
   Ranged left in two lines each so nothing has to align across a gap
   that changes width with the viewport. */
.ct__meta{
  margin:clamp(1.75rem,4vh,2.5rem) 0 0;
  display:grid; gap:.9rem;
}
.ct__meta-r{ display:grid; gap:.15rem }
.ct__meta dt{
  font-family:var(--sans);
  font-size:var(--fs-micro); letter-spacing:.2em; text-transform:uppercase;
  color:var(--bone-faint);
}
.ct__meta dd{
  margin:0;
  font-size:var(--fs-body); line-height:1.45;
  color:var(--bone);
}

/* The two routes that are not the form. A hairline above them because
   they are a different kind of thing from the facts above. */
.ct__direct{
  list-style:none;
  margin:clamp(1.75rem,4vh,2.25rem) 0 0;
  padding:clamp(1.5rem,3vh,1.9rem) 0 0;
  width:100%;
  border-top:1px solid var(--hair);
  display:grid; gap:.9rem;
}
.ct__direct-l{
  display:grid; gap:.15rem;
  color:var(--bone);
  transition:color .3s var(--ease-out);
}
.ct__direct-k{
  font-family:var(--sans);
  font-size:var(--fs-micro); letter-spacing:.2em; text-transform:uppercase;
  color:var(--bone-faint);
  transition:color .3s var(--ease-out);
}
.ct__direct-v{
  font-size:var(--fs-body);
  /* A phone number and an address are read back digit by digit; tabular
     figures keep them from shifting under the eye. */
  font-variant-numeric:tabular-nums lining-nums;
  word-break:break-word;
}
@media (hover:hover) and (pointer:fine){
  .ct__direct-l:hover{ color:var(--bronze-hi) }
  .ct__direct-l:hover .ct__direct-k{ color:var(--bronze-lt) }
}
.ct__direct-l:focus-visible{ outline:2px solid var(--bronze-hi); outline-offset:4px }

/* The firm's line, in the hand the rest of the site signs off in. Sits
   at the foot of the rail so the column ends on a voice rather than on
   another address. */
.ct__sign{
  margin:auto 0 0;
  padding-top:clamp(1.5rem,4vh,2.25rem);
  font-family:var(--script);
  /* Alex Brush runs small for its em and sits high on the line, so it is
     set larger and given room under it that a text face would not need. */
  font-size:var(--fs-head);
  line-height:1.15;
  color:var(--olive-hi);
}

/* ── the fields ───────────────────────────────────────────────────── */
.ct__form{ min-width:0 }

.ct__form-note{
  margin:0 0 clamp(1.5rem,3.5vh,2.25rem);
  font-size:var(--fs-fine);
  color:var(--bone-faint);
}

/* Two up where there is room for two, one where there is not. auto-fit
   rather than a breakpoint: the fields reflow at whatever width 13rem
   stops fitting twice, which is the width the reflow is actually
   about. */
.ct__grid{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));
  gap:clamp(.9rem,2vh,1.35rem) clamp(1.25rem,2.5vw,2rem);
}
.fld--wide{ grid-column:1 / -1 }

/* The field is one object: the label starts down on the line where the
   text will go, and lifts out of the way when there is text to make room
   for. Padding at the top is the space it lifts into — reserved from the
   start, so nothing below moves when it does. */
.fld{
  position:relative;
  margin:0;
  padding-top:1.15rem;
  display:flex; flex-direction:column;
}

.fld__lbl{
  position:absolute;
  left:0;
  /* level with the input's own text, to the pixel: the padding above the
     field plus the field's top padding */
  top:calc(1.15rem + .55rem);
  transform-origin:left top;
  pointer-events:none;            /* the input underneath takes the click */
  font-family:var(--sans);
  font-size:var(--fs-body); line-height:1.5;
  color:var(--bone-faint);
  white-space:nowrap;
  transition:transform .22s var(--ease-out), color .22s var(--ease-out);
}
/* Lifted, and smaller. scale rather than a second font-size, so the move
   is one interpolated transform and not a reflow of the line box. */
.fld__in:focus + .fld__lbl,
.fld__in:not(:placeholder-shown) + .fld__lbl{
  transform:translateY(-1.5rem) scale(.72);
  color:var(--bronze-lt);
}
/* The one warm mark in the form. Red would be the only red on the site
   and would read as an error before anything has gone wrong. */
.fld__req{ color:var(--bronze-lt) }
.fld__opt{
  font-size:var(--fs-fine);
  letter-spacing:.12em; text-transform:uppercase;
  color:var(--bone-faint);
}

.fld__in{
  width:100%;
  /* 16px floor: anything smaller and iOS zooms the page on focus, which
     scrolls the field the reader is typing into out from under them. */
  font-family:var(--sans);
  font-size:var(--fs-body);
  line-height:1.5;
  color:var(--bone);
  background:none;
  border:0;
  border-bottom:1px solid var(--hair);
  border-radius:0;                 /* iOS rounds inputs unasked */
  padding:.55rem 0;
  transition:border-color .3s var(--ease-out);
  -webkit-appearance:none; appearance:none;
}
.fld__in--area{
  resize:vertical;
  min-height:8.5rem;
}
.fld--area{ margin-top:clamp(.35rem,1vh,.75rem) }
.fld--area .fld__lbl{ top:calc(1.15rem + .55rem) }

/* The line under the field, drawn rather than recoloured. A second
   hairline sitting exactly on the first, scaled to nothing and released
   from the left on focus — the field fills in the way a pen would, and
   it is a transform, so it costs nothing to animate.

   --olive-hi, not --olive: this is a hairline carrying a state, and the
   brand green at 1.75:1 would say nothing at 1px. The dark step of the
   same hue reads as green and as 6.2:1. */
.fld__line{
  position:absolute;
  left:0; right:0;
  /* On the input's own underline, not on the floor of the field. The
     error row below is always in the layout — reserved so a message can
     arrive without moving anything — so the input's bottom edge is that
     row's height plus its margin up from the bottom of .fld. Anchored to
     it rather than to the input so the line is not clipped by the
     input's own box. */
  bottom:calc(1.15rem + .4rem);
  /* 2px, not 1: it sits directly on the hairline it replaces, and at the
     same weight the change reads as the line getting slightly greener
     rather than as the field answering. */
  height:2px;
  background:var(--olive-hi);
  transform:scaleX(0);
  transform-origin:left center;
  transition:transform .28s var(--ease-out);
  pointer-events:none;
}

.fld__in:hover{ border-bottom-color:rgba(74,59,41,.42) }
.fld__in:focus{ outline:none }
.fld__in:focus ~ .fld__line{ transform:scaleX(1) }

/* A keyboard needs the ring as well as the line — the line is a state,
   the ring is where you are.

   It goes round the whole field rather than round the input, because the
   label is now above the input rather than beside it: an offset ring on
   the input alone drew its top edge straight through the lifted label.
   Wrapping the row keeps the label inside the thing being outlined,
   which is also what it is — one field, two parts.

   Chrome matches :focus-visible on a text input even for a mouse click,
   so this ring is not keyboard-only in practice. That is why the line
   below it does the quiet work and the ring is kept tight. */
.fld__in:focus-visible{
  outline:2px solid var(--bronze-hi);
  outline-offset:6px;
}
@supports selector(:has(*)){
  .fld__in:focus-visible{ outline:none }
  .fld:has(.fld__in:focus-visible){
    outline:2px solid var(--bronze-hi);
    outline-offset:4px;
    border-radius:4px;
  }
}
.fld__in::placeholder{ color:transparent }   /* the label does this job */

/* ── errors ───────────────────────────────────────────────────────── */
/* The only red on the site, and it is here because a colour that means
   "wrong" cannot be the same one that means "brand". Light enough to
   clear 4.5:1 on --black, which the site's own bronze would not at this
   size. Never the only signal: the message says what to do as well. */
.ct__form{ --danger:#B3261E }

.fld__err{
  display:block;
  /* Reserved whether or not it is filled. A message that appears out of
     nothing pushes every field under it down at the exact moment the
     reader is trying to find what went wrong. */
  min-height:1.15rem;
  margin-top:.4rem;
  font-size:var(--fs-fine);
  line-height:1.15rem;
  color:var(--danger);
}
.fld__in[aria-invalid="true"]{ border-bottom-color:var(--danger) }

/* ── the consent box ──────────────────────────────────────────────── */
/* Drawn, not native: a system checkbox cannot be made to match a page
   set in hairlines. The real input is still there, still focusable and
   still what the form reads — only moved out of sight. */
.fld--check{ margin-top:clamp(1rem,2.5vh,1.5rem) }

.chk{
  display:inline-flex; align-items:flex-start; gap:.85rem;
  cursor:pointer;
  /* comfortably past 44px for a finger without a visible box */
  padding:.6rem 0;
}
.chk__in{
  position:absolute;
  width:1px; height:1px;
  margin:-1px; padding:0; border:0;
  clip-path:inset(50%);
  overflow:hidden; white-space:nowrap;
}
.chk__box{
  flex:0 0 auto;
  display:grid; place-items:center;
  width:20px; height:20px;
  margin-top:.1rem;
  border:1px solid rgba(74,59,41,.45);
  color:var(--black);
  transition:border-color .25s var(--ease-out), background .25s var(--ease-out);
}
.chk__box svg{
  width:13px; height:13px;
  /* Drawn on, not popped in: the tick scales from 0.6 with the box, so
     it arrives as part of the same movement. */
  opacity:0; transform:scale(.6);
  transition:opacity .2s var(--ease-out), transform .2s var(--ease-out);
}
/* Checked is the one place on the page the brand green can be itself
   rather than a darkened version of itself: a 20px square is not type,
   so it is free to be the full #AFCC58. The tick flips to the dark ink
   to sit on it — off-white on this green is 1.75:1 and would leave a
   box that looks checked from across the room and empty up close. */
.chk__in:checked + .chk__box{
  background:var(--olive);
  border-color:var(--olive);
  color:var(--bone);
}
.chk__in:checked + .chk__box svg{ opacity:1; transform:none }
.chk__in:focus-visible + .chk__box{
  outline:2px solid var(--bronze-hi);
  outline-offset:4px;
}
.chk__in[aria-invalid="true"] + .chk__box{ border-color:var(--danger) }
.chk__txt{
  font-size:var(--fs-body);
  line-height:1.45;
  color:var(--bone-dim);
}
@media (hover:hover) and (pointer:fine){
  .chk:hover .chk__box{ border-color:var(--bronze-lt) }
}

/* ── send ─────────────────────────────────────────────────────────── */
/* Right-aligned under the form, which is where the eye finishes. The
   status line shares the row so the answer arrives beside the control
   that asked for it rather than somewhere else on the page. */
.ct__send{
  margin-top:clamp(1.25rem,3vh,2rem);
  display:flex; align-items:center; justify-content:flex-end;
  flex-wrap:wrap;
  gap:1rem clamp(1.5rem,4vw,2.5rem);
}
.ct__status{
  margin:0; margin-right:auto;
  /* Held open so the row does not change height when it fills. */
  min-height:1.3rem;
  font-size:var(--fs-fine); line-height:1.3rem;
  color:var(--bone-dim);
}
.ct__status[data-tone="error"]{ color:var(--danger) }
.ct__status[data-tone="ok"]{ color:var(--bronze-lt) }

/* The submit is the page's own button now — .btn.btn--solid, the same
   pill, bronze and hover wipe as "Message on WhatsApp" at the top of the
   page. It used to be a bespoke underlined word in Marcellus, which made
   two button languages on one screen and left the primary action of the
   page looking lighter than the two secondary ones above it.

   Everything here is what the pill does not already say. */
.ct__submit{
  /* wide enough that the pill reads as the end of the form rather than
     as a chip parked in the corner */
  padding-inline:clamp(1.5rem,4vw,2.4rem);
}
.ct__submit svg{ transition:transform .35s var(--ease-out) }
@media (hover:hover) and (pointer:fine){
  /* the arrow leaves in the direction it points */
  .ct__submit:hover svg{ transform:translateX(3px) }
}

/* Sending: not disabled, because a disabled button loses its focus and
   stops being announced. Held busy instead — aria-busy says what is
   happening, and the pointer says it is not accepting another press. */
.ct__submit[aria-busy="true"]{
  cursor:progress;
  opacity:.72;
}
.ct__submit[aria-busy="true"] svg{ opacity:.4 }

@media (prefers-reduced-motion:reduce){
  .ct__submit svg, .chk__box svg,
  .fld__lbl, .fld__line{ transition:none }
  .ct__submit:hover svg{ transform:none }
}

/* ── entrance ─────────────────────────────────────────────────────── */
/* One pass, on load rather than on scroll: everything here is above the
   fold or just under it, and an observer would fire for all of it in the
   same frame anyway. Staggered by the same 60ms the footer columns use.

   No transform on the title — it is the first thing painted and the
   thing the eye is already on. */
[data-ct-in]{
  opacity:0;
  transform:translateY(14px);
  transition:opacity .55s var(--ease-out), transform .55s var(--ease-out);
  transition-delay:calc(var(--i,0) * 60ms);
}
.is-ready [data-ct-in]{ opacity:1; transform:none }
.no-js [data-ct-in]{ opacity:1; transform:none }

/* ── responsive ───────────────────────────────────────────────────── */
/* The rail stops being a rail. Below this the two columns are each too
   narrow to set a paragraph in, so the panel becomes one column with the
   invitation above the fields — the order it would be read aloud in
   anyway. */
@media (max-width:900px){
  .ct__panel{ grid-template-columns:1fr; gap:clamp(2rem,6vh,3rem) }
  .ct__aside{ max-width:46rem }
  /* On one column the sign-off has the whole width under it and starts
     to read as a heading for the fields rather than as a closing line,
     so it goes. It is still in the footer of every page. */
  .ct__sign{ display:none }
  .ct__direct{ grid-template-columns:repeat(auto-fit,minmax(14rem,1fr)) }
}

@media (max-width:620px){
  /* The send row stops being a row: a pill beside a status line at this
     width leaves neither enough space. */
  .ct__send{ justify-content:stretch }
  .ct__submit{ width:100%; justify-content:center }
  .ct__status{ margin-right:0; text-align:center }
  .ct__actions .btn{ flex:1 1 100%; justify-content:center }
  /* the dock is pinned to the bottom of the window at this width, so the
     end of the page has to stop above it */
  .ct{ padding-bottom:calc(clamp(4rem,12vh,8rem) + 5rem) }
}

/* the fixed bottom dock starts at 900px, not 620 — so does its inset */
@media (max-width:900px){
  .ct{ padding-top:calc(var(--navh) + clamp(3rem,10vh,7rem)) }
  .ftr__legal{ padding-bottom:calc(clamp(1rem,2.4vh,1.5rem) + env(safe-area-inset-bottom) + 5.5rem) }
}

/* ═══ reduced motion ══════════════════════════════════════════════ */
@media (prefers-reduced-motion:reduce){
  [data-ct-in]{ opacity:1; transform:none }
}
