/* ── the contact block ──────────────────────────────────────────────────────
   Text on the left, the four-field form on the right. It used to live in
   tech.css, which meant only the families loading that sheet could render it —
   and the homepage, the page with the most traffic, was not one of them. It is
   its own sheet now: one component, one copy, enqueued wherever a form appears.
   Everything resolves to the design-system tokens in base.css. */
.contact-split { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: stretch; }
.contact-left { display: flex; flex-direction: column; padding-top: 48px; }
.contact-left p { margin-top: auto; margin-bottom: 120px; }
.contact-left h2 { line-height: var(--lh-h2); font-size: var(--fs-h2); font-weight: 400; margin-bottom: 8px; }
/* the frame draws this as the subtitle role in the muted tone (1497:3400), not
   as a second heading — the headline above it is the h3 */
.contact-left h3 { line-height: var(--lh-subtitle); font-size: var(--fs-subtitle); font-weight: 400; color: var(--text-dim); margin-bottom: 0; }
.contact-left p { font-size: var(--fs-p); color: var(--text-dim); }
.form-card { position: relative; background: #fff; border: none; border-radius: var(--r); overflow: hidden; box-shadow: none; }
.form-card::before { content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none; z-index: 2;
    background: linear-gradient(180deg, var(--card-border) 0%, var(--card-border) 42%, rgba(230,235,242,0.35) 74%, rgba(230,235,242,0) 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude; }
.form-body { padding: 32px 32px 32px; }
/* The frame's field is 44 tall on 20px of inset with the value at 16 (1315:2010),
   and the groups sit 24 apart. Ours drew 58 — 18px type on 16px of padding — which
   put the card 42px longer over three fields before anything else was counted.
   44 is also exactly §17's floor for a touch target, so this is as tight as it goes. */
.form-body input, .form-body select, .form-body textarea { width: 100%; font-family: var(--ff); font-size: var(--fs-p-small); color: var(--ink); background: #fff; border: 1px solid var(--card-border); border-radius: var(--r-sm); padding: 11px 20px; margin-bottom: 24px; }
.form-body textarea { min-height: 120px; resize: vertical; padding-top: 14px; }
/* The dropdown draws the site's own chevron — the one in the nav — on the field's
   20px inset. Left to the browser it was the system arrow, a different glyph on every
   platform, pressed against the right border. */
.form-body select { -webkit-appearance: none; appearance: none; padding-right: 48px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='%2304113A' fill-rule='evenodd' clip-rule='evenodd' d='M3.705 5.105a.7.7 0 0 1 .99 0L7 7.41l2.305-2.305a.7.7 0 1 1 .99.99l-2.8 2.8a.7.7 0 0 1-.99 0l-2.8-2.8a.7.7 0 0 1 0-.99Z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 20px center; background-size: 14px 14px; }
.form-body ::placeholder { color: var(--ink); opacity: 0.8; }
/* The amber border marks focus for everyone; the outline is only suppressed for a
   pointer focus. It used to be suppressed for both — and because form.css loads
   after base.css at equal specificity, it beat the :focus-visible ring there and
   left every field on 121 pages with no keyboard focus indicator at all (§17). */
.form-body input:focus, .form-body select:focus, .form-body textarea:focus { border-color: var(--amber); }
.form-body input:focus:not(:focus-visible), .form-body select:focus:not(:focus-visible), .form-body textarea:focus:not(:focus-visible) { outline: none; }
/* One focus indicator, and it is gold. A text field is :focus-visible even under a
   mouse, so base.css's navy ring drew on every click — a thin amber border inside a
   thick navy one. The ring is replaced, not removed: the amber border plus a 3px
   amber halo, which is what a keyboard user gets too (§17). */
.form-body input:focus-visible, .form-body select:focus-visible, .form-body textarea:focus-visible {
  outline: none; box-shadow: 0 0 0 3px rgba(255,189,14,0.35); }

/* form labels — placeholders alone vanish the moment someone starts typing,
   which leaves the field unidentifiable on review and unusable to a screen reader */
.form-body label { display: block; font-size: var(--fs-p-small); font-weight: 600;
  color: var(--ink); margin-bottom: 7px; }
/* the required marker carries meaning, so it cannot be amber-on-white (1.67:1) —
   the system resolves accent-as-text on light backgrounds to navy */
.form-body label .req { color: var(--ink); }
.form-body ::placeholder { color: var(--muted); opacity: 1; }
/* the legal line is the last thing read before submitting, and it was the least
   readable text in the form: 12px of --muted on a phone */
/* The frame's marketing opt-in (unchecked, optional). It sits above the consent
   copy, the way the frame stacks them. 24px of box is short of §17's 44 on its own,
   so the label carries the target: the whole row is clickable, not just the square. */
/* .form-body .form-optin, not .form-optin: `.form-body label` above is 0,1,1 and this
   was 0,1,0, so display:flex, align-items and gap were all being dropped and the label
   stayed a block. What looked like an 8px gap was the whitespace between the tags. */
/* The 44px target comes from 24px of padding under the text rather than a 44px
   min-height (QA): min-height left 22px of slack under a one-line label and none under
   a wrapped one, so the consent line sat 32px below it on desktop and 10px on a phone.
   Now it is ~34px below at every width, and the target is 46px+. */
.form-body .form-optin { display: flex; align-items: flex-start; gap: var(--s-1);
  cursor: pointer; min-height: 0; padding-bottom: var(--s-3); margin-top: 8px; }
/* .form-body label is a field caption — 600 and navy. This one is a sentence beside
   a checkbox, so it takes the supporting tone the frame gives it. */
.form-body .form-optin span { font-size: var(--fs-p-small); font-weight: 400; color: var(--text-dim); line-height: 1.4; }
/* The box matches the text it sits beside — 16px of --fs-p-small — rather than
   standing 4px taller than it. align-items is flex-start, not center, because the
   sentence wraps on a narrow screen and a centred box then floats between the lines.
   2.5px is measured, not derived: the text's ink box is 21px inside a 22.4px line
   box, so the arithmetic answer of 3.2px lands half a pixel low. This centres the
   box on the FIRST line whether the sentence takes one line or three. */
.form-body .form-optin input { width: 16px; height: 16px; flex-shrink: 0; margin: 2.5px 0 0;
  accent-color: var(--navy); cursor: pointer; }
.form-consent { font-size: var(--fs-p-small); color: var(--text-dim); line-height: 1.65; margin: 0 0 24px; }
.form-consent a { color: var(--ink); text-decoration: underline; }

/* ── form submit states ─────────────────────────────────────────────────────
   Replaces the form body once the request is actually accepted, so the visitor
   gets a confirmation instead of the old silence after a mailto: submit. */
.form-state { padding: 8px 0 2px; }
.form-state h3 { line-height: var(--lh-h4); font-size: var(--fs-h4); font-weight: 500; margin-bottom: 8px; }
.form-state p { font-size: var(--fs-p-small); color: var(--text-dim); line-height: var(--lh-p); }
.form-state .btn { margin-top: var(--s-3); }
/* The tick and the headline are one row, centred on each other. The tick used to be
   an inline box nudged by a fixed vertical-align: -6px, which was measured against
   one heading size — so it sat low at every size, and lower still on a phone. */
.form-state.is-ok h3 { display: flex; align-items: center; gap: var(--s-2); }
.form-state.is-ok h3::before { content: "✓"; display: grid; place-items: center; flex-shrink: 0;
  width: 32px; height: 32px; border-radius: 50%; line-height: 1;
  background: var(--amber); color: var(--navy); font-size: var(--fs-h6); }
.form-state.is-err { margin-top: 16px; padding: 16px 24px; border-radius: var(--r-md);
  background: var(--amber-soft); border: 1px solid var(--card-border); }
.form-state.is-err h3 { font-size: var(--fs-h5); line-height: var(--lh-h5); }
.form-state-mail { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
form.is-sending button[type="submit"] { opacity: .6; cursor: progress; }
/* the honeypot must be unreachable for people but present for bots */
.form-body .hp { position: absolute !important; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* the one secondary action, identical on every form: proof + the booking path */

@media (max-width: 940px) {
  .contact-split { grid-template-columns: 1fr; gap: 32px; }
}

/* ── "Prefer another way to talk?" ────────────────────────────────────────────
   The frame keeps this in the copy column beside the card (1338:999), not under
   the submit button: the reply promise, the question, then the two ways to reach
   us as labelled links each carrying its own 16px mark after the words. The page
   had the promise and a "or book a call instead / WhatsApp" aside stacked below
   the button instead, with no icons at all. */
.contact-alt { margin-top: 48px; }
/* Scoped through .contact-alt: .cta-lb-text p and .contact-left p are both (0,1,1)
   and would otherwise win, painting the question the same muted grey as the promise
   above it — the frame has it navy (1338:996), which is what separates the two. */
.contact-alt .ca-reply { font-size: var(--fs-p); line-height: var(--lh-p); color: var(--text-dim); margin: 0; max-width: none; }
/* 24px under the reply promise (QA): it sat 8px above "Prefer another way to talk?",
   so promise, question and links read as one glued block. 0,3,0 because
   `.cta-lightbulb .contact-left p` (0,2,1) zeroes the margin. */
.contact-left .contact-alt .ca-reply { margin-bottom: var(--s-3); }
.contact-alt .ca-lead { font-size: var(--fs-subtitle); line-height: var(--lh-subtitle); color: var(--ink); margin: 0 0 16px; max-width: none; }
.ca-links { display: flex; align-items: center; gap: var(--s-1) var(--s-3); flex-wrap: wrap; }   /* row-gap 8: wrapped, the 24 + the touch padding put the two links 54px apart (QA) */
.ca-link { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-p-small);
  color: var(--ink); text-decoration: underline; text-underline-offset: 4px; }
.ca-ic { width: 16px; height: 16px; flex-shrink: 0; }
.ca-link, .form-consent a, .form-state-mail { transition: text-decoration-color var(--dur-fast); }
.ca-link:hover, .form-consent a:hover, .form-state-mail:hover { text-decoration-color: var(--amber); }
/* 44px of target under a finger, grown with padding so the row keeps its size (§17) */
@media (hover: none) and (pointer: coarse) {
  .ca-link { padding: 12px 0; }
}

/* ── The contact section, as every master frame draws it ──────────────────────
   Tech (1497:3455), Agency (1497:3400), AI (1497:3345), About (1497:3290) and
   Work (1497:3235) are the same 1438x754 band: "Got an idea? Let's make it real."
   over the short-consent form, on the grey gradient. It lived in home.css, which
   only the homepage loads, so the five masters were each carrying their own older
   variant — a page-specific headline, a team photo in the copy column and the long
   three-link consent paragraph. One block now, here, where every family sees it. */
.cta-lightbulb { padding: 96px 0; background: var(--grad-gray); }
/* The Secondary Button here is the Secondary Button. It went yellow on hover once,
   then kept a local copy of the grey hover; both are gone — §09 says the component
   owns its own states and no page overrides them. */
.cta-lb-text h2 { line-height: var(--lh-h2); font-size: var(--fs-h2); font-weight: 400; margin-bottom: 8px; }
.cta-lb-text p { font-size: var(--fs-p); color: var(--text-dim); line-height: 1.7; max-width: 560px; }
.cta-lb-text p + p { margin-top: 24px; }
.cta-lb-text .btn { margin-top: var(--s-5); }
/* No artwork in the copy column — the frame has only the copy and the "another way
   to talk" block. The column reads top-down: 32 under the headline, 24 under the
   subtitle, so .contact-left's push-to-bottom paragraph rules are cancelled here. */
.cta-lightbulb .contact-left { padding-top: 0; }
.cta-lightbulb .contact-left p { margin-top: 0; margin-bottom: 0; }
.cta-lightbulb .contact-left h3 { margin-bottom: 24px; }

/* ── The smallest phones (320–400px) ─────────────────────────────────────────
   Last in the file on purpose: a media query adds no specificity, so these have to
   come after every rule they adjust. At 320 the section's 32px gutter plus the card's
   32px of padding left a 192px field — the form read as cramped and "Send my request"
   needed two rows. The gutter drops to the page's ordinary 24, the card's side padding
   to 16, and the submit button takes the card's full width on one row. */
@media (max-width: 400px) {
  .form-body { padding: 24px 16px; }
  .form-body button[type="submit"] { width: 100%; white-space: nowrap; }
}

/* ── one-click questions (budget, timeline) ─────────────────────────────────
   Optional radios drawn as chips. A chip is a label, so `.form-body label`'s block
   caption style is undone here; the whole chip is the 44px target. */
.form-body .qf-chips { border: 0; padding: 0; margin: 0 0 24px; min-width: 0; }
.form-body .qf-chips legend { padding: 0; font-size: var(--fs-p-small); font-weight: 600; color: var(--ink); margin-bottom: 7px; }
.form-body .qf-chips legend .opt { font-weight: 400; color: var(--text-dim); }
.form-body .chips { display: flex; flex-wrap: wrap; gap: 8px; }
.form-body .chip { display: inline-flex; margin: 0; font-weight: 400; cursor: pointer; position: relative; }
.form-body .chip input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; padding: 0; border: 0; }
.form-body .chip span { display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px; border: 1px solid var(--card-border);
  border-radius: var(--r-pill); background: #fff; color: var(--ink); font-size: var(--fs-p-small); line-height: 1.2;
  transition: background var(--dur-fast) ease, border-color var(--dur-fast) ease; }
.form-body .chip:hover span { border-color: var(--navy); }
.form-body .chip input:checked + span { background: var(--amber); border-color: var(--amber); }
.form-body .chip input:focus-visible + span { box-shadow: 0 0 0 3px rgba(255,189,14,0.35); border-color: var(--amber); }

/* ── what a visitor gets for getting in touch (2026-10-10) ──────────────────
   Three short promises above the reply line, each with a navy tick. */
.contact-alt .ca-offers, .qh-copy .ca-offers { list-style: none; padding: 0; margin: 0 0 var(--s-3); display: grid; gap: var(--s-1); }
.contact-alt .ca-offers li, .qh-copy .ca-offers li { position: relative; padding-left: calc(var(--s-3) + 8px); font-size: var(--fs-p); line-height: 1.45; color: var(--ink); }
.contact-alt .ca-offers li::before, .qh-copy .ca-offers li::before { content: ""; position: absolute; left: 0; top: 4px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--amber) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2304113A' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center / 12px no-repeat; }
