/* ============================================================
   EcoRed — landing page
   Implementation of "EcoRed Landing.dc.html" (Claude Design)
   ============================================================ */

:root {
  --ink:   #50190C;  /* deep laterite brown — page ground */
  --cream: #FEFBE9;  /* bone white — primary text on ink */
  --red:   #B13518;  /* EcoRed signal red */
  --tan:   #BB936D;  /* muted clay — eyebrows, meta */
  --sand:  #D9BFA6;  /* light clay — secondary on ink */
  --blush: #F7DECB;  /* warm tint — secondary on red */
  --mocha: #7A4A33;  /* body copy on cream */
  --bone:  #F2E9D5;
  --cocoa: #EFE3CF;

  /* Focus ring colour. Reset per-ground further down; see :focus-visible. */
  --focus-ring: var(--cream);

  --gutter: clamp(20px, 3.33vw, 48px);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--cream);
  font-family: 'Rubik', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

a { color: inherit; text-decoration: none; transition: color 0.25s; }
a:hover { color: var(--tan); }

img, video { display: block; max-width: 100%; }

/* ── Focus ring ────────────────────────────────────────────
   2.4.11 asks for 3:1 against whatever sits next to the indicator, so the
   ring colour has to follow the ground it lands on. --red only works on
   cream: it is 2.28:1 on --ink and 1:1 inside the red .endcta band, i.e.
   invisible. --cream is 13.59:1 on ink and 5.95:1 on red, so one value
   covers both dark grounds and becomes the default; cream grounds flip
   back to red, also 5.95:1 (contrast is symmetric — same pair of colours,
   necessarily the same ratio).

   Carried as an inherited custom property rather than a pile of
   descendant selectors. A ground sets it once and every focusable
   descendant picks it up however deeply it is nested, which is what stops
   a section moving in the markup from silently reintroducing a red ring
   on red.

   currentColor was the other candidate and does not survive: `a:hover`
   goes to --tan, which is 2.21:1 on red, so a hovered-and-focused link in
   the end CTA would fail exactly where the old ring did. It would also
   tie the ring to the 0.25s colour transition on links.

   No border-radius here, deliberately. It applied to the focused ELEMENT
   rather than to the outline, so square things — .card most visibly — got
   2px corners the moment they took focus. Browsers already follow the
   element's own radius when they draw the outline. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
}

/* Cream grounds. The default would disappear into these. .services and
   .contact matter as much as .wrap--cream: the first holds the .service
   cards, the second a pill and two bare links. Add any new cream section
   here. */
.wrap--cream,
.mission,
.services,
.contact { --focus-ring: var(--red); }

/* Red grounds restating the default. Every one of these is currently a
   top-level sibling of the cream sections above, so they already inherit
   cream from :root and this is a no-op today. It is written down because
   the failure mode if one ever moves inside a .wrap--cream is a red ring
   on red — silent, and the precise bug this block exists to prevent. */
.products,
.stat,
.endcta { --focus-ring: var(--cream); }

/* Broken / absent media collapses instead of showing a broken-image glyph. */
.is-missing { visibility: hidden; }


/* ── Brand mark ────────────────────────────────────────────
   The real ECORED lockup, inline so it inherits currentColor.
   Ratio is 7023:1668 from the source artwork. */
.mark { display: block; width: auto; fill: currentColor; }
.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;
}
.topbar__mark .mark { height: clamp(17px, 1.5vw, 22px); }
.hero__wordmark .mark { height: clamp(56px, 11.6vw, 210px); }

/* ── Shared atoms ─────────────────────────────────────────── */

.tag {
  display: inline-block;
  font-size: 14px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 8px 14px;
  /* The label carries its own gap. .section__title has no margin-top, so on
     every interior section the chip sat flush against the heading — 0px,
     measured across 44 of the 61 uses. */
  margin-bottom: clamp(18px, 2vw, 26px);
}

/* Two kinds of exception, both of which already space themselves.
   Beside, not below — the next element is a flex sibling in a row: */
.mission .tag,
.products__head .tag { margin-bottom: 0; }
/* Followed by a heading that already supplies its own margin-top, so the
   label's margin would stack on top of it and double the gap: */
.masthead__inner .tag,
.arch__intro .tag,
.services .tag,
.contact__lead .tag { margin-bottom: 0; }
.tag--red   { background: var(--red);   color: var(--cream); }
.tag--cream { background: var(--cream); color: var(--red); }
.tag--tan   { background: var(--tan);   color: var(--ink); }

.dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  flex: none;
}
.dot--red   { background: var(--red);   color: var(--cream); }
.dot--cream { background: var(--cream); color: var(--red); width: 22px; height: 22px; font-size: 13px; }
.dot--lg    { width: 34px; height: 34px; background: rgba(177, 53, 24, 0.95); }
.dot--xl    { width: clamp(44px, 3.9vw, 56px); height: clamp(44px, 3.9vw, 56px); font-size: 24px; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  border-radius: 999px;
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 500;
  transition: transform 0.3s var(--ease), filter 0.3s;
}
.pill:hover { transform: translateY(-2px); filter: brightness(1.08); color: inherit; }
.pill--red { background: var(--red); color: var(--cream); padding: 15px 28px; }
.pill--ink { background: var(--ink); color: var(--cream); padding: 16px 30px; font-size: 15px; }

.speclist {
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: clamp(14px, 1.15vw, 16px);
}
.speclist li {
  display: flex;
  gap: 12px;
  align-items: baseline;
}
.speclist li::before {
  content: '\25A0';
  flex: none;
  color: currentColor;
}

/* Image drop-slot: replaced by a real photo via `data-img`, otherwise
   renders a labelled placeholder so layout is never ambiguous. */
.slot {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(187, 147, 109, 0.10);
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px rgba(187, 147, 109, 0.35);
  color: var(--tan);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.6;
}
.slot.has-image { box-shadow: none; }
.slot.has-image::after { content: none; }
.slot::after { content: attr(data-label); }

/* ── Hero ─────────────────────────────────────────────────── */

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  background: var(--ink);
  color: var(--cream);
}
.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Both layers are anchored in PIXELS from the edge they protect, not in
     percentages of the hero.

     Percentages were the bug. The hero is a bottom-anchored flex column, so
     the text block occupies the bottom 28% of the frame at 375x812 but 52%
     at 1440x640 — a %-based ramp slides off the text as the window changes
     shape. The previous version measured a 1.97:1 minimum on the tagline at
     1440x640 because its ramp was still light where the text had risen to.

     Anchored in px, each layer tracks what it protects at every viewport,
     and everything between them is left as clean video — which is the point:
     the middle of the frame now carries no wash at all.

     Top layer: the chrome's worst case is video pixel [229,172,139] (L=0.480)
     45px down, needing alpha >= 0.415; the guard holds >= 0.48 through 100px,
     which covers the topbar at every width (66px tall here, 76px on phones),
     then is gone by 160px. It is the one band that had to get DARKER: cream
     nav on raw video measured 1.70:1, a real WCAG failure.

     Bottom layer: the tagline's worst case is video pixel [233,177,144]
     (L=0.508) sitting 212px up from the hero's bottom edge; 4.5:1 against
     cream needs alpha >= 0.435 there. The stops below hold >= 0.47 across
     the whole 0-360px band the text occupies, and reach zero by 460px. */
  background:
    linear-gradient(to bottom,
      rgba(40, 12, 6, 0.56) 0,
      rgba(40, 12, 6, 0.48) 100px,
      rgba(40, 12, 6, 0)    160px),
    linear-gradient(to top,
      rgba(40, 12, 6, 0.72) 0,
      rgba(40, 12, 6, 0.62) 140px,
      rgba(40, 12, 6, 0.52) 260px,
      rgba(40, 12, 6, 0.46) 360px,
      rgba(40, 12, 6, 0)    460px);
}
.hero__spacer { flex: 1 1 auto; }

.topbar {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  padding: clamp(18px, 2vw, 28px) var(--gutter);
}
.topbar__mark { display: block; color: var(--cream); }
/* The panel only exists to give the mobile toggle something to collapse.
   Above the breakpoint it dissolves so the nav and CTA stay direct flex
   children of .topbar and keep their original space-between placement. */
.topbar__panel { display: contents; }
.topbar__nav {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(14px, 2.5vw, 36px);
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.topbar__cta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.hero__lede {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px;
  padding: 0 var(--gutter) clamp(12px, 2vh, 24px);
}
.hero__tagline {
  max-width: min(620px, 86vw);
  font-size: clamp(19px, 1.9vw, 28px);
  line-height: 1.5;
  font-weight: 300;
  text-shadow: 0 1px 14px rgba(40, 12, 6, 0.5);
  text-wrap: pretty;
}

.hero__wordmark {
  position: relative;
  z-index: 2;
  margin: 0 0 0 -0.045em;
  padding: 0 var(--gutter) clamp(20px, 3vh, 40px);
  line-height: 0;
  color: var(--cream);
}

/* ── Mission ──────────────────────────────────────────────── */

.mission {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(32px, 5vw, 80px);
  padding: clamp(56px, 8vw, 100px) var(--gutter);
  background: var(--cream);
  color: var(--ink);
}
.mission .tag { flex: 0 0 auto; align-self: flex-start; }
.mission__body { flex: 1 1 520px; }
.mission__headline {
  font-size: clamp(30px, 3.6vw, 52px);
  line-height: 1.18;
  font-weight: 400;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}
.mission__note {
  margin-top: clamp(24px, 3vw, 40px);
  max-width: 680px;
  font-size: clamp(17px, 1.35vw, 20px);
  line-height: 1.6;
  font-weight: 300;
  color: var(--mocha);
}

/* ── Vision ───────────────────────────────────────────────── */

.vision {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  background: var(--ink);
  padding: clamp(40px, 8vh, 64px) var(--gutter) clamp(28px, 5vh, 44px);
}
.vision__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.vision__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(80, 25, 12, 0.5)  0%,
    rgba(80, 25, 12, 0.15) 45%,
    rgba(80, 25, 12, 0.45) 100%);
}
.vision > :not(.vision__video):not(.vision__scrim) { position: relative; z-index: 2; }
.vision__spacer { flex: 1 1 auto; }

.vision__eyebrow {
  font-size: 14px;
  line-height: 1.6;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-shadow: 0 1px 12px rgba(80, 25, 12, 0.7);
}
.vision__headline {
  margin: clamp(24px, 4vh, 40px) auto 0 0;
  max-width: min(820px, 92vw);
  font-size: clamp(26px, 3.75vw, 54px);
  line-height: 1.16;
  font-weight: 400;
  letter-spacing: -0.015em;
  text-shadow: 0 2px 24px rgba(80, 25, 12, 0.6);
  text-wrap: pretty;
}
.vision__foot {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 18px;
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-shadow: 0 1px 12px rgba(80, 25, 12, 0.7);
}
.vision__motto { display: flex; align-items: center; gap: 14px; }
.vision__origin { text-align: right; line-height: 1.6; }

/* ── Products (sticky two-panel scroller) ─────────────────── */

.products {
  position: relative;
  width: 100%;
  height: 280vh;
  background: var(--red);
  color: var(--cream);
}
.products__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(24px, 3vw, 56px);
  padding: clamp(32px, 5vh, 64px) var(--gutter);
}

.products__shot {
  position: absolute;
  z-index: 1;
  pointer-events: none;
  object-fit: contain;
  transition: transform 0.8s var(--ease);
}
.products__shot--bag {
  right: -9vw;
  top: -8vh;
  height: 118vh;
  width: min(80vw, 1350px);
  object-position: right center;
  transform: translateX(0);
}
.products__shot--brick {
  right: -6vw;
  top: 50%;
  width: min(70vw, 1200px);
  max-height: 96vh;
  transform: translateY(-50%) translateX(-190%);
}
/* Stage 2: bag exits left, brick enters. */
.products.is-stage2 .products__shot--bag   { transform: translateX(-190%); }
.products.is-stage2 .products__shot--brick { transform: translateY(-50%) translateX(0); }

.products__panel {
  position: relative;
  z-index: 2;
  flex: 0 1 620px;
  min-width: min(88vw, 340px);
  display: flex;
  flex-direction: column;
}
.products__head { display: flex; align-items: center; gap: 18px; }
.products__count {
  font-size: 14px;
  letter-spacing: 0.12em;
  color: var(--blush);
}
.products__track {
  width: min(280px, 60%);
  height: 3px;
  margin-top: 18px;
  background: rgba(254, 251, 233, 0.25);
}
.products__bar {
  height: 100%;
  width: 50%;
  background: var(--cream);
  transition: width 0.5s;
}

.products__slides {
  position: relative;
  flex: 1 1 auto;
  margin-top: clamp(20px, 3vh, 36px);
}
.slide {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(16px, 2vh, 24px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s;
}
.slide.is-active { opacity: 1; pointer-events: auto; }
.slide__index {
  font-size: 14px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blush);
}
.slide__title {
  font-size: clamp(32px, 4vw, 60px);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}
.slide__copy {
  max-width: 520px;
  font-size: clamp(15px, 1.25vw, 18px);
  line-height: 1.6;
  font-weight: 300;
  color: var(--blush);
}

/* ── Products on small screens ─────────────────────────────
   The desktop composition sets the shot and the copy side by side and
   lets the shot bleed off the right edge. Below 860px there is no
   "beside": at 375px the bag covered 73% of the text panel, and at
   768px the contain-fitted bag still covered ~34%. So the stage stops
   being one overlapped layer and becomes a two-row grid - a full-bleed
   picture band on top, the copy underneath.

   The picture row is the LEFTOVER fraction (1fr) and the copy row is
   `auto`. That is deliberate: the copy always claims its true height
   first and the photo absorbs whatever is left, so nothing has to be
   hard-coded. An earlier version reserved the band with a fixed pixel
   constant derived from a 375px measurement; that was width-blind and
   clipped the last spec line at 360px and 320px, because two of slide
   02's spec items wrap to two lines below ~365px.

   The shots stay stage children rather than moving into the slides -
   reparenting them into .products__slides would contain them in the
   copy box and destroy the desktop right-edge bleed. The bag->brick
   swap survives untouched, it just runs across a strip.
   ========================================================== */

@media (max-width: 880px) {
  .products__stage {
    display: grid;
    grid-template-rows: minmax(80px, 1fr) auto;
    row-gap: 20px;
    padding: 16px var(--gutter) 24px;
  }

  /* Full-bleed: negative gutter margins push the photo past the stage
     padding. max-width:none is REQUIRED - the global
     `img, video { max-width: 100% }` at line 41 would otherwise clamp
     the negative-margin width straight back to the content box. */
  .products__stage .products__shot {
    position: relative;
    inset: auto;
    grid-area: 1 / 1;
    align-self: stretch;
    min-height: 0;
    width: calc(100% + 2 * var(--gutter));
    max-width: none;
    height: 100%;
    max-height: none;
    margin-inline: calc(-1 * var(--gutter));
    object-position: center;
  }
  .products__shot--bag { transform: translateX(0); }
  .products__shot--brick { transform: translateY(0) translateX(115%); }
  .products.is-stage2 .products__shot--bag { transform: translateX(-115%); }
  .products.is-stage2 .products__shot--brick { transform: translateY(0) translateX(0); }

  /* min-width:0 replaces the desktop min(88vw, 340px), which is
     meaningless once the panel is a grid item and can force overflow
     at very narrow widths. */
  .products__panel { grid-area: 2 / 1; margin-top: 0; min-width: 0; }

  /* The rail duplicates the "01 / 02" counter and costs 21px of a
     viewport that has none to spare. */
  .products__track { display: none; }

  /* .slide is position:absolute; inset:0, so it contributes no height.
     Invisible on desktop where the flex stage stretches the panel, but
     .products__slides collapses to zero the moment the panel is
     content-height. Stack both slides in one grid cell instead: the box
     is as tall as the taller slide and they still overlap, so the
     crossfade is unaffected. */
  .products__slides { display: grid; margin-top: 16px; }
  .products__slides .slide {
    position: relative;
    inset: auto;
    grid-area: 1 / 1;
    justify-content: flex-start;
    gap: 12px;
  }
  .products__slides .speclist { gap: 8px; }
}

/* ── 30% stat ─────────────────────────────────────────────── */

.stat {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(32px, 5vw, 80px);
  padding: clamp(64px, 9vw, 120px) var(--gutter);
  background: var(--red);
  color: var(--cream);
}
.stat__figure { flex: 1 1 380px; }
.stat__number {
  font-size: clamp(120px, 20vw, 300px);
  font-weight: 600;
  line-height: 0.9;
  letter-spacing: -0.04em;
}
.stat__label {
  margin-top: 12px;
  font-size: clamp(22px, 2.4vw, 36px);
  font-weight: 400;
}
.stat__body {
  flex: 1 1 380px;
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.stat__copy {
  font-size: clamp(17px, 1.5vw, 22px);
  line-height: 1.55;
  font-weight: 300;
  color: var(--blush);
}
.stat .speclist { font-size: 16px; }

/* ── Architecture (horizontal rail) ───────────────────────── */

.arch {
  position: relative;
  width: 100%;
  height: 380vh;
  background: var(--ink);
}
.arch__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  display: flex;
  align-items: center;
}
.arch__rail {
  display: flex;
  align-items: center;
  gap: 6vw;
  padding-left: 6vw;
  will-change: transform;
}
.arch__intro { flex: 0 0 auto; width: max(320px, 34vw); }
.arch__headline {
  margin-top: clamp(20px, 2.5vw, 36px);
  font-size: clamp(40px, 5.4vw, 84px);
  line-height: 1.02;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}
.arch__copy {
  margin-top: clamp(16px, 2vw, 28px);
  max-width: 480px;
  font-size: clamp(15px, 1.25vw, 19px);
  line-height: 1.6;
  font-weight: 300;
  color: var(--sand);
}
.arch__prompt {
  margin-top: clamp(24px, 3vw, 44px);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--tan);
}

.project { flex: 0 0 auto; }
.project--a { width: max(420px, 52vw); }
.project--b { width: max(340px, 40vw); margin-top: 14vh; }
.project--c { width: max(380px, 46vw); margin-top: -8vh; }

.project__frame {
  position: relative;
  overflow: hidden;
  background: rgba(254, 251, 233, 0.04);
}
.project--a .project__frame { height: 72vh; }
.project--b .project__frame { height: 56vh; }
.project--c .project__frame { height: 64vh; }
.project__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1);
  transition: transform 0.6s var(--ease);
}
.project__frame:hover img { transform: scale(1.04); }

.project__caption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 18px;
}
.project__name {
  display: flex;
  gap: 16px;
  align-items: baseline;
  font-size: clamp(20px, 1.9vw, 28px);
  font-weight: 500;
}
.project__num {
  font-size: 13px;
  letter-spacing: 0.14em;
  color: var(--tan);
}
.project__note {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--sand);
}

.arch__outro {
  flex: 0 0 auto;
  width: max(340px, 38vw);
  padding-right: 8vw;
}
.arch__closing {
  font-size: clamp(34px, 4vw, 64px);
  line-height: 1.06;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}
.arch__outro .pill { margin-top: clamp(24px, 3vw, 40px); }
.arch__disclaimer {
  margin-top: clamp(20px, 2.4vw, 32px);
  max-width: 40ch;
  font-size: 13px;
  line-height: 1.55;
  font-weight: 300;
  color: var(--sand);
  text-wrap: pretty;
}

.arch__progress {
  position: absolute;
  left: 6vw;
  right: 6vw;
  bottom: 4vh;
  height: 2px;
  background: rgba(254, 251, 233, 0.18);
}
.arch__progressbar { height: 100%; width: 0; background: var(--red); }

/* ── Services ─────────────────────────────────────────────── */

.services {
  padding: clamp(56px, 8vw, 96px) var(--gutter) clamp(64px, 9vw, 110px);
  background: var(--cream);
  color: var(--ink);
}
.services__headline {
  margin-top: clamp(24px, 3vw, 40px);
  max-width: 1100px;
  font-size: clamp(38px, 6.4vw, 92px);
  line-height: 1.04;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}
.services__grid {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(24px, 2.8vw, 40px);
  margin-top: clamp(48px, 6.7vw, 96px);
}
.service {
  position: relative;
  flex: 1 1 420px;
  height: clamp(380px, 43vw, 620px);
  border-radius: 20px;
  overflow: hidden;
  background: var(--ink);
  color: var(--cream);
}
.service__kicker {
  position: absolute;
  left: clamp(18px, 2.2vw, 32px);
  top: clamp(18px, 2.2vw, 32px);
  font-size: clamp(13px, 1.1vw, 16px);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-shadow: 0 1px 10px rgba(80, 25, 12, 0.7);
  pointer-events: none;
}
.service .dot--xl {
  position: absolute;
  right: clamp(16px, 1.9vw, 28px);
  top: clamp(16px, 1.9vw, 28px);
}
.service__copy {
  position: absolute;
  left: clamp(18px, 2.2vw, 32px);
  bottom: clamp(16px, 1.9vw, 28px);
  max-width: 480px;
  padding-right: 16px;
  font-size: clamp(17px, 1.66vw, 24px);
  line-height: 1.35;
  text-shadow: 0 1px 12px rgba(80, 25, 12, 0.7);
  pointer-events: none;
}

/* ── Sites ring ───────────────────────────────────────────── */

.ring {
  position: relative;
  width: 100%;
  height: clamp(560px, 62.5vw, 900px);
  overflow: hidden;
  background: var(--ink);
}
.ring__head {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: clamp(60px, 12vw, 110px) 20px 0;
  pointer-events: none;
}
.ring__eyebrow {
  font-size: 13px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--tan);
}
.ring__headline {
  text-align: center;
  font-size: clamp(36px, 6.1vw, 88px);
  line-height: 1.04;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--sand);
  text-wrap: pretty;
}

.ring__stage {
  position: absolute;
  left: 50%;
  top: 66%;
  width: 0;
  height: 0;
  perspective: 1400px;
}
.ring__spin {
  position: absolute;
  transform-style: preserve-3d;
  animation: spinRing 46s linear infinite;
}
.ring__cell {
  position: absolute;
  width: clamp(150px, 22vw, 320px);
  height: clamp(100px, 14.6vw, 210px);
  border-radius: 10px;
  overflow: hidden;
  transform:
    translate(-50%, -50%)
    rotateY(calc(var(--i) * 45deg))
    translateZ(clamp(250px, 37.5vw, 540px));
}

.ring__fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 220px;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(80, 25, 12, 0) 0%, rgba(80, 25, 12, 0.9) 100%);
}
.ring__hint {
  position: absolute;
  left: var(--gutter);
  bottom: 36px;
  z-index: 2;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--tan);
}

@keyframes spinRing {
  from { transform: rotateY(0deg); }
  to   { transform: rotateY(360deg); }
}

/* ── Contact ──────────────────────────────────────────────── */

.contact {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: clamp(32px, 5vw, 80px);
  padding: clamp(64px, 9vw, 120px) var(--gutter);
  background: var(--cream);
  color: var(--ink);
}
.contact__lead { flex: 1 1 480px; }
.contact__headline {
  margin-top: clamp(24px, 3vw, 40px);
  font-size: clamp(38px, 6vw, 84px);
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}
.contact__copy {
  margin-top: clamp(20px, 2.5vw, 32px);
  max-width: 560px;
  font-size: clamp(16px, 1.4vw, 20px);
  line-height: 1.6;
  font-weight: 300;
  color: var(--mocha);
}
.contact__lead .pill { margin-top: clamp(28px, 3vw, 44px); }

.contact__details {
  flex: 1 1 320px;
  display: flex;
  flex-direction: column;
  gap: 26px;
  font-size: clamp(17px, 1.5vw, 22px);
}
.contact__details dt {
  margin-bottom: 6px;
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--tan);
}
.contact__details dd { margin: 0; }

/* ── Footer ────────────────────────────────────────────────
   Two rows, no columns.

   The previous footer was a four-column link farm plus an address, a
   stamp, a tagline and a legal strip — five competing blocks for a site
   with ten pages. Everything a visitor actually wants sits in one line
   now; the obligations sit quietly beneath it, separated by a hairline
   rather than by boxes.
   ========================================================== */

.footer {
  background: #1C120D;
  color: var(--cream);
  padding: clamp(40px, 5vw, 64px) var(--gutter) clamp(32px, 4vw, 48px);
}
.footer__inner {
  max-width: 1600px;
  margin: 0 auto;
}

.footer__row {
  display: flex;
  align-items: center;
  gap: clamp(24px, 4vw, 64px);
  flex-wrap: wrap;
}

.footer__mark { display: block; flex: 0 0 auto; color: var(--cream); }
.footer__mark .mark { height: clamp(20px, 1.8vw, 26px); }

/* The nav takes the slack so the mark sits left and the legal line right —
   the same three-part balance the reference footer uses. */
.footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(18px, 2.4vw, 36px);
  margin-inline: auto;
  font-size: 15px;
}
.footer__nav a { color: var(--sand); }
.footer__nav a:hover { color: var(--cream); }

.footer__legal {
  flex: 0 0 auto;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tan);
}

/* Secondary row — present, findable, never competing. */
.footer__row--sub {
  margin-top: clamp(28px, 3.4vw, 44px);
  padding-top: clamp(18px, 2.2vw, 26px);
  border-top: 1px solid rgba(254, 251, 233, 0.12);
  align-items: baseline;
}
.footer__sub {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(14px, 1.8vw, 26px);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.footer__sub a { color: var(--tan); }
.footer__sub a:hover { color: var(--cream); }

@media (max-width: 760px) {
  .footer__row { gap: 20px; }
  .footer__nav { margin-inline: 0; width: 100%; }
  .footer__legal { width: 100%; }
  .footer__sub { width: 100%; }
}

/* ============================================================
   Interior pages
   The homepage opens on a video hero; interior pages open on a
   plain masthead so the nav has a ground to sit on and the
   page announces itself in one screen.
   ============================================================ */

.page { background: var(--ink); }

.masthead {
  position: relative;
  padding-bottom: clamp(48px, 8vh, 96px);
  border-bottom: 1px solid rgba(254, 251, 233, 0.12);
  background: var(--ink);
}
.masthead .topbar { position: relative; }
.masthead__inner {
  padding: clamp(48px, 9vh, 110px) var(--gutter) 0;
  max-width: 1100px;
}
.masthead__title {
  margin-top: 20px;
  font-size: clamp(40px, 6.4vw, 92px);
  line-height: 1.02;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: var(--cream);
}
.masthead__standfirst {
  margin-top: clamp(20px, 3vh, 32px);
  max-width: 58ch;
  font-size: clamp(16px, 1.5vw, 20px);
  line-height: 1.6;
  color: var(--sand);
}

/* ── Interior layout atoms ───────────────────────────────── */

.wrap { padding: clamp(56px, 9vh, 120px) var(--gutter); }
.wrap--cream { background: var(--cream); color: var(--mocha); }
.wrap--tight { max-width: 900px; }

.wrap--cream .section__title,
.wrap--cream .card__title { color: var(--ink); }
.wrap--cream a { color: var(--red); }
.wrap--cream a:hover { color: var(--ink); }

.section__title {
  font-size: clamp(28px, 3.6vw, 52px);
  line-height: 1.08;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--cream);
  max-width: 22ch;
}
.section__intro {
  margin-top: 20px;
  max-width: 62ch;
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.65;
  color: var(--sand);
}
.wrap--cream .section__intro { color: var(--mocha); }

.prose { max-width: 68ch; }
.prose p { margin-top: 18px; line-height: 1.72; }
.prose h2 {
  margin-top: clamp(36px, 5vh, 60px);
  font-size: clamp(22px, 2.4vw, 32px);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--cream);
}
.prose h3 {
  margin-top: 30px;
  font-size: 18px;
  font-weight: 600;
  color: var(--cream);
}
.prose ul { margin-top: 16px; display: grid; gap: 10px; }
.prose li { padding-left: 20px; position: relative; line-height: 1.6; }
.prose li::before {
  content: ""; position: absolute; left: 0; top: 0.6em;
  width: 7px; height: 7px; background: var(--red);
}

/* ── Cards ───────────────────────────────────────────────── */

.cards {
  margin-top: clamp(32px, 5vh, 56px);
  display: grid;
  gap: clamp(16px, 2vw, 28px);
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.card {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 34px);
  border: 1px solid rgba(254, 251, 233, 0.16);
  background: rgba(254, 251, 233, 0.03);
}
.wrap--cream .card {
  border-color: rgba(80, 25, 12, 0.16);
  background: rgba(80, 25, 12, 0.02);
}
.card__media {
  margin: calc(-1 * clamp(22px, 2.4vw, 34px)) calc(-1 * clamp(22px, 2.4vw, 34px)) clamp(20px, 2.4vw, 28px);
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: rgba(80, 25, 12, 0.35);
}
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card__title { font-size: 20px; font-weight: 600; color: var(--cream); }
.card__copy { margin-top: 12px; line-height: 1.62; color: var(--sand); }
.wrap--cream .card__copy { color: var(--mocha); }
.card__meta {
  margin-top: 18px; font-size: 13px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--tan);
}

/* ── Spec tables ─────────────────────────────────────────── */

.spectable { margin-top: 28px; width: 100%; border-collapse: collapse; }
.spectable th, .spectable td {
  padding: 14px 0; text-align: left; vertical-align: top;
  border-bottom: 1px solid rgba(80, 25, 12, 0.14);
  font-size: 15px; line-height: 1.5;
}
.spectable th { width: 44%; font-weight: 500; color: var(--cream); }
.spectable td { color: var(--sand); }
.spectable caption {
  text-align: left; padding-bottom: 10px; font-size: 13px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--sand);
}

/* On cream the same elements flip to the ink palette. --tan measured 2.69:1
   for the caption, so it takes --mocha here rather than the muted token. */
.wrap--cream .prose h2,
.wrap--cream .prose h3,
.wrap--cream .spectable th { color: var(--ink); }
.wrap--cream .spectable td { color: var(--mocha); }
.wrap--cream .spectable caption { color: var(--mocha); }
.spectable th, .spectable td { border-bottom-color: rgba(254, 251, 233, 0.14); }
.wrap--cream .spectable th,
.wrap--cream .spectable td { border-bottom-color: rgba(80, 25, 12, 0.14); }

/* ── Accordion (FAQs) ────────────────────────────────────── */

.faq { margin-top: 12px; border-bottom: 1px solid rgba(80, 25, 12, 0.14); }
.faq summary {
  padding: 20px 36px 20px 0; position: relative; cursor: pointer;
  font-size: clamp(16px, 1.7vw, 20px); font-weight: 500; color: var(--ink);
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+"; position: absolute; right: 4px; top: 50%;
  transform: translateY(-50%); font-size: 22px; color: var(--red);
}
.faq[open] summary::after { content: "\2013"; }
.faq__body { padding-bottom: 22px; max-width: 68ch; line-height: 1.7; }

/* ── Notice ──────────────────────────────────────────────── */

.notice {
  margin-top: 28px; padding: 18px 22px;
  border-left: 3px solid var(--red);
  background: rgba(177, 53, 24, 0.06);
  font-size: 14px; line-height: 1.6; color: var(--mocha);
}
.wrap:not(.wrap--cream) .notice { color: var(--sand); }

/* ── Interior CTA ────────────────────────────────────────── */

.endcta { padding: clamp(56px, 9vh, 110px) var(--gutter); background: var(--red); }
.endcta__title {
  font-size: clamp(28px, 4vw, 56px); line-height: 1.06; font-weight: 600;
  letter-spacing: -0.02em; color: var(--cream); max-width: 20ch;
}
.endcta__copy { margin-top: 18px; max-width: 52ch; line-height: 1.65; color: var(--blush); }
.endcta__actions { margin-top: 32px; display: flex; flex-wrap: wrap; gap: 14px; }

@media (max-width: 720px) {
  .masthead__inner { padding-top: 32px; }
  .card__media { aspect-ratio: 16 / 11; }
}

/* Sourcing line under a headline figure. Quiet, but present — a number on a
   credibility-led site should always say where it comes from. */
.stat__source {
  margin-top: 20px;
  max-width: 48ch;
  font-size: 12px;
  line-height: 1.55;
  letter-spacing: 0.03em;
  color: var(--tan);
}

/* ── Service card scrim ────────────────────────────────────
   The card captions sat on text-shadow alone, which holds over a dark
   photograph and fails over a light one — swap the image and the copy
   becomes unreadable. A scrim makes legibility a property of the card
   rather than a property of whichever picture happens to land in it.

   Both gradients fade to the ink colour at zero alpha, never to
   `transparent`: CSS `transparent` is rgba(0,0,0,0), so fading to it
   would ramp through grey and dirty the image. */
.service::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(to top,
      rgba(80, 25, 12, 0.88) 0%,
      rgba(80, 25, 12, 0.55) 22%,
      rgba(80, 25, 12, 0) 52%),
    linear-gradient(to bottom,
      rgba(80, 25, 12, 0.66) 0%,
      rgba(80, 25, 12, 0) 26%);
}
.service__kicker,
.service__copy,
.service .dot { z-index: 2; }

/* ── Hero and topbar refinements ───────────────────────────
   Taken from the reference: the chrome earns its presence through
   spacing rather than weight, and nothing is allowed to collide.
   ========================================================== */

/* The tagline sat flush against the wordmark — measured 0px between them.
   Two elements that large need air, not adjacency. */
.hero__lede { padding-bottom: clamp(18px, 3vh, 44px); }

/* The mark was 22px against a 167px wordmark below it — too timid to read
   as the same brand. */
.topbar__mark .mark { height: clamp(22px, 2vw, 30px); }

.topbar { padding-top: clamp(24px, 3.4vh, 40px); }

.topbar__nav { font-size: 15px; letter-spacing: 0.11em; }

/* The hero scrim is sized against sampled frames, but the video moves.
   A shadow costs nothing and keeps the chrome readable if a brighter
   frame comes through. text-shadow does not reach the SVG mark, so that
   gets the filter equivalent. Scoped to .hero: on the nine interior
   pages the masthead is solid --ink and a shadow buys nothing, while a
   drop-shadow filter would create a needless containing block. */
.hero .topbar__nav,
.hero .topbar__cta,
.hero .topbar__toggle-label { text-shadow: 0 1px 10px rgba(40, 12, 6, 0.55); }
.hero .topbar__mark .mark { filter: drop-shadow(0 1px 10px rgba(40, 12, 6, 0.5)); }

/* 0.45 cream measured 2.33:1 against the composited hero - under the
   3:1 that WCAG 1.4.11 asks of a control boundary; 0.62 lands at
   3.06:1. Hero-scoped and border-color only, so the interior mastheads
   keep 0.45 (already 3.85:1 on solid ink) and .topbar__cta:hover -
   equal specificity, declared later - still wins. */
.hero .topbar__cta { border-color: rgba(254, 251, 233, 0.62); }

/* Outline pill rather than text-plus-dot. Quieter than a filled button,
   but unmistakably the one action in the bar. */
.topbar__cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 12px 22px;
  border: 1px solid rgba(254, 251, 233, 0.45);
  border-radius: 999px;
  font-size: 13px;
  letter-spacing: 0.11em;
  transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.topbar__cta:hover {
  background: var(--cream);
  border-color: var(--cream);
  color: var(--ink);
  /* The hero-chrome shadow exists to lift cream text off the video.
     Once the pill fills cream and the text goes to ink, it is a smudge. */
  text-shadow: none;
}
.topbar__cta .dot { display: none; }

/* The hero wordmark carries the page; give it room to breathe beneath. */
.hero__wordmark { padding-bottom: clamp(26px, 4vh, 56px); }

/* ── Mobile navigation ─────────────────────────────────────
   The site had no mobile nav at all: five links and a CTA simply
   wrapped, so the topbar grew to 188px — 22% of a 390px-tall viewport
   spent on chrome before a single word of content.

   Collapsed styles are scoped to .js, applied by script.js on boot.
   With JavaScript off the toggle could not open anything, so the nav
   stays in its wrapped state instead of being hidden behind a dead
   button. Slightly untidy beats unreachable.
   ========================================================== */

.topbar__toggle { display: none; }

@media (max-width: 880px) {
  .topbar {
    padding-top: 16px;
    padding-bottom: 16px;
    align-items: center;
    gap: 12px;
  }
  .topbar__mark { margin-right: auto; }
  .topbar__mark .mark { height: 20px; }

  .js .topbar__toggle {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    /* 44px floor: the pill only needed 34px to look right, but a thumb
       needs more than the label does. */
    min-height: 44px;
    padding: 0 16px 0 18px;
    /* Matches .hero .topbar__cta - 3.06:1 against the composited hero.
       This control is the only route to the nav under 860px, so its
       border has to be visible. */
    border: 1px solid rgba(254, 251, 233, 0.62);
    border-radius: 999px;
    background: none;
    color: var(--cream);
    font: inherit;
    font-size: 12px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    cursor: pointer;
  }
  .topbar__burger { display: block; width: 14px; }
  .topbar__burger i {
    display: block;
    height: 1.5px;
    background: currentColor;
    transition: transform 0.25s var(--ease), opacity 0.2s var(--ease);
  }
  .topbar__burger i + i { margin-top: 4px; }
  .topbar__toggle[aria-expanded="true"] .topbar__burger i:first-child {
    transform: translateY(2.75px) rotate(45deg);
  }
  .topbar__toggle[aria-expanded="true"] .topbar__burger i:last-child {
    transform: translateY(-2.75px) rotate(-45deg);
  }

  /* Closed: removed from flow entirely so it cannot pad the bar out. */
  .js .topbar__panel {
    position: absolute;
    top: 100%;
    left: var(--gutter);
    right: var(--gutter);
    display: grid;
    gap: 4px;
    padding: 12px;
    background: rgba(28, 18, 13, 0.96);
    backdrop-filter: blur(14px);
    border: 1px solid rgba(254, 251, 233, 0.14);
    border-radius: 18px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity 0.24s var(--ease), transform 0.24s var(--ease), visibility 0.24s;
  }
  .js .topbar.is-open .topbar__panel {
    opacity: 1;
    visibility: visible;
    transform: none;
  }
  .js .topbar__panel .topbar__nav {
    display: grid;
    gap: 0;
    font-size: 15px;
    letter-spacing: 0.06em;
    text-transform: none;
  }
  .js .topbar__panel .topbar__nav a {
    padding: 13px 14px;
    border-radius: 10px;
  }
  .js .topbar__panel .topbar__nav a:hover { background: rgba(254, 251, 233, 0.08); }
  .js .topbar__panel .topbar__cta {
    margin-top: 6px;
    justify-content: center;
    border-color: var(--red);
    background: var(--red);
    color: var(--cream);
  }
}

/* The topbar is the positioning context for the panel. */
.topbar { position: relative; }

/* ── Phone type scale ──────────────────────────────────────
   Six headline sizes are written as clamp(MIN, Xvw, MAX) whose vw term
   does not reach its own MIN until well past phone width, so each is
   pinned flat at MIN across the entire phone-to-tablet range. Unfreeze
   widths: .masthead__title 625px, .vision__headline 693px,
   .arch__headline 741px, .section__title 778px, .mission__headline
   833px, .arch__closing 850px. At 375px that left .mission__headline
   running 9 lines / 319px.

   NOTE: that 9-line / 319px figure was measured against the OLD mission
   copy. The headline has since been rewritten and now runs 4 lines at
   375px with this ramp applied. The ramp is still doing work, but
   re-measure before re-tuning it — do not reason from the number above.

   The fix is a phone-only ramp rather than a rewrite of the six base
   curves. Rewriting them lands the new straight line ABOVE the old one
   everywhere from ~600px to 1440px: measured, a 768px tablet gained
   +11px on .arch__headline and a 60% taller .mission__headline (5 -> 7
   lines), which is the opposite of the complaint, and on landscape
   phones the growth overran .arch__stage - a 100svh overflow:hidden box
   with align-items:center - clipping .arch__closing at the top and
   .arch__disclaimer at the bottom with no scroll recovery.

   620px is below every unfreeze width above, so each ramp terminates
   exactly on the value the base rule already produces there. Above
   620px the delta is zero by construction, not by rounding.
   ========================================================== */

@media (max-width: 620px) {
  .mission__headline { font-size: clamp(24px, 14.82px + 2.45vw, 30px); }
  .vision__headline  { font-size: clamp(24px, 20.94px + 0.82vw, 26px); }
  .arch__closing     { font-size: clamp(30px, 23.89px + 1.63vw, 34px); }
  .arch__headline    { font-size: clamp(34px, 24.82px + 2.45vw, 40px); }
  .section__title    { font-size: clamp(26px, 22.94px + 0.82vw, 28px); }
  .masthead__title   { font-size: clamp(34px, 24.82px + 2.45vw, 40px); }
}

/* ── Enquiry form ────────────────────────────────────────────
   This file had no rule for `input` or `textarea` at all, so the contact
   form rendered as UA-default controls — roughly 147px wide and 21px
   tall, white on the --ink ground — with <br> doing the layout.

   The numbers below are measured against the *composited* background
   rather than picked by eye. Note that `background-clip` defaults to
   `border-box`, so the element's own fill is painted UNDER the
   translucent border: the border composites over the fill, not over the
   page ink. sRGB relative luminance:

     field fill   rgba(254,251,233,0.05) over --ink  ->  #592417
     border       rgba(254,251,233,0.50) over #592417 -> #AC9080
                    vs the fill inside it    4.18:1
                    vs the page ink outside  4.75:1   (1.4.11 asks 3:1)
     value text   --cream on the fill                 11.95:1
     placeholder  --sand  on the fill                  7.08:1
     label        --sand  on --ink                     8.05:1
     hint         --tan   on --ink                     5.05:1

   0.50 alpha rather than a lighter hairline: at 0.38 the border clears
   the inner boundary by only 3.03:1, which is inside rounding distance
   of the 3:1 floor. 0.50 buys real headroom.

   --tan is the site's usual colour for small uppercase meta (.card__meta,
   .spectable caption), but on the field fill it measures 4.44:1 — under
   AA — so placeholders and labels take --sand instead. This is a
   deliberate deviation from those two rules, not an application of them.
   --tan survives only on the parenthetical hint, which sits on plain ink
   at 5.05:1.
   ========================================================== */

.form {
  margin-top: clamp(30px, 4.5vh, 48px);
  /* 704px sits between the two measures this page already uses:
     .prose's 68ch is ~691px in Rubik at 16px, and .section__intro's 62ch
     at 18px is ~709px. So this is consistency with the existing rhythm,
     not a narrowing of .prose. */
  max-width: 44rem;
}

.form__grid {
  display: grid;
  gap: clamp(22px, 2.8vw, 30px);
}

.field {
  display: grid;
  /* minmax(0,1fr) rather than the default auto track: an auto track sizes
     to its content, which makes the textarea's own max-width:100% resolve
     against itself and stop being a limit. Verified — with an auto track a
     2000px width on the textarea widens the page; with this it clamps. */
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  align-content: start;
  min-width: 0;
}

.field__label {
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--sand);
}
.field__hint {
  letter-spacing: 0.04em;
  text-transform: none;
  color: var(--tan);
}

.field__input,
.field__area {
  width: 100%;
  padding: 13px 16px;
  border: 1px solid rgba(254, 251, 233, 0.5);
  /* Square, like .tag. The pill radius belongs to actions, not to inputs. */
  border-radius: 0;
  background: rgba(254, 251, 233, 0.05);
  color: var(--cream);
  /* Form controls do not inherit font-family from the page. */
  font-family: inherit;
  /* 16px is a floor, not a preference: iOS Safari zooms the viewport on
     focus for anything smaller and leaves the user mid-form at 1.4x.
     Hard px rather than rem for that reason; note this does not scale
     with the user's default font size the way the 44rem cap does. */
  font-size: 16px;
  line-height: 1.5;
  -webkit-appearance: none;
  appearance: none;
  transition: border-color 0.25s var(--ease), background-color 0.25s var(--ease);
}

/* 13 + 24 + 13 + 2px of border = 52, so the floor and the natural height
   agree and the row does not jump. Comfortably over the 44px target. */
.field__input { min-height: 52px; }

.field__area {
  display: block;
  /* rows="6" gives ~182px naturally; this is the floor a drag cannot go
     below, so the control can never be collapsed to a slit. */
  min-height: 160px;
  /* Vertical only. `both` lets a drag widen the textarea past its grid
     track and take the page's horizontal scrollbar with it. */
  resize: vertical;
  max-width: 100%;
  line-height: 1.6;
}

.field__input::placeholder,
.field__area::placeholder {
  color: var(--sand);
  /* Firefox ships placeholders at opacity .54, which would quietly undo
     the measured 7.08:1. */
  opacity: 1;
}

.field__input:hover,
.field__area:hover { border-color: rgba(254, 251, 233, 0.72); }

/* The global ring now resolves to cream on this ground anyway — the form
   sits in a plain .wrap, which is ink — so the outline takes the token
   instead of repeating the literal. What this block still adds over the
   default is the tighter 2px offset and the border/fill lift that marks
   which field is live.

   The border-colour and background below stay cream literals on purpose:
   unlike the ring, they sit ON the control rather than outside it, and
   both are tuned to the ink ground. If this form is ever moved into a
   .wrap--cream the ring will follow automatically and these two will not.

   border-radius:0 is restated here defensively, NOT because the global
   rule would otherwise win: `.field__input { border-radius: 0 }` above
   has the same (0,1,0) specificity as `:focus-visible` and sits much
   later in this file, so it already wins on source order. The global rule
   no longer sets a radius at all, so nothing rounds this today; the
   declaration keeps the field square whatever the default becomes. */
.field__input:focus-visible,
.field__area:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 0;
  border-color: var(--cream);
  background: rgba(254, 251, 233, 0.09);
}

/* Chrome paints autofilled fields with a near-white inset and its own
   text colour, which would put cream on pale blue. Repainting the inset
   with the composited fill keeps the control identical filled or empty.
   Four of these fields carry autocomplete attributes, so this is the
   common path rather than an edge case.

   #592417 is rgba(254,251,233,0.05) over --ink and #602D20 is the same
   cream at 0.09 (the focus lift). These are pre-composited literals: if
   --ink ever changes, every other surface in this form follows the token
   and these two do not. Recompute them if it does. */
.field__input:-webkit-autofill,
.field__input:-webkit-autofill:hover {
  -webkit-text-fill-color: var(--cream);
  -webkit-box-shadow: 0 0 0 1000px #592417 inset;
  caret-color: var(--cream);
}
.field__input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--cream);
  -webkit-box-shadow: 0 0 0 1000px #602D20 inset;
  caret-color: var(--cream);
}

.form__actions { margin-top: clamp(30px, 4vh, 44px); }

/* .pill has only ever been used on <a>. On a <button> the UA sheet still
   supplies an outset border and Arial — font-family is the one font
   property .pill does not already override (.pill does set font-size:
   14px, so only the typeface falls through). Scoped to button.pill, so
   every anchor pill on the site is untouched, as is .topbar__toggle, the
   only other <button> in the markup. */
button.pill {
  border: 0;
  font-family: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
button.pill:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
}

/* Two columns only once two fields can sit side by side without the
   labels wrapping. 760px is a breakpoint the file already uses. Note
   this is the file's only min-width query — at exactly 760px both this
   and the existing (max-width: 760px) footer block are live, which is
   harmless because they govern disjoint properties, but it is worth
   knowing if either is ever widened. */
@media (min-width: 760px) {
  .form__grid { grid-template-columns: 1fr 1fr; }
  .field--wide { grid-column: 1 / -1; }
}
