/* Lovins Construction — single page. Editorial two-column rhythm on desktop,
   stacked on mobile. Strong type, one accent colour, no motion gimmicks. */

:root {
  --ink:     #000000;
  --body:    #5f5a54;
  --muted:   #8b857d;
  --rule:    #e3ded6;
  --rule-lt: #efebe4;
  --paper:   #ffffff;
  --tint:    #faf8f4;
  --dark:    #000000;
  --orange:     #f75f1c;  /* brand accent: outlines, icons, rules, links */
  --orange-dk:  #d2490b;  /* filled buttons — deeper, and clears contrast */
  --orange-dkr: #b03d08;  /* filled-button hover */

  /* Side padding scales with the viewport: 20px on a phone, 32px on a
     tablet, 64px on a desktop. --maxw caps how wide the content ever gets. */
  --pad:  clamp(1.25rem, 4.5vw, 4rem);
  --maxw: 1360px;
  --nav-h: 96px;
}

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

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: var(--orange); }

::selection { background: var(--orange); color: #fff; }
:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }

.wrap {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* Hidden by clipping it to nothing in place, not by parking it at
   left:-9999px. An element sitting 10,000px outside the page still occupies
   that position, and mobile browsers can treat the space as pannable — which
   shows up as the page sliding sideways with nothing in it. This version has
   no size and no position outside the layout at all. */
.skip {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
  background: var(--orange); color: #fff;
  text-decoration: none; z-index: 200;
}
.skip:focus {
  left: .5rem; top: .5rem;
  width: auto; height: auto;
  margin: 0; padding: .75rem 1.25rem;
  clip-path: none; overflow: visible;
}


/* --------------------------------------------------------------- nav --- */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(255, 255, 255, .96);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  /* Hairline only — enough to separate the nav from a white section without
     drawing attention to itself. */
  border-bottom: 1px solid var(--rule);
}
.nav__inner {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 2.25rem);
  min-height: var(--nav-h);
}
.nav__logo { display: block; flex: none; }
/* height:auto is required — the img's height="59" attribute is a
   presentational hint, so setting width alone distorts the logo. */
.nav__logo img { width: clamp(140px, 15vw, 178px); height: auto; }

/* links and actions both sit right; actions stay last */
.nav__nav { margin-left: auto; }

.nav__actions {
  display: flex;
  align-items: center;
  gap: clamp(.9rem, 2vw, 1.5rem);
  flex: none;
}

/* phone: plain text link, never a button — keeps one filled CTA on screen */
.nav__tel {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  font-size: 1.12rem;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  transition: color .2s;
}
.nav__tel:hover { color: var(--orange); }
.nav__tel svg {
  width: 17px; height: 17px; flex: none;
  stroke: var(--orange); stroke-width: 1.9; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(.25rem, 1.4vw, 1.1rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__links a {
  display: block;
  padding: .65rem 1.1rem;
  /* Matches .nav__tel so the three text items in the bar read as one set. */
  font-size: 1.12rem;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  border-radius: 2px;
  transition: color .2s, background .2s;
}
.nav__links a:hover { color: var(--orange); }

/* Menu button. Hidden on desktop, where the whole bar fits on one row. */
.nav__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-right: -.45rem;   /* pulls the icon flush with the logo's edge */
  padding: 0;
  background: none;
  border: 0;
  color: var(--ink);
  cursor: pointer;
}
.nav__toggle svg {
  width: 26px; height: 26px;
  stroke: currentColor; stroke-width: 2; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
}
.nav__toggle-close { display: none; }
.nav__toggle[aria-expanded="true"] .nav__toggle-open { display: none; }
.nav__toggle[aria-expanded="true"] .nav__toggle-close { display: block; }
.nav__links__cta { display: none; }   /* menu-only; desktop has the bar pill */

/* Tablet and below: one row — logo, then the phone icon and the menu button.
   The links move into a panel the button reveals.

   The panel is absolutely positioned rather than part of the flow on purpose:
   opening it must not change the nav's height, because --nav-real feeds every
   anchor offset. A panel that pushed the bar taller would leave each section
   landing short by the height of the open menu. */
@media (max-width: 880px) {
  .nav__inner {
    flex-wrap: nowrap;
    min-height: 0;
    padding-block: .7rem;
    gap: .6rem 1rem;
  }
  .nav__actions { margin-left: auto; }
  .nav__toggle { display: inline-flex; }
  .nav__actions > .btn--sm { display: none; }   /* pill moves into the panel */

  .nav__nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    margin-left: 0;
    /* Fully opaque, and no backdrop-filter. At 98% white over the hero the
       headline still read through it — a translucent panel stacked on a
       translucent nav leaves legibility at the mercy of how the browser
       composites two blurs. A menu gains nothing from being see-through. */
    background: #fff;
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 14px 26px rgba(20, 17, 15, .12);
    display: none;
  }
  .nav__nav[data-open="true"] { display: block; }

  .nav__links {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: .2rem var(--pad) 1.1rem;
  }
  .nav__links a {
    padding: .95rem .1rem;
    font-size: 1.08rem;
  }
  .nav__links li + li a { border-top: 1px solid var(--rule-lt); }
  /* Same treatment as Home / Services / About — it inherits `.nav__links a`
     and the divider rule above, so no styling of its own is needed. */
  .nav__links__cta { display: block; }
}

/* Narrow phones: the number itself won't fit beside the icons, so the handset
   becomes the tap target and keeps a 44px hit area. */
@media (max-width: 500px) {
  .nav__logo img { width: 142px; }
  .nav__telnum { display: none; }
  /* The padding is the tap target; the negative margin keeps it from pushing
     the bar taller than the logo already does. */
  .nav__tel { padding: .75rem; margin: -.75rem; }
  .nav__tel svg { width: 25px; height: 25px; }
  .nav__toggle { width: 48px; height: 48px; }
  .nav__toggle svg { width: 30px; height: 30px; }
  .nav__actions { gap: .5rem; }
  .nav__inner { padding-block: .7rem; }
}

/* ---------------------------------------------------------- typography -- */
h1, h2, h3 {
  font-weight: 700;
  letter-spacing: -.025em;
  line-height: 1.06;
  margin: 0;
  text-wrap: balance;
}

h1 { font-size: clamp(2.6rem, 7vw, 5.3rem); max-width: 16ch; }

/* Section headings carry the structure now that the small orange labels
   are gone — so they need real size. */
h2 {
  font-size: clamp(2.1rem, 4.4vw, 3.4rem);
  max-width: 18ch;
  margin-bottom: clamp(2rem, 4vw, 2.75rem);
}
/* balance (inherited from h2) evens the lines out and keeps the block
   narrow — pretty lets it fill the width instead. */
.h2--wide { max-width: 34ch; text-wrap: pretty; }
/* Second line: its own row, smaller, orange. */
.h2__sub {
  display: block;
  margin-top: .5em;
  font-size: .46em;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--orange);
}

h3 { font-size: 1.2rem; font-weight: 600; letter-spacing: -.01em; }

p { margin: 0 0 1.15rem; color: var(--body); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

/* Small orange section label with a rule beside it */
.label {
  display: flex;
  align-items: center;
  gap: .8rem;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--orange);
  margin: 0 0 1.5rem;
}
.label::after {
  content: "";
  height: 2px;
  flex: 1;
  background: var(--orange);
  opacity: .28;
}

/* ---------------------------------------------------------------- hero -- */
/* Photo hero: 2px blur on the image, directional gradient over it so the
   text sits on near-solid dark while the framing stays readable on the right.
   The scale() compensates for blur sampling past the image edges — without
   it you get a soft translucent border. Raise it if the blur ever increases. */
.hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  align-items: center;
  background: #000000;            /* fallback if the image fails to load */
  padding-block: clamp(2.75rem, 5vw, 4.25rem);
  /* The photo is 2:1. A short hero crops most of its height away, so this
     is set tall enough to show the framing rather than a thin slice of it.
     Deliberately stops short of full-screen — filling the viewport pushes
     the upscale from 1.30x to 1.41x and the softness becomes obvious. */
  min-height: clamp(520px, 74vh, 780px);
}
.hero__bg {
  position: absolute;
  /* Negative inset enlarges the element past the container instead of
     transform:scale(), so object-position still anchors predictably.
     The overhang hides the soft edge the blur would otherwise leave. */
  inset: -14px;
  width: calc(100% + 28px);
  height: calc(100% + 28px);
  object-fit: cover;
  object-position: 50% 0%;   /* show the top of the frame, not the middle */
  z-index: -2;
  filter: blur(2px);
}
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(100deg,
    rgba(6, 5, 4, .88) 0%,
    rgba(6, 5, 4, .72) 44%,
    rgba(6, 5, 4, .30) 80%,
    rgba(6, 5, 4, .18) 100%);
}
.hero__inner { position: relative; z-index: 2; width: 100%; }

/* Headline reveals as one block: an upward wipe with a slight lift.
   Single movement, no per-word stagger. */
@keyframes heroReveal {
  from { clip-path: inset(100% 0 0 0); transform: translateY(18px); }
  to   { clip-path: inset(0 0 0 0);    transform: translateY(0); }
}
.hero h1 {
  animation: heroReveal 1.7s cubic-bezier(.16, 1, .3, 1) both;
}

/* Two ways in: the form, or a direct call. */
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .85rem;
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
  animation: heroReveal 1.7s cubic-bezier(.16, 1, .3, 1) .25s both;
}
/* Orange outline disappears against the warm tones in the photo, so the
   secondary goes white here. */
.hero .btn--line {
  border-color: rgba(255, 255, 255, .6);
  color: #fff;
}
.hero .btn--line:hover { background: #fff; color: var(--ink); border-color: #fff; }
.btn__ico {
  width: 18px; height: 18px; flex: none;
  stroke: currentColor; stroke-width: 1.9; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
}

@media (prefers-reduced-motion: reduce) {
  .hero h1, .hero__actions { animation: none; }
}

/* Sized for the current headline length — 3 lines on desktop. If the wording
   changes materially, re-check the line count rather than assuming. */
.hero h1 {
  color: #fff;
  max-width: 19ch;
  font-size: clamp(2.7rem, 6.3vw, 5.6rem);
}
/* On phones the text spans the full width, so a sideways gradient would
   leave the right edge bright behind it. Go near-uniform instead. */
@media (max-width: 760px) {
  .hero::after {
    background: linear-gradient(170deg,
      rgba(6, 5, 4, .82) 0%,
      rgba(6, 5, 4, .74) 55%,
      rgba(6, 5, 4, .80) 100%);
  }
  /* min-height:0 let the hero shrink to its content — about 415px, of which
     the sticky nav ate 135, leaving the photo barely visible. svh (not vh)
     because vh on phones measures the viewport with the browser chrome
     retracted, so a vh-sized hero gets clipped on load. */
  .hero { min-height: 64svh; }

  /* Portrait crop: at this size the photo is cut by ~726px across and 0px
     down, so only the horizontal value does anything — the full height is
     already in frame. 25% moves the visible slice from the middle third of
     the photo to the left of centre.

     No blur here. The 2px on the base rule hides softness from upscaling —
     desktop stretches this 1166px-wide file past its native size. A phone
     renders it at 0.94x, slightly under native, so there is nothing to hide
     and the blur only throws away detail that is actually there. */
  .hero__bg { object-position: 25% 0%; filter: none; }
}

/* Stack the hero buttons, each only as wide as its own label. Full width came
   out at 89% of the screen — a lot of empty orange either side of two short
   words. Left-aligned so they line up with the headline above them. */
@media (max-width: 500px) {
  .hero__actions { flex-direction: column; align-items: flex-start; gap: .7rem; }
  .hero__actions .btn { width: auto; }
}

/* ------------------------------------------------------------ buttons -- */
/* Pill system: solid orange primary, thick orange outline secondary. */
.actions { display: flex; flex-wrap: wrap; gap: .75rem; }
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  padding: .95rem 2rem;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  border-radius: 999px;
  border: 1.5px solid transparent;
  text-decoration: none;
  white-space: nowrap;
  /* Anchors get this free; <button> does not — without it the form's submit
     button was the one control on the page that didn't look clickable. */
  cursor: pointer;
  transition: background .2s, color .2s, border-color .2s;
}
/* On light backgrounds the button uses the brand orange, so it matches the
   logo sitting next to it in the nav. */
.btn--solid { background: var(--orange); border-color: var(--orange); color: #fff; }
.btn--solid:hover { background: var(--orange-dk); border-color: var(--orange-dk); color: #fff; }

.btn--line {
  background: transparent;
  border: 2.5px solid var(--orange);
  color: var(--orange);
  padding: .85rem 1.9rem;
}
.btn--line:hover { background: var(--orange); color: #fff; }

.btn--sm { padding: .8rem 1.7rem; font-size: 1rem; }
.btn--line.btn--sm { padding: .6rem 1.4rem; }

.btn__ico {
  width: 17px; height: 17px; flex: none;
  stroke: currentColor; stroke-width: 1.9; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
}

/* -------------------------------------------------- section scaffolding -- */
.section {
  padding-block: clamp(3rem, 7vw, 5.5rem);
  border-bottom: 1px solid var(--rule);
  /* --nav-real is the nav's measured height, set by script on load and on
     resize. The literal fallback only applies with JS off. The old value was
     var(--nav-h) + 8px, where the 8px was clearance for an orange bar under
     the navbar that has since been removed — that stray 8px was the gap that
     showed above a section after jumping to it. */
  scroll-margin-top: var(--nav-real, var(--nav-h));
}
.section--tint { background: var(--tint); }

/* Concrete texture under a dark scrim — same treatment as the footer. */
.section--concrete {
  background-color: #000;
  background-image:
    linear-gradient(rgba(5, 4, 4, .85), rgba(5, 4, 4, .85)),
    url("../img/concrete.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-top-color: transparent;
}
/* Phones get a rotated copy of the same texture. The source is 886x360, and
   the section on a phone is roughly 375x1200 — covering that with a landscape
   image means scaling it 3.4x and showing about 13% of its width, at which
   point it reads as grey mush rather than concrete. The 360x886 version
   covers the same box at 1.4x, showing 76% of it. Rotated rather than
   re-cropped so it stays the identical material. */
@media (max-width: 760px) {
  .section--concrete {
    background-image:
      linear-gradient(rgba(5, 4, 4, .85), rgba(5, 4, 4, .85)),
      url("../img/concrete-tall.jpg");
  }
}

.section--concrete h2 { color: #fff; }
.section--concrete p { color: rgba(255, 255, 255, .74); }
.section--concrete .footnote { color: rgba(255, 255, 255, .52); }
.section--concrete .stats { border-top-color: rgba(255, 255, 255, .16); }
.section--concrete .stats dd { color: rgba(255, 255, 255, .6); }

/* The section under the hero previously got a tighter top (2.5rem vs 5.5rem)
   to close the gap below the photo. That made its heading sit 48px higher off
   the section top than every other heading on the page, so it's been removed —
   all four sections now share the same padding and their headings line up. */

/* Below 880px the nav wraps to two rows, taller than --nav-h, so anchor
   targets need a bigger offset or they land underneath it. This is only the
   no-JS fallback now — --nav-real above supersedes it and is exact. Declared
   after .section so it wins; media queries add no specificity. */
@media (max-width: 880px) {
  .section { scroll-margin-top: var(--nav-real, 116px); }
}

/* Body copy needs a measure cap now that sections run full width. */
#about p { max-width: 68ch; }

/* Text one side, portrait the other. Swap the column order in the
   grid-template-columns line to put the photo on the left instead. */
.about__grid {
  display: grid;
  /* Fixed, modest portrait column — a proportional one grew far taller than
     the two paragraphs beside it. Top-aligned so any slack falls below the
     text rather than being split above and below it. */
  grid-template-columns: minmax(0, 56ch) clamp(180px, 18vw, 225px);
  justify-content: space-between;
  gap: clamp(2rem, 5vw, 4rem);
  /* The heading lives inside the left column, so the photo centres against
     heading + copy as one block rather than against the copy alone. */
  align-items: center;
}
@media (max-width: 860px) {
  .about__grid { grid-template-columns: 1fr; gap: 2.25rem; }
}

/* No frame and no forced ratio — the photo sits at its own proportions. */
.about__photo { margin: 0; }
.about__photo img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 6px;
}
/* Desktop: lift the caption out of the flow so the figure's height is just
   the photo. Otherwise centring includes the caption and the photo itself
   sits high against the text beside it. */
@media (min-width: 861px) {
  .about__photo { position: relative; }
  .about__photo figcaption {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
  }
}

.about__photo figcaption {
  margin-top: .85rem;
  text-align: center;
  font-size: .95rem;
  font-weight: 600;
  color: #fff;
  letter-spacing: -.005em;
}
.about__photo figcaption span { color: rgba(255, 255, 255, .4); }
.about__photo figcaption em {
  font-style: italic;
  font-weight: 400;
  color: rgba(255, 255, 255, .68);
}

/* ----------------------------------------------------------- services -- */
/* Three outlined cards, icon over label, centred as a group. */
.services {
  list-style: none;
  padding: 0;
  margin: 0 auto;
  max-width: 100%;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.6vw, 2rem);
}
@media (max-width: 760px) { .services { grid-template-columns: 1fr; max-width: 420px; } }

.services li {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  text-align: center;
  padding: 3.5rem 2rem;
  border: 2px solid var(--orange);
  border-radius: 8px;
  transition: background .2s;
}
.services li:hover { background: rgba(247, 95, 28, .06); }

.services svg {
  width: 66px;
  height: 66px;
  flex: none;
  stroke: var(--orange);
  stroke-width: 1.6;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Solid-weight icons paint with fill instead of stroke. Without this they'd
   inherit `fill: none` above and render as nothing. */
.services svg.ico--solid {
  fill: var(--orange);
  stroke: none;
}

/* Optical sizing. Every icon box is 66px tall, but each drawing filled its own
   viewBox by a different amount (75/82/86%) and sat at a different height
   inside it, so the visible artwork came out different sizes at different
   heights. Each viewBox in the HTML is therefore cropped to its own artwork
   and scaled so the ink is exactly 56px tall with 5px clear above and below.
   The inline stroke-width compensates for that per-icon scale so every
   outline still renders at 4.4px. The building icon is wider than it is tall,
   so it carries an explicit width to keep its side padding at 5px too.
   If you swap an icon, re-crop its viewBox or it will look off again. */
/* Reserve two lines. "Tenant improvements & build-outs" wraps while the other
   two labels don't, and because the card centres its contents that extra line
   would otherwise push its icon ~17px higher than its neighbours'. Holding the
   space keeps every card's content block the same height, so the icons and the
   first line of text line up straight across the row. The trade-off is that
   the two short labels carry an empty line beneath them — unavoidable while
   the cards are equal height and one label needs two lines. */
.services h3 {
  margin: 0;
  min-height: 2.6em;   /* 2 lines at line-height 1.3 */
  font-size: 1.6rem;
  line-height: 1.3;
  text-wrap: balance;
}
/* Cards go single-column here and get narrower than they are on desktop, so
   the longest label needs a nudge down to stay on one line. 1.45rem is the
   actual ceiling (261px of 267px available); 1.4rem leaves room for the
   fallback font, which is wider than Archivo. Must stay after the rule above
   — media queries add no specificity, so order is what decides this. */
@media (max-width: 760px) {
  .services h3 { font-size: 1.4rem; }
}

/* Result message under the send button. */
.form__status {
  margin: 1.25rem 0 0;
  padding: .9rem 1.1rem;
  border-radius: 8px;
  font-size: 1.02rem;
  line-height: 1.5;
  text-align: center;
  text-wrap: balance;
}
.form__status--ok {
  background: rgba(247, 95, 28, .08);
  border: 1px solid rgba(247, 95, 28, .35);
  color: var(--ink);
}
.form__status--err {
  background: #fdf3f0;
  border: 1px solid #e0b4a6;
  color: #8a2c0d;
}
.btn:disabled { opacity: .7; cursor: default; }

/* ------------------------------------------------------ scroll reveal -- */
/* Everything here is scoped to .js, which an inline script in the head adds
   before first paint. Without scripting the rules never apply and the page
   renders normally — content must never need JS to become visible.
   Same easing and distance as the hero headline so the page feels of a piece. */
.js .reveal {
  opacity: 0;
  transform: translateY(20px);
  transition:
    opacity .7s cubic-bezier(.16, 1, .3, 1),
    transform .7s cubic-bezier(.16, 1, .3, 1);
}
.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

/* Cards land one after another rather than as a block. Small delays — past
   ~.12s apart it starts to feel like the page is loading slowly. */
.js .services li.reveal:nth-child(2) { transition-delay: .09s; }
.js .services li.reveal:nth-child(3) { transition-delay: .18s; }

/* Respect the OS setting. Motion-sensitive visitors get the content with no
   movement at all, rather than a shortened version of it. */
@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* -------------------------------------------------------------- stats -- */
.stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem;
  margin: 2.5rem 0 0;
  padding-top: 2rem;
  border-top: 1px solid var(--rule-lt);
}
@media (max-width: 560px) { .stats { grid-template-columns: 1fr; gap: 1.25rem; } }
.stats dt {
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1;
  color: var(--orange);
}
.stats dd {
  margin: .5rem 0 0;
  font-size: .88rem;
  color: var(--muted);
  line-height: 1.45;
}
.footnote { margin-top: 1.25rem; font-size: .82rem; color: var(--muted); }

/* -------------------------------------------------------------- areas -- */
/* Grid rather than CSS columns: each column sizes to its own widest city,
   so the block hugs its content and centres cleanly — while rows stay
   left-aligned inside, keeping every check mark on the same vertical line.
   Column-major flow preserves the reading order down each column. */
/* City list on the left, map on the right. */
.areas__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(260px, 32%, 400px);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
@media (max-width: 860px) {
  .areas__grid { grid-template-columns: 1fr; gap: 2.5rem; }
  /* Map above the list once stacked: the shape orients you before the names
     do. Only the visual order changes — the DOM keeps list-then-map, which is
     the order a screen reader follows, and either reads fine. */
  .areas__map { max-width: 420px; margin-inline: auto; order: -1; }
}
/* drop-shadow, not box-shadow: the PNG is a transparent silhouette, so a
   box-shadow would trace the image's rectangle instead of the coastline.
   Two layers rather than one — a tight contact shadow to seat the shape and a
   wide soft one for ambient depth. A single mid-blur shadow at an opacity
   heavy enough to read ends up looking like a grey smudge; splitting it keeps
   the edge crisp while staying light. Tinted with the page's warm ink rather
   than pure black so it sits in the palette. */
.areas__map {
  display: block;
  width: 100%;
  height: auto;
  filter:
    drop-shadow(0 4px 5px rgba(20, 17, 15, .22))
    drop-shadow(0 18px 32px rgba(20, 17, 15, .42));
}

/* Two columns inside the narrower left track — 12 cities across 6 rows.
   Column-major flow keeps the reading order running down each column, and
   rows stay left-aligned so every check mark shares a vertical line. */
.areas {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(6, auto);
  /* Columns hug their widest city, then get distributed evenly across the
     track. Equal-width (1fr) tracks would leave a dead gap on the right,
     since the city names vary a lot in length. */
  grid-auto-columns: max-content;
  justify-content: space-evenly;
  column-gap: 1.5rem;
  width: 100%;
}
@media (max-width: 520px) { .areas { grid-template-rows: repeat(12, auto); } }
.areas li {
  display: flex;
  align-items: center;
  /* Left-aligned inside each column so every check mark lines up. */
  justify-content: flex-start;
  gap: clamp(.6rem, .9vw, .85rem);
  /* Sized up to hold its own against the map alongside it. The clamps matter:
     at full size the two columns need ~374px, but the cards go single-column
     on a phone where only ~335px is available, so the lower bound is what
     keeps the list from overflowing there. */
  font-size: clamp(1.15rem, 1.6vw, 1.3rem);
  padding-block: clamp(.5rem, .8vw, .7rem);
  break-inside: avoid;
  border-bottom: 1px solid var(--rule-lt);
}
/* Orange disc with a white check, drawn inline so there's no extra file
   and no markup on each list item. */
/* Sits under the list, centred against it rather than against the whole track,
   so it reads as belonging to the cities and not to the map beside them. */
.areas__note {
  margin: 1.6rem 0 0;
  text-align: center;
  color: var(--body);
  font-size: clamp(1rem, 1.2vw, 1.08rem);
  line-height: 1.55;
  text-wrap: balance;
}

.areas li::before {
  content: "";
  flex: none;
  width: clamp(24px, 2vw, 28px);
  height: clamp(24px, 2vw, 28px);
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='10' fill='%23f75f1c'/%3E%3Cpath d='M5.9 10.3l2.6 2.6 5.6-5.6' fill='none' stroke='%23fff' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* --------------------------------------------------- contact centred --- */
#contact { text-align: center; }
#contact h2 {
  max-width: 24ch;
  margin-inline: auto;
  text-wrap: balance;
}
/* Labels and inputs stay left-aligned inside the centred column — centred
   form labels are hard to scan. */
#contact .field { text-align: left; }
#contact .form__send { justify-content: center; }
#contact .form__fallback { text-align: left; }

/* ------------------------------------------------------------- form ---- */
.form { max-width: 720px; margin-inline: auto; }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
@media (max-width: 560px) { .form__row { grid-template-columns: 1fr; } }

.field { display: grid; gap: .45rem; margin-bottom: 1.25rem; }
.field label {
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.field label span { color: var(--orange); }

.field input,
.field textarea {
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--paper);
  /* The section sits on --tint, so the default --rule border is nearly
     invisible against it. This is dark enough to clear 3:1 non-text contrast. */
  border: 1.5px solid #8b857e;
  border-radius: 4px;
  padding: .85rem 1rem;
  width: 100%;
  transition: border-color .2s, box-shadow .2s;
}
.field textarea { resize: vertical; min-height: 130px; line-height: 1.6; }
.field input::placeholder, .field textarea::placeholder { color: var(--muted); }
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--orange);
  box-shadow: 0 0 0 3px rgba(247, 95, 28, .16);
}

.form__send {
  display: flex; align-items: center; gap: 1.5rem;
  flex-wrap: wrap; margin-top: 1.75rem;
}
.form__note { margin: 0; font-size: .92rem; color: var(--muted); }
.form__note a { color: var(--orange); text-decoration: none; font-weight: 600; }
.form__note a:hover { text-decoration: underline; }

.form__fallback {
  margin: 1.5rem 0 0;
  padding: .9rem 1.1rem;
  border-left: 3px solid var(--orange);
  background: rgba(247, 95, 28, .07);
  font-size: .95rem;
  color: var(--ink);
}
.form__fallback a { color: var(--orange); font-weight: 600; }

/* Honeypot — hidden from people, visible to bots. Clipped in place rather
   than parked at left:-9999px, same reason as .skip above. Deliberately not
   display:none or hidden, which some bots check for. */
.hp {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

/* ------------------------------------------------- legal pages ------- */
/* Privacy policy and terms. Long-form reading, so the measure is capped near
   68 characters — past roughly 75 the eye loses its place returning to the
   next line. */
.legal {
  max-width: 68ch;
  margin-inline: auto;
}
.legal h1 {
  font-size: clamp(2.1rem, 5vw, 3.1rem);
  margin: 0 0 .6rem;
}
.legal__updated {
  margin: 0 0 2.5rem;
  color: var(--muted);
  font-size: .95rem;
  letter-spacing: .02em;
}
.legal h2 {
  font-size: clamp(1.3rem, 2.4vw, 1.6rem);
  margin: 2.6rem 0 .8rem;
}
.legal p,
.legal li {
  color: var(--body);
  font-size: 1.05rem;
  line-height: 1.75;
}
.legal p { margin: 0 0 1.1rem; }
.legal ul { margin: 0 0 1.1rem; padding-left: 1.25rem; }
.legal li { margin-bottom: .5rem; }
.legal a { color: var(--orange); font-weight: 600; }
.legal a:hover { text-decoration: none; }
.legal strong { color: var(--ink); }
.legal__note {
  margin-top: 2.5rem;
  padding: 1.1rem 1.25rem;
  background: var(--tint);
  border-left: 3px solid var(--orange);
  border-radius: 4px;
  font-size: 1rem;
}

.footer__legal a { color: rgba(255, 255, 255, .62); }
.footer__legal a:hover { color: var(--orange); }

/* ------------------------------------------------- form consent ------- */
/* The checkbox is the record that consent was given, so it is a real required
   input rather than a styled div — the browser blocks submission without it
   and FormSubmit includes it in the email. */
.field--consent { margin-top: .35rem; }
.field--consent .consent {
  display: flex;
  align-items: flex-start;
  gap: .65rem;
  text-align: left;
  cursor: pointer;
  /* This is a <label>, so it inherits `.field label` — uppercase, bold, wide
     tracking, orange via `.field label span`. That treatment is right for a
     field name above an input and wrong for a sentence of consent text, so all
     of it is reset here. Scoped through .field--consent to outrank it. */
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}
.field--consent .consent input[type="checkbox"] {
  flex: none;
  width: 17px;
  height: 17px;
  margin: 0;
  /* Sits the box on the first line's optical centre rather than its top. */
  margin-top: .15em;
  accent-color: var(--orange);
  cursor: pointer;
}
.field--consent .consent > span {
  /* Deliberately below the 1rem of the inputs — supporting text, not a field. */
  font-size: .87rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.5;
  color: var(--body);
}
.field--consent .consent a {
  color: var(--orange);
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
}
/* The required marker keeps the accent the other fields use for it. */
.field--consent .consent span span { color: var(--orange); }

/* ------------------------------------------------------ unused legacy -- */
.contact__lines a {
  display: inline-block;
  font-size: clamp(1.4rem, 3vw, 2rem);
  font-weight: 700;
  letter-spacing: -.02em;
  text-decoration: none;
  margin-bottom: .6rem;
  word-break: break-word;
}
.contact__lines a:hover { text-decoration: underline; }
.contact__lines p { margin-top: 1.5rem; max-width: 46ch; }

/* ------------------------------------------------------------- footer -- */
/* Flat black. The concrete texture belongs to the About section — using it
   twice on one page turns it into wallpaper instead of an accent. */
.footer {
  background: #000;
  color: rgba(255, 255, 255, .72);
  padding-block: clamp(2.5rem, 5vw, 3.5rem) 2.5rem;
  font-size: .92rem;
}
/* Brand block plus three link columns that hug their own content, spread
   across the remaining width.

   Equal-width tracks looked wrong here: the columns started at even intervals,
   but the eye reads the space between the text, not between the tracks. The
   email address is ~220px wide and nearly touched the next heading, while the
   short Site links left a large void before Legal. Sizing each column to its
   content and letting space-between distribute the slack evens the gaps that
   are actually visible. */
.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 21rem) repeat(3, max-content);
  justify-content: space-between;
  gap: clamp(1.75rem, 4vw, 3.25rem);
}
/* Two-up: brand and Contact on the first row, Site and Legal on the second —
   an even 2x2 rather than a stray column hanging on its own. */
@media (max-width: 860px) { .footer__top { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .footer__top { grid-template-columns: 1fr; gap: 2rem; } }

/* Matches the navbar logo exactly — height:auto keeps the ratio, since the
   img's height attribute would otherwise squash it. */
.footer img {
  width: clamp(120px, 13vw, 150px);
  height: auto;
  margin-bottom: 1.15rem;
}
.footer__brand p {
  margin: 0;
  color: rgba(255, 255, 255, .6);
  max-width: 34ch;
  line-height: 1.6;
}

.footer__col h4 {
  margin: 0 0 .9rem;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--orange);
}
.footer__col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .55rem;
}
.footer a { color: rgba(255, 255, 255, .72); text-decoration: none; transition: color .2s; }
.footer a:hover { color: var(--orange); }

/* Contact rows: icon and label align on the first line even when the
   email wraps, so the text stays flush in a column. */
.footer__col a:has(svg) {
  display: inline-flex;
  align-items: flex-start;
  gap: .6rem;
}
.footer__col svg {
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: .15em;
  stroke: var(--orange);
  stroke-width: 1.9;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Footer links were 16–24px tall — under the ~44px both Apple and Google
   recommend, and these are the phone and email, the two things someone on a
   phone is most likely to reach for. Padding gives the hit area without
   moving the text, and the negative margin keeps the visual spacing as it
   was; the row gap absorbs the rest. */
@media (max-width: 760px) {
  .footer__col ul { gap: .15rem; }
  .footer__col li a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding-block: .35rem;
  }
  .footer__col a:has(svg) { align-items: center; }
  .footer__col a:has(svg) svg { margin-top: 0; }
}

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: .5rem 1.5rem;
  margin-top: clamp(2.25rem, 5vw, 3rem);
  padding-top: 1.4rem;
  border-top: 1px solid rgba(255, 255, 255, .14);
  font-size: .78rem;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .5);
}
