/* ============================================================================
   Better Devices — HubSpot form skin

   Restyles the form HubSpot renders into `.hs-form-html` so it matches the
   hand-built forms it replaced. Nothing here changes behaviour: HubSpot owns
   validation, captcha and submission.

   HOW THE EMBED WORKS, AND WHY THESE SELECTORS LOOK LIKE THIS
   -----------------------------------------------------------
   The developer embed fetches a server-rendered blob and injects it. The blob
   is NOT unstyled: it carries two <style> blocks of its own, a base sheet and
   a theme sheet namespaced to

       [id="<instance-id>-r"][data-hsfc-id="Renderer"] …

   That namespace is an id + attribute selector, so (1,1,1) at minimum and
   usually more. A bare `.hsfc-TextInput` loses to it every time. Every rule
   below is therefore anchored on `.bd-hsform [data-hsfc-id="Renderer"]`,
   which is (0,2,1) before the rest of the selector, and the few that still
   tie are marked !important with the reason given inline.

   Class names: `hsfc-*` and `data-hsfc-id` are HubSpot's, not ours, and
   HubSpot can change them in a release. tests/e2e/hubspot-forms.js asserts
   the rendered form still matches the design, so a silent re-skin upstream
   fails the suite rather than quietly shipping an unstyled form.

   The design reproduced here is the one that was in parts/footer.html before
   the migration, kept verbatim:
     label   14px #C7D3DA, 11px below it
     input   transparent, 1px rgba(255,255,255,0.16), radius 8, #F2F1ED,
             15px Hanken Grotesk, padding 14/16, amber focus border
     submit  12px/600, 0.12em tracking, #0A0A0A on #FFFFFF, radius 8,
             padding 16/30, #ffd35e on hover
     rows    two equal columns, 22px gutter, 26px between rows
   ========================================================================== */

/* The wrapper we control. It carries the spacing the old <form> had, so the
   surrounding layout does not shift when the embed replaces it. */
.bd-hsform { margin-top: 34px; }

/* Reserve the height the form will occupy. The embed arrives over the
   network and injects in one go; without a floor the footer collapses and
   then jumps, which is a layout shift on every page of the site. The value
   is the measured height of the form it replaces. */
.bd-hsform:not([data-loaded]) { min-height: 560px; }

/* ── structure ─────────────────────────────────────────────────────────── */
.bd-hsform [data-hsfc-id="Renderer"] { font-family: 'Hanken Grotesk', system-ui, sans-serif; }

.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Form,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Step {
  display: flex; flex-direction: column; gap: 26px;
  background: none; border: 0; padding: 0; margin: 0;
}

/* Rows. HubSpot already groups the fields the way the old form did — Name
   with Email, Company with Role, then Subject and Message on their own — so
   the pairing comes from its markup and is NOT rebuilt here. (First attempt
   assumed one Row per field and rebuilt the grid on .hsfc-Step__Content;
   reading the rendered DOM showed that was wrong and would have produced a
   four-column jumble.)

   Flex, not `grid-template-columns: repeat(auto-fit, minmax(0, 1fr))`. That
   was the first attempt and it is a trap: auto-fit packs in as many tracks as
   FIT, and with a minimum of 0 an unlimited number fit, so a two-field row
   computed to twenty columns. (minmax(200px, 1fr) would bound it, but then
   the breakpoint is the track minimum rather than the site's, and the two
   disagree somewhere in the middle.)

   Flex children with `flex: 1 1 0` divide the row evenly however many there
   are: two fields give two equal columns, one field fills the row. No rule
   per row, and a field added in HubSpot needs nothing here. min-width:0 is
   required because a flex item will not shrink below its content's intrinsic
   width without it, and a long placeholder would push the field out of the
   card. */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Step__Content {
  display: flex; flex-direction: column; gap: 26px;
}
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Row {
  display: flex; gap: 22px; margin: 0; padding: 0;
}
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Row > * { flex: 1 1 0; min-width: 0; }

/* The form carries its own heading and intro ("Get in touch" / "Tell Better
   Devices what the product needs to do."), which is the SAME copy already in
   the template above the form. Showing both prints it twice. The template's
   copy is kept rather than HubSpot's: it is the version under version
   control, it is already translated on the German page, and it does not
   change under us when someone edits the form in HubSpot. */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Row:has(> .hsfc-RichText) { display: none; }

.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextField,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-EmailField,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextareaField {
  display: flex; flex-direction: column; gap: 11px;
}

/* ── labels ────────────────────────────────────────────────────────────── */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-FieldLabel {
  font-size: 14px; font-weight: 400; color: #C7D3DA;
  letter-spacing: 0; text-transform: none; margin: 0; padding: 0;
}

/* ── inputs ────────────────────────────────────────────────────────────── */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextInput,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextareaInput {
  width: 100%;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.16);
  border-radius: 8px;
  color: #F2F1ED;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: 15px;
  padding: 14px 16px;
  outline: none;
  box-shadow: none;
  transition: border-color .3s;
}
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextareaInput { min-height: 112px; resize: vertical; }

.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextInput::placeholder,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextareaInput::placeholder {
  color: rgba(242,241,237,0.42);
}

/* Amber on focus, as everywhere else on the site. !important because the
   namespaced theme sheet sets a focus border of its own at equal weight and
   wins on source order, the embed's <style> being injected into the body
   after this file in the <head>. */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextInput:focus,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextareaInput:focus {
  border-color: #ffbe0b !important;
  box-shadow: none !important;
}

/* Autofill. Chrome paints its own background over the field, which on a dark
   ground produces white-on-white. The inset shadow trick repaints it. */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextInput:-webkit-autofill {
  -webkit-text-fill-color: #F2F1ED;
  -webkit-box-shadow: 0 0 0 1000px #0B0C0E inset;
  transition: background-color 9999s ease-in-out 0s;
}

/* ── errors ────────────────────────────────────────────────────────────── */
/* HubSpot owns validation now, so these messages are the only feedback the
   visitor gets. They were invisible at first: the default colour is near
   black, which on this footer is a message that is technically present and
   practically absent. */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-ErrorAlert,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-NavigationRow__Alerts .hsfc-ErrorAlert {
  color: #ff8a7a; font-size: 13px; line-height: 1.45; margin: 0;
}
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-InfoAlert { color: #c9ccd1; font-size: 13px; }
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-NavigationRow__Alerts:empty { display: none; }
/* The required asterisk. Amber rather than HubSpot's red: on this ground red
   reads as an error state on a field nobody has touched yet. */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-FieldLabel__RequiredIndicator { color: #ffbe0b; }
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextInput[aria-invalid="true"],
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextareaInput[aria-invalid="true"] {
  border-color: #ff8a7a;
}

/* ── submit ────────────────────────────────────────────────────────────── */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-NavigationRow { margin: 0; padding: 0; }
/* HubSpot right-aligns the button; the form it replaces had it hard left.
   align-self on the button alone does nothing about it — the alignment comes
   from justify-content on this wrapper, which is where it has to be undone.

   !important, and this is the general case for this file: HubSpot's theme
   sheet is namespaced [id="…"][data-hsfc-id="Renderer"] .hsfc-X, which is
   (0,3,0). The selector here is .bd-hsform + [data-hsfc-id] + .hsfc-X, also
   (0,3,0). A tie goes to source order, and HubSpot's <style> is injected into
   the body long after this file is linked in the head, so HubSpot always
   wins a tie. Without this the rule computes and loses silently, which is
   precisely what happened on the first pass. */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-NavigationRow__Buttons { justify-content: flex-start !important; }
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Button {
  align-self: flex-start;
  width: auto;
  cursor: pointer;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: 12px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #0A0A0A;
  background-color: #FFFFFF;
  border: none; border-radius: 8px;
  padding: 16px 30px;
  transition: background .3s;
}
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Button:hover { background: #ffd35e; }
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Button[aria-busy="true"] { opacity: .6; cursor: default; }

/* ── the reCAPTCHA badge ───────────────────────────────────────────────── */
/* Google's reCAPTCHA badge.
   HubSpot moves Google's own floating badge INTO the form (it arrives as
   .grecaptcha-badge.hs-badge-moved). It is a 256x60 iframe serving Google's
   blue widget, and because it is an iframe nothing in here can reach its
   interior — the blue is not overridable, only hideable.

   Google's terms allow hiding the badge on the condition that the reCAPTCHA
   attribution is displayed visibly in the form instead. That is exactly what
   is done: the badge is hidden and .bd-hsform-legal below carries the
   required wording with both links. Hiding the badge WITHOUT that text would
   breach the terms, so the two changes belong together and the test asserts
   the text is present and visible. */
.bd-hsform .grecaptcha-badge { display: none !important; }
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-ReCaptchaV3,
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-ReCaptchaV3__Badge__Container { min-height: 0; margin: 0; padding: 0; }
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-Row:has(> .hsfc-ReCaptchaV3) { display: none; }

/* The attribution that replaces the badge. It sits outside the embed, in our
   own markup, so it survives a HubSpot re-skin. Hidden until the form has
   actually rendered: a reCAPTCHA notice under an empty space, on a page
   whose visitor never reached the form, is a statement about nothing. */
.bd-hsform-legal {
  margin: 14px 0 0; font-size: 11px; line-height: 1.5; color: #7d858d;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
}
.bd-hsform-legal a { color: #9aa3ab; text-decoration: underline; text-underline-offset: 2px; }
.bd-hsform-legal a:hover { color: #ffbe0b; }
.bd-hsform:not([data-loaded]) + .bd-hsform-legal { display: none; }

/* ── the success state ─────────────────────────────────────────────────── */
.bd-hsform [data-hsfc-id="Renderer"] .hsfc-ConfirmationMessage,
.bd-hsform [data-hsfc-id="Renderer"] [data-hsfc-id="ConfirmationMessage"] {
  color: #F2F1ED; font-size: 15px; line-height: 1.6;
}

/* ── the contact page ──────────────────────────────────────────────────── */
/* Same form, different skin: on /contact/ the fields are underlines rather
   than boxes (.finD), on a lighter ground. Scoped by a modifier on the
   wrapper so the two never interfere. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-TextInput,
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-TextareaInput {
  border: none;
  border-bottom: 1px solid rgba(242,241,237,0.2);
  border-radius: 0;
  padding: 14px 0;
  font-size: 17px;
  transition: border-color .25s;
}
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-TextInput:focus,
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-TextareaInput:focus {
  border-bottom-color: #ffbe0b !important;
}
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-FieldLabel {
  font-family: 'Hanken Grotesk', system-ui, sans-serif; font-size: 11px;
  letter-spacing: 0.12em; text-transform: uppercase; color: #6B6E74;
}
/* The brief's rows sit 30px apart, not 26px. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-Step__Content { gap: 30px; }

/* The Subject field.
   The HubSpot form carries one; the page it has to match does not, and
   adding a field would change the design this migration exists to keep. It
   is hidden rather than deleted because deleting it is a change in HubSpot,
   not here, and hiding it costs nothing — it submits empty and no one sees
   it. Removing it from the form in HubSpot is the tidier end state; at that
   point this rule simply stops matching and can go. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-Row:has(input[name="0-1/subject"]) { display: none; }

/* The brief's submit button is larger and square, on near-white. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-Button {
  font-size: 15px; font-weight: 700; letter-spacing: 0;
  text-transform: none;
  color: #000000; background-color: #F4F4F4;
  border-radius: 0; padding: 18px 34px;
}
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-Button:hover { background: #ffffff; }

/* ── the Stage chips ───────────────────────────────────────────────────
   HubSpot renders Stage as a radio group: a group label, then four
   (label, input) sibling PAIRS where the label comes FIRST in source. The
   page it replaces drew these as four pressable chips.

   The radio itself is not display:none'd. A hidden-by-display input is not
   focusable, which would make the chips unreachable by keyboard — and
   unlike the old markup, which was <span role="button"> with click handlers
   and no keyboard support at all, a radio group is properly operable with
   the arrow keys. That behaviour is worth keeping, so the input is made
   invisible but still in the layout and still focusable.

   The checked state is drawn on the LABEL while the state lives on the input
   INSIDE it, hence :has(> …).

   That "inside" is worth pinning down, because the server-rendered blob and
   the hydrated DOM disagree. The SSR HTML has label and input as siblings
   with the label first; React then re-nests them, so what a visitor actually
   has is a wrapper div > label > (input + span). The first version of this
   rule was written from the SSR markup with :has(+ …) and silently never
   matched — the chips hovered correctly and simply never showed a selection.
   Read the hydrated DOM, not the blob. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioFieldGroup {
  display: flex; flex-direction: column; gap: 14px;
}
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioFieldGroup__Options {
  display: flex; flex-wrap: wrap; gap: 10px;
}
/* React wraps each option in an anonymous div. display:contents removes it
   from the box tree so the gap above applies between the chips themselves
   rather than between four invisible wrappers. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioFieldGroup__Options > div { display: contents; }
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioInput {
  position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; pointer-events: none;
}
/* The input is absolutely positioned, so the label it sits in has to be the
   containing block — otherwise it escapes to the nearest positioned ancestor
   and the focus ring is drawn somewhere else entirely. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioFieldGroup__Options .hsfc-FieldLabel { position: relative; }
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioFieldGroup__Options .hsfc-FieldLabel {
  cursor: pointer;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: 13px; font-weight: 500; letter-spacing: 0; text-transform: none;
  color: #9A9DA3;
  background: transparent;
  border: 1px solid rgba(242,241,237,0.22);
  border-radius: 0;
  padding: 10px 18px;
  margin: 0;
  transition: color .2s ease, background .2s ease, border-color .2s ease;
}
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioFieldGroup__Options .hsfc-FieldLabel:hover {
  border-color: rgba(242,241,237,0.45); color: #F2F1ED;
}
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-FieldLabel:has(> .hsfc-RadioInput:checked) {
  color: #0A0A0A !important; background: #ffbe0b !important; border-color: #ffbe0b !important;
}
/* The focus ring the hidden input can no longer show for itself. */
.bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-FieldLabel:has(> .hsfc-RadioInput:focus-visible) {
  outline: 2px solid #ffbe0b; outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .bd-hsform--line [data-hsfc-id="Renderer"] .hsfc-RadioFieldGroup__Options .hsfc-FieldLabel { transition: none; }
}

/* ── narrow screens ────────────────────────────────────────────────────── */
/* This file is not media-gated at the <link>, unlike mobile.css, so the
   mobile rules live here behind the site's usual 900px breakpoint. */
@media (max-width: 900px) {
  .bd-hsform [data-hsfc-id="Renderer"] .hsfc-Row { flex-direction: column; gap: 22px; }
  .bd-hsform:not([data-loaded]) { min-height: 720px; }
  .bd-hsform [data-hsfc-id="Renderer"] .hsfc-Button { width: 100%; text-align: center; }
}

/* ── reduced motion ────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextInput,
  .bd-hsform [data-hsfc-id="Renderer"] .hsfc-TextareaInput,
  .bd-hsform [data-hsfc-id="Renderer"] .hsfc-Button { transition: none; }
}
